mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 23:44:11 +01:00
Compare commits
1300 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a37698a9de | ||
|
|
11ba459d1b | ||
|
|
215786d740 | ||
|
|
fe9ca2b9cc | ||
|
|
41a479b48d | ||
|
|
eb630efdf5 | ||
|
|
91b7130671 | ||
|
|
80be4b4371 | ||
|
|
21d7de7d8d | ||
|
|
4a431be0a9 | ||
|
|
b84ac80aaa | ||
|
|
b6d0843e55 | ||
|
|
396fc2cdcd | ||
|
|
11308dc76a | ||
|
|
7fddd6acdc | ||
|
|
10b7fa6014 | ||
|
|
cbe749af13 | ||
|
|
c5f3598f4e | ||
|
|
12e9e453d8 | ||
|
|
6e50a1c4be | ||
|
|
e3e416b467 | ||
|
|
fcb6a4069a | ||
|
|
02f83c4f52 | ||
|
|
452c6c3df6 | ||
|
|
2966efcfde | ||
|
|
1cf36a5d01 | ||
|
|
1973f7526e | ||
|
|
866dfe4531 | ||
|
|
f617cb556b | ||
|
|
a7000b2582 | ||
|
|
27332931d0 | ||
|
|
36e32b23e2 | ||
|
|
b9708c8d37 | ||
|
|
10683d943f | ||
|
|
9ad8c6a96d | ||
|
|
c74d9bbafb | ||
|
|
22df5d09fb | ||
|
|
931593a59e | ||
|
|
7092dc6f86 | ||
|
|
c3f80a5876 | ||
|
|
821eb4df8c | ||
|
|
e276587fdc | ||
|
|
a454a43111 | ||
|
|
045f58144e | ||
|
|
8ffea22f0e | ||
|
|
c630676792 | ||
|
|
0bbb586094 | ||
|
|
efa1f4ee43 | ||
|
|
e184eb6aae | ||
|
|
6e9fcb81f0 | ||
|
|
774ecb4003 | ||
|
|
c555d1274b | ||
|
|
261bc83811 | ||
|
|
2dbaf4dfa1 | ||
|
|
5df9f38391 | ||
|
|
93763c5932 | ||
|
|
242caff990 | ||
|
|
e97e9907c7 | ||
|
|
ac330b6665 | ||
|
|
a4a70a1287 | ||
|
|
dda8651ba2 | ||
|
|
83e2d48f4d | ||
|
|
a903711a7a | ||
|
|
9e17a611d2 | ||
|
|
1e11ed3041 | ||
|
|
0449470cdf | ||
|
|
21a5a2d4ef | ||
|
|
a848cb28f1 | ||
|
|
b7ae6b143e | ||
|
|
ba0c18eec1 | ||
|
|
61366b347d | ||
|
|
edab6643f6 | ||
|
|
ce36ef3aad | ||
|
|
fdb439f054 | ||
|
|
2229f3f015 | ||
|
|
18a5df1ce7 | ||
|
|
f5cb29fadd | ||
|
|
d929732fb6 | ||
|
|
6c36f3bc7c | ||
|
|
c6a59a5309 | ||
|
|
b02c30a525 | ||
|
|
46a647ae66 | ||
|
|
8943a67f85 | ||
|
|
f132d751f5 | ||
|
|
4ba8c9ff9e | ||
|
|
955ae6c392 | ||
|
|
ed33b327fb | ||
|
|
f8f7486be2 | ||
|
|
3215875e5f | ||
|
|
62da90418a | ||
|
|
7c620fae78 | ||
|
|
daef0c3a8f | ||
|
|
52b6d399a0 | ||
|
|
f210f63ec4 | ||
|
|
41694050ea | ||
|
|
3f612cfc90 | ||
|
|
272acdd8d3 | ||
|
|
5a4375cb71 | ||
|
|
5b3d7e2354 | ||
|
|
189cefac1b | ||
|
|
87d36914c4 | ||
|
|
72e14805b1 | ||
|
|
87e2f76793 | ||
|
|
8fbd53d72d | ||
|
|
586cc1f003 | ||
|
|
a77753cfef | ||
|
|
5e464ef3a2 | ||
|
|
0727c9774b | ||
|
|
ee43a365dc | ||
|
|
d5ba0d9ed5 | ||
|
|
7031a52a43 | ||
|
|
12af164dcc | ||
|
|
362cd72e87 | ||
|
|
cb19520b6b | ||
|
|
c74972488d | ||
|
|
b78e40153f | ||
|
|
f7212662b9 | ||
|
|
97c8e1f7a9 | ||
|
|
2afc6d1c62 | ||
|
|
708858da39 | ||
|
|
41dccb468e | ||
|
|
36cfe9e5c2 | ||
|
|
1c7fd57f2e | ||
|
|
f920851420 | ||
|
|
04b8fe3326 | ||
|
|
9fb4374efa | ||
|
|
885cabd6ef | ||
|
|
e00f9c2a83 | ||
|
|
fe24eda4b4 | ||
|
|
483d80b576 | ||
|
|
70a0a3c52e | ||
|
|
5b2f32c08a | ||
|
|
5bad1e1c88 | ||
|
|
2706ebf638 | ||
|
|
9fe3261424 | ||
|
|
9df087984f | ||
|
|
cbb9bcd93d | ||
|
|
ad8f7576e0 | ||
|
|
e205d85728 | ||
|
|
a01d5c2760 | ||
|
|
b57423386c | ||
|
|
22e525452a | ||
|
|
6c84074b02 | ||
|
|
6cc4a6e1be | ||
|
|
04e41ab676 | ||
|
|
3ad64d9823 | ||
|
|
8de7ff5470 | ||
|
|
0a44f34c2c | ||
|
|
4f900e36c1 | ||
|
|
571d90618e | ||
|
|
fe8436f2ad | ||
|
|
a4459901e8 | ||
|
|
bef8fd566f | ||
|
|
0f9994ac74 | ||
|
|
8691ccc013 | ||
|
|
bac7b50090 | ||
|
|
39ff1e318c | ||
|
|
1f8110573c | ||
|
|
7c128e0f6e | ||
|
|
11b50b4917 | ||
|
|
9d771be8b2 | ||
|
|
02315b8aa1 | ||
|
|
7fb3d133e3 | ||
|
|
65fb29a1d4 | ||
|
|
703a432f7b | ||
|
|
4d37942717 | ||
|
|
137067ea43 | ||
|
|
67e12dcc74 | ||
|
|
911c0d2c1b | ||
|
|
4b9648affa | ||
|
|
804afa65f2 | ||
|
|
c56dc6cade | ||
|
|
c4f7055e1a | ||
|
|
44b6dfab1d | ||
|
|
50e79b8c97 | ||
|
|
185825f83c | ||
|
|
c6be331f14 | ||
|
|
c0dd418992 | ||
|
|
c03d5167f6 | ||
|
|
56d625b7b9 | ||
|
|
e74b23f305 | ||
|
|
6e7d2abc9a | ||
|
|
3bebb9bf0f | ||
|
|
1063697e85 | ||
|
|
7bdad90564 | ||
|
|
1dccc54814 | ||
|
|
1c255213e1 | ||
|
|
cb2c172483 | ||
|
|
b43063a6d2 | ||
|
|
3974406f1b | ||
|
|
21aed2d2e1 | ||
|
|
820c257e96 | ||
|
|
56e5616080 | ||
|
|
6f7351ecc3 | ||
|
|
8f56032d49 | ||
|
|
1b6ca2ed14 | ||
|
|
d956682b9f | ||
|
|
76759f1847 | ||
|
|
d441595ee6 | ||
|
|
2be0250aee | ||
|
|
b2a3b10065 | ||
|
|
22b181a8f7 | ||
|
|
71d07be646 | ||
|
|
95e56e7148 | ||
|
|
ec471520f2 | ||
|
|
65b516d761 | ||
|
|
7d45d5e0ce | ||
|
|
d8e3966825 | ||
|
|
65409373eb | ||
|
|
50a1449a2a | ||
|
|
8150faaa40 | ||
|
|
019542ff10 | ||
|
|
20def0de02 | ||
|
|
4a30a875f0 | ||
|
|
61905f111b | ||
|
|
1832d72e51 | ||
|
|
4d63b9e463 | ||
|
|
7b54f51d75 | ||
|
|
eaf94bc10a | ||
|
|
9d6b5d347c | ||
|
|
e82a16146e | ||
|
|
9797021511 | ||
|
|
c27a9e8193 | ||
|
|
c5506dab5d | ||
|
|
d120109a78 | ||
|
|
b529f3d4cc | ||
|
|
ea11b76461 | ||
|
|
09c54d4fed | ||
|
|
c5f00c5363 | ||
|
|
256808c473 | ||
|
|
487d89d970 | ||
|
|
ed3665b8c5 | ||
|
|
ef7a9ff02e | ||
|
|
aa4416ac4e | ||
|
|
c734eec9e1 | ||
|
|
e1caf61a18 | ||
|
|
f7879256c1 | ||
|
|
1c86eba9d9 | ||
|
|
6a089aa1b7 | ||
|
|
9a3749c1ed | ||
|
|
d82b755557 | ||
|
|
5b83f2a554 | ||
|
|
1f2b01937b | ||
|
|
18bd0ae096 | ||
|
|
512f368cbf | ||
|
|
853d9ee87f | ||
|
|
e9440d0d4c | ||
|
|
381a988f43 | ||
|
|
5d6ec4ce56 | ||
|
|
182e644a33 | ||
|
|
c719fbf7fc | ||
|
|
9c3b053727 | ||
|
|
d32db977eb | ||
|
|
1eca74180c | ||
|
|
0210e090f2 | ||
|
|
baf413ee55 | ||
|
|
8e6c001bf3 | ||
|
|
127e940ed4 | ||
|
|
aa8072118d | ||
|
|
f07bbfa174 | ||
|
|
e3a2c5b05b | ||
|
|
3ba5e30a96 | ||
|
|
7bf49011a3 | ||
|
|
bd84dd9f2c | ||
|
|
c3fbdc907c | ||
|
|
f7817714a8 | ||
|
|
c485286114 | ||
|
|
bd8e44f835 | ||
|
|
eddf4226b7 | ||
|
|
804578e38d | ||
|
|
d464fe5d67 | ||
|
|
885428627f | ||
|
|
4c90a90131 | ||
|
|
ec2aadb7cf | ||
|
|
9d5ab75dbd | ||
|
|
d5b145e052 | ||
|
|
163f5dba8a | ||
|
|
e026ecf92f | ||
|
|
dcda5084d0 | ||
|
|
496441cae6 | ||
|
|
cf75937b1d | ||
|
|
d7fc003216 | ||
|
|
de54575e04 | ||
|
|
12093c311c | ||
|
|
5b9ca03fa6 | ||
|
|
bb820ab388 | ||
|
|
c68f56ecd4 | ||
|
|
910bae64e9 | ||
|
|
40258fb02a | ||
|
|
011ed65ea1 | ||
|
|
411d934e6a | ||
|
|
582de40960 | ||
|
|
79d7ac7c8e | ||
|
|
6d86bd516b | ||
|
|
f71bf1416c | ||
|
|
8a1a27ee19 | ||
|
|
dc30cb2e4a | ||
|
|
24e669c65b | ||
|
|
80e6ba2d96 | ||
|
|
beef4b2738 | ||
|
|
56f1369000 | ||
|
|
7bcde47081 | ||
|
|
35663281fa | ||
|
|
0b8f75f799 | ||
|
|
7309c046ae | ||
|
|
6dfd44b731 | ||
|
|
d6e7309c34 | ||
|
|
e15bad7d3b | ||
|
|
4450d6af2f | ||
|
|
efa7a4ee55 | ||
|
|
757130847f | ||
|
|
c562098ef7 | ||
|
|
42606873af | ||
|
|
df8b73999f | ||
|
|
234b0e6825 | ||
|
|
6ca9f8ad31 | ||
|
|
dbc0971b99 | ||
|
|
26127c8218 | ||
|
|
f4b8b4cae3 | ||
|
|
d641ff3ab7 | ||
|
|
a08904a936 | ||
|
|
c13158cdb5 | ||
|
|
70a8c50d47 | ||
|
|
991c87530f | ||
|
|
9d5156e0e0 | ||
|
|
ec3ac05a1f | ||
|
|
a20fe37e98 | ||
|
|
ee76eaedd6 | ||
|
|
b90b200cd1 | ||
|
|
873af8865c | ||
|
|
906ac14fa9 | ||
|
|
fcffe9d188 | ||
|
|
a84748a544 | ||
|
|
320c7646f0 | ||
|
|
f4f3c3bd37 | ||
|
|
2bb6acfa22 | ||
|
|
da5a8b0fd1 | ||
|
|
aa22956f87 | ||
|
|
8b300358e9 | ||
|
|
54c5edc5da | ||
|
|
5f08d98f66 | ||
|
|
4d45f8d012 | ||
|
|
b1c48929e4 | ||
|
|
612479b632 | ||
|
|
013dcdf93e | ||
|
|
d22d3945ee | ||
|
|
d4960080ea | ||
|
|
4bc3d0ce2d | ||
|
|
e1243532ba | ||
|
|
f41a80a309 | ||
|
|
b447418f07 | ||
|
|
95b523c2fa | ||
|
|
7dadd6e410 | ||
|
|
8ec75b9d45 | ||
|
|
e4b3086429 | ||
|
|
c56bda2f60 | ||
|
|
56566d83fd | ||
|
|
0e4dc43171 | ||
|
|
7154aa05a6 | ||
|
|
1e80d76383 | ||
|
|
0ca2e33e7c | ||
|
|
38b10b6c10 | ||
|
|
e9444d3055 | ||
|
|
7e884dc69f | ||
|
|
24c7ff4cfa | ||
|
|
456f8be6e5 | ||
|
|
dfab460478 | ||
|
|
04154fa40c | ||
|
|
6e9fab849c | ||
|
|
06555eb03e | ||
|
|
6b8d1b4b76 | ||
|
|
73d9ea42f0 | ||
|
|
06f648b714 | ||
|
|
155034092f | ||
|
|
11af11b3be | ||
|
|
3246c36984 | ||
|
|
c12a5cc98b | ||
|
|
18ee7b194d | ||
|
|
14c0733949 | ||
|
|
f3a2a24ee4 | ||
|
|
26ec1269a5 | ||
|
|
2811eb66c5 | ||
|
|
9b0ccb8943 | ||
|
|
e33bdab4e9 | ||
|
|
573b5fc879 | ||
|
|
678821d54d | ||
|
|
5772042dd3 | ||
|
|
7868a38137 | ||
|
|
7bccfef3bd | ||
|
|
736838474a | ||
|
|
d553fad58d | ||
|
|
9e66ef5460 | ||
|
|
9550ec6efd | ||
|
|
bc9e1b1d94 | ||
|
|
7672858d6b | ||
|
|
2a2a9ab8b7 | ||
|
|
d8354c6666 | ||
|
|
ba04725ee3 | ||
|
|
7664633f18 | ||
|
|
edcc211988 | ||
|
|
389e8f2de6 | ||
|
|
070ac49d1b | ||
|
|
40dbb2ce17 | ||
|
|
cd5dd04352 | ||
|
|
592792dd7a | ||
|
|
cfe892d35e | ||
|
|
e01f48303b | ||
|
|
f0c8b348c6 | ||
|
|
ac5d24a848 | ||
|
|
e8a37ff0af | ||
|
|
a60a8c0c4f | ||
|
|
e78f8c803e | ||
|
|
6ea0ba52d1 | ||
|
|
8b0d9670f9 | ||
|
|
440dd90316 | ||
|
|
5f86a60954 | ||
|
|
50e0ea5ec5 | ||
|
|
b566bead31 | ||
|
|
52731d7b0a | ||
|
|
465532014b | ||
|
|
7153ae9614 | ||
|
|
0feb0fe972 | ||
|
|
b3cdbfc71b | ||
|
|
3a32bd62ef | ||
|
|
c1adf880a4 | ||
|
|
b0332b6ef5 | ||
|
|
965e7b48df | ||
|
|
27f41baa9a | ||
|
|
43615450ad | ||
|
|
956b8958fb | ||
|
|
024459408a | ||
|
|
78ccc44014 | ||
|
|
85397c4e28 | ||
|
|
0253f7d069 | ||
|
|
ddcb709fd1 | ||
|
|
a7d11c6670 | ||
|
|
bbb4959f22 | ||
|
|
254b3fe9aa | ||
|
|
35c016482b | ||
|
|
62895eedb7 | ||
|
|
32809ae7d4 | ||
|
|
73e1e3422d | ||
|
|
2d9041c045 | ||
|
|
60d6bb79b3 | ||
|
|
6afb3a06ac | ||
|
|
3cdf22e9b2 | ||
|
|
b05b41c7cc | ||
|
|
114a340527 | ||
|
|
cfa5163590 | ||
|
|
53535dd82d | ||
|
|
34f17074ca | ||
|
|
157d404019 | ||
|
|
d1ef987dca | ||
|
|
fd8c7c99bd | ||
|
|
4295437b3e | ||
|
|
92a08a1865 | ||
|
|
fbd7abf4e2 | ||
|
|
306ec09118 | ||
|
|
a5e41b224f | ||
|
|
4abcf75b34 | ||
|
|
7131a505be | ||
|
|
9b42af0149 | ||
|
|
27b9748f86 | ||
|
|
1301aa5c35 | ||
|
|
1b80789288 | ||
|
|
ca0232ae7b | ||
|
|
2fb0ecc446 | ||
|
|
0c41db76e2 | ||
|
|
e120b149dc | ||
|
|
e33596960a | ||
|
|
d04fb645ec | ||
|
|
87c5ee67c0 | ||
|
|
a53a046351 | ||
|
|
afda182b4e | ||
|
|
d883e3e661 | ||
|
|
c9526130b7 | ||
|
|
d6e3c7d1b7 | ||
|
|
0fdf6bfbb2 | ||
|
|
e628c68f09 | ||
|
|
8f858c2ddf | ||
|
|
f8f6e201b9 | ||
|
|
52f56e1bb0 | ||
|
|
187f7e591e | ||
|
|
272e8eac4f | ||
|
|
d423d741b2 | ||
|
|
102b2d76f4 | ||
|
|
bb31cedcba | ||
|
|
bf02e04ae5 | ||
|
|
c66884be0a | ||
|
|
e7b94d3132 | ||
|
|
b219161011 | ||
|
|
fd7d30333f | ||
|
|
d734cdaf48 | ||
|
|
f60d0b10e0 | ||
|
|
a1c6e32e28 | ||
|
|
156cb03069 | ||
|
|
cdb75729cb | ||
|
|
d1a812f04c | ||
|
|
b190ba1268 | ||
|
|
87ed90a825 | ||
|
|
a03a4af1e6 | ||
|
|
e48e4e7139 | ||
|
|
6ef31b7983 | ||
|
|
d378658a62 | ||
|
|
ed02758940 | ||
|
|
b28c45c84c | ||
|
|
42af530c63 | ||
|
|
7c3de1e976 | ||
|
|
afdd43c5e3 | ||
|
|
5bc756716e | ||
|
|
6a6c069896 | ||
|
|
9499799f80 | ||
|
|
5632f9786c | ||
|
|
6f79e07e90 | ||
|
|
601f18bbab | ||
|
|
6f750cf584 | ||
|
|
3b7c92d022 | ||
|
|
af12b47c90 | ||
|
|
2d9140e56c | ||
|
|
0351ff30b4 | ||
|
|
6d13dac2c3 | ||
|
|
e2174b9ad4 | ||
|
|
2038785b09 | ||
|
|
0c8650d2e5 | ||
|
|
3e7edea7be | ||
|
|
b3d625b9fc | ||
|
|
d02fb69ca7 | ||
|
|
8cf4bc58a7 | ||
|
|
b8aed0f004 | ||
|
|
03a6473bd4 | ||
|
|
7d95c02b57 | ||
|
|
767507d195 | ||
|
|
1d96cbeb07 | ||
|
|
0bf33ec7e9 | ||
|
|
37196b42ed | ||
|
|
5f7453d031 | ||
|
|
88b7ef5345 | ||
|
|
213d591eb0 | ||
|
|
c04f73e86b | ||
|
|
2033c171f0 | ||
|
|
8ce708dade | ||
|
|
fa60322ff1 | ||
|
|
e9bcd170c0 | ||
|
|
118b5c8a7d | ||
|
|
b5bcfa8d90 | ||
|
|
e496ab06b2 | ||
|
|
d7c960e150 | ||
|
|
9de7b4ba35 | ||
|
|
bb1a18f187 | ||
|
|
68cdd6b8a9 | ||
|
|
34dab0c498 | ||
|
|
f9511aba17 | ||
|
|
d96015f2c1 | ||
|
|
e66d577f21 | ||
|
|
47653eeba7 | ||
|
|
a0463c85a1 | ||
|
|
519093dceb | ||
|
|
08c5d0e4c1 | ||
|
|
cae4526b3b | ||
|
|
79381d7fd2 | ||
|
|
d8c141b1c9 | ||
|
|
3b3f4044cb | ||
|
|
c0256428f9 | ||
|
|
85a5ddb980 | ||
|
|
06bc58f383 | ||
|
|
b739c00414 | ||
|
|
d65bd97956 | ||
|
|
b952f9da4a | ||
|
|
388b3257fc | ||
|
|
6318077278 | ||
|
|
9fee51bafb | ||
|
|
397ec9587b | ||
|
|
862d08f57d | ||
|
|
ebb541e4f5 | ||
|
|
e05d780bec | ||
|
|
84b383154d | ||
|
|
f458826643 | ||
|
|
5d808992e6 | ||
|
|
03f9a9fcac | ||
|
|
7ea9b5b2f3 | ||
|
|
b8fc24f093 | ||
|
|
bddba15403 | ||
|
|
894f1c4f28 | ||
|
|
19665d4ad9 | ||
|
|
18a8e86b2a | ||
|
|
3af724a941 | ||
|
|
5b378ee9dd | ||
|
|
d5889a90d4 | ||
|
|
81c8eb2830 | ||
|
|
507a073203 | ||
|
|
abe67d9e4e | ||
|
|
741f44e8b7 | ||
|
|
c715b9488a | ||
|
|
5107ef5119 | ||
|
|
ca28eeb596 | ||
|
|
f51eb96c69 | ||
|
|
b032299b05 | ||
|
|
5a2576bc29 | ||
|
|
3375629d06 | ||
|
|
37232bc222 | ||
|
|
d2f0deec9c | ||
|
|
5a25e44177 | ||
|
|
08d9d58894 | ||
|
|
0254a75c95 | ||
|
|
a7cfcefee2 | ||
|
|
f78c057b45 | ||
|
|
4bd3084403 | ||
|
|
9d817f0c77 | ||
|
|
68b8cc1d20 | ||
|
|
fb8b0f4f65 | ||
|
|
94d45fc77f | ||
|
|
5144f62da9 | ||
|
|
a58ba7fcc5 | ||
|
|
17bec06c98 | ||
|
|
d531ec846f | ||
|
|
07849922b8 | ||
|
|
63ba267da0 | ||
|
|
93526c8a44 | ||
|
|
c7c2f4a482 | ||
|
|
dc0e739667 | ||
|
|
e74b2e32c9 | ||
|
|
a2dd9b1d48 | ||
|
|
474fdc0473 | ||
|
|
e6ab3ac9f9 | ||
|
|
ae59780297 | ||
|
|
cbdafbf264 | ||
|
|
76fa4a3090 | ||
|
|
896c6b79db | ||
|
|
bb052f30d6 | ||
|
|
ebbff7f615 | ||
|
|
9d76ac96bc | ||
|
|
1926a40277 | ||
|
|
6cd108badf | ||
|
|
5993d74eec | ||
|
|
84b98a2265 | ||
|
|
0df071b4db | ||
|
|
bfd092b499 | ||
|
|
23f2ea5031 | ||
|
|
29165b8ef4 | ||
|
|
c862179465 | ||
|
|
7a371f8b26 | ||
|
|
7a7b968c1b | ||
|
|
9571404907 | ||
|
|
ae946f6821 | ||
|
|
6128625706 | ||
|
|
ea2a3c1980 | ||
|
|
9b6814aee9 | ||
|
|
6544659251 | ||
|
|
dcff39da25 | ||
|
|
94ba32af57 | ||
|
|
cc08613304 | ||
|
|
95941f4dc5 | ||
|
|
3f2e20fe44 | ||
|
|
67124a4104 | ||
|
|
174bcf56d3 | ||
|
|
d9fd3b47e1 | ||
|
|
89492f8904 | ||
|
|
42dc73964c | ||
|
|
449316257a | ||
|
|
3b621adcb2 | ||
|
|
8d9d4e67ca | ||
|
|
eb43a02bce | ||
|
|
7f034f60d6 | ||
|
|
c4cf10b6e6 | ||
|
|
b2a1404ce0 | ||
|
|
52a97db259 | ||
|
|
e5ccb4271e | ||
|
|
27a2614b7d | ||
|
|
5cc39848ff | ||
|
|
b9d719d636 | ||
|
|
3e6b3a2755 | ||
|
|
88167fb3ae | ||
|
|
b5685a9d76 | ||
|
|
4c652a87c0 | ||
|
|
1f8bd69aef | ||
|
|
71d9bb18e5 | ||
|
|
067b3364ee | ||
|
|
b2494ebaf7 | ||
|
|
f73ca10b6c | ||
|
|
fe23e099fe | ||
|
|
333411535e | ||
|
|
cc6272e84a | ||
|
|
c7857835c7 | ||
|
|
9bfaf10468 | ||
|
|
095d5c9442 | ||
|
|
4fa6f85c2e | ||
|
|
76a9978fc5 | ||
|
|
4b46b2776a | ||
|
|
907505ccf9 | ||
|
|
3e35dafefb | ||
|
|
11ee5126ef | ||
|
|
37bdb1ba2f | ||
|
|
a9fc1083c7 | ||
|
|
9be804fb35 | ||
|
|
38a466fc21 | ||
|
|
aa5ee45034 | ||
|
|
5b1e1d0d6a | ||
|
|
a818a09469 | ||
|
|
0d439a08fc | ||
|
|
9032f25d64 | ||
|
|
28efea7ac1 | ||
|
|
f2f0badc77 | ||
|
|
00e3d5c0d2 | ||
|
|
c08edc207c | ||
|
|
6477e2e1bb | ||
|
|
694da178c4 | ||
|
|
d80b890cd0 | ||
|
|
6b9c038b31 | ||
|
|
b071f920e9 | ||
|
|
3cd28d1559 | ||
|
|
72563d8ef1 | ||
|
|
375798a344 | ||
|
|
b401c65684 | ||
|
|
d7c78b3ce2 | ||
|
|
4a4c03a225 | ||
|
|
85b31701f4 | ||
|
|
28b3110895 | ||
|
|
011e6d895b | ||
|
|
0f7099acfa | ||
|
|
96296fe211 | ||
|
|
51a60e637c | ||
|
|
078f13fdb1 | ||
|
|
c210afd086 | ||
|
|
81d9071b01 | ||
|
|
94ca9c4df9 | ||
|
|
bff2ae319f | ||
|
|
5bd30381cf | ||
|
|
121a615ce3 | ||
|
|
5877b66c83 | ||
|
|
1ba86a91f9 | ||
|
|
e56847ee8d | ||
|
|
1fbbfd1063 | ||
|
|
1fed66fff3 | ||
|
|
c607a7e35d | ||
|
|
06d6968951 | ||
|
|
7ed8a9f638 | ||
|
|
52595138cd | ||
|
|
eca2b01307 | ||
|
|
df758679cc | ||
|
|
6b6300d117 | ||
|
|
b535e11f5a | ||
|
|
a4ad8d0a61 | ||
|
|
e66eb537d8 | ||
|
|
b8ef2e68ba | ||
|
|
30b10d3b6b | ||
|
|
f09347841c | ||
|
|
f87e8ca522 | ||
|
|
2871f64f68 | ||
|
|
cd1c5f5799 | ||
|
|
890fcac73f | ||
|
|
d7851ed090 | ||
|
|
678df3cc46 | ||
|
|
2f48b2e302 | ||
|
|
a816a6ff8d | ||
|
|
b189d2a39b | ||
|
|
5424682fdb | ||
|
|
ad3ce7c536 | ||
|
|
d4dd300e28 | ||
|
|
99fd6b97db | ||
|
|
684e9e3537 | ||
|
|
1bde863124 | ||
|
|
5c34e3d988 | ||
|
|
b7c4e084f3 | ||
|
|
36395ae355 | ||
|
|
f4881d11c7 | ||
|
|
bbde89e0f9 | ||
|
|
fb22107be8 | ||
|
|
5ada93b46c | ||
|
|
b798b43733 | ||
|
|
1669eb3759 | ||
|
|
071316c606 | ||
|
|
d8b5dd7bd2 | ||
|
|
868c1cface | ||
|
|
8e83baf72b | ||
|
|
54858c63f5 | ||
|
|
bc3d03c462 | ||
|
|
c94476b9a2 | ||
|
|
73812dc400 | ||
|
|
cfdc7eb74a | ||
|
|
a0ad331023 | ||
|
|
2561358f9d | ||
|
|
3fd7dae8f9 | ||
|
|
426a0933b1 | ||
|
|
2f8062d296 | ||
|
|
d18165ebe9 | ||
|
|
38796f9d0c | ||
|
|
79b887d189 | ||
|
|
8dc92e7ccf | ||
|
|
e04e5913de | ||
|
|
5c734cdabc | ||
|
|
f924e99f70 | ||
|
|
e179825896 | ||
|
|
94a561f0e4 | ||
|
|
56e14fc107 | ||
|
|
29f0b74824 | ||
|
|
de682d5530 | ||
|
|
571a3341da | ||
|
|
8edf7f2d60 | ||
|
|
5408545c07 | ||
|
|
d504fb209f | ||
|
|
ee53c3a71e | ||
|
|
bcf88d24f3 | ||
|
|
b2935139b4 | ||
|
|
635e7cfeec | ||
|
|
49b6c3bed7 | ||
|
|
3f7ab67506 | ||
|
|
df26833eb1 | ||
|
|
df1ebaebf9 | ||
|
|
26bd04857d | ||
|
|
4c4ed14af5 | ||
|
|
f0c5a1b382 | ||
|
|
59ebc49d46 | ||
|
|
bfde101f6b | ||
|
|
2a0dce848c | ||
|
|
d759fed5e4 | ||
|
|
f731abe4e8 | ||
|
|
74111212a3 | ||
|
|
f86c1a87f9 | ||
|
|
1f2493914f | ||
|
|
e3efa7dc3d | ||
|
|
6612782021 | ||
|
|
bdaabcea93 | ||
|
|
e6b6be2624 | ||
|
|
b1f1c10878 | ||
|
|
d5b0bb021f | ||
|
|
dd70275b41 | ||
|
|
6aa782bd8b | ||
|
|
029545703f | ||
|
|
8183674fc6 | ||
|
|
7391f64776 | ||
|
|
776385cdc9 | ||
|
|
17bafc037c | ||
|
|
e785352050 | ||
|
|
43e4408df1 | ||
|
|
78a68bb361 | ||
|
|
ec2a2d3505 | ||
|
|
2625477d35 | ||
|
|
a655dd639d | ||
|
|
3e61fd2452 | ||
|
|
c11753d91c | ||
|
|
5884e6b3cf | ||
|
|
46e4ba4518 | ||
|
|
a9c9e48cdb | ||
|
|
e32c1a4447 | ||
|
|
fb99c25594 | ||
|
|
6011aa2ac9 | ||
|
|
048c1ecf72 | ||
|
|
40360da454 | ||
|
|
0ce9ff4557 | ||
|
|
625bfbb6fe | ||
|
|
4290d94841 | ||
|
|
8ff2151448 | ||
|
|
610d69fb2e | ||
|
|
822308b3a4 | ||
|
|
c1d1e562ad | ||
|
|
379a0bd785 | ||
|
|
a477443c8d | ||
|
|
ed693c03ab | ||
|
|
48f6635917 | ||
|
|
d6707743a9 | ||
|
|
6f474686ad | ||
|
|
affd8ea8a6 | ||
|
|
abd2748a09 | ||
|
|
6a1c9b9f26 | ||
|
|
ac04aa3359 | ||
|
|
051d68c902 | ||
|
|
ce81f7abb6 | ||
|
|
03fbde3403 | ||
|
|
59ddab9f94 | ||
|
|
225cbb450e | ||
|
|
7cace73844 | ||
|
|
2562d33997 | ||
|
|
a1e5b32e49 | ||
|
|
0c5df83a95 | ||
|
|
cf79456ff3 | ||
|
|
a0a378a91a | ||
|
|
9cf42f6cc8 | ||
|
|
c022d12a0a | ||
|
|
3d72060bf5 | ||
|
|
43709576c0 | ||
|
|
8ca6610e75 | ||
|
|
b02d347541 | ||
|
|
a39789cf89 | ||
|
|
58534dca39 | ||
|
|
538e9c11e8 | ||
|
|
cba6e8d8a3 | ||
|
|
fd9fe85ef3 | ||
|
|
7ed3e10d0f | ||
|
|
fe98b850d1 | ||
|
|
b1494d0dd9 | ||
|
|
0655904683 | ||
|
|
bad5aff764 | ||
|
|
440f2639b5 | ||
|
|
5f2245595f | ||
|
|
03db4e7a14 | ||
|
|
04030e07e3 | ||
|
|
80832877eb | ||
|
|
aa63847502 | ||
|
|
4171c5c35f | ||
|
|
8e06a95502 | ||
|
|
1c84934777 | ||
|
|
0294f50339 | ||
|
|
33098943a1 | ||
|
|
b953a6ab3e | ||
|
|
7287cab6f4 | ||
|
|
df4cad3ff1 | ||
|
|
66b6206daf | ||
|
|
6f40459215 | ||
|
|
24430e630f | ||
|
|
3420db26a5 | ||
|
|
c27cafb02f | ||
|
|
09a2dbc185 | ||
|
|
015c5b7a96 | ||
|
|
4710ae6eb7 | ||
|
|
c6b98dd5c1 | ||
|
|
61d3a4e48f | ||
|
|
be4c1e2c1c | ||
|
|
6f499dd36e | ||
|
|
03661158f1 | ||
|
|
b9866008b2 | ||
|
|
2557d6e7d5 | ||
|
|
79582b55a4 | ||
|
|
46b5293867 | ||
|
|
54ca6ad3d9 | ||
|
|
2ab8161182 | ||
|
|
3c7f5db5e7 | ||
|
|
e72bc65df8 | ||
|
|
c039414adc | ||
|
|
d45cbb36f7 | ||
|
|
ba5dd94240 | ||
|
|
fe84cbd978 | ||
|
|
f8ddcc7b70 | ||
|
|
c0d7737872 | ||
|
|
97e6c53add | ||
|
|
68ac5d7300 | ||
|
|
e0eebc0b4e | ||
|
|
fb28de971c | ||
|
|
ae637c7c96 | ||
|
|
6bc24b6064 | ||
|
|
db60dfb707 | ||
|
|
ee2aa67959 | ||
|
|
eff42f5d3b | ||
|
|
3a1810bc42 | ||
|
|
1f1ab2b10c | ||
|
|
d0d9b1ce50 | ||
|
|
307145502c | ||
|
|
5c84703cf0 | ||
|
|
cb2745be24 | ||
|
|
b613b9b302 | ||
|
|
624f6bcfc5 | ||
|
|
ce2f1edaac | ||
|
|
140efb4240 | ||
|
|
e3847ac24b | ||
|
|
b583bd2edc | ||
|
|
c654a2067d | ||
|
|
3531d5c5b7 | ||
|
|
e31fa066fc | ||
|
|
bc476295cb | ||
|
|
98781fac6d | ||
|
|
efeee395d1 | ||
|
|
b17505f86e | ||
|
|
f89dd77dbc | ||
|
|
f8b6fb51c2 | ||
|
|
d80c2293dc | ||
|
|
3cecdeff1c | ||
|
|
3f819eaa19 | ||
|
|
01de060093 | ||
|
|
7c39a8baf1 | ||
|
|
ebcaaa089a | ||
|
|
121635d636 | ||
|
|
68885496dd | ||
|
|
9a56c3acfd | ||
|
|
a0259636b1 | ||
|
|
0844beca79 | ||
|
|
dbe3e94d5c | ||
|
|
9a43083b3b | ||
|
|
c6e472b98b | ||
|
|
0834e36b38 | ||
|
|
f7cf825975 | ||
|
|
189c03dfea | ||
|
|
990be7a105 | ||
|
|
813c80499c | ||
|
|
495cedc04f | ||
|
|
cbf2919dcd | ||
|
|
7cbdfaa5d4 | ||
|
|
374ce20d0c | ||
|
|
1dbc2051cc | ||
|
|
7e38d4be50 | ||
|
|
8451ffdfb4 | ||
|
|
c2b9716e28 | ||
|
|
b15621639b | ||
|
|
4ca998ce91 | ||
|
|
71ec3da4ac | ||
|
|
43b8522b85 | ||
|
|
8b820ce403 | ||
|
|
4301a5d39d | ||
|
|
51519a3659 | ||
|
|
2693e81bad | ||
|
|
eca746f558 | ||
|
|
01865ed7f9 | ||
|
|
14dfe04714 | ||
|
|
befa0b4429 | ||
|
|
68ff589f9c | ||
|
|
04e1f32860 | ||
|
|
636356f479 | ||
|
|
313ea72017 | ||
|
|
98b4a0178f | ||
|
|
602be73d0b | ||
|
|
9bfc9a0a76 | ||
|
|
7e6a040983 | ||
|
|
905d47cab5 | ||
|
|
2118a5da71 | ||
|
|
c2c0221535 | ||
|
|
ba0a9709f4 | ||
|
|
80121b89c3 | ||
|
|
3b48428897 | ||
|
|
018d6a98e9 | ||
|
|
522108dc69 | ||
|
|
5451ced894 | ||
|
|
944c24e18f | ||
|
|
248f8d6fd4 | ||
|
|
ebe467b77a | ||
|
|
e9b7ccd475 | ||
|
|
b213f2baea | ||
|
|
50970810d2 | ||
|
|
17fbc504e2 | ||
|
|
237980097d | ||
|
|
44b53e40a0 | ||
|
|
6c6ad0a45b | ||
|
|
623fa8ec63 | ||
|
|
02c79fd0a2 | ||
|
|
05bb797768 | ||
|
|
096cfdc529 | ||
|
|
5354b0b32b | ||
|
|
88ec528071 | ||
|
|
5584d42813 | ||
|
|
b696b140b2 | ||
|
|
1de6ac1765 | ||
|
|
d2d895fdf5 | ||
|
|
a8ad3004ea | ||
|
|
b707db9364 | ||
|
|
36c53249a0 | ||
|
|
f9ed88a084 | ||
|
|
294964b227 | ||
|
|
8a6fba1981 | ||
|
|
1fe0ce5640 | ||
|
|
7bbe907edc | ||
|
|
6f7d364826 | ||
|
|
ea2681e08c | ||
|
|
c66728e478 | ||
|
|
ba9c6bd8b3 | ||
|
|
b23b468c0f | ||
|
|
f5144c2bb1 | ||
|
|
ee95dd2480 | ||
|
|
c1805ce47f | ||
|
|
23556e3a43 | ||
|
|
e70e1de75e | ||
|
|
091b989715 | ||
|
|
b238283104 | ||
|
|
de2eed5187 | ||
|
|
8e046cafda | ||
|
|
f02df401ff | ||
|
|
28eba2512a | ||
|
|
e3931274ae | ||
|
|
a55245d203 | ||
|
|
7b0acbdad3 | ||
|
|
9cc583c47b | ||
|
|
fa2fd6dcdf | ||
|
|
9d242e1623 | ||
|
|
ec6a8537e9 | ||
|
|
402e95b9e2 | ||
|
|
2fcf50bb88 | ||
|
|
2e8c629195 | ||
|
|
9c5b3818eb | ||
|
|
c9bb27a3ff | ||
|
|
f96a2092e7 | ||
|
|
5dafb92dd3 | ||
|
|
63f0a0ef11 | ||
|
|
bb9236deae | ||
|
|
1a66a6491a | ||
|
|
1528d85f5f | ||
|
|
3773a88cc1 | ||
|
|
856a73fb87 | ||
|
|
d0bc9a0bb8 | ||
|
|
751b7c8560 | ||
|
|
4fccae157b | ||
|
|
85dc7c9452 | ||
|
|
1cf25e49ae | ||
|
|
ab6cd49fcb | ||
|
|
5411e60c81 | ||
|
|
15da430459 | ||
|
|
5adb0fd556 | ||
|
|
2876d7e08c | ||
|
|
59c3f565a3 | ||
|
|
86191b36d8 | ||
|
|
b737252076 | ||
|
|
e688d04511 | ||
|
|
76fd5c0b5f | ||
|
|
d944f386ce | ||
|
|
9f9fd8550f | ||
|
|
772954711a | ||
|
|
3644553886 | ||
|
|
c073611597 | ||
|
|
802d20d554 | ||
|
|
38c12104b3 | ||
|
|
399662ef99 | ||
|
|
3daf4c62db | ||
|
|
9937f62f9d | ||
|
|
cf17e197ce | ||
|
|
2f8b5e5eeb | ||
|
|
d22fb83443 | ||
|
|
af093ba86a | ||
|
|
020f47ab2c | ||
|
|
8f0095f751 | ||
|
|
b7fc2e4012 | ||
|
|
1dcf830141 | ||
|
|
a10cdaba74 | ||
|
|
435a177283 | ||
|
|
d22d657886 | ||
|
|
1da5618204 | ||
|
|
318c069f3d | ||
|
|
d0267f60ae | ||
|
|
ea3c7703c4 | ||
|
|
95e04178ea | ||
|
|
cf0792cd64 | ||
|
|
e41a8833e2 | ||
|
|
0b2ce4289d | ||
|
|
a13270ef91 | ||
|
|
64ca121f5b | ||
|
|
0a5708272d | ||
|
|
fec15ffe66 | ||
|
|
0b0340bf21 | ||
|
|
cf716ce2a3 | ||
|
|
5ebcb41224 | ||
|
|
2d9c0e16b9 | ||
|
|
20e0ab3d6e | ||
|
|
d94ccc441a | ||
|
|
fdff9e048b | ||
|
|
64e1d7e276 | ||
|
|
5f82c78dc7 | ||
|
|
b3a0c6119e | ||
|
|
342ac0444c | ||
|
|
d9469360fe | ||
|
|
0df420cd17 | ||
|
|
ab44522016 | ||
|
|
bc875653d2 | ||
|
|
ee41204d6c | ||
|
|
c4d1113bb8 | ||
|
|
6582569150 | ||
|
|
69d0ceaf5b | ||
|
|
60165d5216 | ||
|
|
76ca7dc9e8 | ||
|
|
fcc5342775 | ||
|
|
b4534f455a | ||
|
|
0d3203476c | ||
|
|
9fa6157c37 | ||
|
|
2ee674f11b | ||
|
|
820d7108e4 | ||
|
|
9a6a562ed2 | ||
|
|
f3f9e98aa4 | ||
|
|
fc375da128 | ||
|
|
172ceba464 | ||
|
|
ecae7ea983 | ||
|
|
a204a786c0 | ||
|
|
79a2ab8abd | ||
|
|
af13b42eab | ||
|
|
75721091f2 | ||
|
|
6e327e245b | ||
|
|
6bfead405d | ||
|
|
a4f5620076 | ||
|
|
134a427901 | ||
|
|
27c3facb2b | ||
|
|
fa9848d1be | ||
|
|
1afd7d2ae4 | ||
|
|
c622efe95c | ||
|
|
c0949421ad | ||
|
|
0e21edcc77 | ||
|
|
df85186407 | ||
|
|
403ecf7fba | ||
|
|
d143f0880b | ||
|
|
c185e1fc4f | ||
|
|
68c180f7eb | ||
|
|
c903fc2246 | ||
|
|
7d5898f302 | ||
|
|
a4bdd3aaf4 | ||
|
|
5bae9deecc | ||
|
|
ffbd666aca | ||
|
|
4ec3aeec92 | ||
|
|
c5a00a1bf1 | ||
|
|
1b1e91f0dc | ||
|
|
21fc272674 | ||
|
|
5716c11450 | ||
|
|
f7e77e9fa6 | ||
|
|
ad8c3db6b4 | ||
|
|
d5c3b8d8e2 | ||
|
|
6ac6aea29f | ||
|
|
7ce06c0797 | ||
|
|
53bf1f424a | ||
|
|
b793cded34 | ||
|
|
47ec6d4917 | ||
|
|
e586ba8d6e | ||
|
|
8a913a18cf | ||
|
|
a09e579451 | ||
|
|
79c5dc2ce2 | ||
|
|
95d7bc5319 | ||
|
|
d3f5d05063 | ||
|
|
b30a35ceb5 | ||
|
|
fac22c9729 | ||
|
|
44e329c38d | ||
|
|
e48ad8cb61 | ||
|
|
359dc73526 | ||
|
|
8169e81e9a | ||
|
|
f62f087bcc | ||
|
|
4eb0b3086d | ||
|
|
1c81650572 | ||
|
|
59e3367da8 | ||
|
|
2b4b0fed83 | ||
|
|
fe6ae6f2eb | ||
|
|
79d994acf7 | ||
|
|
9dbbb8e012 | ||
|
|
17fafbf85b | ||
|
|
f3a6455ed8 | ||
|
|
5336d9e965 | ||
|
|
97d11ba910 | ||
|
|
2fa68a3abc | ||
|
|
a88365ca49 | ||
|
|
f3053c39c2 | ||
|
|
c7ac39b86a | ||
|
|
88eece7188 | ||
|
|
b7d101cdfb | ||
|
|
56ade2b44c | ||
|
|
2a38eb757d | ||
|
|
954ba60611 | ||
|
|
f9a984f3e4 | ||
|
|
8fd9acb2ce | ||
|
|
915a88afd4 | ||
|
|
c755b66d8c | ||
|
|
2f8dc0fa0f | ||
|
|
a34747f893 | ||
|
|
4dd6c813b1 | ||
|
|
8e96094977 | ||
|
|
dd41ebf861 | ||
|
|
f732df751c | ||
|
|
acfab8fb63 | ||
|
|
23193153c6 | ||
|
|
a908dcd752 | ||
|
|
b8a80cb267 | ||
|
|
ad1412c7e2 | ||
|
|
4bf51515be | ||
|
|
05f97dd75f | ||
|
|
90cab29a3c | ||
|
|
445b488603 | ||
|
|
7141e0f4b0 | ||
|
|
ffc756e4a4 | ||
|
|
8bed267feb | ||
|
|
13933e488c | ||
|
|
d48f76e965 | ||
|
|
a025c95054 | ||
|
|
b2fa9c468f | ||
|
|
8feef7fd8a | ||
|
|
8330f3d498 | ||
|
|
88e7bd225c | ||
|
|
1b0f818c4b | ||
|
|
32dc1225ca | ||
|
|
2b35cf5047 | ||
|
|
df66e28545 | ||
|
|
0ab060c958 | ||
|
|
dfceef4ca6 | ||
|
|
49f82e6377 | ||
|
|
c5957f1146 | ||
|
|
b357e7d7d6 | ||
|
|
e7342b0eb8 | ||
|
|
f33f4e3406 | ||
|
|
3d57c720e0 | ||
|
|
ef5d156b98 | ||
|
|
8722447f2f | ||
|
|
e6047dcbf5 | ||
|
|
df1d10f1cb | ||
|
|
d1dd60f928 | ||
|
|
7bea59fea1 | ||
|
|
17703ce572 | ||
|
|
6d556249aa | ||
|
|
16b5db448b | ||
|
|
8ef7f073b3 | ||
|
|
22c647e3f8 | ||
|
|
bf94dfa894 | ||
|
|
43d3cfefb5 | ||
|
|
e0855093ea | ||
|
|
c147062aaa | ||
|
|
480c2fab04 | ||
|
|
53fb619da1 | ||
|
|
c0a7bbaa72 | ||
|
|
12b27778f5 | ||
|
|
babcd6a92a | ||
|
|
d80d24c348 | ||
|
|
2c78a91a00 | ||
|
|
109e5dfe2c | ||
|
|
93ff327b54 | ||
|
|
359c188089 | ||
|
|
6682eafb4d |
122
.gitignore
vendored
Normal file
122
.gitignore
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
|
||||
/toolkit/
|
||||
/conf/*
|
||||
/env-*/*
|
||||
|
||||
# composer reserver directory, from sources, populate/update using "composer install"
|
||||
vendor/*
|
||||
test/vendor/*
|
||||
|
||||
# all datas but listing prevention
|
||||
data/*
|
||||
!data/.htaccess
|
||||
!data/index.php
|
||||
!data/web.config
|
||||
|
||||
# iTop extensions
|
||||
extensions/*
|
||||
!extensions/readme.txt
|
||||
|
||||
# all logs but listing prevention
|
||||
log/*
|
||||
!log/.htaccess
|
||||
!log/index.php
|
||||
!log/web.config
|
||||
|
||||
|
||||
# Jetbrains
|
||||
.idea/**
|
||||
!.idea/encodings.xml
|
||||
!.idea/codeStyles
|
||||
!.idea/codeStyles/*
|
||||
!.idea/inspectionProfiles
|
||||
!.idea/inspectionProfiles/*
|
||||
|
||||
|
||||
|
||||
# CMake
|
||||
cmake-build-*/
|
||||
|
||||
# Mongo Explorer plugin
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Cursive Clojure plugin
|
||||
.idea/replstate.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Editor-based Rest Client
|
||||
.idea/httpRequests
|
||||
### Eclipse template
|
||||
|
||||
.metadata
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
.recommenders
|
||||
.project
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# PyDev specific (Python IDE for Eclipse)
|
||||
*.pydevproject
|
||||
|
||||
# CDT-specific (C/C++ Development Tooling)
|
||||
.cproject
|
||||
|
||||
# CDT- autotools
|
||||
.autotools
|
||||
|
||||
# Java annotation processor (APT)
|
||||
.factorypath
|
||||
|
||||
# PDT-specific (PHP Development Tools)
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# Tern plugin
|
||||
.tern-project
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
# STS (Spring Tool Suite)
|
||||
.springBeans
|
||||
|
||||
# Code Recommenders
|
||||
.recommenders/
|
||||
|
||||
# Annotation Processing
|
||||
.apt_generated/
|
||||
|
||||
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||
.cache-main
|
||||
.scala_dependencies
|
||||
.worksheet
|
||||
45
.idea/codeStyles/Project.xml
generated
Normal file
45
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,45 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<PHPCodeStyleSettings>
|
||||
<option name="CONCAT_SPACES" value="false" />
|
||||
<option name="PHPDOC_BLANK_LINE_BEFORE_TAGS" value="true" />
|
||||
<option name="PHPDOC_BLANK_LINES_AROUND_PARAMETERS" value="true" />
|
||||
<option name="PHPDOC_WRAP_LONG_LINES" value="true" />
|
||||
<option name="LOWER_CASE_BOOLEAN_CONST" value="true" />
|
||||
<option name="LOWER_CASE_NULL_CONST" value="true" />
|
||||
<option name="BLANK_LINES_BEFORE_RETURN_STATEMENT" value="1" />
|
||||
<option name="KEEP_RPAREN_AND_LBRACE_ON_ONE_LINE" value="true" />
|
||||
<option name="PHPDOC_USE_FQCN" value="true" />
|
||||
</PHPCodeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="BRACE_STYLE" value="2" />
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="PHP">
|
||||
<option name="BLANK_LINES_AFTER_PACKAGE" value="1" />
|
||||
<option name="BRACE_STYLE" value="2" />
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
|
||||
<option name="CALL_PARAMETERS_WRAP" value="1" />
|
||||
<option name="METHOD_PARAMETERS_WRAP" value="1" />
|
||||
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" />
|
||||
<option name="ARRAY_INITIALIZER_WRAP" value="5" />
|
||||
<option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true" />
|
||||
<option name="ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE" value="true" />
|
||||
<option name="IF_BRACE_FORCE" value="3" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="3" />
|
||||
<option name="WHILE_BRACE_FORCE" value="3" />
|
||||
<option name="FOR_BRACE_FORCE" value="3" />
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Combodo" />
|
||||
</state>
|
||||
</component>
|
||||
6
.idea/encodings.xml
generated
Normal file
6
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="PROJECT" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
154
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
154
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,154 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="BladeControlDirectives" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CheckEmptyScriptTag" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CheckImageSize" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CheckNodeTest" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CheckTagEmptyBody" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CheckValidXmlInScriptTagBody" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CoffeeScriptArgumentsOutsideFunction" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CoffeeScriptFunctionSignatures" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CoffeeScriptInfiniteLoop" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CoffeeScriptLiteralNotFunction" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CoffeeScriptSillyAssignment" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CoffeeScriptSwitchStatementWithNoDefaultBranch" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CoffeeScriptUnusedLocalSymbols" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="ComposeFileStructure" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssFloatPxLength" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidAtRule" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidCharsetRule" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidElement" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidFunction" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidHtmlTagReference" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidImport" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidMediaFeature" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidPropertyValue" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssInvalidPseudoSelector" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssMissingComma" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssNegativeValue" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssNoGenericFontName" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssOptimizeSimilarProperties" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssOverwrittenProperties" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssRedundantUnit" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssUnitlessNumber" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CssUnknownProperty" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="myCustomPropertiesEnabled" value="false" />
|
||||
<option name="myIgnoreVendorSpecificProperties" value="false" />
|
||||
<option name="myCustomPropertiesList">
|
||||
<value>
|
||||
<list size="0" />
|
||||
</value>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="CssUnknownTarget" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssUnresolvedClass" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssUnresolvedCustomProperty" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssUnresolvedCustomPropertySet" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CssUnusedSymbol" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CucumberExamplesColon" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CucumberMissedExamples" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="CucumberTableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="CucumberUndefinedStep" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="DockerFileArgumentCount" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="DuplicateKeyInSection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="DuplicateSectionInFile" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="EmptyEventHandler" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="FileHeaderInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="GherkinBrokenTableInspection" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="GherkinMisplacedBackground" enabled="false" level="ERROR" enabled_by_default="false" />
|
||||
<inspection_tool class="HamlNestedTagContent" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HardwiredNamespacePrefix" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlDeprecatedTag" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlExtraClosingTag" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlFormInputWithoutLabel" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlMissingClosingTag" enabled="false" level="INFORMATION" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlPresentationalElement" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlUnknownAnchorTarget" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlUnknownAttribute" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="myValues">
|
||||
<value>
|
||||
<list size="0" />
|
||||
</value>
|
||||
</option>
|
||||
<option name="myCustomValuesEnabled" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="HtmlUnknownBooleanAttribute" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="HtmlUnknownTag" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="myValues">
|
||||
<value>
|
||||
<list size="6">
|
||||
<item index="0" class="java.lang.String" itemvalue="nobr" />
|
||||
<item index="1" class="java.lang.String" itemvalue="noembed" />
|
||||
<item index="2" class="java.lang.String" itemvalue="comment" />
|
||||
<item index="3" class="java.lang.String" itemvalue="noscript" />
|
||||
<item index="4" class="java.lang.String" itemvalue="embed" />
|
||||
<item index="5" class="java.lang.String" itemvalue="script" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myCustomValuesEnabled" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="HtmlUnknownTarget" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="ImplicitTypeConversion" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="BITS" value="1720" />
|
||||
<option name="FLAG_EXPLICIT_CONVERSION" value="true" />
|
||||
<option name="IGNORE_NODESET_TO_BOOLEAN_VIA_STRING" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="IndexZeroUsage" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="JSLastCommaInArrayLiteral" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="JSLastCommaInObjectLiteral" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="JSUnresolvedFunction" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="JSUnresolvedVariable" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="LossyEncoding" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="MissingSinceTagDocInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="MysqlParsingInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="NonAsciiCharacters" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpDocMissingReturnTagInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpDocMissingThrowsInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpIncludeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpRedundantClosingTagInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUndefinedClassConstantInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUndefinedClassInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUndefinedConstantInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUndefinedFieldInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUndefinedFunctionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUndefinedGotoLabelInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUndefinedMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUndefinedNamespaceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PhpUnusedParameterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="RedundantTypeConversion" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="CHECK_ANY" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="RequiredAttributes" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="myAdditionalRequiredHtmlAttributes" value="" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="SqlAddNotNullColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlAmbiguousColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlAutoIncrementDuplicateInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlCheckUsingColumnsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlConstantConditionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlDeprecateTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlDerivedTableAliasInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlDialectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlDropIndexedColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlIdentifierInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlInsertValuesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlNullComparisonInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlPostgresqlSelectFromProcedureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlResolveInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlShouldBeInGroupByInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlSignatureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlStorageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SqlUnusedVariableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="TaskProblemsInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="TypeScriptPreferShortImport" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="XmlDefaultAttributeValue" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="XmlUnboundNsPrefix" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="XmlUnusedNamespaceDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="XsltUnusedDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="XsltVariableShadowing" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
</profile>
|
||||
</component>
|
||||
0
.jenkins/bin/archive/gather_external_files.sh
Executable file
0
.jenkins/bin/archive/gather_external_files.sh
Executable file
14
.jenkins/bin/init/append_files.sh
Executable file
14
.jenkins/bin/init/append_files.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
# create target dirs
|
||||
mkdir -p var
|
||||
mkdir -p toolkit
|
||||
|
||||
# cleanup target dirs
|
||||
rm -rf toolkit/*
|
||||
|
||||
# fill target dirs
|
||||
curl http://www.combodo.com/documentation/iTopDataModelToolkit-2.3.zip | tar xvz --directory toolkit
|
||||
cp -r .jenkins/configuration/default-environment/unattended_install/* toolkit
|
||||
11
.jenkins/bin/init/composer_install.sh
Executable file
11
.jenkins/bin/init/composer_install.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
# on the root dir
|
||||
composer install
|
||||
|
||||
|
||||
# under the test dir
|
||||
cd test
|
||||
composer install
|
||||
13
.jenkins/bin/init/debug.sh
Executable file
13
.jenkins/bin/init/debug.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
whoami
|
||||
pwd
|
||||
ls
|
||||
|
||||
echo "$BRANCH_NAME:${BRANCH_NAME}"
|
||||
|
||||
echo "printenv :"
|
||||
printenv
|
||||
|
||||
8
.jenkins/bin/tests/phpunit.sh
Executable file
8
.jenkins/bin/tests/phpunit.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -x
|
||||
|
||||
cd test
|
||||
|
||||
export DEBUG_UNIT_TEST="0"
|
||||
|
||||
php vendor/bin/phpunit --log-junit ../var/test/phpunit-log.junit.xml --teamcity
|
||||
6
.jenkins/bin/unattended_install/default_env.sh
Executable file
6
.jenkins/bin/unattended_install/default_env.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -x
|
||||
|
||||
cd toolkit
|
||||
php unattended_install.php default-params.xml
|
||||
@@ -0,0 +1,285 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
*
|
||||
* Configuration file, generated by the iTop configuration wizard
|
||||
*
|
||||
* The file is used in MetaModel::LoadConfig() which does all the necessary initialization job
|
||||
*
|
||||
*/
|
||||
$MySettings = array(
|
||||
|
||||
// access_message: Message displayed to the users when there is any access restriction
|
||||
// default: 'iTop is temporarily frozen, please wait... (the admin team)'
|
||||
'access_message' => 'iTop is temporarily frozen, please wait... (the admin team)',
|
||||
|
||||
// access_mode: Access mode: ACCESS_READONLY = 0, ACCESS_ADMIN_WRITE = 2, ACCESS_FULL = 3
|
||||
// default: 3
|
||||
'access_mode' => 3,
|
||||
|
||||
'allowed_login_types' => 'form|basic|external',
|
||||
|
||||
// apc_cache.enabled: If set, the APC cache is allowed (the PHP extension must also be active)
|
||||
// default: true
|
||||
'apc_cache.enabled' => true,
|
||||
|
||||
// apc_cache.query_ttl: Time to live set in APC for the prepared queries (seconds - 0 means no timeout)
|
||||
// default: 3600
|
||||
'apc_cache.query_ttl' => 3600,
|
||||
|
||||
// app_root_url: Root URL used for navigating within the application, or from an email to the application (you can put $SERVER_NAME$ as a placeholder for the server's name)
|
||||
// default: ''
|
||||
'app_root_url' => 'http://127.0.0.1/itop/svn/trunk/',
|
||||
|
||||
// buttons_position: Position of the forms buttons: bottom | top | both
|
||||
// default: 'both'
|
||||
'buttons_position' => 'both',
|
||||
|
||||
// cas_include_path: The path where to find the phpCAS library
|
||||
// default: '/usr/share/php'
|
||||
'cas_include_path' => '/usr/share/php',
|
||||
|
||||
// cron_max_execution_time: Duration (seconds) of the page cron.php, must be shorter than php setting max_execution_time and shorter than the web server response timeout
|
||||
// default: 600
|
||||
'cron_max_execution_time' => 600,
|
||||
|
||||
// csv_file_default_charset: Character set used by default for downloading and uploading data as a CSV file. Warning: it is case sensitive (uppercase is preferable).
|
||||
// default: 'ISO-8859-1'
|
||||
'csv_file_default_charset' => 'ISO-8859-1',
|
||||
|
||||
'csv_import_charsets' => array (
|
||||
),
|
||||
|
||||
// csv_import_history_display: Display the history tab in the import wizard
|
||||
// default: false
|
||||
'csv_import_history_display' => false,
|
||||
|
||||
// date_and_time_format: Format for date and time display (per language)
|
||||
// default: array (
|
||||
// 'default' =>
|
||||
// array (
|
||||
// 'date' => 'Y-m-d',
|
||||
// 'time' => 'H:i:s',
|
||||
// 'date_time' => '$date $time',
|
||||
// ),
|
||||
// )
|
||||
'date_and_time_format' => array (
|
||||
'default' =>
|
||||
array (
|
||||
'date' => 'Y-m-d',
|
||||
'time' => 'H:i:s',
|
||||
'date_time' => '$date $time',
|
||||
),
|
||||
'FR FR' =>
|
||||
array (
|
||||
'date' => 'd/m/Y',
|
||||
'time' => 'H:i:s',
|
||||
'date_time' => '$date $time',
|
||||
),
|
||||
),
|
||||
|
||||
'db_host' => '',
|
||||
|
||||
'db_name' => 'itop_ci_main',
|
||||
|
||||
'db_pwd' => 'c8mb0do',
|
||||
|
||||
'db_subname' => '',
|
||||
|
||||
'db_user' => 'root',
|
||||
|
||||
// deadline_format: The format used for displaying "deadline" attributes: any string with the following placeholders: $date$, $difference$
|
||||
// default: '$difference$'
|
||||
'deadline_format' => '$difference$',
|
||||
|
||||
'default_language' => 'EN US',
|
||||
|
||||
// disable_attachments_download_legacy_portal: Disable attachments download from legacy portal
|
||||
// default: true
|
||||
'disable_attachments_download_legacy_portal' => true,
|
||||
|
||||
// draft_attachments_lifetime: Lifetime (in seconds) of drafts' attachments and inline images: after this duration, the garbage collector will delete them.
|
||||
// default: 3600
|
||||
'draft_attachments_lifetime' => 3600,
|
||||
|
||||
// email_asynchronous: If set, the emails are sent off line, which requires cron.php to be activated. Exception: some features like the email test utility will force the serialized mode
|
||||
// default: false
|
||||
'email_asynchronous' => false,
|
||||
|
||||
// email_default_sender_address: Default address provided in the email from header field.
|
||||
// default: ''
|
||||
'email_default_sender_address' => '',
|
||||
|
||||
// email_default_sender_label: Default label provided in the email from header field.
|
||||
// default: ''
|
||||
'email_default_sender_label' => '',
|
||||
|
||||
// email_transport: Mean to send emails: PHPMail (uses the function mail()) or SMTP (implements the client protocole)
|
||||
// default: 'PHPMail'
|
||||
'email_transport' => 'SMTP',
|
||||
|
||||
// email_transport_smtp.host: host name or IP address (optional)
|
||||
// default: 'localhost'
|
||||
'email_transport_smtp.host' => 'smtp.combodo.com',
|
||||
|
||||
// email_transport_smtp.password: Authentication password (optional)
|
||||
// default: ''
|
||||
'email_transport_smtp.password' => '++combodo++',
|
||||
|
||||
// email_transport_smtp.port: port number (optional)
|
||||
// default: 25
|
||||
'email_transport_smtp.port' => 25,
|
||||
|
||||
// email_transport_smtp.username: Authentication user (optional)
|
||||
// default: ''
|
||||
'email_transport_smtp.username' => 'test2@combodo.com',
|
||||
|
||||
// email_validation_pattern: Regular expression to validate/detect the format of an eMail address
|
||||
// default: '[a-zA-Z0-9._&\'-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9-]{2,}'
|
||||
'email_validation_pattern' => '[a-zA-Z0-9._&\'-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z0-9-]{2,}',
|
||||
|
||||
'encryption_key' => '@iT0pEncr1pti0n!',
|
||||
|
||||
'ext_auth_variable' => '$_SERVER[\'REMOTE_USER\']',
|
||||
|
||||
'fast_reload_interval' => '60',
|
||||
|
||||
// graphviz_path: Path to the Graphviz "dot" executable for graphing objects lifecycle
|
||||
// default: '/usr/bin/dot'
|
||||
'graphviz_path' => '/usr/bin/dot',
|
||||
|
||||
// inline_image_max_display_width: The maximum width (in pixels) when displaying images inside an HTML formatted attribute. Images will be displayed using this this maximum width.
|
||||
// default: '250'
|
||||
'inline_image_max_display_width' => 250,
|
||||
|
||||
// inline_image_max_storage_width: The maximum width (in pixels) when uploading images to be used inside an HTML formatted attribute. Images larger than the given size will be downsampled before storing them in the database.
|
||||
// default: '1600'
|
||||
'inline_image_max_storage_width' => 1600,
|
||||
|
||||
// link_set_attribute_qualifier: Link set from string: attribute qualifier (encloses both the attcode and the value)
|
||||
// default: '\''
|
||||
'link_set_attribute_qualifier' => '\'',
|
||||
|
||||
// link_set_attribute_separator: Link set from string: attribute separator
|
||||
// default: ';'
|
||||
'link_set_attribute_separator' => ';',
|
||||
|
||||
// link_set_item_separator: Link set from string: line separator
|
||||
// default: '|'
|
||||
'link_set_item_separator' => '|',
|
||||
|
||||
// link_set_value_separator: Link set from string: value separator (between the attcode and the value itself
|
||||
// default: ':'
|
||||
'link_set_value_separator' => ':',
|
||||
|
||||
'log_global' => true,
|
||||
|
||||
'log_issue' => true,
|
||||
|
||||
'log_notification' => true,
|
||||
|
||||
'log_web_service' => true,
|
||||
|
||||
// max_combo_length: The maximum number of elements in a drop-down list. If more then an autocomplete will be used
|
||||
// default: 50
|
||||
'max_combo_length' => 50,
|
||||
|
||||
'max_display_limit' => '15',
|
||||
|
||||
// max_linkset_output: Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.
|
||||
// default: 100
|
||||
'max_linkset_output' => 100,
|
||||
|
||||
'min_display_limit' => '10',
|
||||
|
||||
// online_help: Hyperlink to the online-help web page
|
||||
// default: 'http://www.combodo.com/itop-help'
|
||||
'online_help' => 'http://www.combodo.com/itop-help',
|
||||
|
||||
// php_path: Path to the php executable in CLI mode
|
||||
// default: 'php'
|
||||
'php_path' => 'php',
|
||||
|
||||
// portal_tickets: CSV list of classes supported in the portal
|
||||
// default: 'UserRequest'
|
||||
'portal_tickets' => 'UserRequest',
|
||||
|
||||
'query_cache_enabled' => true,
|
||||
|
||||
// search_manual_submit: Force manual submit of search requests (class => true)
|
||||
// default: false
|
||||
'search_manual_submit' => array (
|
||||
'Person' => true,
|
||||
),
|
||||
|
||||
'secure_connection_required' => false,
|
||||
|
||||
// session_name: The name of the cookie used to store the PHP session id
|
||||
// default: 'iTop'
|
||||
'session_name' => 'iTop',
|
||||
|
||||
// shortcut_actions: Actions that are available as direct buttons next to the "Actions" menu
|
||||
// default: 'UI:Menu:Modify,UI:Menu:New'
|
||||
'shortcut_actions' => 'UI:Menu:Modify,UI:Menu:New',
|
||||
|
||||
// source_dir: Source directory for the datamodel files. (which gets compiled to env-production).
|
||||
// default: ''
|
||||
'source_dir' => 'datamodels/2.x/',
|
||||
|
||||
'standard_reload_interval' => '300',
|
||||
|
||||
// synchro_trace: Synchronization details: none, display, save (includes 'display')
|
||||
// default: 'none'
|
||||
'synchro_trace' => 'none',
|
||||
|
||||
// timezone: Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitely configured in PHP
|
||||
// default: 'Europe/Paris'
|
||||
'timezone' => 'Europe/Paris',
|
||||
|
||||
// tracking_level_linked_set_default: Default tracking level if not explicitely set at the attribute level, for AttributeLinkedSet (defaults to NONE in case of a fresh install, LIST otherwise - this to preserve backward compatibility while upgrading from a version older than 2.0.3 - see TRAC #936)
|
||||
// default: 1
|
||||
'tracking_level_linked_set_default' => 0,
|
||||
|
||||
// url_validation_pattern: Regular expression to validate/detect the format of an URL (URL attributes and Wiki formatting for Text attributes)
|
||||
// default: '(https?|ftp)\\://([a-zA-Z0-9+!*(),;?&=\\$_.-]+(\\:[a-zA-Z0-9+!*(),;?&=\\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\\:[0-9]{2,5})?(/([a-zA-Z0-9%+\\$_-]\\.?)+)*/?(\\?[a-zA-Z+&\\$_.-][a-zA-Z0-9;:[\\]@&%=+/\\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\\$_.-]*)?'
|
||||
'url_validation_pattern' => '(https?|ftp)\\://([a-zA-Z0-9+!*(),;?&=\\$_.-]+(\\:[a-zA-Z0-9+!*(),;?&=\\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\\:[0-9]{2,5})?(/([a-zA-Z0-9%+\\$_-]\\.?)+)*/?(\\?[a-zA-Z+&\\$_.-][a-zA-Z0-9;:[\\]@&%=+/\\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\\$_.-]*)?',
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* Modules specific settings
|
||||
*
|
||||
*/
|
||||
$MyModuleSettings = array(
|
||||
'itop-attachments' => array (
|
||||
'allowed_classes' => array (
|
||||
0 => 'Ticket',
|
||||
),
|
||||
'position' => 'relations',
|
||||
'preview_max_width' => 290,
|
||||
),
|
||||
'itop-backup' => array (
|
||||
'mysql_bindir' => '',
|
||||
'week_days' => 'monday, tuesday, wednesday, thursday, friday',
|
||||
'time' => '23:30',
|
||||
'retention_count' => 5,
|
||||
'enabled' => true,
|
||||
'debug' => false,
|
||||
),
|
||||
'molkobain-console-tooltips' => array (
|
||||
'decoration_class' => 'fas fa-question',
|
||||
'enabled' => true,
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* Data model modules to be loaded. Names are specified as relative paths
|
||||
*
|
||||
*/
|
||||
$MyModules = array(
|
||||
'addons' => array (
|
||||
'user rights' => 'addons/userrights/userrightsprofile.class.inc.php',
|
||||
),
|
||||
);
|
||||
?>
|
||||
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<installation>
|
||||
<mode>upgrade</mode>
|
||||
<preinstall>
|
||||
<copies type="array"/>
|
||||
</preinstall>
|
||||
<source_dir>datamodels/2.x/</source_dir>
|
||||
<datamodel_version>2.5.0</datamodel_version>
|
||||
<previous_configuration_file>/var/lib/jenkins/workspace/iTop-CI/unattended_install/default-config-itop.php</previous_configuration_file>
|
||||
<extensions_dir>extensions</extensions_dir>
|
||||
<target_env>production</target_env>
|
||||
<workspace_dir></workspace_dir>
|
||||
<database>
|
||||
<server></server>
|
||||
<user>root</user>
|
||||
<pwd>c8mb0do</pwd>
|
||||
<name>itop_ci</name>
|
||||
<db_tls_enabled></db_tls_enabled>
|
||||
<db_tls_ca></db_tls_ca>
|
||||
<prefix></prefix>
|
||||
</database>
|
||||
<url>http://127.0.0.1/itop/svn/trunk/</url>
|
||||
<graphviz_path>/usr/bin/dot</graphviz_path>
|
||||
<admin_account>
|
||||
<user>admin</user>
|
||||
<pwd>admin</pwd>
|
||||
<language>EN US</language>
|
||||
</admin_account>
|
||||
<language>EN US</language>
|
||||
<selected_modules type="array">
|
||||
<item>authent-external</item>
|
||||
<item>authent-local</item>
|
||||
<item>itop-backup</item>
|
||||
<item>itop-config</item>
|
||||
<item>itop-profiles-itil</item>
|
||||
<item>itop-sla-computation</item>
|
||||
<item>itop-tickets</item>
|
||||
<item>itop-welcome-itil</item>
|
||||
<item>itop-config-mgmt</item>
|
||||
<item>itop-attachments</item>
|
||||
<item>itop-datacenter-mgmt</item>
|
||||
<item>itop-endusers-devices</item>
|
||||
<item>itop-storage-mgmt</item>
|
||||
<item>itop-virtualization-mgmt</item>
|
||||
<item>itop-bridge-virtualization-storage</item>
|
||||
<item>itop-service-mgmt</item>
|
||||
<item>itop-request-mgmt</item>
|
||||
<item>itop-portal</item>
|
||||
<item>itop-portal-base</item>
|
||||
<item>itop-change-mgmt</item>
|
||||
</selected_modules>
|
||||
<selected_extensions type="array">
|
||||
<item>itop-config-mgmt-core</item>
|
||||
<item>itop-config-mgmt-datacenter</item>
|
||||
<item>itop-config-mgmt-end-user</item>
|
||||
<item>itop-config-mgmt-storage</item>
|
||||
<item>itop-config-mgmt-virtualization</item>
|
||||
<item>itop-service-mgmt-enterprise</item>
|
||||
<item>itop-ticket-mgmt-simple-ticket</item>
|
||||
<item>itop-ticket-mgmt-simple-ticket-enhanced-portal</item>
|
||||
<item>itop-change-mgmt-simple</item>
|
||||
</selected_extensions>
|
||||
<sample_data>1</sample_data>
|
||||
<old_addon></old_addon>
|
||||
<options>
|
||||
<generate_config>1</generate_config>
|
||||
</options>
|
||||
<mysql_bindir></mysql_bindir>
|
||||
</installation>
|
||||
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
//this scrit will be run under the ./toolkit directory, relatively to the document root
|
||||
|
||||
require_once('../approot.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once(APPROOT.'/application/clipage.class.inc.php');
|
||||
require_once(APPROOT.'/core/config.class.inc.php');
|
||||
require_once(APPROOT.'/core/log.class.inc.php');
|
||||
require_once(APPROOT.'/core/kpi.class.inc.php');
|
||||
require_once(APPROOT.'/core/cmdbsource.class.inc.php');
|
||||
require_once(APPROOT.'/setup/setuppage.class.inc.php');
|
||||
require_once(APPROOT.'/setup/wizardcontroller.class.inc.php');
|
||||
require_once(APPROOT.'/setup/wizardsteps.class.inc.php');
|
||||
require_once(APPROOT.'/setup/applicationinstaller.class.inc.php');
|
||||
|
||||
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
$sParamFile = utils::ReadParam('response_file', 'default-params.xml', true /* CLI allowed */, 'raw_data');
|
||||
$bCheckConsistency = (utils::ReadParam('check_consistency', '0', true /* CLI allowed */) == '1');
|
||||
|
||||
$oParams = new XMLParameters($sParamFile);
|
||||
$sMode = $oParams->Get('mode');
|
||||
|
||||
if ($sMode == 'install')
|
||||
{
|
||||
echo "Installation mode detected.\n";
|
||||
$bClean = utils::ReadParam('clean', false, true /* CLI allowed */);
|
||||
if ($bClean)
|
||||
{
|
||||
echo "Cleanup mode detected.\n";
|
||||
$sTargetEnvironment = $oParams->Get('target_env', '');
|
||||
if ($sTargetEnvironment == '')
|
||||
{
|
||||
$sTargetEnvironment = 'production';
|
||||
}
|
||||
$sTargetDir = APPROOT.'env-'.$sTargetEnvironment;
|
||||
|
||||
// Configuration file
|
||||
$sConfigFile = APPCONF.$sTargetEnvironment.'/'.ITOP_CONFIG_FILE;
|
||||
if (file_exists($sConfigFile))
|
||||
{
|
||||
echo "Trying to delete the configuration file: '$sConfigFile'.\n";
|
||||
@chmod($sConfigFile, 0770); // RWX for owner and group, nothing for others
|
||||
unlink($sConfigFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "No config file to delete ($sConfigFile does not exist).\n";
|
||||
}
|
||||
|
||||
// env-xxx directory
|
||||
if (file_exists($sTargetDir))
|
||||
{
|
||||
if (is_dir($sTargetDir))
|
||||
{
|
||||
echo "Emptying the target directory '$sTargetDir'.\n";
|
||||
SetupUtils::tidydir($sTargetDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
die("ERROR the target dir '$sTargetDir' exists, but is NOT a directory !!!\nExiting.\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "No target directory to delete ($sTargetDir does not exist).\n";
|
||||
}
|
||||
|
||||
// Database
|
||||
$aDBSettings = $oParams->Get('database', array());
|
||||
$sDBServer = $aDBSettings['server'];
|
||||
$sDBUser = $aDBSettings['user'];
|
||||
$sDBPwd = $aDBSettings['pwd'];
|
||||
$sDBName = $aDBSettings['name'];
|
||||
$sDBPrefix = $aDBSettings['prefix'];
|
||||
|
||||
if ($sDBPrefix != '')
|
||||
{
|
||||
die("Cleanup not implemented for a partial database (prefix= '$sDBPrefix')\nExiting.");
|
||||
}
|
||||
|
||||
$oMysqli = new mysqli($sDBServer, $sDBUser, $sDBPwd);
|
||||
if ($oMysqli->connect_errno)
|
||||
{
|
||||
die("Cannot connect to the MySQL server (".$mysqli->connect_errno . ") ".$mysqli->connect_error."\nExiting");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($oMysqli->select_db($sDBName))
|
||||
{
|
||||
echo "Deleting database '$sDBName'\n";
|
||||
$oMysqli->query("DROP DATABASE `$sDBName`");
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "The database '$sDBName' does not seem to exist. Nothing to cleanup.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$bHasErrors = false;
|
||||
$aChecks = SetupUtils::CheckBackupPrerequisites(APPROOT.'data'); // mmm should be the backup destination dir
|
||||
|
||||
$aSelectedModules = $oParams->Get('selected_modules');
|
||||
$sSourceDir = $oParams->Get('source_dir', 'datamodels/latest');
|
||||
$sExtensionDir = $oParams->Get('extensions_dir', 'extensions');
|
||||
$aChecks = array_merge($aChecks, SetupUtils::CheckSelectedModules($sSourceDir, $sExtensionDir, $aSelectedModules));
|
||||
|
||||
|
||||
foreach($aChecks as $oCheckResult)
|
||||
{
|
||||
switch($oCheckResult->iSeverity)
|
||||
{
|
||||
case CheckResult::ERROR:
|
||||
$bHasErrors = true;
|
||||
$sHeader = "Error";
|
||||
break;
|
||||
|
||||
case CheckResult::WARNING:
|
||||
$sHeader = "Warning";
|
||||
break;
|
||||
|
||||
case CheckResult::INFO:
|
||||
default:
|
||||
$sHeader = "Info";
|
||||
break;
|
||||
}
|
||||
echo $sHeader.": ".$oCheckResult->sLabel;
|
||||
if (strlen($oCheckResult->sDescription))
|
||||
{
|
||||
echo ' - '.$oCheckResult->sDescription;
|
||||
}
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
if ($bHasErrors)
|
||||
{
|
||||
echo "Encountered stopper issues. Aborting...\n";
|
||||
die;
|
||||
}
|
||||
|
||||
$bFoundIssues = false;
|
||||
|
||||
$bInstall = utils::ReadParam('install', true, true /* CLI allowed */);
|
||||
if ($bInstall)
|
||||
{
|
||||
echo "Starting the unattended installation...\n";
|
||||
$oWizard = new ApplicationInstaller($oParams);
|
||||
$bRes = $oWizard->ExecuteAllSteps();
|
||||
if (!$bRes)
|
||||
{
|
||||
echo "\nencountered installation issues!";
|
||||
$bFoundIssues = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "No installation requested.\n";
|
||||
}
|
||||
if (!$bFoundIssues && $bCheckConsistency)
|
||||
{
|
||||
echo "Checking data model consistency.\n";
|
||||
ob_start();
|
||||
$sCheckRes = '';
|
||||
try
|
||||
{
|
||||
MetaModel::CheckDefinitions(false);
|
||||
$sCheckRes = ob_get_clean();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$sCheckRes = ob_get_clean()."\nException: ".$e->getMessage();
|
||||
}
|
||||
if (strlen($sCheckRes) > 0)
|
||||
{
|
||||
echo $sCheckRes;
|
||||
echo "\nfound consistency issues!";
|
||||
$bFoundIssues = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bFoundIssues)
|
||||
{
|
||||
// last line: used to check the install
|
||||
// the only way to track issues in case of Fatal error or even parsing error!
|
||||
echo "\ninstalled!";
|
||||
exit;
|
||||
}
|
||||
62
Jenkinsfile
vendored
Normal file
62
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
pipeline {
|
||||
agent any
|
||||
stages {
|
||||
|
||||
stage('init') {
|
||||
parallel {
|
||||
stage('debug') {
|
||||
steps {
|
||||
sh './.jenkins/bin/init/debug.sh'
|
||||
}
|
||||
}
|
||||
stage('append files to project') {
|
||||
steps {
|
||||
sh './.jenkins/bin/init/append_files.sh'
|
||||
}
|
||||
}
|
||||
stage('composer install') {
|
||||
steps {
|
||||
sh './.jenkins/bin/init/composer_install.sh'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('unattended_install') {
|
||||
parallel {
|
||||
stage('unattended_install default env') {
|
||||
steps {
|
||||
sh './.jenkins/bin/unattended_install/default_env.sh'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stage('test') {
|
||||
parallel {
|
||||
stage('phpunit') {
|
||||
steps {
|
||||
sh './.jenkins/bin/tests/phpunit.sh'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
post {
|
||||
always {
|
||||
junit 'var/test/phpunit-log.junit.xml'
|
||||
}
|
||||
failure {
|
||||
slackSend(channel: "#jenkins-itop", color: '#FF0000', message: "Ho no! Build failed! (${currentBuild.result}), Job '${env.JOB_NAME} [${env.BUILD_NUMBER}]' (${env.BUILD_URL})")
|
||||
}
|
||||
}
|
||||
|
||||
environment {
|
||||
DEBUG_UNIT_TEST = '0'
|
||||
}
|
||||
options {
|
||||
timeout(time: 20, unit: 'MINUTES')
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"category" => "addon/userrights,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
@@ -75,8 +75,8 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'user_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name','description')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('default_search', array ('name','description'));
|
||||
}
|
||||
|
||||
protected static $m_aCacheProfiles = null;
|
||||
@@ -137,11 +137,8 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
$oUserRights = UserRights::GetModuleInstance();
|
||||
|
||||
$aDisplayData = array();
|
||||
foreach (MetaModel::GetClasses('bizmodel') as $sClass)
|
||||
foreach (MetaModel::GetClasses('bizmodel,grant_by_profile') as $sClass)
|
||||
{
|
||||
// Skip non instantiable classes
|
||||
if (MetaModel::IsAbstract($sClass)) continue;
|
||||
|
||||
$aStimuli = array();
|
||||
foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus)
|
||||
{
|
||||
@@ -183,7 +180,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
if (!$bEditMode)
|
||||
{
|
||||
$oPage->SetCurrentTab(Dict::S('UI:UserManagement:GrantMatrix'));
|
||||
$this->DoShowGrantSumary($oPage);
|
||||
$this->DoShowGrantSumary($oPage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +200,12 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
// preserve DB integrity by deleting links to users
|
||||
protected function OnDelete()
|
||||
{
|
||||
// Don't remove admin profile
|
||||
if ($this->Get('name') === ADMIN_PROFILE_NAME)
|
||||
{
|
||||
throw new SecurityException(Dict::Format('UI:Login:Error:AccessAdmin'));
|
||||
}
|
||||
|
||||
// Note: this may break the rule that says: "a user must have at least ONE profile" !
|
||||
$oLnkSet = $this->Get('user_list');
|
||||
while($oLnk = $oLnkSet->Fetch())
|
||||
@@ -239,7 +242,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"category" => "addon/userrights,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "userid",
|
||||
"state_attcode" => "",
|
||||
@@ -284,6 +287,59 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
}
|
||||
return parent::CheckToDelete($oDeletionPlan);
|
||||
}
|
||||
|
||||
protected function OnInsert()
|
||||
{
|
||||
$this->CheckIfProfileIsAllowed(UR_ACTION_CREATE);
|
||||
}
|
||||
|
||||
protected function OnUpdate()
|
||||
{
|
||||
$this->CheckIfProfileIsAllowed(UR_ACTION_MODIFY);
|
||||
}
|
||||
|
||||
protected function OnDelete()
|
||||
{
|
||||
$this->CheckIfProfileIsAllowed(UR_ACTION_DELETE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $iActionCode
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \SecurityException
|
||||
*/
|
||||
protected function CheckIfProfileIsAllowed($iActionCode)
|
||||
{
|
||||
// When initializing or admin, we need to let everything pass trough
|
||||
if (!UserRights::IsLoggedIn() || UserRights::IsAdministrator()) { return; }
|
||||
|
||||
// Only administrators can manage administrators
|
||||
$iOrigUserId = $this->GetOriginal('userid');
|
||||
if (!empty($iOrigUserId))
|
||||
{
|
||||
$oUser = MetaModel::GetObject('User', $iOrigUserId, true, true);
|
||||
if (UserRights::IsAdministrator($oUser) && !UserRights::IsAdministrator())
|
||||
{
|
||||
throw new SecurityException(Dict::Format('UI:Login:Error:AccessRestricted'));
|
||||
}
|
||||
}
|
||||
$oUser = MetaModel::GetObject('User', $this->Get('userid'), true, true);
|
||||
if (UserRights::IsAdministrator($oUser) && !UserRights::IsAdministrator())
|
||||
{
|
||||
throw new SecurityException(Dict::Format('UI:Login:Error:AccessRestricted'));
|
||||
}
|
||||
if (!UserRights::IsActionAllowed(get_class($this), $iActionCode, DBObjectSet::FromObject($this)))
|
||||
{
|
||||
throw new SecurityException(Dict::Format('UI:Error:ObjectCannotBeUpdated'));
|
||||
}
|
||||
if (!UserRights::IsAdministrator() && ($this->Get('profile') === ADMIN_PROFILE_NAME))
|
||||
{
|
||||
throw new SecurityException(Dict::Format('UI:Login:Error:AccessAdmin'));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class URP_UserOrg extends UserRightsBaseClassGUI
|
||||
@@ -292,7 +348,7 @@ class URP_UserOrg extends UserRightsBaseClassGUI
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"category" => "addon/userrights,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "userid",
|
||||
"state_attcode" => "",
|
||||
@@ -324,6 +380,42 @@ class URP_UserOrg extends UserRightsBaseClassGUI
|
||||
{
|
||||
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Org', $this->Get('userlogin'), $this->Get('allowed_org_name'));
|
||||
}
|
||||
|
||||
|
||||
protected function OnInsert()
|
||||
{
|
||||
$this->CheckIfOrgIsAllowed();
|
||||
}
|
||||
|
||||
protected function OnUpdate()
|
||||
{
|
||||
$this->CheckIfOrgIsAllowed();
|
||||
}
|
||||
|
||||
protected function OnDelete()
|
||||
{
|
||||
$this->CheckIfOrgIsAllowed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \CoreException
|
||||
*/
|
||||
protected function CheckIfOrgIsAllowed()
|
||||
{
|
||||
if (!UserRights::IsLoggedIn() || UserRights::IsAdministrator()) { return; }
|
||||
|
||||
$oUser = UserRights::GetUserObject();
|
||||
$oAddon = UserRights::GetModuleInstance();
|
||||
$aOrgs = $oAddon->GetUserOrgs($oUser, '');
|
||||
if (count($aOrgs) > 0)
|
||||
{
|
||||
$iOrigOrgId = $this->GetOriginal('allowed_org_id');
|
||||
if ((!empty($iOrigOrgId) && !in_array($iOrigOrgId, $aOrgs)) || !in_array($this->Get('allowed_org_id'), $aOrgs))
|
||||
{
|
||||
throw new SecurityException(Dict::Format('Class:User/Error:OrganizationNotAllowed'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -412,11 +504,15 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
|
||||
/**
|
||||
* Read and cache organizations allowed to the given user
|
||||
*
|
||||
* @param oUser
|
||||
* @param sClass -not used here but can be used in overloads
|
||||
*
|
||||
* @param $oUser
|
||||
* @param $sClass (not used here but can be used in overloads)
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function GetUserOrgs($oUser, $sClass)
|
||||
public function GetUserOrgs($oUser, $sClass)
|
||||
{
|
||||
$iUser = $oUser->GetKey();
|
||||
if (!array_key_exists($iUser, $this->m_aUserOrgs))
|
||||
@@ -430,7 +526,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
$oUserOrgSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sUserOrgQuery), array(), array('userid' => $iUser));
|
||||
while ($aRow = $oUserOrgSet->FetchAssoc())
|
||||
{
|
||||
$oUserOrg = $aRow['UserOrg'];
|
||||
$oOrg = $aRow['Org'];
|
||||
$this->m_aUserOrgs[$iUser][] = $oOrg->GetKey();
|
||||
}
|
||||
|
||||
@@ -97,8 +97,8 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'user_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name', 'description')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('default_search', array ('name', 'description'));
|
||||
}
|
||||
|
||||
protected $m_bCheckReservedNames = true;
|
||||
@@ -613,11 +613,15 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
|
||||
/**
|
||||
* Read and cache organizations allowed to the given user
|
||||
*
|
||||
* @param oUser
|
||||
* @param sClass -not used here but can be used in overloads
|
||||
*
|
||||
* @param $oUser
|
||||
* @param $sClass (not used here but can be used in overloads)
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function GetUserOrgs($oUser, $sClass)
|
||||
public function GetUserOrgs($oUser, $sClass)
|
||||
{
|
||||
$iUser = $oUser->GetKey();
|
||||
if (!array_key_exists($iUser, $this->m_aUserOrgs))
|
||||
@@ -631,7 +635,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
$oUserOrgSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sUserOrgQuery), array(), array('userid' => $iUser));
|
||||
while ($aRow = $oUserOrgSet->FetchAssoc())
|
||||
{
|
||||
$oUserOrg = $aRow['UserOrg'];
|
||||
$oOrg = $aRow['Org'];
|
||||
$this->m_aUserOrgs[$iUser][] = $oOrg->GetKey();
|
||||
}
|
||||
|
||||
@@ -78,8 +78,8 @@ class URP_Profiles extends UserRightsBaseClass
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'user_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name', 'description')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('default_search', array ('name', 'description'));
|
||||
}
|
||||
|
||||
function GetGrantAsHtml($oUserRights, $sClass, $sAction)
|
||||
|
||||
@@ -79,7 +79,7 @@ class Html2Text {
|
||||
// replace with spaces
|
||||
|
||||
$html = str_replace(" ", " ", $html);
|
||||
$html = mb_str_replace("\xa0", " ", $html); // DO NOT USE str_replace since it breaks the "à" character which is \xc3 \xa0 in UTF-8
|
||||
$html = mb_str_replace("\xc2\xa0", " ", $html); // DO NOT USE str_replace since it breaks the "à" character which is \xc3 \xa0 in UTF-8
|
||||
|
||||
$html = static::fixNewlines($html);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2018 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
* Simple web page with no includes, header or fancy formatting, useful to
|
||||
* generate HTML fragments when called by an AJAX method
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -53,7 +53,9 @@ class ajax_page extends WebPage implements iTabbedPage
|
||||
$this->sContentType = 'text/html';
|
||||
$this->sContentDisposition = 'inline';
|
||||
$this->m_sMenu = "";
|
||||
}
|
||||
|
||||
utils::InitArchiveMode();
|
||||
}
|
||||
|
||||
public function AddTabContainer($sTabContainer, $sPrefix = '')
|
||||
{
|
||||
@@ -202,32 +204,16 @@ EOF
|
||||
// Render the tabs in the page (if any)
|
||||
$this->s_content = $this->m_oTabs->RenderIntoContent($this->s_content, $this);
|
||||
|
||||
// Additional UI widgets to be activated inside the ajax fragment ??
|
||||
if (($this->sContentType == 'text/html') && (preg_match('/class="date-pick"/', $this->s_content) || preg_match('/class="datetime-pick"/', $this->s_content)) )
|
||||
// Additional UI widgets to be activated inside the ajax fragment
|
||||
// Important: Testing the content type is not enough because some ajax handlers have not correctly positionned the flag (e.g json response corrupted by the script)
|
||||
if (($this->sContentType == 'text/html') && (preg_match('/class="date-pick"/', $this->s_content) || preg_match('/class="datetime-pick"/', $this->s_content)) )
|
||||
{
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
$(".date-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
$(".datetime-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd 00:00:00',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true
|
||||
});
|
||||
PrepareWidgets();
|
||||
EOF
|
||||
);
|
||||
}
|
||||
}
|
||||
$s_captured_output = $this->ob_get_clean_safe();
|
||||
if (($this->sContentType == 'text/html') && ($this->sContentDisposition == 'inline'))
|
||||
{
|
||||
@@ -258,9 +244,18 @@ EOF
|
||||
//echo $this->s_deferred_content;
|
||||
if (count($this->a_scripts) > 0)
|
||||
{
|
||||
echo "<script type=\"text/javascript\">\n";
|
||||
echo implode("\n", $this->a_scripts);
|
||||
echo "\n</script>\n";
|
||||
echo "<script type=\"text/javascript\">\n";
|
||||
echo implode("\n", $this->a_scripts);
|
||||
echo "\n</script>\n";
|
||||
}
|
||||
if (count($this->a_linked_scripts) > 0)
|
||||
{
|
||||
echo "<script type=\"text/javascript\">\n";
|
||||
foreach($this->a_linked_scripts as $sScriptUrl)
|
||||
{
|
||||
echo '$.getScript('.json_encode($sScriptUrl).");\n";
|
||||
}
|
||||
echo "\n</script>\n";
|
||||
}
|
||||
if (!empty($this->s_deferred_content))
|
||||
{
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
require_once(APPROOT.'/application/applicationcontext.class.inc.php');
|
||||
require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
|
||||
require_once(APPROOT.'/application/displayblock.class.inc.php');
|
||||
require_once(APPROOT.'/application/sqlblock.class.inc.php');
|
||||
require_once(APPROOT.'/application/audit.category.class.inc.php');
|
||||
require_once(APPROOT.'/application/audit.rule.class.inc.php');
|
||||
require_once(APPROOT.'/application/query.class.inc.php');
|
||||
|
||||
@@ -1,362 +1,362 @@
|
||||
<?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 ApplicationContext
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* Usage:
|
||||
* 1) Build the application's context by constructing the object
|
||||
* (the object will read some of the page's parameters)
|
||||
*
|
||||
* 2) Add these parameters to hyperlinks or to forms using the helper, functions
|
||||
* GetForLink(), GetForForm() or GetAsHash()
|
||||
*/
|
||||
class ApplicationContext
|
||||
{
|
||||
protected $aNames;
|
||||
protected $aValues;
|
||||
protected static $aDefaultValues; // Cache shared among all instances
|
||||
|
||||
public function __construct($bReadContext = true)
|
||||
{
|
||||
$this->aNames = array(
|
||||
'org_id', 'menu'
|
||||
);
|
||||
if ($bReadContext)
|
||||
{
|
||||
$this->ReadContext();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the context directly in the PHP parameters (either POST or GET)
|
||||
* return nothing
|
||||
*/
|
||||
protected function ReadContext()
|
||||
{
|
||||
if (!isset(self::$aDefaultValues))
|
||||
{
|
||||
self::$aDefaultValues = array();
|
||||
$aContext = utils::ReadParam('c', array(), false, 'context_param');
|
||||
foreach($this->aNames as $sName)
|
||||
{
|
||||
$sValue = isset($aContext[$sName]) ? $aContext[$sName] : '';
|
||||
// TO DO: check if some of the context parameters are mandatory (or have default values)
|
||||
if (!empty($sValue))
|
||||
{
|
||||
self::$aDefaultValues[$sName] = $sValue;
|
||||
}
|
||||
// Hmm, there must be a better (more generic) way to handle the case below:
|
||||
// When there is only one possible (allowed) organization, the context must be
|
||||
// fixed to this org
|
||||
if ($sName == 'org_id')
|
||||
{
|
||||
if (MetaModel::IsValidClass('Organization'))
|
||||
{
|
||||
$oSearchFilter = new DBObjectSearch('Organization');
|
||||
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
|
||||
$oSet = new CMDBObjectSet($oSearchFilter);
|
||||
$iCount = $oSet->Count();
|
||||
if ($iCount == 1)
|
||||
{
|
||||
// Only one possible value for org_id, set it in the context
|
||||
$oOrg = $oSet->Fetch();
|
||||
self::$aDefaultValues[$sName] = $oOrg->GetKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->aValues = self::$aDefaultValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value for the given parameter
|
||||
* @param string $sParamName Name of the parameter to read
|
||||
* @return mixed The value for this parameter
|
||||
*/
|
||||
public function GetCurrentValue($sParamName, $defaultValue = '')
|
||||
{
|
||||
if (isset($this->aValues[$sParamName]))
|
||||
{
|
||||
return $this->aValues[$sParamName];
|
||||
}
|
||||
return $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context as string with the format name1=value1&name2=value2....
|
||||
* return string The context as a string to be appended to an href property
|
||||
*/
|
||||
public function GetForLink()
|
||||
{
|
||||
$aParams = array();
|
||||
foreach($this->aValues as $sName => $sValue)
|
||||
{
|
||||
$aParams[] = "c[$sName]".'='.urlencode($sValue);
|
||||
}
|
||||
return implode("&", $aParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context as sequence of input tags to be inserted inside a <form> tag
|
||||
* return string The context as a sequence of <input type="hidden" /> tags
|
||||
*/
|
||||
public function GetForForm()
|
||||
{
|
||||
$sContext = "";
|
||||
foreach($this->aValues as $sName => $sValue)
|
||||
{
|
||||
$sContext .= "<input type=\"hidden\" name=\"c[$sName]\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n";
|
||||
}
|
||||
return $sContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context as a hash array 'parameter_name' => value
|
||||
* return array The context information
|
||||
*/
|
||||
public function GetAsHash()
|
||||
{
|
||||
$aReturn = array();
|
||||
foreach($this->aValues as $sName => $sValue)
|
||||
{
|
||||
$aReturn["c[$sName]"] = $sValue;
|
||||
}
|
||||
return $aReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the context parameters NAMEs
|
||||
* @return array The list of context parameters
|
||||
*/
|
||||
public function GetNames()
|
||||
{
|
||||
return $this->aNames;
|
||||
}
|
||||
/**
|
||||
* Removes the specified parameter from the context, for example when the same parameter
|
||||
* is already a search parameter
|
||||
* @param string $sParamName Name of the parameter to remove
|
||||
* @return none
|
||||
*/
|
||||
public function Reset($sParamName)
|
||||
{
|
||||
if (isset($this->aValues[$sParamName]))
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
<?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 ApplicationContext
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* Usage:
|
||||
* 1) Build the application's context by constructing the object
|
||||
* (the object will read some of the page's parameters)
|
||||
*
|
||||
* 2) Add these parameters to hyperlinks or to forms using the helper, functions
|
||||
* GetForLink(), GetForForm() or GetAsHash()
|
||||
*/
|
||||
class ApplicationContext
|
||||
{
|
||||
protected $aNames;
|
||||
protected $aValues;
|
||||
protected static $aDefaultValues; // Cache shared among all instances
|
||||
|
||||
public function __construct($bReadContext = true)
|
||||
{
|
||||
$this->aNames = array(
|
||||
'org_id', 'menu'
|
||||
);
|
||||
if ($bReadContext)
|
||||
{
|
||||
$this->ReadContext();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the context directly in the PHP parameters (either POST or GET)
|
||||
* return nothing
|
||||
*/
|
||||
protected function ReadContext()
|
||||
{
|
||||
if (!isset(self::$aDefaultValues))
|
||||
{
|
||||
self::$aDefaultValues = array();
|
||||
$aContext = utils::ReadParam('c', array(), false, 'context_param');
|
||||
foreach($this->aNames as $sName)
|
||||
{
|
||||
$sValue = isset($aContext[$sName]) ? $aContext[$sName] : '';
|
||||
// TO DO: check if some of the context parameters are mandatory (or have default values)
|
||||
if (!empty($sValue))
|
||||
{
|
||||
self::$aDefaultValues[$sName] = $sValue;
|
||||
}
|
||||
// Hmm, there must be a better (more generic) way to handle the case below:
|
||||
// When there is only one possible (allowed) organization, the context must be
|
||||
// fixed to this org
|
||||
if ($sName == 'org_id')
|
||||
{
|
||||
if (MetaModel::IsValidClass('Organization'))
|
||||
{
|
||||
$oSearchFilter = new DBObjectSearch('Organization');
|
||||
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
|
||||
$oSet = new CMDBObjectSet($oSearchFilter);
|
||||
$iCount = $oSet->CountWithLimit(2);
|
||||
if ($iCount == 1)
|
||||
{
|
||||
// Only one possible value for org_id, set it in the context
|
||||
$oOrg = $oSet->Fetch();
|
||||
self::$aDefaultValues[$sName] = $oOrg->GetKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->aValues = self::$aDefaultValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current value for the given parameter
|
||||
* @param string $sParamName Name of the parameter to read
|
||||
* @return mixed The value for this parameter
|
||||
*/
|
||||
public function GetCurrentValue($sParamName, $defaultValue = '')
|
||||
{
|
||||
if (isset($this->aValues[$sParamName]))
|
||||
{
|
||||
return $this->aValues[$sParamName];
|
||||
}
|
||||
return $defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context as string with the format name1=value1&name2=value2....
|
||||
* return string The context as a string to be appended to an href property
|
||||
*/
|
||||
public function GetForLink()
|
||||
{
|
||||
$aParams = array();
|
||||
foreach($this->aValues as $sName => $sValue)
|
||||
{
|
||||
$aParams[] = "c[$sName]".'='.urlencode($sValue);
|
||||
}
|
||||
return implode("&", $aParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context as sequence of input tags to be inserted inside a <form> tag
|
||||
* return string The context as a sequence of <input type="hidden" /> tags
|
||||
*/
|
||||
public function GetForForm()
|
||||
{
|
||||
$sContext = "";
|
||||
foreach($this->aValues as $sName => $sValue)
|
||||
{
|
||||
$sContext .= "<input type=\"hidden\" name=\"c[$sName]\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n";
|
||||
}
|
||||
return $sContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the context as a hash array 'parameter_name' => value
|
||||
* return array The context information
|
||||
*/
|
||||
public function GetAsHash()
|
||||
{
|
||||
$aReturn = array();
|
||||
foreach($this->aValues as $sName => $sValue)
|
||||
{
|
||||
$aReturn["c[$sName]"] = $sValue;
|
||||
}
|
||||
return $aReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the context parameters NAMEs
|
||||
* @return array The list of context parameters
|
||||
*/
|
||||
public function GetNames()
|
||||
{
|
||||
return $this->aNames;
|
||||
}
|
||||
/**
|
||||
* Removes the specified parameter from the context, for example when the same parameter
|
||||
* is already a search parameter
|
||||
* @param string $sParamName Name of the parameter to remove
|
||||
* @return none
|
||||
*/
|
||||
public function Reset($sParamName)
|
||||
{
|
||||
if (isset($this->aValues[$sParamName]))
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -308,6 +308,43 @@ interface iPopupMenuExtension
|
||||
* $param is null
|
||||
*/
|
||||
const MENU_USER_ACTIONS = 5;
|
||||
/**
|
||||
* Insert an item into the Action menu on an object item in an objects list in the portal
|
||||
*
|
||||
* $param is an array('portal_id' => $sPortalId, 'object' => $oObject) containing the portal id and a DBObject instance (the object on the current line)
|
||||
*/
|
||||
const PORTAL_OBJLISTITEM_ACTIONS = 7;
|
||||
/**
|
||||
* Insert an item into the Action menu on an object details page in the portal
|
||||
*
|
||||
* $param is an array('portal_id' => $sPortalId, 'object' => $oObject) containing the portal id and a DBObject instance (the object currently displayed)
|
||||
*/
|
||||
const PORTAL_OBJDETAILS_ACTIONS = 8;
|
||||
|
||||
/**
|
||||
* Insert an item into the Actions menu of a list in the portal
|
||||
* Note: This is not implemented yet !
|
||||
*
|
||||
* $param is an array('portal_id' => $sPortalId, 'object_set' => $oSet) containing DBObjectSet containing the list of objects
|
||||
* @todo
|
||||
*/
|
||||
const PORTAL_OBJLIST_ACTIONS = 6;
|
||||
/**
|
||||
* Insert an item into the user menu of the portal
|
||||
* Note: This is not implemented yet !
|
||||
*
|
||||
* $param is the portal id
|
||||
* @todo
|
||||
*/
|
||||
const PORTAL_USER_ACTIONS = 9;
|
||||
/**
|
||||
* Insert an item into the navigation menu of the portal
|
||||
* Note: This is not implemented yet !
|
||||
*
|
||||
* $param is the portal id
|
||||
* @todo
|
||||
*/
|
||||
const PORTAL_MENU_ACTIONS = 10;
|
||||
|
||||
/**
|
||||
* Get the list of items to be added to a menu.
|
||||
@@ -334,17 +371,21 @@ abstract class ApplicationPopupMenuItem
|
||||
protected $sUID;
|
||||
/** @ignore */
|
||||
protected $sLabel;
|
||||
/** @ignore */
|
||||
protected $aCssClasses;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $sUID The unique identifier of this menu in iTop... make sure you pass something unique enough
|
||||
* @param string $sLabel The display label of the menu (must be localized)
|
||||
*/
|
||||
* @param string $sLabel The display label of the menu (must be localized)
|
||||
* @param array $aCssClasses The CSS classes to add to the menu
|
||||
*/
|
||||
public function __construct($sUID, $sLabel)
|
||||
{
|
||||
$this->sUID = $sUID;
|
||||
$this->sLabel = $sLabel;
|
||||
$this->aCssClasses = array();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -368,6 +409,35 @@ abstract class ApplicationPopupMenuItem
|
||||
{
|
||||
return $this->sLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CSS classes
|
||||
*
|
||||
* @return array
|
||||
* @ignore
|
||||
*/
|
||||
public function GetCssClasses()
|
||||
{
|
||||
return $this->aCssClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $aCssClasses
|
||||
*/
|
||||
public function SetCssClasses($aCssClasses)
|
||||
{
|
||||
$this->aCssClasses = $aCssClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a CSS class to the CSS classes that will be put on the menu item
|
||||
*
|
||||
* @param $sCssClass
|
||||
*/
|
||||
public function AddCssClass($sCssClass)
|
||||
{
|
||||
$this->aCssClasses[] = $sCssClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the components to create a popup menu item in HTML
|
||||
@@ -415,7 +485,7 @@ class URLPopupMenuItem extends ApplicationPopupMenuItem
|
||||
/** @ignore */
|
||||
public function GetMenuItem()
|
||||
{
|
||||
return array ('label' => $this->GetLabel(), 'url' => $this->sURL, 'target' => $this->sTarget);
|
||||
return array ('label' => $this->GetLabel(), 'url' => $this->sURL, 'target' => $this->sTarget, 'css_classes' => $this->aCssClasses);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +521,7 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
|
||||
public function GetMenuItem()
|
||||
{
|
||||
// Note: the semicolumn is a must here!
|
||||
return array ('label' => $this->GetLabel(), 'onclick' => $this->sJSCode.'; return false;', 'url' => '#');
|
||||
return array ('label' => $this->GetLabel(), 'onclick' => $this->sJSCode.'; return false;', 'url' => '#', 'css_classes' => $this->aCssClasses);
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
@@ -483,10 +553,34 @@ class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
|
||||
/** @ignore */
|
||||
public function GetMenuItem()
|
||||
{
|
||||
return array ('label' => '<hr class="menu-separator">', 'url' => '');
|
||||
return array ('label' => '<hr class="menu-separator">', 'url' => '', 'css_classes' => $this->aCssClasses);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for adding an item as a button that browses to the given URL
|
||||
*
|
||||
* @package Extensibility
|
||||
* @api
|
||||
* @since 2.0
|
||||
*/
|
||||
class URLButtonItem extends URLPopupMenuItem
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for adding an item as a button that runs some JS code
|
||||
*
|
||||
* @package Extensibility
|
||||
* @api
|
||||
* @since 2.0
|
||||
*/
|
||||
class JSButtonItem extends JSPopupMenuItem
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add content to any iTopWebPage
|
||||
*
|
||||
@@ -528,6 +622,128 @@ interface iPageUIExtension
|
||||
public function GetBannerHtml(iTopWebPage $oPage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add content to any enhanced portal page
|
||||
*
|
||||
* IMPORTANT! Experimental API, may be removed at anytime, we don't recommend to use it just now!
|
||||
*
|
||||
* @package Extensibility
|
||||
* @api
|
||||
* @since 2.4
|
||||
*/
|
||||
interface iPortalUIExtension
|
||||
{
|
||||
const ENUM_PORTAL_EXT_UI_BODY = 'Body';
|
||||
const ENUM_PORTAL_EXT_UI_NAVIGATION_MENU = 'NavigationMenu';
|
||||
const ENUM_PORTAL_EXT_UI_MAIN_CONTENT = 'MainContent';
|
||||
|
||||
/**
|
||||
* Returns an array of CSS file urls
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return array
|
||||
*/
|
||||
public function GetCSSFiles(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns inline (raw) CSS
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return string
|
||||
*/
|
||||
public function GetCSSInline(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns an array of JS file urls
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return array
|
||||
*/
|
||||
public function GetJSFiles(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns raw JS code
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return string
|
||||
*/
|
||||
public function GetJSInline(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns raw HTML code to put at the end of the <body> tag
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return string
|
||||
*/
|
||||
public function GetBodyHTML(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns raw HTML code to put at the end of the #main-wrapper element
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return string
|
||||
*/
|
||||
public function GetMainContentHTML(\Silex\Application $oApp);
|
||||
/**
|
||||
* Returns raw HTML code to put at the end of the #topbar and #sidebar elements
|
||||
*
|
||||
* @param \Silex\Application $oApp
|
||||
* @return string
|
||||
*/
|
||||
public function GetNavigationMenuHTML(\Silex\Application $oApp);
|
||||
}
|
||||
|
||||
/**
|
||||
* IMPORTANT! Experimental API, may be removed at anytime, we don't recommend to use it just now!
|
||||
*/
|
||||
abstract class AbstractPortalUIExtension implements iPortalUIExtension
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetCSSFiles(\Silex\Application $oApp)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetCSSInline(\Silex\Application $oApp)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetJSFiles(\Silex\Application $oApp)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetJSInline(\Silex\Application $oApp)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetBodyHTML(\Silex\Application $oApp)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetMainContentHTML(\Silex\Application $oApp)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function GetNavigationMenuHTML(\Silex\Application $oApp)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add new operations to the REST/JSON web service
|
||||
*
|
||||
|
||||
@@ -34,7 +34,7 @@ class AuditCategory extends cmdbAbstractObject
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "application",
|
||||
"category" => "application, grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
@@ -53,8 +53,8 @@ class AuditCategory extends cmdbAbstractObject
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'definition_set', 'rules_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('description', )); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('name', 'description')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('name', 'description', 'definition_set')); // Criteria of the advanced search form
|
||||
MetaModel::Init_SetZListItems('standard_search', array('description', 'definition_set')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -35,7 +35,7 @@ class AuditRule extends cmdbAbstractObject
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "application",
|
||||
"category" => "application, grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
@@ -57,8 +57,8 @@ class AuditRule extends cmdbAbstractObject
|
||||
MetaModel::Init_SetZListItems('details', array('category_id', 'name', 'description', 'query', 'valid_flag')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('category_id', 'description', 'valid_flag')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('category_id', 'name', 'description', 'valid_flag')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('category_id', 'name', 'description', 'valid_flag', 'query')); // Criteria of the advanced search form
|
||||
MetaModel::Init_SetZListItems('standard_search', array('category_id', 'name', 'description', 'valid_flag', 'query')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'category_id')); // Criteria of the advanced search form
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -23,7 +23,7 @@ require_once(APPROOT.'core/modelreflection.class.inc.php');
|
||||
/**
|
||||
* A user editable dashboard page
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
abstract class Dashboard
|
||||
@@ -115,15 +115,13 @@ abstract class Dashboard
|
||||
$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);
|
||||
|
||||
$oNewDashlet = $this->InitDashletFromDOMNode($oDomNode);
|
||||
$aDashletOrder[] = array('rank' => $iRank, 'dashlet' => $oNewDashlet);
|
||||
}
|
||||
usort($aDashletOrder, array(get_class($this), 'SortOnRank'));
|
||||
@@ -147,6 +145,20 @@ abstract class Dashboard
|
||||
}
|
||||
}
|
||||
|
||||
protected function InitDashletFromDOMNode($oDomNode)
|
||||
{
|
||||
$sId = $oDomNode->getAttribute('id');
|
||||
$sDashletType = $oDomNode->getAttribute('xsi:type');
|
||||
|
||||
// Test if dashlet can be instanciated, otherwise (uninstalled, broken, ...) we display a placeholder
|
||||
$sClass = static::GetDashletClassFromType($sDashletType);
|
||||
$oNewDashlet = new $sClass($this->oMetaModel, $sId);
|
||||
$oNewDashlet->SetDashletType($sDashletType);
|
||||
$oNewDashlet->FromDOMNode($oDomNode);
|
||||
|
||||
return $oNewDashlet;
|
||||
}
|
||||
|
||||
static function SortOnRank($aItem1, $aItem2)
|
||||
{
|
||||
return ($aItem1['rank'] > $aItem2['rank']) ? +1 : -1;
|
||||
@@ -220,7 +232,7 @@ abstract class Dashboard
|
||||
$oNode = $oDoc->createElement('dashlet');
|
||||
$oDashletsNode->appendChild($oNode);
|
||||
$oNode->setAttribute('id', $oDashlet->GetID());
|
||||
$oNode->setAttribute('xsi:type', get_class($oDashlet));
|
||||
$oNode->setAttribute('xsi:type', $oDashlet->GetDashletType());
|
||||
$oDashletRank = $oDoc->createElement('rank', $iDashletRank);
|
||||
$oNode->appendChild($oDashletRank);
|
||||
$iDashletRank++;
|
||||
@@ -245,7 +257,10 @@ abstract class Dashboard
|
||||
$sDashletClass = $aDashletParams['dashlet_class'];
|
||||
$sId = $aDashletParams['dashlet_id'];
|
||||
$oNewDashlet = new $sDashletClass($this->oMetaModel, $sId);
|
||||
|
||||
if (isset($aDashletParams['dashlet_type']))
|
||||
{
|
||||
$oNewDashlet->SetDashletType($aDashletParams['dashlet_type']);
|
||||
}
|
||||
$oForm = $oNewDashlet->GetForm();
|
||||
$oForm->SetParamsContainer($sId);
|
||||
$oForm->SetPrefix('');
|
||||
@@ -414,24 +429,11 @@ EOF
|
||||
$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)
|
||||
$oPage->add('<div id="select_dashlet" style="text-align:center; max-height:120px; overflow-y:auto;">');
|
||||
$aAvailableDashlets = $this->GetAvailableDashlets();
|
||||
foreach($aAvailableDashlets as $sDashletClass => $aInfo)
|
||||
{
|
||||
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('<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>');
|
||||
|
||||
@@ -465,6 +467,38 @@ EOF
|
||||
|
||||
$oPage->add('</div>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an array of dashlets available for selection.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function GetAvailableDashlets()
|
||||
{
|
||||
$aDashlets = array();
|
||||
|
||||
foreach( get_declared_classes() as $sDashletClass)
|
||||
{
|
||||
// DashletUnknown is not among the selection as it is just a fallback for dashlets that can't instanciated.
|
||||
if ( is_subclass_of($sDashletClass, 'Dashlet') && !in_array($sDashletClass, array('DashletUnknown', 'DashletProxy')) )
|
||||
{
|
||||
$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);
|
||||
$aDashlets[$sDashletClass] = $aInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aDashlets;
|
||||
}
|
||||
|
||||
protected function GetNewDashletId()
|
||||
{
|
||||
@@ -480,6 +514,15 @@ EOF
|
||||
}
|
||||
|
||||
abstract protected function SetFormParams($oForm);
|
||||
|
||||
public static function GetDashletClassFromType($sType, $oFactory = null)
|
||||
{
|
||||
if (is_subclass_of($sType, 'Dashlet'))
|
||||
{
|
||||
return $sType;
|
||||
}
|
||||
return 'DashletUnknown';
|
||||
}
|
||||
}
|
||||
|
||||
class RuntimeDashboard extends Dashboard
|
||||
@@ -515,8 +558,6 @@ class RuntimeDashboard extends Dashboard
|
||||
// Assuming there is at most one couple {user, menu}!
|
||||
$oUserDashboard = $oUDSet->Fetch();
|
||||
$oUserDashboard->Set('contents', $sXml);
|
||||
|
||||
$oUserDashboard->DBUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -525,9 +566,10 @@ class RuntimeDashboard extends Dashboard
|
||||
$oUserDashboard->Set('user_id', UserRights::GetUserId());
|
||||
$oUserDashboard->Set('menu_code', $this->sId);
|
||||
$oUserDashboard->Set('contents', $sXml);
|
||||
|
||||
$oUserDashboard->DBInsert();
|
||||
}
|
||||
}
|
||||
utils::PushArchiveMode(false);
|
||||
$oUserDashboard->DBWrite();
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
|
||||
public function Revert()
|
||||
@@ -540,7 +582,9 @@ class RuntimeDashboard extends Dashboard
|
||||
{
|
||||
// Assuming there is at most one couple {user, menu}!
|
||||
$oUserDashboard = $oUDSet->Fetch();
|
||||
utils::PushArchiveMode(false);
|
||||
$oUserDashboard->DBDelete();
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -754,33 +798,65 @@ EOF
|
||||
|
||||
public static function GetDashletCreationForm($sOQL = null)
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContextMenuId = $oAppContext->GetCurrentValue('menu', null);
|
||||
|
||||
$oForm = new DesignerForm();
|
||||
|
||||
// Get the list of all 'dashboard' menus in which we can insert a dashlet
|
||||
$aAllMenus = ApplicationMenu::ReflectionMenuNodes();
|
||||
$sRootMenuId = ApplicationMenu::GetRootMenuId($sContextMenuId);
|
||||
$aAllowedDashboards = array();
|
||||
foreach($aAllMenus as $idx => $aMenu)
|
||||
$sDefaultDashboard = null;
|
||||
|
||||
// Store the parent menus for acces check
|
||||
$aParentMenus = array();
|
||||
foreach($aAllMenus as $idx => $aMenu)
|
||||
{
|
||||
/** @var MenuNode $oMenu */
|
||||
$oMenu = $aMenu['node'];
|
||||
if (count(ApplicationMenu::GetChildren($oMenu->GetIndex())) > 0)
|
||||
{
|
||||
$aParentMenus[$oMenu->GetMenuId()] = $aMenu;
|
||||
}
|
||||
}
|
||||
|
||||
foreach($aAllMenus as $idx => $aMenu)
|
||||
{
|
||||
$oMenu = $aMenu['node'];
|
||||
$sParentId = $aMenu['parent'];
|
||||
if ($oMenu instanceof DashboardMenuNode)
|
||||
{
|
||||
$sMenuLabel = $oMenu->GetTitle();
|
||||
$sParentLabel = Dict::S('Menu:'.$sParentId);
|
||||
if ($sParentLabel != $sMenuLabel)
|
||||
{
|
||||
$aAllowedDashboards[$oMenu->GetMenuId()] = $sParentLabel.' - '.$sMenuLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAllowedDashboards[$oMenu->GetMenuId()] = $sMenuLabel;
|
||||
}
|
||||
}
|
||||
if ($oMenu instanceof DashboardMenuNode)
|
||||
{
|
||||
// Get the root parent for access check
|
||||
$sParentId = $aMenu['parent'];
|
||||
$aParentMenu = $aParentMenus[$sParentId];
|
||||
while (isset($aParentMenus[$aParentMenu['parent']]))
|
||||
{
|
||||
// grand parent exists
|
||||
$sParentId = $aParentMenu['parent'];
|
||||
$aParentMenu = $aParentMenus[$sParentId];
|
||||
}
|
||||
$oParentMenu = $aParentMenu['node'];
|
||||
if ($oMenu->IsEnabled() && $oParentMenu->IsEnabled())
|
||||
{
|
||||
$sMenuLabel = $oMenu->GetTitle();
|
||||
$sParentLabel = Dict::S('Menu:'.$sParentId);
|
||||
if ($sParentLabel != $sMenuLabel)
|
||||
{
|
||||
$aAllowedDashboards[$oMenu->GetMenuId()] = $sParentLabel.' - '.$sMenuLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAllowedDashboards[$oMenu->GetMenuId()] = $sMenuLabel;
|
||||
}
|
||||
if (empty($sDefaultDashboard) && ($sRootMenuId == ApplicationMenu::GetRootMenuId($oMenu->GetMenuId())))
|
||||
{
|
||||
$sDefaultDashboard = $oMenu->GetMenuId();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
asort($aAllowedDashboards);
|
||||
|
||||
$aKeys = array_keys($aAllowedDashboards); // Select the first one by default
|
||||
$sDefaultDashboard = $aKeys[0];
|
||||
$oField = new DesignerComboField('menu_id', Dict::S('UI:DashletCreation:Dashboard'), $sDefaultDashboard);
|
||||
$oField->SetAllowedValues($aAllowedDashboards);
|
||||
$oField->SetMandatory(true);
|
||||
@@ -841,7 +917,7 @@ EOF
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#dashlet_creation_dlg').dialog({
|
||||
width: 400,
|
||||
width: 600,
|
||||
modal: true,
|
||||
title: '$sDialogTitle',
|
||||
buttons: [
|
||||
|
||||
@@ -104,7 +104,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
|
||||
// Trim the list of cells to remove the invisible/empty ones at the end of the array
|
||||
$aCells = $this->TrimCellsArray($aCells);
|
||||
|
||||
$oPage->add('<table style="width:100%"><tbody>');
|
||||
$oPage->add('<table style="width:100%;table-layout:fixed;"><tbody>');
|
||||
$iCellIdx = 0;
|
||||
$fColSize = 100 / $this->iNbCols;
|
||||
$sStyle = $bEditMode ? 'border: 1px #ccc dashed; width:'.$fColSize.'%;' : 'width: '.$fColSize.'%;';
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.5">
|
||||
<portals>
|
||||
<portal id="legacy_portal" _delta="define">
|
||||
<url>portal/index.php</url>
|
||||
@@ -19,4 +19,9 @@
|
||||
</deny>
|
||||
</portal>
|
||||
</portals>
|
||||
<menus>
|
||||
<menu id="AdminTools" xsi:type="MenuGroup" _delta="define">
|
||||
<rank>80</rank>
|
||||
</menu>
|
||||
</menus>
|
||||
</itop_design>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -18,7 +18,7 @@
|
||||
/**
|
||||
* Data Table to display a set of objects in a tabular manner in HTML
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -31,7 +31,8 @@ class DataTable
|
||||
protected $iNbObjects; // Total number of objects inthe set
|
||||
protected $bUseCustomSettings; // Whether or not the current display uses custom settings
|
||||
protected $oDefaultSettings; // the default settings for displaying such a list
|
||||
|
||||
protected $bShowObsoleteData;
|
||||
|
||||
/**
|
||||
* @param $iListId mixed Unique ID for this div/table in the page
|
||||
* @param $oSet DBObjectSet The set of data to display
|
||||
@@ -47,6 +48,7 @@ class DataTable
|
||||
$this->iNbObjects = $oSet->Count();
|
||||
$this->bUseCustomSettings = false;
|
||||
$this->oDefaultSettings = null;
|
||||
$this->bShowObsoleteData = $oSet->GetShowObsoleteData();
|
||||
}
|
||||
|
||||
public function Display(WebPage $oPage, DataTableSettings $oSettings, $bActionsMenu, $sSelectMode, $bViewLink, $aExtraParams)
|
||||
@@ -145,7 +147,9 @@ class DataTable
|
||||
$sHtml .= "<tr><td class=\"datacontents\">$sDataTable</td></tr>";
|
||||
$sHtml .= "</table>\n";
|
||||
$oPage->add_at_the_end($sConfigDlg);
|
||||
|
||||
|
||||
$aExtraParams['show_obsolete_data'] = $this->bShowObsoleteData;
|
||||
|
||||
$aOptions = array(
|
||||
'sPersistentId' => '',
|
||||
'sFilter' => $this->oSet->GetFilter()->serialize(),
|
||||
@@ -170,6 +174,7 @@ class DataTable
|
||||
}
|
||||
$sJSOptions = json_encode($aOptions);
|
||||
$oPage->add_ready_script("$('#datatable_{$this->iListId}').datatable($sJSOptions);");
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
@@ -293,7 +298,7 @@ EOF;
|
||||
if (!$oPage->IsPrintableVersion())
|
||||
{
|
||||
$sMenuTitle = Dict::S('UI:ConfigureThisList');
|
||||
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><img src="../images/toolkit_menu.png?itopversion='.ITOP_VERSION.'"><ul>';
|
||||
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><img src="../images/toolkit_menu.png?t='.utils::GetCacheBusterTimestamp().'"><ul>';
|
||||
|
||||
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
|
||||
$aActions = array(
|
||||
@@ -486,6 +491,7 @@ EOF;
|
||||
{
|
||||
$aExtraParams['query_params'][$sName] = $sValue;
|
||||
}
|
||||
$aExtraParams['show_obsolete_data'] = $this->bShowObsoleteData;
|
||||
|
||||
$sHtml .= "<tr><td>";
|
||||
$sHtml .= $oPage->GetTable($aAttribs, $aValues);
|
||||
@@ -566,33 +572,6 @@ EOF
|
||||
{
|
||||
$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;
|
||||
}
|
||||
|
||||
@@ -601,7 +580,7 @@ EOF
|
||||
$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))."');");
|
||||
$oPage->add_ready_script("$('#pager{$this->iListId}').html('".json_encode($sHtml)."');");
|
||||
if ($iDefaultPageSize < 1)
|
||||
{
|
||||
$oPage->add_ready_script("$('#pager{$this->iListId}').parent().hide()");
|
||||
@@ -928,8 +907,15 @@ class DataTableSettings implements Serializable
|
||||
}
|
||||
else if ($oAttDef->IsExternalField())
|
||||
{
|
||||
$oExtAttDef = $oAttDef->GetExtAttDef();
|
||||
$sLabel = Dict::Format('UI:ExtField_AsRemoteField', $oAttDef->GetLabel(), $oExtAttDef->GetLabel());
|
||||
if ($oAttDef->IsFriendlyName())
|
||||
{
|
||||
$sLabel = Dict::Format('UI:ExtKey_AsFriendlyName', $oAttDef->GetLabel());
|
||||
}
|
||||
else
|
||||
{
|
||||
$oExtAttDef = $oAttDef->GetExtAttDef();
|
||||
$sLabel = Dict::Format('UI:ExtField_AsRemoteField', $oAttDef->GetLabel(), $oExtAttDef->GetLabel());
|
||||
}
|
||||
}
|
||||
elseif ($oAttDef instanceof AttributeFriendlyName)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -462,7 +462,7 @@ class ExcelExporter
|
||||
$this->aAuthorizedClasses = array();
|
||||
foreach($aClasses as $sAlias => $sClassName)
|
||||
{
|
||||
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) && (UR_ALLOWED_YES || UR_ALLOWED_DEPENDS))
|
||||
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO)
|
||||
{
|
||||
$this->aAuthorizedClasses[$sAlias] = $sClassName;
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ class DesignerForm
|
||||
|
||||
public function RenderAsPropertySheet($oP, $bReturnHTML = false, $sNotifyParentSelector = null)
|
||||
{
|
||||
$sReturn = '';
|
||||
$sReturn = '';
|
||||
$sActionUrl = addslashes($this->sSubmitTo);
|
||||
$sJSSubmitParams = json_encode($this->aSubmitParams);
|
||||
$sFormId = $this->GetFormId();
|
||||
@@ -360,6 +360,7 @@ EOF
|
||||
<<<EOF
|
||||
$('#$sDialogId').dialog({
|
||||
height: 'auto',
|
||||
maxHeight: $(window).height() - 8,
|
||||
width: $iDialogWidth,
|
||||
modal: true,
|
||||
autoOpen: $sAutoOpen,
|
||||
@@ -531,7 +532,7 @@ EOF
|
||||
|
||||
public function GetFieldId($sCode)
|
||||
{
|
||||
return $this->GetPrefix().'attr_'.$sCode;
|
||||
return $this->GetPrefix().'attr_'.utils::GetSafeId($sCode.$this->GetSuffix());
|
||||
}
|
||||
|
||||
public function GetFieldName($sCode)
|
||||
@@ -881,7 +882,7 @@ class DesignerTextField extends DesignerFormField
|
||||
$this->sValidationPattern = $sValidationPattern;
|
||||
}
|
||||
|
||||
public function SetForbiddenValues($aValues, $sExplain)
|
||||
public function SetForbiddenValues($aValues, $sExplain, $bCaseSensitive = true)
|
||||
{
|
||||
$aForbiddenValues = $aValues;
|
||||
|
||||
@@ -893,7 +894,7 @@ class DesignerTextField extends DesignerFormField
|
||||
|
||||
}
|
||||
|
||||
$this->aForbiddenValues[] = array('values' => $aForbiddenValues, 'message' => $sExplain);
|
||||
$this->aForbiddenValues[] = array('values' => $aForbiddenValues, 'message' => $sExplain, 'case_sensitive' => $bCaseSensitive);
|
||||
}
|
||||
|
||||
public function Render(WebPage $oP, $sFormId, $sRenderMode='dialog')
|
||||
@@ -1367,6 +1368,36 @@ class RunTimeIconSelectionField extends DesignerIconSelectionField
|
||||
}
|
||||
|
||||
static protected function FindIconsOnDisk($sBaseDir, $sDir = '')
|
||||
{
|
||||
$aFiles = null;
|
||||
$sKey = $sBaseDir.'/'.$sDir;
|
||||
$sShortKey = abs(crc32($sKey));
|
||||
$sCacheFile = utils::GetCachePath().'available-icons-'.$sShortKey.'.php';
|
||||
$sCacheClass = 'AvailableIcons_'.$sShortKey;
|
||||
if (file_exists($sCacheFile))
|
||||
{
|
||||
require_once($sCacheFile);
|
||||
if ($sCacheClass::$sKey === $sKey) // crc32 collision detection
|
||||
{
|
||||
$aFiles = $sCacheClass::$aIconFiles;
|
||||
}
|
||||
}
|
||||
if ($aFiles === null)
|
||||
{
|
||||
$aFiles = self::_FindIconsOnDisk($sBaseDir, $sDir);
|
||||
$sAvailableIcons = '<?php'.PHP_EOL;
|
||||
$sAvailableIcons .= '// Generated and used by '.__METHOD__.PHP_EOL;
|
||||
$sAvailableIcons .= 'class '.$sCacheClass.PHP_EOL;
|
||||
$sAvailableIcons .= '{'.PHP_EOL;
|
||||
$sAvailableIcons .= ' static $sKey = '.var_export($sKey, true).';'.PHP_EOL;
|
||||
$sAvailableIcons .= ' static $aIconFiles = '.var_export($aFiles, true).';'.PHP_EOL;
|
||||
$sAvailableIcons .= '}'.PHP_EOL;
|
||||
file_put_contents($sCacheFile, $sAvailableIcons, LOCK_EX);
|
||||
}
|
||||
return $aFiles;
|
||||
}
|
||||
|
||||
static protected function _FindIconsOnDisk($sBaseDir, $sDir = '')
|
||||
{
|
||||
$aResult = array();
|
||||
// Populate automatically the list of icon files
|
||||
@@ -1378,7 +1409,7 @@ class RunTimeIconSelectionField extends DesignerIconSelectionField
|
||||
if (($sFile != '.') && ($sFile != '..') && ($sFile != 'lifecycle') && is_dir($sBaseDir.'/'.$sDir.'/'.$sFile))
|
||||
{
|
||||
$sDirSubPath = ($sDir == '') ? $sFile : $sDir.'/'.$sFile;
|
||||
$aResult = array_merge($aResult, self::FindIconsOnDisk($sBaseDir, $sDirSubPath));
|
||||
$aResult = array_merge($aResult, self::_FindIconsOnDisk($sBaseDir, $sDirSubPath));
|
||||
}
|
||||
if (preg_match("/\.(png|jpg|jpeg|gif)$/i", $sFile, $aMatches)) // png, jp(e)g and gif are considered valid
|
||||
{
|
||||
@@ -1408,8 +1439,12 @@ class RunTimeIconSelectionField extends DesignerIconSelectionField
|
||||
|
||||
public function GetDefaultValue($sClass = 'Contact')
|
||||
{
|
||||
$sIconPath = MetaModel::GetClassIcon($sClass, false);
|
||||
$sIcon = str_replace(utils::GetAbsoluteUrlModulesRoot(), '', $sIconPath);
|
||||
$sIcon = '';
|
||||
if (MetaModel::IsValidClass($sClass))
|
||||
{
|
||||
$sIconPath = MetaModel::GetClassIcon($sClass, false);
|
||||
$sIcon = str_replace(utils::GetAbsoluteUrlModulesRoot(), '', $sIconPath);
|
||||
}
|
||||
return $sIcon;
|
||||
}
|
||||
}
|
||||
@@ -1490,7 +1525,6 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
|
||||
public function AddSubForm($oSubForm, $sLabel, $sValue)
|
||||
{
|
||||
$idx = count($this->aSubForms);
|
||||
$this->aSubForms[] = array('form' => $oSubForm, 'label' => $sLabel, 'value' => $sValue);
|
||||
if ($sValue == $this->defaultRealValue)
|
||||
{
|
||||
@@ -1504,7 +1538,7 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
$sName = $this->oForm->GetFieldName($this->sCode);
|
||||
$sReadOnly = $this->IsReadOnly() ? 'disabled="disabled"' : '';
|
||||
|
||||
|
||||
$this->aCSSClasses[] = 'formSelector';
|
||||
|
||||
$sCSSClasses = '';
|
||||
@@ -1520,8 +1554,6 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
|
||||
if ($this->IsReadOnly())
|
||||
{
|
||||
$aSelected = array();
|
||||
$aHiddenValues = array();
|
||||
$sDisplayValue = '';
|
||||
$sHiddenValue = '';
|
||||
foreach($this->aSubForms as $iKey => $aFormData)
|
||||
@@ -1537,8 +1569,6 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
$sHtml = "<select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
|
||||
foreach($this->aSubForms as $iKey => $aFormData)
|
||||
{
|
||||
@@ -1554,7 +1584,6 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
{
|
||||
$sHtml .= '</td><td class="prop_icon prop_apply"><span title="Apply" class="ui-icon ui-icon-circle-check"/></td><td class="prop_icon prop_cancel"><span title="Revert" class="ui-icon ui-icon-circle-close"/></td></tr>';
|
||||
}
|
||||
|
||||
foreach($this->aSubForms as $sKey => $aFormData)
|
||||
{
|
||||
$sId = $this->oForm->GetFieldId($this->sCode);
|
||||
@@ -1580,25 +1609,7 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
$oSubForm->SetHierarchyPath($sPath);
|
||||
|
||||
$oSubForm->SetDisplayed($sKey == $this->defaultValue);
|
||||
$sState = ($sKey == $this->defaultValue) ? 'visible' : 'hidden';
|
||||
//$sHtml .= "</tbody><tbody data-selector=\"$sSelector\" data-path=\"$sPath\" data-state=\"$sState\" $sStyle>";
|
||||
$sHtml .= $oSubForm->RenderAsPropertySheet($oP, true);
|
||||
|
||||
$sState = $this->oForm->IsDisplayed() ? 'visible' : 'hidden';
|
||||
$sParentStyle = '';
|
||||
if ($oParent = $this->oForm->GetParentForm())
|
||||
{
|
||||
$sParentStyle = ($oParent->IsDisplayed()) ? '' : 'style="display:none"';
|
||||
$sParentSelector = $oParent->GetHierarchyParent();
|
||||
$sParentPath = $oParent->GetHierarchyPath();
|
||||
}
|
||||
else
|
||||
{
|
||||
$sParentSelector = '';
|
||||
$sParentPath = '';
|
||||
}
|
||||
|
||||
//$sHtml .= "</tbody><tbody data-selector=\"$sParentSelector\" data-path=\"$sParentPath\" data-state=\"$sState\" $sParentStyle>";
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1646,7 +1657,6 @@ EOF
|
||||
if ($selectedValue == $aFormData['value'])
|
||||
{
|
||||
$this->defaultValue =$iKey;
|
||||
$aDefaultValues = $this->oForm->GetDefaultValues();
|
||||
$oSubForm = $aFormData['form'];
|
||||
$oSubForm->SetDefaultValues($aAllDefaultValues);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class LoginWebPage
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -42,6 +42,7 @@ class LoginWebPage extends NiceWebPage
|
||||
const EXIT_CODE_WRONGCREDENTIALS = 3;
|
||||
const EXIT_CODE_MUSTBEADMIN = 4;
|
||||
const EXIT_CODE_PORTALUSERNOTAUTHORIZED = 5;
|
||||
const EXIT_CODE_NOTAUTHORIZED = 6;
|
||||
|
||||
protected static $sHandlerClass = __class__;
|
||||
public static function RegisterHandler($sClass)
|
||||
@@ -56,8 +57,13 @@ class LoginWebPage extends NiceWebPage
|
||||
|
||||
protected static $m_sLoginFailedMessage = '';
|
||||
|
||||
public function __construct($sTitle = 'iTop Login')
|
||||
public function __construct($sTitle = null)
|
||||
{
|
||||
if($sTitle === null)
|
||||
{
|
||||
$sTitle = Dict::S('UI:Login:Title');
|
||||
}
|
||||
|
||||
parent::__construct($sTitle);
|
||||
$this->SetStyleSheet();
|
||||
$this->add_header("Cache-control: no-cache");
|
||||
@@ -90,12 +96,12 @@ class LoginWebPage extends NiceWebPage
|
||||
$sLogo = 'itop-logo-external.png';
|
||||
$sBrandingLogo = 'login-logo.png';
|
||||
}
|
||||
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_VERSION);
|
||||
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
|
||||
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/'.$sLogo.'?itopversion='.ITOP_VERSION;
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlAppRoot().'images/'.$sLogo.'?t='.utils::GetCacheBusterTimestamp();
|
||||
if (file_exists(MODULESROOT.'branding/'.$sBrandingLogo))
|
||||
{
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/'.$sBrandingLogo.'?itopversion='.ITOP_VERSION;
|
||||
$sDisplayIcon = utils::GetAbsoluteUrlModulesRoot().'branding/'.$sBrandingLogo.'?t='.utils::GetCacheBusterTimestamp();
|
||||
}
|
||||
$this->add("<div id=\"login-logo\"><a href=\"".htmlentities($sIconUrl, ENT_QUOTES, 'UTF-8')."\"><img title=\"$sVersionShort\" src=\"$sDisplayIcon\"></a></div>\n");
|
||||
}
|
||||
@@ -112,7 +118,7 @@ class LoginWebPage extends NiceWebPage
|
||||
|
||||
case 'basic':
|
||||
case 'url':
|
||||
$this->add_header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
|
||||
$this->add_header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION));
|
||||
$this->add_header('HTTP/1.0 401 Unauthorized');
|
||||
$this->add_header('Content-type: text/html; charset=iso-8859-1');
|
||||
// Note: displayed when the user will click on Cancel
|
||||
@@ -189,7 +195,7 @@ class LoginWebPage extends NiceWebPage
|
||||
*/
|
||||
public function ForgotPwdLink()
|
||||
{
|
||||
$sUrl = '?loginop=forgot_pwd';
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot() . 'pages/UI.php?loginop=forgot_pwd';
|
||||
$sHtml = "<a href=\"$sUrl\" target=\"_blank\">".Dict::S('UI:Login:ForgotPwd')."</a>";
|
||||
return $sHtml;
|
||||
}
|
||||
@@ -224,7 +230,8 @@ class LoginWebPage extends NiceWebPage
|
||||
try
|
||||
{
|
||||
UserRights::Login($sAuthUser); // Set the user's language (if possible!)
|
||||
$oUser = UserRights::GetUserObject();
|
||||
/** @var UserInternal $oUser */
|
||||
$oUser = UserRights::GetUserObject();
|
||||
if ($oUser == null)
|
||||
{
|
||||
throw new Exception(Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser));
|
||||
@@ -248,15 +255,12 @@ class LoginWebPage extends NiceWebPage
|
||||
$sToken = substr(md5(APPROOT.uniqid()), 0, 16);
|
||||
$oUser->Set('reset_pwd_token', $sToken);
|
||||
CMDBObject::SetTrackInfo('Reset password');
|
||||
$oUser->AllowWrite(true);
|
||||
$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);
|
||||
@@ -430,6 +434,8 @@ EOF
|
||||
// Unset all of the session variables.
|
||||
unset($_SESSION['auth_user']);
|
||||
unset($_SESSION['login_mode']);
|
||||
unset($_SESSION['archive_mode']);
|
||||
unset($_SESSION['impersonate_user']);
|
||||
UserRights::_ResetSessionCache();
|
||||
// If it's desired to kill the session, also delete the session cookie.
|
||||
// Note: This will destroy the session, and not just the session data!
|
||||
@@ -572,96 +578,95 @@ EOF
|
||||
break;
|
||||
}
|
||||
$index++;
|
||||
|
||||
//echo "\nsLoginMode: $sLoginMode (user: $sAuthUser / pwd: $sAuthPwd\n)";
|
||||
if ($sLoginMode == '')
|
||||
}
|
||||
//echo "\nsLoginMode: $sLoginMode (user: $sAuthUser / pwd: $sAuthPwd\n)";
|
||||
if ($sLoginMode == '')
|
||||
{
|
||||
// First connection
|
||||
$sDesiredLoginMode = utils::ReadParam('login_mode');
|
||||
if (in_array($sDesiredLoginMode, $aAllowedLoginTypes))
|
||||
{
|
||||
// First connection
|
||||
$sDesiredLoginMode = utils::ReadParam('login_mode');
|
||||
if (in_array($sDesiredLoginMode, $aAllowedLoginTypes))
|
||||
$sLoginMode = $sDesiredLoginMode;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLoginMode = $aAllowedLoginTypes[0]; // First in the list...
|
||||
}
|
||||
if (array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER))
|
||||
{
|
||||
// X-Combodo-Ajax is a special header automatically added to all ajax requests
|
||||
// Let's reply that we're currently logged-out
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
exit;
|
||||
}
|
||||
if (($iOnExit == self::EXIT_HTTP_401) || ($sLoginMode == 'basic'))
|
||||
{
|
||||
header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION));
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
header('Content-type: text/html; charset=iso-8859-1');
|
||||
exit;
|
||||
}
|
||||
else if($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
if (($sAuthUser !== '') && ($sAuthPwd === null))
|
||||
{
|
||||
$sLoginMode = $sDesiredLoginMode;
|
||||
return self::EXIT_CODE_MISSINGPASSWORD;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLoginMode = $aAllowedLoginTypes[0]; // First in the list...
|
||||
}
|
||||
if (array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER))
|
||||
{
|
||||
// X-Combodo-Ajax is a special header automatically added to all ajax requests
|
||||
// Let's reply that we're currently logged-out
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
exit;
|
||||
return self::EXIT_CODE_MISSINGLOGIN;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage = self::NewLoginWebPage();
|
||||
$oPage->DisplayLoginForm( $sLoginMode, false /* no previous failed attempt */);
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $sLoginMode, $sAuthentication))
|
||||
{
|
||||
//echo "Check Credentials returned false for user $sAuthUser!";
|
||||
self::ResetSession();
|
||||
if (($iOnExit == self::EXIT_HTTP_401) || ($sLoginMode == 'basic'))
|
||||
{
|
||||
header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
|
||||
header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION));
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
header('Content-type: text/html; charset=iso-8859-1');
|
||||
exit;
|
||||
}
|
||||
else if($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
if (($sAuthUser !== '') && ($sAuthPwd === null))
|
||||
{
|
||||
return self::EXIT_CODE_MISSINGPASSWORD;
|
||||
}
|
||||
else
|
||||
{
|
||||
return self::EXIT_CODE_MISSINGLOGIN;
|
||||
}
|
||||
return self::EXIT_CODE_WRONGCREDENTIALS;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage = self::NewLoginWebPage();
|
||||
$oPage->DisplayLoginForm( $sLoginMode, false /* no previous failed attempt */);
|
||||
$oPage->DisplayLoginForm( $sLoginMode, true /* failed attempt */);
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, $sLoginMode, $sAuthentication))
|
||||
// User is Ok, let's save it in the session and proceed with normal login
|
||||
UserRights::Login($sAuthUser, $sAuthentication); // Login & set the user's language
|
||||
|
||||
if (MetaModel::GetConfig()->Get('log_usage'))
|
||||
{
|
||||
//echo "Check Credentials returned false for user $sAuthUser!";
|
||||
self::ResetSession();
|
||||
if (($iOnExit == self::EXIT_HTTP_401) || ($sLoginMode == 'basic'))
|
||||
{
|
||||
header('WWW-Authenticate: Basic realm="'.Dict::Format('UI:iTopVersion:Short', ITOP_VERSION));
|
||||
header('HTTP/1.0 401 Unauthorized');
|
||||
header('Content-type: text/html; charset=iso-8859-1');
|
||||
exit;
|
||||
}
|
||||
else if($iOnExit == self::EXIT_RETURN)
|
||||
{
|
||||
return self::EXIT_CODE_WRONGCREDENTIALS;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage = self::NewLoginWebPage();
|
||||
$oPage->DisplayLoginForm( $sLoginMode, true /* failed attempt */);
|
||||
$oPage->output();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// User is Ok, let's save it in the session and proceed with normal login
|
||||
UserRights::Login($sAuthUser, $sAuthentication); // Login & set the user's language
|
||||
|
||||
if (MetaModel::GetConfig()->Get('log_usage'))
|
||||
{
|
||||
$oLog = new EventLoginUsage();
|
||||
$oLog->Set('userinfo', UserRights::GetUser());
|
||||
$oLog->Set('user_id', UserRights::GetUserObject()->GetKey());
|
||||
$oLog->Set('message', 'Successful login');
|
||||
$oLog->DBInsertNoReload();
|
||||
}
|
||||
|
||||
$_SESSION['auth_user'] = $sAuthUser;
|
||||
$_SESSION['login_mode'] = $sLoginMode;
|
||||
UserRights::_InitSessionCache();
|
||||
$oLog = new EventLoginUsage();
|
||||
$oLog->Set('userinfo', UserRights::GetUser());
|
||||
$oLog->Set('user_id', UserRights::GetUserObject()->GetKey());
|
||||
$oLog->Set('message', 'Successful login');
|
||||
$oLog->DBInsertNoReload();
|
||||
}
|
||||
|
||||
$_SESSION['auth_user'] = $sAuthUser;
|
||||
$_SESSION['login_mode'] = $sLoginMode;
|
||||
UserRights::_InitSessionCache();
|
||||
}
|
||||
}
|
||||
return self::EXIT_CODE_OK;
|
||||
@@ -697,26 +702,36 @@ EOF
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* - 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_...)
|
||||
*
|
||||
* @return int|mixed|string
|
||||
* @throws \Exception
|
||||
*/
|
||||
static function DoLogin($bMustBeAdmin = false, $bIsAllowedToPortalUsers = false, $iOnExit = self::EXIT_PROMPT)
|
||||
{
|
||||
$sRequestedPortalId = $bIsAllowedToPortalUsers ? 'legacy_portal' : 'backoffice';
|
||||
return self::DoLoginEx($sRequestedPortalId, $bMustBeAdmin, $iOnExit);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the user is already authentified, if yes, then performs some additional validations to redirect towards the desired "portal"
|
||||
* Check if the user is already authentified, if yes, then performs some additional validations to redirect towards
|
||||
* the desired "portal"
|
||||
*
|
||||
* @param string|null $sRequestedPortalId The requested "portal" interface, null for any
|
||||
* @param bool $bMustBeAdmin Whether or not the user must be an admin to access the current page
|
||||
* @param int iOnExit What action to take if the user is not logged on (one of the class constants EXIT_...)
|
||||
*
|
||||
* @return int|mixed|string
|
||||
* @throws \Exception
|
||||
*/
|
||||
static function DoLoginEx($sRequestedPortalId = null, $bMustBeAdmin = false, $iOnExit = self::EXIT_PROMPT)
|
||||
{
|
||||
|
||||
@@ -61,11 +61,23 @@ require_once(APPROOT."/application/user.dashboard.class.inc.php");
|
||||
|
||||
class ApplicationMenu
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
static $bAdditionalMenusLoaded = false;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
static $aRootMenus = array();
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
static $aMenusIndex = array();
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
static $sFavoriteSiloQuery = 'SELECT Organization';
|
||||
|
||||
|
||||
static public function LoadAdditionalMenus()
|
||||
{
|
||||
if (!self::$bAdditionalMenusLoaded)
|
||||
@@ -96,7 +108,7 @@ class ApplicationMenu
|
||||
/**
|
||||
* 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
|
||||
* @return void
|
||||
*/
|
||||
static public function SetFavoriteSiloQuery($sOQL)
|
||||
{
|
||||
@@ -111,11 +123,34 @@ class ApplicationMenu
|
||||
{
|
||||
return self::$sFavoriteSiloQuery;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check wether a menu Id is enabled or not
|
||||
* @param $sMenuId
|
||||
* @throws DictExceptionMissingString
|
||||
*/
|
||||
static public function CheckMenuIdEnabled($sMenuId)
|
||||
{
|
||||
self::LoadAdditionalMenus();
|
||||
$oMenuNode = self::GetMenuNode(self::GetMenuIndexById($sMenuId));
|
||||
if (is_null($oMenuNode) || !$oMenuNode->IsEnabled())
|
||||
{
|
||||
require_once(APPROOT.'/setup/setuppage.class.inc.php');
|
||||
$oP = new SetupPage(Dict::S('UI:PageTitle:FatalError'));
|
||||
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessRestricted')."</h1>\n");
|
||||
$oP->p("<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/logoff.php\">".Dict::S('UI:LogOffMenu')."</a>");
|
||||
$oP->output();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function to add a menu entry into the application, can be called during the definition
|
||||
* of the data model objects
|
||||
* @param MenuNode $oMenuNode
|
||||
* @param $iParentIndex
|
||||
* @param $fRank
|
||||
* @return int
|
||||
*/
|
||||
static public function InsertMenu(MenuNode $oMenuNode, $iParentIndex, $fRank)
|
||||
{
|
||||
@@ -148,6 +183,7 @@ class ApplicationMenu
|
||||
// the menu already exists, let's combine the conditions that make it visible
|
||||
self::$aMenusIndex[$index]['node']->AddCondition($oMenuNode);
|
||||
}
|
||||
|
||||
return $index;
|
||||
}
|
||||
|
||||
@@ -159,9 +195,12 @@ class ApplicationMenu
|
||||
self::LoadAdditionalMenus();
|
||||
return self::$aMenusIndex;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Entry point to display the whole menu into the web page, used by iTopWebPage
|
||||
* @param $oPage
|
||||
* @param $aExtraParams
|
||||
* @throws DictExceptionMissingString
|
||||
*/
|
||||
static public function DisplayMenu($oPage, $aExtraParams)
|
||||
{
|
||||
@@ -172,34 +211,65 @@ class ApplicationMenu
|
||||
$iActiveMenu = self::GetMenuIndexById(self::GetActiveNodeId());
|
||||
foreach(self::$aRootMenus as $aMenu)
|
||||
{
|
||||
if (!self::CanDisplayMenu($aMenu)) { continue; }
|
||||
$oMenuNode = self::GetMenuNode($aMenu['index']);
|
||||
if (!$oMenuNode->IsEnabled()) continue; // Don't display a non-enabled menu
|
||||
$oPage->AddToMenu('<h3>'.$oMenuNode->GetTitle().'</h3>');
|
||||
$oPage->AddToMenu('<h3 id="'.utils::GetSafeId('AccordionMenu_'.$oMenuNode->GetMenuID()).'">'.$oMenuNode->GetTitle().'</h3>');
|
||||
$oPage->AddToMenu('<div>');
|
||||
$oPage->AddToMenu('<ul>');
|
||||
$aChildren = self::GetChildren($aMenu['index']);
|
||||
if (count($aChildren) > 0)
|
||||
$bActive = self::DisplaySubMenu($oPage, $aChildren, $aExtraParams, $iActiveMenu);
|
||||
$oPage->AddToMenu('</ul>');
|
||||
if ($bActive)
|
||||
{
|
||||
$oPage->AddToMenu('<ul>');
|
||||
$bActive = self::DisplaySubMenu($oPage, $aChildren, $aExtraParams, $iActiveMenu);
|
||||
$oPage->AddToMenu('</ul>');
|
||||
if ($bActive)
|
||||
{
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
// Accordion Menu
|
||||
$("#accordion").css({display:'block'}).accordion({ header: "h3", navigation: true, heightStyle: "content", collapsible: true, active: $iAccordion, icons: false, animate:true }); // collapsible will be enabled once the item will be selected
|
||||
// Accordion Menu
|
||||
$("#accordion").css({display:'block'}).accordion({ header: "h3", navigation: true, heightStyle: "content", collapsible: true, active: $iAccordion, icons: false, animate:true }); // collapsible will be enabled once the item will be selected
|
||||
EOF
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
$oPage->AddToMenu('</div>');
|
||||
$iAccordion++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Recursively check if the menu and at least one of his sub-menu is enabled
|
||||
* @param array $aMenu menu entry
|
||||
* @return bool true if at least one menu is enabled
|
||||
*/
|
||||
static private function CanDisplayMenu($aMenu)
|
||||
{
|
||||
$oMenuNode = self::GetMenuNode($aMenu['index']);
|
||||
if ($oMenuNode->IsEnabled())
|
||||
{
|
||||
$aChildren = self::GetChildren($aMenu['index']);
|
||||
if (count($aChildren) > 0)
|
||||
{
|
||||
foreach($aChildren as $aSubMenu)
|
||||
{
|
||||
if (self::CanDisplayMenu($aSubMenu))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the display of the sub-menus (called recursively if necessary)
|
||||
* @param WebPage $oPage
|
||||
* @param array $aMenus
|
||||
* @param array $aExtraParams
|
||||
* @param int $iActiveMenu
|
||||
* @return true if the currently selected menu is one of the submenus
|
||||
* @throws DictExceptionMissingString
|
||||
*/
|
||||
static protected function DisplaySubMenu($oPage, $aMenus, $aExtraParams, $iActiveMenu = -1)
|
||||
{
|
||||
@@ -217,13 +287,12 @@ EOF
|
||||
$sHyperlink = $oMenu->GetHyperlink($aExtraParams);
|
||||
if ($sHyperlink != '')
|
||||
{
|
||||
$oPage->AddToMenu('<li'.$sCSSClass.'><a href="'.$oMenu->GetHyperlink($aExtraParams).'">'.$oMenu->GetTitle().'</a></li>');
|
||||
$oPage->AddToMenu('<li id="'.utils::GetSafeId('AccordionMenu_'.$oMenu->GetMenuID()).'" '.$sCSSClass.'><a href="'.$oMenu->GetHyperlink($aExtraParams).'">'.$oMenu->GetTitle().'</a></li>');
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->AddToMenu('<li'.$sCSSClass.'>'.$oMenu->GetTitle().'</li>');
|
||||
$oPage->AddToMenu('<li id="'.utils::GetSafeId('AccordionMenu_'.$oMenu->GetMenuID()).'" '.$sCSSClass.'>'.$oMenu->GetTitle().'</li>');
|
||||
}
|
||||
$aCurrentMenu = self::$aMenusIndex[$index];
|
||||
if ($iActiveMenu == $index)
|
||||
{
|
||||
$bActive = true;
|
||||
@@ -238,8 +307,12 @@ EOF
|
||||
}
|
||||
return $bActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to sort the menus based on their rank
|
||||
* @param $a
|
||||
* @param $b
|
||||
* @return int
|
||||
*/
|
||||
static public function CompareOnRank($a, $b)
|
||||
{
|
||||
@@ -254,17 +327,21 @@ EOF
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to retrieve the MenuNodeObject based on its ID
|
||||
* Helper function to retrieve the MenuNode Object based on its ID
|
||||
* @param int $index
|
||||
* @return MenuNode|null
|
||||
*/
|
||||
static public function GetMenuNode($index)
|
||||
{
|
||||
return isset(self::$aMenusIndex[$index]) ? self::$aMenusIndex[$index]['node'] : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to get the list of child(ren) of a menu
|
||||
* @param int $index
|
||||
* @return array
|
||||
*/
|
||||
static public function GetChildren($index)
|
||||
{
|
||||
@@ -303,8 +380,11 @@ EOF
|
||||
$sMenuId = self::GetDefaultMenuId();
|
||||
}
|
||||
return $sMenuId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|string
|
||||
*/
|
||||
static public function GetDefaultMenuId()
|
||||
{
|
||||
static $sDefaultMenuId = null;
|
||||
@@ -320,6 +400,25 @@ EOF
|
||||
}
|
||||
return $sDefaultMenuId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sMenuId
|
||||
* @return string
|
||||
*/
|
||||
static public function GetRootMenuId($sMenuId)
|
||||
{
|
||||
$iMenuIndex = self::GetMenuIndexById($sMenuId);
|
||||
if ($iMenuIndex == -1)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
$oMenu = ApplicationMenu::GetMenuNode($iMenuIndex);
|
||||
while ($oMenu->GetParentIndex() != -1)
|
||||
{
|
||||
$oMenu = ApplicationMenu::GetMenuNode($oMenu->GetParentIndex());
|
||||
}
|
||||
return $oMenu->GetMenuId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -350,8 +449,17 @@ EOF
|
||||
*/
|
||||
abstract class MenuNode
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sMenuId;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $index;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $iParentIndex;
|
||||
|
||||
/**
|
||||
@@ -378,7 +486,7 @@ abstract class MenuNode
|
||||
* Stimulus to check: if the user can 'apply' this stimulus, then she/he can see this menu
|
||||
*/
|
||||
protected $m_aEnableStimuli;
|
||||
|
||||
|
||||
/**
|
||||
* Create a menu item, sets the condition to have it displayed 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)
|
||||
@@ -388,9 +496,8 @@ abstract class MenuNode
|
||||
* @param mixed $iActionCode 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...
|
||||
* @param string $sEnableStimulus The user can see this menu if she/he has enough rights to apply this stimulus
|
||||
* @return MenuNode
|
||||
*/
|
||||
public function __construct($sMenuId, $iParentIndex = -1, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
public function __construct($sMenuId, $iParentIndex = -1, $fRank = 0.0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
{
|
||||
$this->sMenuId = $sMenuId;
|
||||
$this->iParentIndex = $iParentIndex;
|
||||
@@ -409,21 +516,43 @@ abstract class MenuNode
|
||||
$this->index = ApplicationMenu::InsertMenu($this, $iParentIndex, $fRank);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function ReflectionProperties()
|
||||
{
|
||||
return $this->aReflectionProperties;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetMenuId()
|
||||
{
|
||||
return $this->sMenuId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function GetParentIndex()
|
||||
{
|
||||
return $this->iParentIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws DictExceptionMissingString
|
||||
*/
|
||||
public function GetTitle()
|
||||
{
|
||||
return Dict::S("Menu:$this->sMenuId", str_replace('_', ' ', $this->sMenuId));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws DictExceptionMissingString
|
||||
*/
|
||||
public function GetLabel()
|
||||
{
|
||||
$sRet = Dict::S("Menu:$this->sMenuId+", "");
|
||||
@@ -442,7 +571,10 @@ abstract class MenuNode
|
||||
}
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function GetIndex()
|
||||
{
|
||||
return $this->index;
|
||||
@@ -458,6 +590,10 @@ abstract class MenuNode
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $aExtraParams
|
||||
* @return string
|
||||
*/
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
$aExtraParams['c[menu]'] = $this->GetMenuId();
|
||||
@@ -500,7 +636,10 @@ abstract class MenuNode
|
||||
}
|
||||
if ($this->m_aEnableActions[$index] != null)
|
||||
{
|
||||
// Menus access rights ignore the archive mode
|
||||
utils::PushArchiveMode(false);
|
||||
$iResult = UserRights::IsActionAllowed($sClass, $this->m_aEnableActions[$index]);
|
||||
utils::PopArchiveMode();
|
||||
if (!($iResult & $this->m_aEnableActionResults[$index]))
|
||||
{
|
||||
return false;
|
||||
@@ -515,9 +654,19 @@ abstract class MenuNode
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param array $aExtraParams
|
||||
* @return mixed
|
||||
*/
|
||||
public abstract function RenderContent(WebPage $oPage, $aExtraParams = array());
|
||||
|
||||
|
||||
/**
|
||||
* @param $sHyperlink
|
||||
* @param $aExtraParams
|
||||
* @return string
|
||||
*/
|
||||
protected function AddParams($sHyperlink, $aExtraParams)
|
||||
{
|
||||
if (count($aExtraParams) > 0)
|
||||
@@ -551,13 +700,17 @@ class MenuGroup extends MenuNode
|
||||
* @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 MenuGroup
|
||||
* @param string $sEnableStimulus
|
||||
*/
|
||||
public function __construct($sMenuId, $fRank, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
{
|
||||
parent::__construct($sMenuId, -1 /* no parent, groups are at root level */, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param array $aExtraParams
|
||||
*/
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
assert(false); // Shall never be called, groups do not display any content
|
||||
@@ -570,6 +723,9 @@ class MenuGroup extends MenuNode
|
||||
*/
|
||||
class TemplateMenuNode extends MenuNode
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sTemplateFile;
|
||||
|
||||
/**
|
||||
@@ -581,23 +737,34 @@ class TemplateMenuNode extends MenuNode
|
||||
* @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
|
||||
* @param string $sEnableStimulus
|
||||
*/
|
||||
public function __construct($sMenuId, $sTemplateFile, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
public function __construct($sMenuId, $sTemplateFile, $iParentIndex, $fRank = 0.0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
{
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
$this->sTemplateFile = $sTemplateFile;
|
||||
$this->aReflectionProperties['template_file'] = $sTemplateFile;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $aExtraParams
|
||||
* @return string
|
||||
*/
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
if ($this->sTemplateFile == '') return '';
|
||||
return parent::GetHyperlink($aExtraParams);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param array $aExtraParams
|
||||
* @return mixed|void
|
||||
* @throws DictExceptionMissingString
|
||||
*/
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
|
||||
$sTemplate = @file_get_contents($this->sTemplateFile);
|
||||
if ($sTemplate !== false)
|
||||
{
|
||||
@@ -618,17 +785,29 @@ class TemplateMenuNode extends MenuNode
|
||||
*/
|
||||
class OQLMenuNode extends MenuNode
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sPageTitle;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sOQL;
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $bSearch;
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
protected $bSearchFormOpen;
|
||||
|
||||
/**
|
||||
* Extra parameters to be passed to the display block to fine tune its appearence
|
||||
*/
|
||||
protected $m_aParams;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a menu item based on an OQL query 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)
|
||||
@@ -639,22 +818,16 @@ class OQLMenuNode extends MenuNode
|
||||
* @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
|
||||
* @param string $sEnableStimulus
|
||||
* @param bool $bSearchFormOpen
|
||||
*/
|
||||
public function __construct($sMenuId, $sOQL, $iParentIndex, $fRank = 0, $bSearch = false, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null, $bSearchFormOpen = null)
|
||||
public function __construct($sMenuId, $sOQL, $iParentIndex, $fRank = 0.0, $bSearch = false, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null, $bSearchFormOpen = null)
|
||||
{
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
$this->sPageTitle = "Menu:$sMenuId+";
|
||||
$this->sOQL = $sOQL;
|
||||
$this->bSearch = $bSearch;
|
||||
if ($bSearchFormOpen == null)
|
||||
{
|
||||
$this->bSearchFormOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open');
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->bSearchFormOpen = $bSearchFormOpen;
|
||||
}
|
||||
$this->bSearchFormOpen = $bSearchFormOpen;
|
||||
$this->m_aParams = array();
|
||||
$this->aReflectionProperties['oql'] = $sOQL;
|
||||
$this->aReflectionProperties['do_search'] = $bSearch;
|
||||
@@ -674,9 +847,18 @@ class OQLMenuNode extends MenuNode
|
||||
$this->aReflectionProperties[$sKey] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param array $aExtraParams
|
||||
* @return mixed|void
|
||||
* @throws CoreException
|
||||
* @throws DictExceptionMissingString
|
||||
* @throws OQLException
|
||||
*/
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
|
||||
OQLMenuNode::RenderOQLSearch
|
||||
(
|
||||
$this->sOQL,
|
||||
@@ -690,6 +872,19 @@ class OQLMenuNode extends MenuNode
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sOql
|
||||
* @param $sTitle
|
||||
* @param $sUsageId
|
||||
* @param $bSearchPane
|
||||
* @param $bSearchOpen
|
||||
* @param WebPage $oPage
|
||||
* @param array $aExtraParams
|
||||
* @param bool $bEnableBreadcrumb
|
||||
* @throws CoreException
|
||||
* @throws DictExceptionMissingString
|
||||
* @throws OQLException
|
||||
*/
|
||||
public static function RenderOQLSearch($sOql, $sTitle, $sUsageId, $bSearchPane, $bSearchOpen, WebPage $oPage, $aExtraParams = array(), $bEnableBreadcrumb = false)
|
||||
{
|
||||
$sUsageId = utils::GetSafeId($sUsageId);
|
||||
@@ -725,22 +920,28 @@ class OQLMenuNode extends MenuNode
|
||||
*/
|
||||
class SearchMenuNode extends MenuNode
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sPageTitle;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sClass;
|
||||
|
||||
|
||||
/**
|
||||
* Create a menu item based on an OQL query 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 $sClass The class of objects to search for
|
||||
* @param string $sPageTitle Title displayed into the page's content (will be looked-up in the dictionnary for translation)
|
||||
* @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 bool $bSearch (not used)
|
||||
* @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
|
||||
* @param string $sEnableStimulus
|
||||
*/
|
||||
public function __construct($sMenuId, $sClass, $iParentIndex, $fRank = 0, $bSearch = false, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
public function __construct($sMenuId, $sClass, $iParentIndex, $fRank = 0.0, $bSearch = false, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
{
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
$this->sPageTitle = "Menu:$sMenuId+";
|
||||
@@ -748,12 +949,20 @@ class SearchMenuNode extends MenuNode
|
||||
$this->aReflectionProperties['class'] = $sClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param array $aExtraParams
|
||||
* @return mixed|void
|
||||
* @throws DictExceptionMissingString
|
||||
* @throws Exception
|
||||
*/
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
|
||||
$oPage->SetBreadCrumbEntry("menu-".$this->sMenuId, $this->GetTitle(), '', '', utils::GetAbsoluteUrlAppRoot().'images/search.png');
|
||||
|
||||
$oSearch = new DBObjectSearch($this->sClass);
|
||||
$aParams = array_merge(array('open' => true, 'table_id' => 'Menu_'.utils::GetSafeId($this->GetMenuId())), $aExtraParams);
|
||||
$aParams = array_merge(array('table_id' => 'Menu_'.utils::GetSafeId($this->GetMenuId())), $aExtraParams);
|
||||
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
|
||||
$oBlock->Display($oPage, 0);
|
||||
}
|
||||
@@ -768,8 +977,11 @@ class SearchMenuNode extends MenuNode
|
||||
*/
|
||||
class WebPageMenuNode extends MenuNode
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sHyperlink;
|
||||
|
||||
|
||||
/**
|
||||
* Create a menu item that points to any web page (not only UI.php)
|
||||
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
|
||||
@@ -779,21 +991,29 @@ class WebPageMenuNode extends MenuNode
|
||||
* @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
|
||||
* @param string $sEnableStimulus
|
||||
*/
|
||||
public function __construct($sMenuId, $sHyperlink, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
public function __construct($sMenuId, $sHyperlink, $iParentIndex, $fRank = 0.0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
{
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
$this->sHyperlink = $sHyperlink;
|
||||
$this->aReflectionProperties['url'] = $sHyperlink;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aExtraParams
|
||||
* @return string
|
||||
*/
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
$aExtraParams['c[menu]'] = $this->GetMenuId();
|
||||
return $this->AddParams( $this->sHyperlink, $aExtraParams);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param array $aExtraParams
|
||||
*/
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
assert(false); // Shall never be called, the external web page will handle the display by itself
|
||||
@@ -808,8 +1028,11 @@ class WebPageMenuNode extends MenuNode
|
||||
*/
|
||||
class NewObjectMenuNode extends MenuNode
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sClass;
|
||||
|
||||
|
||||
/**
|
||||
* Create a menu item that points to the URL for creating a new object, the menu will be added only if the current user has enough
|
||||
* rights to create such an object (or an object of a child class)
|
||||
@@ -817,15 +1040,22 @@ class NewObjectMenuNode extends MenuNode
|
||||
* @param string $sClass URL to the page to load. Use relative URL if you want to keep the application portable !
|
||||
* @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
|
||||
* @return MenuNode
|
||||
* @param string $sEnableClass
|
||||
* @param int|null $iActionCode
|
||||
* @param int $iAllowedResults
|
||||
* @param string $sEnableStimulus
|
||||
*/
|
||||
public function __construct($sMenuId, $sClass, $iParentIndex, $fRank = 0)
|
||||
public function __construct($sMenuId, $sClass, $iParentIndex, $fRank = 0.0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
{
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank);
|
||||
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
|
||||
$this->sClass = $sClass;
|
||||
$this->aReflectionProperties['class'] = $sClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $aExtraParams
|
||||
* @return string
|
||||
*/
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=new&class='.$this->sClass;
|
||||
@@ -836,6 +1066,7 @@ class NewObjectMenuNode extends MenuNode
|
||||
/**
|
||||
* Overload the check of the "enable" state of this menu to take into account
|
||||
* derived classes of objects
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function IsEnabled()
|
||||
{
|
||||
@@ -854,7 +1085,12 @@ class NewObjectMenuNode extends MenuNode
|
||||
}
|
||||
}
|
||||
return $bActionIsAllowed;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param string[] $aExtraParams
|
||||
*/
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
assert(false); // Shall never be called, the external web page will handle the display by itself
|
||||
@@ -867,32 +1103,44 @@ require_once(APPROOT.'application/dashboard.class.inc.php');
|
||||
*/
|
||||
class DashboardMenuNode extends MenuNode
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
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 string $sDashboardFile
|
||||
* @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
|
||||
* @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS
|
||||
* @param string $sEnableStimulus
|
||||
*/
|
||||
public function __construct($sMenuId, $sDashboardFile, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
public function __construct($sMenuId, $sDashboardFile, $iParentIndex, $fRank = 0.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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string[] $aExtraParams
|
||||
* @return string
|
||||
*/
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
if ($this->sDashboardFile == '') return '';
|
||||
return parent::GetHyperlink($aExtraParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null|RuntimeDashboard
|
||||
* @throws CoreException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetDashboard()
|
||||
{
|
||||
$sDashboardDefinition = @file_get_contents($this->sDashboardFile);
|
||||
@@ -924,8 +1172,15 @@ class DashboardMenuNode extends MenuNode
|
||||
return $oDashboard;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param string[] $aExtraParams
|
||||
* @throws CoreException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
|
||||
$oDashboard = $this->GetDashboard();
|
||||
if ($oDashboard != null)
|
||||
{
|
||||
@@ -999,7 +1254,12 @@ EOF
|
||||
$oPage->p("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @throws CoreException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function RenderEditor(WebPage $oPage)
|
||||
{
|
||||
$oDashboard = $this->GetDashboard();
|
||||
@@ -1012,7 +1272,11 @@ EOF
|
||||
$oPage->p("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $oDashlet
|
||||
* @throws Exception
|
||||
*/
|
||||
public function AddDashlet($oDashlet)
|
||||
{
|
||||
$oDashboard = $this->GetDashboard();
|
||||
@@ -1034,15 +1298,28 @@ EOF
|
||||
*/
|
||||
class ShortcutContainerMenuNode extends MenuNode
|
||||
{
|
||||
/**
|
||||
* @param string[] $aExtraParams
|
||||
* @return string
|
||||
*/
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param string[] $aExtraParams
|
||||
* @return mixed|void
|
||||
*/
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws CoreException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function PopulateChildMenus()
|
||||
{
|
||||
// Load user shortcuts in DB
|
||||
@@ -1054,7 +1331,7 @@ class ShortcutContainerMenuNode extends MenuNode
|
||||
while ($oShortcut = $oBMSet->Fetch())
|
||||
{
|
||||
$sName = $this->GetMenuId().'_'.$oShortcut->GetKey();
|
||||
$oShortcutMenu = new ShortcutMenuNode($sName, $oShortcut, $this->GetIndex(), $fRank++);
|
||||
new ShortcutMenuNode($sName, $oShortcut, $this->GetIndex(), $fRank++);
|
||||
}
|
||||
|
||||
// Complete the tree
|
||||
@@ -1070,8 +1347,11 @@ require_once(APPROOT.'application/shortcut.class.inc.php');
|
||||
*/
|
||||
class ShortcutMenuNode extends MenuNode
|
||||
{
|
||||
/**
|
||||
* @var Shortcut
|
||||
*/
|
||||
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)
|
||||
@@ -1081,15 +1361,20 @@ class ShortcutMenuNode extends MenuNode
|
||||
* @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
|
||||
* @param string $sEnableStimulus
|
||||
*/
|
||||
public function __construct($sMenuId, $oShortcut, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null)
|
||||
public function __construct($sMenuId, $oShortcut, $iParentIndex, $fRank = 0.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();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string[] $aExtraParams
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function GetHyperlink($aExtraParams)
|
||||
{
|
||||
$sContext = $this->oShortcut->Get('context');
|
||||
@@ -1105,16 +1390,31 @@ class ShortcutMenuNode extends MenuNode
|
||||
return parent::GetHyperlink($aExtraParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param string[] $aExtraParams
|
||||
* @return mixed|void
|
||||
* @throws DictExceptionMissingString
|
||||
*/
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
|
||||
$this->oShortcut->RenderContent($oPage, $aExtraParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function GetTitle()
|
||||
{
|
||||
return $this->oShortcut->Get('name');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function GetLabel()
|
||||
{
|
||||
return $this->oShortcut->Get('name');
|
||||
|
||||
@@ -37,10 +37,11 @@ class NiceWebPage extends WebPage
|
||||
{
|
||||
parent::__construct($s_title, $bPrintable);
|
||||
$this->m_aReadyScripts = array();
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-1.10.0.min.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate-1.2.1.min.js'); // Needed since many other plugins still rely on oldies like $.browser
|
||||
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/ui-lightness/jquery-ui-1.10.3.custom.min.css');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-ui-1.10.3.custom.min.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-1.12.4.min.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-migrate-1.4.1.min.js'); // Needed since many other plugins still rely on oldies like $.browser
|
||||
$this->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/ui-lightness/jquery-ui-1.11.4.custom.css');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery-ui-1.11.4.custom.min.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/utils.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/hovertip.js');
|
||||
// table sorting
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.tablesorter.js');
|
||||
@@ -50,7 +51,25 @@ class NiceWebPage extends WebPage
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/datatable.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.positionBy.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.popupmenu.js');
|
||||
$this->add_ready_script(
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/searchformforeignkeys.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/latinise/latinise.min.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_handler.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_handler_history.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_raw.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_string.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_external_field.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_numeric.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_enum.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_external_key.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_hierarchical_key.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_abstract.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date.js');
|
||||
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_time.js');
|
||||
|
||||
$this->add_dict_entries('UI:Combo');
|
||||
|
||||
$this->add_ready_script(
|
||||
<<< EOF
|
||||
//add new widget called TruncatedList to properly display truncated lists when they are sorted
|
||||
$.tablesorter.addWidget({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* Class PortalWebPage
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2013 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -96,15 +96,88 @@ class PortalWebPage extends NiceWebPage
|
||||
$this->add_linked_script("../js/ckeditor/ckeditor.js");
|
||||
$this->add_linked_script("../js/ckeditor/adapters/jquery.js");
|
||||
|
||||
$this->add_linked_script("../js/jquery-ui-timepicker-addon.js");
|
||||
$this->add_linked_script("../js/jquery-ui-timepicker-addon-i18n.min.js");
|
||||
$this->add_linked_stylesheet("../css/jquery-ui-timepicker-addon.css");
|
||||
|
||||
$sJSDisconnectedMessage = json_encode(Dict::S('UI:DisconnectedDlgMessage'));
|
||||
$sJSTitle = json_encode(Dict::S('UI:DisconnectedDlgTitle'));
|
||||
$sJSLoginAgain = json_encode(Dict::S('UI:LoginAgain'));
|
||||
$sJSStayOnThePage = json_encode(Dict::S('UI:StayOnThePage'));
|
||||
$sJSDaysMin = json_encode(array(Dict::S('DayOfWeek-Sunday-Min'), Dict::S('DayOfWeek-Monday-Min'), Dict::S('DayOfWeek-Tuesday-Min'), Dict::S('DayOfWeek-Wednesday-Min'),
|
||||
Dict::S('DayOfWeek-Thursday-Min'), Dict::S('DayOfWeek-Friday-Min'), Dict::S('DayOfWeek-Saturday-Min')));
|
||||
$sJSMonthsShort = json_encode(array(Dict::S('Month-01-Short'), Dict::S('Month-02-Short'), Dict::S('Month-03-Short'), Dict::S('Month-04-Short'), Dict::S('Month-05-Short'), Dict::S('Month-06-Short'),
|
||||
Dict::S('Month-07-Short'), Dict::S('Month-08-Short'), Dict::S('Month-09-Short'), Dict::S('Month-10-Short'), Dict::S('Month-11-Short'), Dict::S('Month-12-Short')));
|
||||
$iFirstDayOfWeek = (int) Dict::S('Calendar-FirstDayOfWeek');
|
||||
$aDaysMin = array(Dict::S('DayOfWeek-Sunday-Min'), Dict::S('DayOfWeek-Monday-Min'), Dict::S('DayOfWeek-Tuesday-Min'), Dict::S('DayOfWeek-Wednesday-Min'),
|
||||
Dict::S('DayOfWeek-Thursday-Min'), Dict::S('DayOfWeek-Friday-Min'), Dict::S('DayOfWeek-Saturday-Min'));
|
||||
$aMonthsShort = array(Dict::S('Month-01-Short'), Dict::S('Month-02-Short'), Dict::S('Month-03-Short'), Dict::S('Month-04-Short'), Dict::S('Month-05-Short'), Dict::S('Month-06-Short'),
|
||||
Dict::S('Month-07-Short'), Dict::S('Month-08-Short'), Dict::S('Month-09-Short'), Dict::S('Month-10-Short'), Dict::S('Month-11-Short'), Dict::S('Month-12-Short'));
|
||||
$sTimeFormat = AttributeDateTime::GetFormat()->ToTimeFormat();
|
||||
$oTimeFormat = new DateTimeFormat($sTimeFormat);
|
||||
$sJSLangShort = json_encode(strtolower(substr(Dict::GetUserLanguage(), 0, 2)));
|
||||
|
||||
// Date picker options
|
||||
$aPickerOptions = array(
|
||||
'showOn' => 'button',
|
||||
'buttonImage' => '../images/calendar.png',
|
||||
'buttonImageOnly' => true,
|
||||
'dateFormat' => AttributeDate::GetFormat()->ToDatePicker(),
|
||||
'constrainInput' => false,
|
||||
'changeMonth' => true,
|
||||
'changeYear' => true,
|
||||
'dayNamesMin' => $aDaysMin,
|
||||
'monthNamesShort' => $aMonthsShort,
|
||||
'firstDay' => (int) Dict::S('Calendar-FirstDayOfWeek'),
|
||||
);
|
||||
$sJSDatePickerOptions = json_encode($aPickerOptions);
|
||||
|
||||
// Time picker additional options
|
||||
$aPickerOptions['showOn'] = '';
|
||||
$aPickerOptions['buttonImage'] = null;
|
||||
$aPickerOptions['timeFormat'] = $oTimeFormat->ToDatePicker();
|
||||
$aPickerOptions['controlType'] = 'select';
|
||||
$aPickerOptions['closeText'] = Dict::S('UI:Button:Ok');
|
||||
$sJSDateTimePickerOptions = json_encode($aPickerOptions);
|
||||
if ($sJSLangShort != '"en"')
|
||||
{
|
||||
// More options that cannot be passed via json_encode since they must be evaluated client-side
|
||||
$aMoreJSOptions = ",
|
||||
'timeText': $.timepicker.regional[$sJSLangShort].timeText,
|
||||
'hourText': $.timepicker.regional[$sJSLangShort].hourText,
|
||||
'minuteText': $.timepicker.regional[$sJSLangShort].minuteText,
|
||||
'secondText': $.timepicker.regional[$sJSLangShort].secondText,
|
||||
'currentText': $.timepicker.regional[$sJSLangShort].currentText
|
||||
}";
|
||||
$sJSDateTimePickerOptions = substr($sJSDateTimePickerOptions, 0, -1).$aMoreJSOptions;
|
||||
}
|
||||
$this->add_script(
|
||||
<<< EOF
|
||||
function PrepareWidgets()
|
||||
{
|
||||
// note: each action implemented here must be idempotent,
|
||||
// because this helper function might be called several times on a given page
|
||||
|
||||
$(".date-pick").datepicker($sJSDatePickerOptions);
|
||||
|
||||
// Hack for the date and time picker addon issue on Chrome (see #1305)
|
||||
// The workaround is to instantiate the widget on demand
|
||||
// It relies on the same markup, thus reverting to the original implementation should be straightforward
|
||||
$(".datetime-pick:not(.is-widget-ready)").each(function(){
|
||||
var oInput = this;
|
||||
$(oInput).addClass('is-widget-ready');
|
||||
$('<img class="datetime-pick-button" src="../images/calendar.png">')
|
||||
.insertAfter($(this))
|
||||
.on('click', function(){
|
||||
$(oInput)
|
||||
.datetimepicker($sJSDateTimePickerOptions)
|
||||
.datetimepicker('show')
|
||||
.datetimepicker('option', 'onClose', function(dateText,inst){
|
||||
$(oInput).datetimepicker('destroy');
|
||||
})
|
||||
.on('click keypress', function(){
|
||||
$(oInput).datetimepicker('hide');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
EOF
|
||||
);
|
||||
|
||||
$this->add_ready_script(
|
||||
<<<EOF
|
||||
@@ -146,34 +219,10 @@ try
|
||||
}
|
||||
});
|
||||
|
||||
$(".date-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true,
|
||||
dayNamesMin: $sJSDaysMin,
|
||||
monthNamesShort: $sJSMonthsShort,
|
||||
firstDay: $iFirstDayOfWeek
|
||||
});
|
||||
|
||||
$(".datetime-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd 00:00:00',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true,
|
||||
dayNamesMin: $sJSDaysMin,
|
||||
monthNamesShort: $sJSMonthsShort,
|
||||
firstDay: $iFirstDayOfWeek
|
||||
});
|
||||
PrepareWidgets();
|
||||
|
||||
//$('.resizable').resizable(); // Make resizable everything that claims to be resizable !
|
||||
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry_html').toggle(); });
|
||||
$('.caselog_header').click( function () { $(this).toggleClass('open').next('.caselog_entry,.caselog_entry_html').toggle(); });
|
||||
|
||||
$(document).ajaxSend(function(event, jqxhr, options) {
|
||||
jqxhr.setRequestHeader('X-Combodo-Ajax', 'true');
|
||||
@@ -338,7 +387,7 @@ EOF
|
||||
{
|
||||
$sReadOnly = Dict::S('UI:AccessRO-Users');
|
||||
$sAdminMessage = trim(MetaModel::GetConfig()->Get('access_message'));
|
||||
$sApplicationBanner .= '<div id="admin-banner">';
|
||||
$sApplicationBanner .= '<div class="app-message">';
|
||||
$sApplicationBanner .= '<img src="../images/locked.png" style="vertical-align:middle;">';
|
||||
$sApplicationBanner .= ' <b>'.$sReadOnly.'</b>';
|
||||
if (strlen($sAdminMessage) > 0)
|
||||
@@ -767,7 +816,7 @@ EOF
|
||||
$sClass = get_class($oObj);
|
||||
|
||||
$sStimulus = trim(utils::ReadPostedParam('apply_stimulus', ''));
|
||||
$sTargetState = '';
|
||||
$aExpectedAttributes = array();
|
||||
if (!empty($sStimulus))
|
||||
{
|
||||
// Compute the target state
|
||||
@@ -777,10 +826,10 @@ EOF
|
||||
{
|
||||
throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus, $oObj->GetName(), $oObj->GetStateLabel()));
|
||||
}
|
||||
$sTargetState = $aTransitions[$sStimulus]['target_state'];
|
||||
}
|
||||
$aExpectedAttributes = $oObj->GetTransitionAttributes($sStimulus /*, current state*/);
|
||||
}
|
||||
|
||||
$oObj->UpdateObjectFromPostedForm('' /* form prefix */, $aAttList, $sTargetState);
|
||||
$oObj->UpdateObjectFromPostedForm('' /* form prefix */, $aAttList, $aExpectedAttributes);
|
||||
|
||||
// Optional: apply a stimulus
|
||||
//
|
||||
@@ -903,7 +952,7 @@ EOF
|
||||
$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");
|
||||
$this->add_ready_script("$(window).on('unload', function() { OnUnload('$sTransactionId') } );\n");
|
||||
}
|
||||
|
||||
public function WizardFormButtons($iButtonFlags)
|
||||
|
||||
@@ -32,7 +32,7 @@ abstract class Query extends cmdbAbstractObject
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb,view_in_gui,application",
|
||||
"category" => "core/cmdb,view_in_gui,application,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
@@ -54,7 +54,8 @@ abstract class Query extends cmdbAbstractObject
|
||||
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
|
||||
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +65,7 @@ class QueryOQL extends Query
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb,view_in_gui,application",
|
||||
"category" => "core/cmdb,view_in_gui,application,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
@@ -83,7 +84,6 @@ class QueryOQL extends Query
|
||||
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())
|
||||
|
||||
@@ -197,7 +197,7 @@ class ShortcutOQL extends Shortcut
|
||||
}
|
||||
|
||||
$bSearchPane = true;
|
||||
$bSearchOpen = false;
|
||||
$bSearchOpen = true;
|
||||
try
|
||||
{
|
||||
OQLMenuNode::RenderOQLSearch($this->Get('oql'), $this->Get('name'), 'shortcut_'.$this->GetKey(), $bSearchPane, $bSearchOpen, $oPage, $aExtraParams, true);
|
||||
|
||||
@@ -1,531 +0,0 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* SqlBlock - display tables or charts, given an SQL query - use cautiously!
|
||||
*
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
require_once(APPROOT.'/application/webpage.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
|
||||
require_once(APPROOT.'/pages/php-ofc-library/open-flash-chart.php');
|
||||
|
||||
/**
|
||||
* Helper class to design optimized dashboards, based on an SQL query
|
||||
*
|
||||
*/
|
||||
class SqlBlock
|
||||
{
|
||||
protected $m_sQuery;
|
||||
protected $m_aColumns;
|
||||
protected $m_sTitle;
|
||||
protected $m_sType;
|
||||
protected $m_aParams;
|
||||
|
||||
public function __construct($sQuery, $aColumns, $sTitle, $sType, $aParams = array())
|
||||
{
|
||||
$this->m_sQuery = $sQuery;
|
||||
$this->m_aColumns = $aColumns;
|
||||
$this->m_sTitle = $sTitle;
|
||||
$this->m_sType = $sType;
|
||||
$this->m_aParams = $aParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a SqlBlock object from an XML template
|
||||
/*
|
||||
*
|
||||
* <sqlblock>
|
||||
* <sql>SELECT date_format(start_date, '%d') AS Date, count(*) AS Count FROM ticket WHERE DATE_SUB(NOW(), INTERVAL 15 DAY) < start_date AND finalclass = 'UserIssue' GROUP BY date_format(start_date, '%d') AND $CONDITION(param1, ticket.org_id)$</sql>
|
||||
* <type>table</type>
|
||||
* <title>UserRequest:Overview-Title</title>
|
||||
* <parameter>
|
||||
* <name>param1</name>
|
||||
* <type>context</type>
|
||||
* <mapping>org_id</mapping>
|
||||
* </parameter>
|
||||
* <column>
|
||||
* <name>Date</name>
|
||||
* <label>UserRequest:Overview-Date</label>
|
||||
* <drilldown></drilldown>
|
||||
* </column>
|
||||
* <column>
|
||||
* <name>Count</name>
|
||||
* <label>UserRequest:Overview-Count</label>
|
||||
* <drilldown>SELECT UserIssue WHERE date_format(start_date, '%d') = :Date</drilldown>
|
||||
* </column>
|
||||
* </sqlblock>
|
||||
*
|
||||
* Tags
|
||||
* - sql: a (My)SQL query. Do not forget to use html entities (e.g. < for <)
|
||||
* - type: table (default), bars or pie. If bars or pie is selected only the two first columns are taken into account.
|
||||
* - title: optional title, typed in clear or given as a dictionnary entry
|
||||
* - parameter: specifies how to map the context parameters (namely org_id) to a given named parameter in the query.
|
||||
* The expression $CONDITION(<param_name>, <sql_column_name>) will be automatically replaced by:
|
||||
* either the string "1" if there is no restriction on the organisation in iTop
|
||||
* or the string "(<sql_column_name>=<value_of_org_id>)" if there is a limitation to one organizations in iTop
|
||||
* or the string "(<sql_column_name> IN (<values_of_org_id>))" if there is a limitation to a given set of organizations in iTop
|
||||
* - column: specification of a column (not displayed if omitted)
|
||||
* - column / name: name of the column in the SQL query (use aliases)
|
||||
* - column / label: label, typed in clear or given as a dictionnary entry
|
||||
* - column / drilldown: NOT IMPLEMENTED YET - OQL with parameters corresponding to column names (in the query)
|
||||
*
|
||||
* @param $sTemplate string The XML template
|
||||
* @return DisplayBlock The DisplayBlock object, or null if the template is invalid
|
||||
*/
|
||||
public static function FromTemplate($sTemplate)
|
||||
{
|
||||
$oXml = simplexml_load_string('<root>'.$sTemplate.'</root>', 'SimpleXMLElement', LIBXML_NOCDATA);
|
||||
if (false)
|
||||
{
|
||||
// Debug
|
||||
echo "<pre>\n";
|
||||
print_r($oXml);
|
||||
echo "</pre>\n";
|
||||
}
|
||||
|
||||
if (isset($oXml->title))
|
||||
{
|
||||
$sTitle = (string)$oXml->title;
|
||||
}
|
||||
if (isset($oXml->type))
|
||||
{
|
||||
$sType = (string)$oXml->type;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sType = 'table';
|
||||
}
|
||||
if (!isset($oXml->sql))
|
||||
{
|
||||
throw new Exception('Missing tag "sql" in sqlblock');
|
||||
}
|
||||
$sQuery = (string)$oXml->sql;
|
||||
|
||||
$aColumns = array();
|
||||
if (isset($oXml->column))
|
||||
{
|
||||
foreach ($oXml->column AS $oColumnData)
|
||||
{
|
||||
if (!isset($oColumnData->name))
|
||||
{
|
||||
throw new Exception("Missing tag 'name' in sqlblock/column");
|
||||
}
|
||||
$sName = (string) $oColumnData->name;
|
||||
if (strlen($sName) == 0)
|
||||
{
|
||||
throw new Exception("Empty tag 'name' in sqlblock/column");
|
||||
}
|
||||
|
||||
$aColumns[$sName] = array();
|
||||
if (isset($oColumnData->label))
|
||||
{
|
||||
$sLabel = (string)$oColumnData->label;
|
||||
if (strlen($sLabel) > 0)
|
||||
{
|
||||
$aColumns[$sName]['label'] = Dict::S($sLabel);
|
||||
}
|
||||
}
|
||||
if (isset($oColumnData->drilldown))
|
||||
{
|
||||
$sDrillDown = (string)$oColumnData->drilldown;
|
||||
if (strlen($sDrillDown) > 0)
|
||||
{
|
||||
$aColumns[$sName]['drilldown'] = $sDrillDown;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$aParams = array();
|
||||
if (isset($oXml->parameter))
|
||||
{
|
||||
foreach ($oXml->parameter AS $oParamData)
|
||||
{
|
||||
if (!isset($oParamData->name))
|
||||
{
|
||||
throw new Exception("Missing tag 'name' for parameter in sqlblock/column");
|
||||
}
|
||||
$sName = (string) $oParamData->name;
|
||||
if (strlen($sName) == 0)
|
||||
{
|
||||
throw new Exception("Empty tag 'name' for parameter in sqlblock/column");
|
||||
}
|
||||
if (!isset($oParamData->mapping))
|
||||
{
|
||||
throw new Exception("Missing tag 'mapping' for parameter in sqlblock/column");
|
||||
}
|
||||
$sMapping = (string) $oParamData->mapping;
|
||||
if (strlen($sMapping) == 0)
|
||||
{
|
||||
throw new Exception("Empty tag 'mapping' for parameter in sqlblock/column");
|
||||
}
|
||||
|
||||
if (isset($oParamData->type))
|
||||
{
|
||||
$sParamType = $oParamData->type;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sParamType = 'context';
|
||||
}
|
||||
$aParams[$sName] = array('mapping' => $sMapping, 'type' => $sParamType);
|
||||
}
|
||||
}
|
||||
|
||||
return new SqlBlock($sQuery, $aColumns, $sTitle, $sType, $aParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the defined parameters into the SQL query
|
||||
* @return string the SQL query to execute
|
||||
*/
|
||||
public function BuildQuery()
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sQuery = $this->m_sQuery;
|
||||
$sQuery = str_replace('$DB_PREFIX$', MetaModel::GetConfig()->GetDBSubname(), $sQuery); // put the tables DB prefix (if any)
|
||||
foreach($this->m_aParams as $sName => $aParam)
|
||||
{
|
||||
if ($aParam['type'] == 'context')
|
||||
{
|
||||
$sSearchPattern = '/\$CONDITION\('.$sName.',([^\)]+)\)\$/';
|
||||
$value = $oAppContext->GetCurrentValue($aParam['mapping']);
|
||||
if (empty($value))
|
||||
{
|
||||
$sSQLExpr = '(1)';
|
||||
}
|
||||
else
|
||||
{
|
||||
// Special case for managing the hierarchy of organizations
|
||||
if (($aParam['mapping'] == 'org_id') && ( MetaModel::IsValidClass('Organization')))
|
||||
{
|
||||
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass('Organization');
|
||||
if ($sHierarchicalKeyCode != false)
|
||||
{
|
||||
// organizations are in hierarchy... gather all the orgs below the given one...
|
||||
$sOQL = "SELECT Organization AS node JOIN Organization AS root ON node.$sHierarchicalKeyCode BELOW root.id WHERE root.id = :value";
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array(), array('value' => $value));
|
||||
$aOrgIds = array();
|
||||
while($oOrg = $oSet->Fetch())
|
||||
{
|
||||
$aOrgIds[]= $oOrg->GetKey();
|
||||
}
|
||||
$sSQLExpr = '($1 IN('.implode(',', $aOrgIds).'))';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSQLExpr = '($1 = '.CMDBSource::Quote($value).')';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSQLExpr = '($1 = '.CMDBSource::Quote($value).')';
|
||||
}
|
||||
}
|
||||
$sQuery = preg_replace($sSearchPattern, $sSQLExpr, $sQuery);
|
||||
}
|
||||
}
|
||||
return $sQuery;
|
||||
}
|
||||
|
||||
public function RenderContent(WebPage $oPage, $aExtraParams = array())
|
||||
{
|
||||
if (empty($aExtraParams['currentId']))
|
||||
{
|
||||
$sId = 'sqlblock_'.$oPage->GetUniqueId(); // Works only if the page is not an Ajax one !
|
||||
}
|
||||
else
|
||||
{
|
||||
$sId = $aExtraParams['currentId'];
|
||||
}
|
||||
// $oPage->add($this->GetRenderContent($oPage, $aExtraParams, $sId));
|
||||
|
||||
$sQuery = $this->BuildQuery();
|
||||
$res = CMDBSource::Query($sQuery);
|
||||
$aQueryCols = CMDBSource::GetColumns($res);
|
||||
|
||||
// Prepare column definitions (check + give default values)
|
||||
//
|
||||
foreach($this->m_aColumns as $sName => $aColumnData)
|
||||
{
|
||||
if (!in_array($sName, $aQueryCols))
|
||||
{
|
||||
throw new Exception("Unknown column name '$sName' in sqlblock column");
|
||||
}
|
||||
if (!isset($aColumnData['label']))
|
||||
{
|
||||
$this->m_aColumns[$sName]['label'] = $sName;
|
||||
}
|
||||
if (isset($aColumnData['drilldown']) && !empty($aColumnData['drilldown']))
|
||||
{
|
||||
// Check if the OQL is valid
|
||||
try
|
||||
{
|
||||
$this->m_aColumns[$sName]['filter'] = DBObjectSearch::FromOQL($aColumnData['drilldown']);
|
||||
}
|
||||
catch(OQLException $e)
|
||||
{
|
||||
unset($aColumnData['drilldown']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen($this->m_sTitle) > 0)
|
||||
{
|
||||
$oPage->add("<h2>".Dict::S($this->m_sTitle)."</h2>\n");
|
||||
}
|
||||
|
||||
switch ($this->m_sType)
|
||||
{
|
||||
case 'bars':
|
||||
case 'pie':
|
||||
$aColNames = array_keys($this->m_aColumns);
|
||||
$sXColName = $aColNames[0];
|
||||
$sYColName = $aColNames[1];
|
||||
$aData = array();
|
||||
$aRows = array();
|
||||
while($aRow = CMDBSource::FetchArray($res))
|
||||
{
|
||||
$aData[$aRow[$sXColName]] = $aRow[$sYColName];
|
||||
$aRows[$aRow[$sXColName]] = $aRow;
|
||||
}
|
||||
$this->RenderChart($oPage, $sId, $aData, $this->m_aColumns[$sYColName]['drilldown'], $aRows);
|
||||
break;
|
||||
|
||||
default:
|
||||
case 'table':
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
if (!empty($sContext))
|
||||
{
|
||||
$sContext = '&'.$sContext;
|
||||
}
|
||||
$aDisplayConfig = array();
|
||||
foreach($this->m_aColumns as $sName => $aColumnData)
|
||||
{
|
||||
$aDisplayConfig[$sName] = array('label' => $aColumnData['label'], 'description' => '');
|
||||
}
|
||||
|
||||
$aDisplayData = array();
|
||||
while($aRow = CMDBSource::FetchArray($res))
|
||||
{
|
||||
$aSQLColNames = array_keys($aRow);
|
||||
$aDisplayRow = array();
|
||||
foreach($this->m_aColumns as $sName => $aColumnData)
|
||||
{
|
||||
if (isset($aColumnData['filter']))
|
||||
{
|
||||
$sFilter = $aColumnData['drilldown'];
|
||||
$sClass = $aColumnData['filter']->GetClass();
|
||||
$sFilter = str_replace('SELECT '.$sClass, '', $sFilter);
|
||||
foreach($aSQLColNames as $sColName)
|
||||
{
|
||||
$sFilter = str_replace(':'.$sColName, "'".addslashes( $aRow[$sColName] )."'", $sFilter);
|
||||
}
|
||||
$sURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_oql&search_form=0&oql_class='.$sClass.'&oql_clause='.urlencode($sFilter).'&format=html'.$sContext;
|
||||
$aDisplayRow[$sName] = '<a href="'.$sURL.'">'.$aRow[$sName]."</a>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$aDisplayRow[$sName] = $aRow[$sName];
|
||||
}
|
||||
}
|
||||
$aDisplayData[] = $aDisplayRow;
|
||||
}
|
||||
$oPage->table($aDisplayConfig, $aDisplayData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetRenderContent(WebPage $oPage, $aExtraParams = array(), $sId)
|
||||
{
|
||||
$sHtml = '';
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
protected function RenderChart($oPage, $sId, $aValues, $sDrillDown = '', $aRows = array())
|
||||
{
|
||||
// 1- Compute Open Flash Chart data
|
||||
//
|
||||
$aValueKeys = array();
|
||||
$index = 0;
|
||||
if ((count($aValues) > 0) && ($sDrillDown != ''))
|
||||
{
|
||||
$oFilter = DBObjectSearch::FromOQL($sDrillDown);
|
||||
$sClass = $oFilter->GetClass();
|
||||
$sOQLClause = str_replace('SELECT '.$sClass, '', $sDrillDown);
|
||||
$aSQLColNames = array_keys(current($aRows)); // Read the list of columns from the current (i.e. first) element of the array
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_oql&search_form=0&oql_class='.$sClass.'&format=html&'.$oAppContext->GetForLink().'&oql_clause=';
|
||||
}
|
||||
$aURLs = array();
|
||||
foreach($aValues as $key => $value)
|
||||
{
|
||||
// Make sure that values are integers (so that max() will work....)
|
||||
// and build an array of STRING with the keys (numeric keys are transformed into string by PHP :-(
|
||||
$aValues[$key] = (int)$value;
|
||||
$aValueKeys[] = (string)$key;
|
||||
|
||||
// Build the custom query for the 'drill down' on each element
|
||||
if ($sDrillDown != '')
|
||||
{
|
||||
$sFilter = $sOQLClause;
|
||||
foreach($aSQLColNames as $sColName)
|
||||
{
|
||||
$sFilter = str_replace(':'.$sColName, "'".addslashes( $aRows[$key][$sColName] )."'", $sFilter);
|
||||
$aURLs[$index] = $sURL.urlencode($sFilter);
|
||||
}
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
|
||||
$oChart = new open_flash_chart();
|
||||
|
||||
if ($this->m_sType == 'bars')
|
||||
{
|
||||
$oChartElement = new bar_glass();
|
||||
|
||||
if (count($aValues) > 0)
|
||||
{
|
||||
$maxValue = max($aValues);
|
||||
}
|
||||
else
|
||||
{
|
||||
$maxValue = 1;
|
||||
}
|
||||
$oYAxis = new y_axis();
|
||||
$aMagicValues = array(1,2,5,10);
|
||||
$iMultiplier = 1;
|
||||
$index = 0;
|
||||
$iTop = $aMagicValues[$index % count($aMagicValues)]*$iMultiplier;
|
||||
while($maxValue > $iTop)
|
||||
{
|
||||
$index++;
|
||||
$iTop = $aMagicValues[$index % count($aMagicValues)]*$iMultiplier;
|
||||
if (($index % count($aMagicValues)) == 0)
|
||||
{
|
||||
$iMultiplier = $iMultiplier * 10;
|
||||
}
|
||||
}
|
||||
//echo "oYAxis->set_range(0, $iTop, $iMultiplier);\n";
|
||||
$oYAxis->set_range(0, $iTop, $iMultiplier);
|
||||
$oChart->set_y_axis( $oYAxis );
|
||||
$aBarValues = array();
|
||||
foreach($aValues as $iValue)
|
||||
{
|
||||
$oBarValue = new bar_value($iValue);
|
||||
$oBarValue->on_click("ofc_drilldown_{$sId}");
|
||||
$aBarValues[] = $oBarValue;
|
||||
}
|
||||
$oChartElement->set_values($aBarValues);
|
||||
//$oChartElement->set_values(array_values($aValues));
|
||||
$oXAxis = new x_axis();
|
||||
$oXLabels = new x_axis_labels();
|
||||
// set them vertical
|
||||
$oXLabels->set_vertical();
|
||||
// set the label text
|
||||
$oXLabels->set_labels($aValueKeys);
|
||||
// Add the X Axis Labels to the X Axis
|
||||
$oXAxis->set_labels( $oXLabels );
|
||||
$oChart->set_x_axis( $oXAxis );
|
||||
}
|
||||
else
|
||||
{
|
||||
$oChartElement = new pie();
|
||||
$oChartElement->set_start_angle( 35 );
|
||||
$oChartElement->set_animate( true );
|
||||
$oChartElement->set_tooltip( '#label# - #val# (#percent#)' );
|
||||
$oChartElement->set_colours( array('#FF8A00', '#909980', '#2C2B33', '#CCC08D', '#596664') );
|
||||
|
||||
$aData = array();
|
||||
foreach($aValues as $sValue => $iValue)
|
||||
{
|
||||
$oPieValue = new pie_value($iValue, $sValue); //@@ BUG: not passed via ajax !!!
|
||||
$oPieValue->on_click("ofc_drilldown_{$sId}");
|
||||
$aData[] = $oPieValue;
|
||||
}
|
||||
|
||||
$oChartElement->set_values( $aData );
|
||||
$oChart->x_axis = null;
|
||||
}
|
||||
|
||||
// Title given in HTML
|
||||
//$oTitle = new title($this->m_sTitle);
|
||||
//$oChart->set_title($oTitle);
|
||||
$oChart->set_bg_colour('#FFFFFF');
|
||||
$oChart->add_element( $oChartElement );
|
||||
|
||||
$sData = $oChart->toPrettyString();
|
||||
$sData = json_encode($sData);
|
||||
|
||||
// 2- Declare the Javascript function that will render the chart data\
|
||||
//
|
||||
$oPage->add_script(
|
||||
<<< EOF
|
||||
function ofc_get_data_{$sId}()
|
||||
{
|
||||
return $sData;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
|
||||
if (count($aURLs) > 0)
|
||||
{
|
||||
$sURLList = '';
|
||||
foreach($aURLs as $index => $sURL)
|
||||
{
|
||||
$sURLList .= "\taURLs[$index] = '".addslashes($sURL)."';\n";
|
||||
}
|
||||
|
||||
$oPage->add_script(
|
||||
<<< EOF
|
||||
function ofc_drilldown_{$sId}(index)
|
||||
{
|
||||
var aURLs = new Array();
|
||||
{$sURLList}
|
||||
var sURL = aURLs[index];
|
||||
|
||||
window.location.href = sURL; // Navigate !
|
||||
}
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
// 3- Insert the Open Flash chart
|
||||
//
|
||||
$oPage->add("<div id=\"$sId\"><div>\n");
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
swfobject.embedSWF( "../images/open-flash-chart.swf",
|
||||
"{$sId}",
|
||||
"100%", "300","9.0.0",
|
||||
"expressInstall.swf",
|
||||
{"get-data":"ofc_get_data_{$sId}", "id":"{$sId}"},
|
||||
{'wmode': 'transparent'}
|
||||
);
|
||||
EOF
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -1,49 +1,62 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* File to include to initialize the datamodel in memory
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once(APPROOT.'/core/contexttag.class.inc.php');
|
||||
session_name('itop-'.md5(APPROOT));
|
||||
session_start();
|
||||
$sSwitchEnv = utils::ReadParam('switch_env', null);
|
||||
if (($sSwitchEnv != null) && (file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE)))
|
||||
{
|
||||
$_SESSION['itop_env'] = $sSwitchEnv;
|
||||
$sEnv = $sSwitchEnv;
|
||||
// TODO: reset the credentials as well ??
|
||||
}
|
||||
else if (isset($_SESSION['itop_env']))
|
||||
{
|
||||
$sEnv = $_SESSION['itop_env'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sEnv = ITOP_DEFAULT_ENV;
|
||||
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
|
||||
}
|
||||
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
|
||||
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, true /* $bAllowCache */, false /* $bTraceSourceFiles */, $sEnv);
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* File to include to initialize the datamodel in memory
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
require_once(APPROOT.'/core/contexttag.class.inc.php');
|
||||
session_name('itop-'.md5(APPROOT));
|
||||
session_start();
|
||||
$sSwitchEnv = utils::ReadParam('switch_env', null);
|
||||
$bAllowCache = true;
|
||||
if (($sSwitchEnv != null) && (file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE)) && isset($_SESSION['itop_env']) && ($_SESSION['itop_env'] !== $sSwitchEnv))
|
||||
{
|
||||
$_SESSION['itop_env'] = $sSwitchEnv;
|
||||
$sEnv = $sSwitchEnv;
|
||||
$bAllowCache = false;
|
||||
// Reset the opcache since otherwise the PHP "model" files may still be cached !!
|
||||
if (function_exists('opcache_reset'))
|
||||
{
|
||||
// Zend opcode cache
|
||||
opcache_reset();
|
||||
}
|
||||
if (function_exists('apc_clear_cache'))
|
||||
{
|
||||
// APC(u) cache
|
||||
apc_clear_cache();
|
||||
}
|
||||
// TODO: reset the credentials as well ??
|
||||
}
|
||||
else if (isset($_SESSION['itop_env']))
|
||||
{
|
||||
$sEnv = $_SESSION['itop_env'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sEnv = ITOP_DEFAULT_ENV;
|
||||
$_SESSION['itop_env'] = ITOP_DEFAULT_ENV;
|
||||
}
|
||||
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
|
||||
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class DisplayTemplate
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -353,11 +353,15 @@ class ObjectDetailsTemplate extends DisplayTemplate
|
||||
if ($iFlags & OPT_ATT_SLAVE)
|
||||
{
|
||||
$iSynchroFlags = $this->m_oObj->GetSynchroReplicaFlags($sAttCode, $aReasons);
|
||||
$sSynchroIcon = " <img id=\"synchro_$sInputId\" src=\"../images/transp-lock.png\" style=\"vertical-align:middle\"/>";
|
||||
$sSynchroIcon = " <img id=\"synchro_$iInputId\" src=\"../images/transp-lock.png\" style=\"vertical-align:middle\"/>";
|
||||
$sTip = '';
|
||||
foreach($aReasons as $aRow)
|
||||
{
|
||||
$sTip .= "<p>Synchronized with {$aRow['name']} - {$aRow['description']}</p>";
|
||||
$sDescription = htmlentities($aRow['description'], ENT_QUOTES, 'UTF-8');
|
||||
$sDescription = str_replace(array("\r\n", "\n"), "<br/>", $sDescription);
|
||||
$sTip .= "<div class='synchro-source'>";
|
||||
$sTip .= "<div class='synchro-source-title'>Synchronized with {$aRow['name']}</div>";
|
||||
$sTip .= "<div class='synchro-source-description'>$sDescription</div>";
|
||||
}
|
||||
$oPage->add_ready_script("$('#synchro_$iInputId').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,15 +19,15 @@
|
||||
* Class UIExtKeyWidget
|
||||
* UI wdiget for displaying and editing external keys when
|
||||
* A simple drop-down list is not enough...
|
||||
*
|
||||
*
|
||||
* The layout is the following
|
||||
*
|
||||
*
|
||||
* +-- #label_<id> (input)-------+ +-----------+
|
||||
* | | | Browse... |
|
||||
* +-----------------------------+ +-----------+
|
||||
*
|
||||
*
|
||||
* And the popup dialog has the following layout:
|
||||
*
|
||||
*
|
||||
* +------------------- ac_dlg_<id> (div)-----------+
|
||||
* + +--- ds_<id> (div)---------------------------+ |
|
||||
* | | +------------- fs_<id> (form)------------+ | |
|
||||
@@ -54,20 +54,23 @@
|
||||
* | | +--------+ +-----+ | |
|
||||
* | +--------------------------------------------+ |
|
||||
* +------------------------------------------------+
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/application/webpage.class.inc.php');
|
||||
require_once(APPROOT.'/application/displayblock.class.inc.php');
|
||||
|
||||
class UIExtKeyWidget
|
||||
class UIExtKeyWidget
|
||||
{
|
||||
const ENUM_OUTPUT_FORMAT_CSV = 'csv';
|
||||
const ENUM_OUTPUT_FORMAT_JSON = 'json';
|
||||
|
||||
protected $iId;
|
||||
protected $sTargetClass;
|
||||
protected $sAttCode;
|
||||
protected $bSearchMode;
|
||||
|
||||
|
||||
//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)
|
||||
{
|
||||
@@ -94,36 +97,36 @@ class UIExtKeyWidget
|
||||
$this->sAttCode = $sAttCode;
|
||||
$this->bSearchMode = $bSearchMode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param array $aArgs Extra context arguments
|
||||
* @return string The HTML fragment to be inserted into the page
|
||||
*/
|
||||
public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = null, $sDisplayStyle = 'select', $bSearchMultiple = true)
|
||||
public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, DBObjectset $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = null, $sDisplayStyle = 'select', $bSearchMultiple = true)
|
||||
{
|
||||
if (!is_null($bSearchMode))
|
||||
{
|
||||
$this->bSearchMode = $bSearchMode;
|
||||
}
|
||||
$sTitle = addslashes($sTitle);
|
||||
$sTitle = addslashes($sTitle);
|
||||
$oPage->add_linked_script('../js/extkeywidget.js');
|
||||
$oPage->add_linked_script('../js/forms-json-utils.js');
|
||||
|
||||
$bCreate = (!$this->bSearchMode) && (!MetaModel::IsAbstract($this->sTargetClass)) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
|
||||
|
||||
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
|
||||
$bExtensions = true;
|
||||
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
|
||||
$sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
|
||||
|
||||
$sHTMLValue = "<span style=\"white-space:nowrap\">"; // no wrap
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey\">";
|
||||
$sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL());
|
||||
if($this->bSearchMode)
|
||||
{
|
||||
$sWizHelper = 'null';
|
||||
$sWizHelperJSON = "''";
|
||||
$sJSSearchMode = 'true';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isset($aArgs['wizHelper']))
|
||||
@@ -141,16 +144,22 @@ class UIExtKeyWidget
|
||||
{
|
||||
throw new Exception('Implementation: null value for allowed values definition');
|
||||
}
|
||||
elseif ($oAllowedValues->Count() < $iMaxComboLength)
|
||||
$oAllowedValues->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
// Don't automatically launch the search if the table is huge
|
||||
$bDoSearch = !utils::IsHighCardinality($this->sTargetClass);
|
||||
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
|
||||
|
||||
// We just need to compare the number of entries with MaxComboLength, so no need to get the real count.
|
||||
if (!$oAllowedValues->CountExceeds($iMaxComboLength))
|
||||
{
|
||||
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
|
||||
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
|
||||
switch($sDisplayStyle)
|
||||
{
|
||||
case 'radio':
|
||||
case 'radio_horizontal':
|
||||
case 'radio_vertical':
|
||||
$sValidationField = "<span id=\"v_{$this->iId}\"></span><span id=\"fstatus_{$this->iId}\"></span>";
|
||||
$sHTMLValue = '';
|
||||
$sValidationField = null;
|
||||
|
||||
$bVertical = ($sDisplayStyle != 'radio_horizontal');
|
||||
$bExtensions = false;
|
||||
$oAllowedValues->Rewind();
|
||||
@@ -158,54 +167,57 @@ class UIExtKeyWidget
|
||||
while($oObj = $oAllowedValues->Fetch())
|
||||
{
|
||||
$aAllowedValues[$oObj->GetKey()] = $oObj->GetName();
|
||||
}
|
||||
$sHTMLValue = $oPage->GetRadioButtons($aAllowedValues, $value, $this->iId, "{$sAttrFieldPrefix}{$sFieldName}", $bMandatory, $bVertical, $sValidationField);
|
||||
}
|
||||
$sHTMLValue .= $oPage->GetRadioButtons($aAllowedValues, $value, $this->iId, "{$sAttrFieldPrefix}{$sFieldName}", false /* $bMandatory will be placed manually */, $bVertical, $sValidationField);
|
||||
$aEventsList[] ='change';
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
case 'list':
|
||||
default:
|
||||
$sSelectMode = 'true';
|
||||
|
||||
|
||||
$sHelpText = ''; //$this->oAttDef->GetHelpOnEdition();
|
||||
|
||||
$sHTMLValue .= "<div class=\"field_select_wrapper\">\n";
|
||||
|
||||
if ($this->bSearchMode)
|
||||
{
|
||||
if ($bSearchMultiple)
|
||||
{
|
||||
$sHTMLValue = "<select class=\"multiselect\" multiple title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}[]\" id=\"$this->iId\">\n";
|
||||
$sHTMLValue .= "<select class=\"multiselect\" multiple title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}[]\" id=\"$this->iId\">\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHTMLValue = "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
|
||||
$sHTMLValue .= "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
|
||||
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : Dict::S('UI:SearchValue:Any');
|
||||
$sHTMLValue .= "<option value=\"\">$sDisplayValue</option>\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHTMLValue = "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
|
||||
$sHTMLValue .= "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
|
||||
$sHTMLValue .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>\n";
|
||||
}
|
||||
|
||||
$oAllowedValues->Rewind();
|
||||
while($oObj = $oAllowedValues->Fetch())
|
||||
{
|
||||
$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';
|
||||
$sSelected = 'selected';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSelected = (is_array($value) && in_array($key, $value)) || ($value == $key) ? ' selected' : '';
|
||||
$sSelected = (is_array($value) && in_array($key, $value)) || ($value == $key) ? 'selected' : '';
|
||||
}
|
||||
$sHTMLValue .= "<option value=\"$key\"$sSelected>$display_value</option>\n";
|
||||
$sHTMLValue .= "<option value=\"$key\" $sSelected>$display_value</option>\n";
|
||||
}
|
||||
$sHTMLValue .= "</select>\n";
|
||||
$sHTMLValue .= "</div>\n";
|
||||
|
||||
if (($this->bSearchMode) && $bSearchMultiple)
|
||||
{
|
||||
$aOptions = array(
|
||||
@@ -221,7 +233,7 @@ class UIExtKeyWidget
|
||||
}
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
|
||||
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
|
||||
$('#$this->iId').bind('update', function() { oACWidget_{$this->iId}.Update(); } );
|
||||
$('#$this->iId').bind('change', function() { $(this).trigger('extkeychange') } );
|
||||
@@ -233,8 +245,6 @@ EOF
|
||||
else
|
||||
{
|
||||
// Too many choices, use an autocomplete
|
||||
$sSelectMode = 'false';
|
||||
|
||||
// Check that the given value is allowed
|
||||
$oSearch = $oAllowedValues->GetFilter();
|
||||
$oSearch->AddCondition('id', $value);
|
||||
@@ -253,20 +263,19 @@ EOF
|
||||
$sDisplayValue = $this->GetObjectName($value);
|
||||
}
|
||||
$iMinChars = isset($aArgs['iMinChars']) ? $aArgs['iMinChars'] : 3; //@@@ $this->oAttDef->GetMinAutoCompleteChars();
|
||||
$iFieldSize = isset($aArgs['iFieldSize']) ? $aArgs['iFieldSize'] : 20; //@@@ $this->oAttDef->GetMaxSize();
|
||||
|
||||
|
||||
// the input for the auto-complete
|
||||
$sHTMLValue = "<input count=\"".$oAllowedValues->Count()."\" type=\"text\" id=\"label_$this->iId\" size=\"$iFieldSize\" value=\"$sDisplayValue\"/> ";
|
||||
$sHTMLValue .= "<img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.Search();\"/>";
|
||||
|
||||
$sHTMLValue .= "<input class=\"field_autocomplete\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\"/>";
|
||||
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_search_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_search.gif?t=".utils::GetCacheBusterTimestamp()."\" onClick=\"oACWidget_{$this->iId}.Search();\"/></span>";
|
||||
|
||||
// another hidden input to store & pass the object's Id
|
||||
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" />\n";
|
||||
|
||||
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
|
||||
$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->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', false, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
|
||||
oACWidget_{$this->iId}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
|
||||
$('#label_$this->iId').autocomplete(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { scroll:true, minChars:{$iMinChars}, autoFill:false, matchContains:true, mustMatch: true, keyHolder:'#{$this->iId}', extraParams:{operation:'ac_extkey', sTargetClass:'{$this->sTargetClass}',sFilter:'$sFilter',bSearchMode:$JSSearchMode, json: function() { return $sWizHelperJSON; } }});
|
||||
$('#label_$this->iId').keyup(function() { if ($(this).val() == '') { $('#$this->iId').val(''); } } ); // Useful for search forms: empty value in the "label", means no value, immediatly !
|
||||
@@ -281,7 +290,7 @@ EOF
|
||||
}
|
||||
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false)
|
||||
{
|
||||
$sHTMLValue .= "<img id=\"mini_tree_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_tree.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/> ";
|
||||
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_tree_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_tree.gif?t=".utils::GetCacheBusterTimestamp()."\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"/></span>";
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
if ($('#ac_tree_{$this->iId}').length == 0)
|
||||
@@ -293,7 +302,9 @@ EOF
|
||||
}
|
||||
if ($bCreate && $bExtensions)
|
||||
{
|
||||
$sHTMLValue .= "<img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_add.gif?itopversion=".ITOP_VERSION."\" onClick=\"oACWidget_{$this->iId}.CreateObject();\"/> ";
|
||||
$sCallbackName = (MetaModel::IsAbstract($this->sTargetClass)) ? 'SelectObjectClass' : 'CreateObject';
|
||||
|
||||
$sHTMLValue .= "<span class=\"field_input_btn\"><img id=\"mini_add_{$this->iId}\" style=\"border:0;vertical-align:middle;cursor:pointer;\" src=\"../images/mini_add.gif?t=".utils::GetCacheBusterTimestamp()."\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\"/></span>";
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
if ($('#ajax_{$this->iId}').length == 0)
|
||||
@@ -303,14 +314,17 @@ EOF
|
||||
EOF
|
||||
);
|
||||
}
|
||||
if (($sDisplayStyle == 'select') || ($sDisplayStyle == 'list'))
|
||||
{
|
||||
$sHTMLValue .= "<span id=\"v_{$this->iId}\"></span><span id=\"fstatus_{$this->iId}\"></span>";
|
||||
}
|
||||
$sHTMLValue .= "</span>"; // end of no wrap
|
||||
$sHTMLValue .= "</div>";
|
||||
|
||||
// Note: This test is no longer necessary as we changed the markup to extract validation decoration in the standard .field_input_xxx container
|
||||
//if (($sDisplayStyle == 'select') || ($sDisplayStyle == 'list'))
|
||||
//{
|
||||
$sHTMLValue .= "<span class=\"form_validation\" id=\"v_{$this->iId}\"></span><span class=\"field_status\" id=\"fstatus_{$this->iId}\"></span>";
|
||||
//}
|
||||
|
||||
return $sHTMLValue;
|
||||
}
|
||||
|
||||
|
||||
public function GetSearchDialog(WebPage $oPage, $sTitle, $oCurrObject = null)
|
||||
{
|
||||
$sHTML = '<div class="wizContainer" style="vertical-align:top;"><div id="dc_'.$this->iId.'">';
|
||||
@@ -328,10 +342,18 @@ EOF
|
||||
$aParams = array();
|
||||
$oFilter = new DBObjectSearch($this->sTargetClass);
|
||||
}
|
||||
$bOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open');
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oBlock = new DisplayBlock($oFilter, 'search', false, $aParams);
|
||||
$sHTML .= $oBlock->GetDisplay($oPage, $this->iId, array('open' => $bOpen, 'currentId' => $this->iId));
|
||||
$sHTML .= $oBlock->GetDisplay($oPage, $this->iId,
|
||||
array(
|
||||
'menu' => false,
|
||||
'currentId' => $this->iId,
|
||||
'table_id' => "dr_{$this->iId}",
|
||||
'table_inner_id' => "{$this->iId}_results",
|
||||
'selection_mode' => true,
|
||||
'selection_type' => 'single',
|
||||
'cssCount' => '#count_'.$this->iId)
|
||||
);
|
||||
$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";
|
||||
$sHTML .= "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n";
|
||||
@@ -355,9 +377,13 @@ EOF
|
||||
|
||||
/**
|
||||
* Search for objects to be selected
|
||||
*
|
||||
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
|
||||
* @param $sFilter
|
||||
* @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
|
||||
* @param null $oObj
|
||||
*
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function SearchObjectsToSelect(WebPage $oP, $sFilter, $sRemoteClass = '', $oObj = null)
|
||||
{
|
||||
@@ -365,39 +391,113 @@ EOF
|
||||
{
|
||||
throw new Exception('Implementation: null value for allowed values definition');
|
||||
}
|
||||
|
||||
|
||||
$oFilter = DBObjectSearch::FromOQL($sFilter);
|
||||
if (strlen($sRemoteClass) > 0)
|
||||
{
|
||||
$oFilter->ChangeClass($sRemoteClass);
|
||||
}
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false, array('query_params' => array('this' => $oObj)));
|
||||
|
||||
// Current extkey value, so we can display event if it is not available anymore (eg. archived).
|
||||
$iCurrentExtKeyId = (is_null($oObj)) ? 0 : $oObj->Get($this->sAttCode);
|
||||
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false, array('query_params' => array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId)));
|
||||
$oBlock->Display($oP, $this->iId.'_results', array('this' => $oObj, 'cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'table_id' => 'select_'.$this->sAttCode)); // Don't display the 'Actions' menu on the results
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param string $sOutputFormat
|
||||
* @param null $sOperation for the values @see ValueSetObjects->LoadValues()
|
||||
*
|
||||
* @throws CoreException
|
||||
* @throws OQLException
|
||||
*/
|
||||
public function AutoComplete(WebPage $oP, $sFilter, $oObj = null, $sContains, $sOutputFormat = self::ENUM_OUTPUT_FORMAT_CSV, $sOperation = null)
|
||||
{
|
||||
if (is_null($sFilter))
|
||||
{
|
||||
throw new Exception('Implementation: null value for allowed values definition');
|
||||
}
|
||||
|
||||
// Current extkey value, so we can display event if it is not available anymore (eg. archived).
|
||||
$iCurrentExtKeyId = (is_null($oObj) || $this->sAttCode === '') ? 0 : $oObj->Get($this->sAttCode);
|
||||
|
||||
$oValuesSet = new ValueSetObjects($sFilter, 'friendlyname'); // Bypass GetName() to avoid the encoding by htmlentities
|
||||
$iMax = 150;
|
||||
$oValuesSet->SetLimit($iMax);
|
||||
$oValuesSet->SetSort(false);
|
||||
$oValuesSet->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$aValues = $oValuesSet->GetValues(array('this' => $oObj), $sContains);
|
||||
foreach($aValues as $sKey => $sFriendlyName)
|
||||
|
||||
if (empty($sOperation) || 'equals_start_with' == $sOperation)
|
||||
{
|
||||
$aValues = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'equals');
|
||||
asort($aValues);
|
||||
|
||||
$iMax -= count($aValues);
|
||||
if ($iMax > 0 ) {
|
||||
$oValuesSet->SetLimit($iMax);
|
||||
$aValuesStartWith = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'start_with');
|
||||
asort($aValuesStartWith);
|
||||
foreach ($aValuesStartWith as $sKey => $sFriendlyName) {
|
||||
if (!isset($aValues[$sKey])) {
|
||||
$aValues[$sKey] = $sFriendlyName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aValues = array();
|
||||
}
|
||||
|
||||
$iMax -= count($aValues);
|
||||
if ($iMax > 0 && (empty($sOperation) || 'contains' == $sOperation))
|
||||
{
|
||||
$oP->add(trim($sFriendlyName)."\t".$sKey."\n");
|
||||
$oValuesSet->SetLimit($iMax);
|
||||
$aValuesContains = $oValuesSet->GetValues(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'contains');
|
||||
asort($aValuesContains);
|
||||
foreach($aValuesContains as $sKey => $sFriendlyName)
|
||||
{
|
||||
if (!isset($aValues[$sKey]))
|
||||
{
|
||||
$aValues[$sKey] = $sFriendlyName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch($sOutputFormat)
|
||||
{
|
||||
case static::ENUM_OUTPUT_FORMAT_JSON:
|
||||
|
||||
$aJsonMap = array();
|
||||
foreach ($aValues as $sKey => $sLabel)
|
||||
{
|
||||
$aJsonMap[] = array('value' => $sKey, 'label' => $sLabel);
|
||||
}
|
||||
|
||||
$oP->SetContentType('application/json');
|
||||
$oP->add(json_encode($aJsonMap));
|
||||
break;
|
||||
|
||||
case static::ENUM_OUTPUT_FORMAT_CSV:
|
||||
foreach($aValues as $sKey => $sFriendlyName)
|
||||
{
|
||||
$oP->add(trim($sFriendlyName)."\t".$sKey."\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Invalid output format, "'.$sOutputFormat.'" given.');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the display name of the selected object, to fill back the autocomplete
|
||||
*/
|
||||
@@ -416,11 +516,56 @@ EOF
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the form to select a leaf class from the $this->sTargetClass (that should be abstract)
|
||||
* Note: Inspired from UILinksWidgetDirect::GetObjectCreationDialog()
|
||||
*
|
||||
* @param WebPage $oPage
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
*/
|
||||
public function GetClassSelectionForm(WebPage $oPage)
|
||||
{
|
||||
// For security reasons: check that the "proposed" class is actually a subclass of the linked class
|
||||
// and that the current user is allowed to create objects of this class
|
||||
$aSubClasses = MetaModel::EnumChildClasses($this->sTargetClass);
|
||||
$aPossibleClasses = array();
|
||||
foreach($aSubClasses as $sCandidateClass)
|
||||
{
|
||||
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
|
||||
{
|
||||
$aPossibleClasses[$sCandidateClass] = MetaModel::GetName($sCandidateClass);
|
||||
}
|
||||
}
|
||||
|
||||
$sDialogTitle = '';
|
||||
$oPage->add('<div id="ac_create_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div id="dcr_'.$this->iId.'">');
|
||||
$oPage->add('<form>');
|
||||
|
||||
$sClassLabel = MetaModel::GetName($this->sTargetClass);
|
||||
$oPage->add('<p>'.Dict::Format('UI:SelectTheTypeOf_Class_ToCreate', $sClassLabel));
|
||||
$oPage->add('<nobr><select name="class">');
|
||||
asort($aPossibleClasses);
|
||||
foreach($aPossibleClasses as $sClassName => $sClassLabel)
|
||||
{
|
||||
$oPage->add("<option value=\"$sClassName\">$sClassLabel</option>");
|
||||
}
|
||||
$oPage->add('</select>');
|
||||
$oPage->add(' <button type="submit" class="action" style="margin-top:15px;"><span>' . Dict::S('UI:Button:Ok') . '</span></button></nobr></p>');
|
||||
|
||||
$oPage->add('</form>');
|
||||
$oPage->add('</div></div></div>');
|
||||
$oPage->add_ready_script("\$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
|
||||
$oPage->add_ready_script("$('#dcr_{$this->iId} form').removeAttr('onsubmit');");
|
||||
$oPage->add_ready_script("$('#dcr_{$this->iId} form').bind('submit.uilinksWizard', oACWidget_{$this->iId}.DoSelectObjectClass);");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the form to create a new object of the 'target' class
|
||||
*/
|
||||
public function GetObjectCreationForm(WebPage $oPage, $oCurrObject)
|
||||
public function GetObjectCreationForm(WebPage $oPage, $oCurrObject, $aPrefillFormParam)
|
||||
{
|
||||
// Set all the default values in an object and clone this "default" object
|
||||
$oNewObj = MetaModel::NewObject($this->sTargetClass);
|
||||
@@ -428,7 +573,7 @@ EOF
|
||||
// 1st - set context values
|
||||
$oAppContext = new ApplicationContext();
|
||||
$oAppContext->InitObjectFromContext($oNewObj);
|
||||
|
||||
$oNewObj->PrefillForm('creation_from_extkey', $aPrefillFormParam);
|
||||
// 2nd set the default values from the constraint on the external key... if any
|
||||
if ( ($oCurrObject != null) && ($this->sAttCode != ''))
|
||||
{
|
||||
@@ -445,7 +590,7 @@ EOF
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 3rd - set values from the page argument 'default'
|
||||
$oNewObj->UpdateObjectFromArg('default');
|
||||
|
||||
@@ -462,7 +607,7 @@ EOF
|
||||
$aFieldsComments[$sAttCode] = ' <img src="../images/transp-lock.png" style="vertical-align:middle" title="'.htmlentities(Dict::S('UI:UploadNotSupportedInThisMode')).'"/>';
|
||||
}
|
||||
}
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), array('formPrefix' => $this->iId, 'noRelations' => true, 'fieldsFlags' => $aFieldsFlags, 'fieldsComments' => $aFieldsComments));
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), array('formPrefix' => $this->iId, 'noRelations' => true, 'fieldsFlags' => $aFieldsFlags, 'fieldsComments' => $aFieldsComments));
|
||||
$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', maxHeight: $(window).height() - 50, autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
|
||||
@@ -482,21 +627,12 @@ EOF
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
$oFilter = DBObjectSearch::FromOQL($sFilter);
|
||||
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
|
||||
$oSet = new DBObjectSet($oFilter, array(), array('this' => $oObj, 'current_extkey_id' => $currValue));
|
||||
|
||||
$oSet->SetShowObsoleteData(utils::ShowObsoleteData());
|
||||
|
||||
$sHKAttCode = MetaModel::IsHierarchicalClass($this->sTargetClass);
|
||||
$this->DumpTree($oPage, $oSet, $sHKAttCode, $currValue);
|
||||
@@ -505,7 +641,7 @@ EOF
|
||||
$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");
|
||||
@@ -527,7 +663,7 @@ EOF
|
||||
}
|
||||
else
|
||||
{
|
||||
return array('error' => implode(' ', $aErrors), 'id' => 0);
|
||||
return array('error' => implode(' ', $aErrors), 'id' => 0);
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
@@ -550,7 +686,7 @@ EOF
|
||||
$aTree[$iParentId][$oObj->GetKey()] = $oObj->GetName();
|
||||
$aNodes[$oObj->GetKey()] = $oObj;
|
||||
}
|
||||
|
||||
|
||||
$aParents = array_keys($aTree);
|
||||
$aRoots = array();
|
||||
foreach($aParents as $id)
|
||||
@@ -565,7 +701,7 @@ EOF
|
||||
$this->DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue)
|
||||
{
|
||||
$bSelect = true;
|
||||
@@ -600,4 +736,3 @@ EOF
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,9 +20,8 @@
|
||||
* Class UIHTMLEditorWidget
|
||||
* UI wdiget for displaying and editing one-way encrypted passwords
|
||||
*
|
||||
* @author Phil Eddies
|
||||
* @author Romain Quetiez
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -65,7 +64,7 @@ class UIHTMLEditorWidget
|
||||
$sHelpText = $this->m_sHelpText;
|
||||
$sValidationField = $this->m_sValidationField;
|
||||
|
||||
$sHtmlValue = "<table><tr><td><textarea class=\"htmlEditor\" title=\"$sHelpText\" name=\"attr_{$this->m_sFieldPrefix}{$sCode}\" rows=\"10\" cols=\"10\" id=\"$iId\">$sValue</textarea></td><td>$sValidationField</td></tr></table>";
|
||||
$sHtmlValue = "<div class=\"field_input_zone field_input_html\"><textarea class=\"htmlEditor\" title=\"$sHelpText\" name=\"attr_{$this->m_sFieldPrefix}{$sCode}\" rows=\"10\" cols=\"10\" id=\"$iId\">$sValue</textarea></div>$sValidationField";
|
||||
|
||||
// Replace the text area with CKEditor
|
||||
// To change the default settings of the editor,
|
||||
@@ -99,9 +98,26 @@ class UIHTMLEditorWidget
|
||||
|
||||
// Could also be bound to 'instanceReady.ckeditor'
|
||||
$oPage->add_ready_script("$('#$iId').bind('validate', function(evt, sFormId) { return ValidateCKEditField('$iId', '', {$this->m_sMandatory}, sFormId, '') } );\n");
|
||||
$oPage->add_ready_script("$('#$iId').bind('update', function() { BlockField('cke_$iId', $('#$iId').attr('disabled')); $(this).data('ckeditorInstance').setReadOnly($(this).prop('disabled')); } );\n");
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$iId').bind('update', function(evt){
|
||||
BlockField('cke_$iId', $('#$iId').attr('disabled'));
|
||||
//Delayed execution - ckeditor must be properly initialized before setting readonly
|
||||
var retryCount = 0;
|
||||
var oMe = $('#$iId');
|
||||
var delayedSetReadOnly = function () {
|
||||
if (oMe.data('ckeditorInstance').editable() == undefined && retryCount++ < 10) {
|
||||
setTimeout(delayedSetReadOnly, retryCount * 100); //Wait a while longer each iteration
|
||||
}
|
||||
else
|
||||
{
|
||||
oMe.data('ckeditorInstance').setReadOnly(oMe.prop('disabled'));
|
||||
}
|
||||
};
|
||||
setTimeout(delayedSetReadOnly, 50);
|
||||
});
|
||||
EOF
|
||||
);
|
||||
return $sHtmlValue;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* Class UILinksWidgetDirect
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -30,7 +30,14 @@ class UILinksWidgetDirect
|
||||
protected $sInputid;
|
||||
protected $sNameSuffix;
|
||||
protected $sLinkedClass;
|
||||
|
||||
|
||||
/**
|
||||
* UILinksWidgetDirect constructor.
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode
|
||||
* @param string $sInputId
|
||||
* @param string $sNameSuffix
|
||||
*/
|
||||
public function __construct($sClass, $sAttCode, $sInputId, $sNameSuffix = '')
|
||||
{
|
||||
$this->sClass = $sClass;
|
||||
@@ -71,8 +78,15 @@ class UILinksWidgetDirect
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param DBObjectSet|ormLinkSet $oValue
|
||||
* @param array $aArgs
|
||||
* @param string $sFormPrefix
|
||||
* @param DBObject $oCurrentObj
|
||||
*/
|
||||
public function Display(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
|
||||
{
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
switch($oLinksetDef->GetEditMode())
|
||||
@@ -115,8 +129,16 @@ class UILinksWidgetDirect
|
||||
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, true /* bDisplayMenu*/);
|
||||
}
|
||||
}
|
||||
|
||||
protected function DisplayAsBlock(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $bDisplayMenu)
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param DBObjectSet $oValue
|
||||
* @param array $aArgs
|
||||
* @param string $sFormPrefix
|
||||
* @param DBObject $oCurrentObj
|
||||
* @param bool $bDisplayMenu
|
||||
*/
|
||||
protected function DisplayAsBlock(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $bDisplayMenu)
|
||||
{
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
$sTargetClass = $oLinksetDef->GetLinkedClass();
|
||||
@@ -143,6 +165,7 @@ class UILinksWidgetDirect
|
||||
'target_attr' => $oLinksetDef->GetExtKeyToMe(),
|
||||
'object_id' => $oCurrentObj ? $oCurrentObj->GetKey() : null,
|
||||
'menu' => $bDisplayMenu,
|
||||
'menu_actions_target' => '_blank',
|
||||
'default' => $aDefaults,
|
||||
'table_id' => $this->sClass.'_'.$this->sAttCode,
|
||||
);
|
||||
@@ -151,46 +174,12 @@ class UILinksWidgetDirect
|
||||
$oBlock->Display($oPage, $this->sInputid, $aParams);
|
||||
}
|
||||
}
|
||||
|
||||
protected function DisplayEditInPlace(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $aButtons = array('create', 'delete'))
|
||||
{
|
||||
$aAttribs = $this->GetTableConfig();
|
||||
|
||||
$oValue->Rewind();
|
||||
$oPage->add('<table class="listContainer" id="'.$this->sInputid.'"><tr><td>');
|
||||
|
||||
$aData = array();
|
||||
while($oLinkObj = $oValue->Fetch())
|
||||
{
|
||||
$aRow = array();
|
||||
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.$oLinkObj->GetKey().'"/>';
|
||||
foreach($this->aZlist as $sLinkedAttCode)
|
||||
{
|
||||
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
|
||||
}
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
$oPage->table($aAttribs, $aData);
|
||||
$oPage->add('</td></tr></table>'); //listcontainer
|
||||
$sInputName = $sFormPrefix.'attr_'.$this->sAttCode;
|
||||
$aLabels = array(
|
||||
'delete' => Dict::S('UI:Button:Delete'),
|
||||
// 'modify' => 'Modify...' ,
|
||||
'creation_title' => Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sLinkedClass)),
|
||||
'create' => Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($this->sLinkedClass)),
|
||||
'remove' => Dict::S('UI:Button:Remove'),
|
||||
'add' => Dict::Format('UI:AddAnExisting_Class', MetaModel::GetName($this->sLinkedClass)),
|
||||
'selection_title' => Dict::Format('UI:SelectionOf_Class', MetaModel::GetName($this->sLinkedClass)),
|
||||
);
|
||||
$oContext = new ApplicationContext();
|
||||
$sSubmitUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?'.$oContext->GetForLink();
|
||||
$sJSONLabels = json_encode($aLabels);
|
||||
$sJSONButtons = json_encode($aButtons);
|
||||
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
|
||||
$oPage->add_ready_script("$('#{$this->sInputid}').directlinks({class_name: '$this->sClass', att_code: '$this->sAttCode', input_name:'$sInputName', labels: $sJSONLabels, submit_to: '$sSubmitUrl', buttons: $sJSONButtons, oWizardHelper: $sWizHelper });");
|
||||
}
|
||||
|
||||
public function GetObjectCreationDlg(WebPage $oPage, $sProposedRealClass = '')
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param string $sProposedRealClass
|
||||
*/
|
||||
public function GetObjectCreationDlg(WebPage $oPage, $sProposedRealClass = '', $oSourceObj = null)
|
||||
{
|
||||
// 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
|
||||
@@ -215,14 +204,17 @@ class UILinksWidgetDirect
|
||||
$aKeys = array_keys($aPossibleClasses);
|
||||
$sRealClass = $aKeys[0];
|
||||
}
|
||||
|
||||
|
||||
if ($sRealClass != '')
|
||||
{
|
||||
$oPage->add("<h1>".MetaModel::GetClassIcon($sRealClass)." ".Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($sRealClass))."</h1>\n");
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
|
||||
$aFieldFlags = array( $sExtKeyToMe => OPT_ATT_HIDDEN);
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, null, array(), array('formPrefix' => $this->sInputid, 'noRelations' => true, 'fieldsFlags' => $aFieldFlags));
|
||||
$oObj = DBObject::MakeDefaultInstance($sRealClass);
|
||||
$aPrefillParam = array('source_obj' => $oSourceObj);
|
||||
$oObj->PrefillForm('creation_from_editinplace', $aPrefillParam);
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), array('formPrefix' => $this->sInputid, 'noRelations' => true, 'fieldsFlags' => $aFieldFlags));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -239,13 +231,89 @@ class UILinksWidgetDirect
|
||||
}
|
||||
$oPage->add('</div></div>');
|
||||
}
|
||||
|
||||
public function GetObjectsSelectionDlg($oPage, $oCurrentObj)
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param DBObjectSet $oValue
|
||||
* @param array $aArgs
|
||||
* @param string $sFormPrefix
|
||||
* @param DBObject $oCurrentObj
|
||||
* @param array $aButtons
|
||||
*/
|
||||
protected function DisplayEditInPlace(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $aButtons = array('create', 'delete'))
|
||||
{
|
||||
$aAttribs = $this->GetTableConfig();
|
||||
|
||||
$oValue->Rewind();
|
||||
$oPage->add('<table class="listContainer" id="'.$this->sInputid.'"><tr><td>');
|
||||
|
||||
$aData = array();
|
||||
while($oLinkObj = $oValue->Fetch())
|
||||
{
|
||||
$aRow = array();
|
||||
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.$oLinkObj->GetKey().'"/>';
|
||||
foreach($this->aZlist as $sLinkedAttCode)
|
||||
{
|
||||
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
|
||||
}
|
||||
$aData[] = $aRow;
|
||||
}
|
||||
$oPage->table($aAttribs, $aData);
|
||||
$oPage->add('</td></tr></table>'); //listcontainer
|
||||
$sInputName = $sFormPrefix.'attr_'.$this->sAttCode;
|
||||
$aLabels = array(
|
||||
'delete' => Dict::S('UI:Button:Delete'),
|
||||
// 'modify' => 'Modify...' ,
|
||||
'creation_title' => Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sLinkedClass)),
|
||||
'create' => Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($this->sLinkedClass)),
|
||||
'remove' => Dict::S('UI:Button:Remove'),
|
||||
'add' => Dict::Format('UI:AddAnExisting_Class', MetaModel::GetName($this->sLinkedClass)),
|
||||
'selection_title' => Dict::Format('UI:SelectionOf_Class', MetaModel::GetName($this->sLinkedClass)),
|
||||
);
|
||||
$oContext = new ApplicationContext();
|
||||
$sSubmitUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?'.$oContext->GetForLink();
|
||||
$sJSONLabels = json_encode($aLabels);
|
||||
$sJSONButtons = json_encode($aButtons);
|
||||
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
|
||||
// Don't automatically launch the search if the table is huge
|
||||
$bDoSearch = !utils::IsHighCardinality($this->sLinkedClass);
|
||||
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
|
||||
$oPage->add_ready_script("$('#{$this->sInputid}').directlinks({class_name: '$this->sClass', att_code: '$this->sAttCode', input_name:'$sInputName', labels: $sJSONLabels, submit_to: '$sSubmitUrl', buttons: $sJSONButtons, oWizardHelper: $sWizHelper, do_search: $sJSDoSearch});");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param DBObject $oCurrentObj
|
||||
* @param $aAlreadyLinked
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function GetObjectsSelectionDlg($oPage, $oCurrentObj, $aAlreadyLinked)
|
||||
{
|
||||
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
|
||||
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
$valuesDef = $oLinksetDef->GetValuesDef();
|
||||
|
||||
$oHiddenFilter = new DBObjectSearch($this->sLinkedClass);
|
||||
if (($oCurrentObj != null) && MetaModel::IsSameFamilyBranch($this->sLinkedClass, $this->sClass))
|
||||
{
|
||||
// Prevent linking to self if the linked object is of the same family
|
||||
// and already present in the database
|
||||
if (!$oCurrentObj->IsNew())
|
||||
{
|
||||
$oHiddenFilter->AddCondition('id', $oCurrentObj->GetKey(), '!=');
|
||||
}
|
||||
}
|
||||
if (count($aAlreadyLinked) > 0)
|
||||
{
|
||||
$oHiddenFilter->AddCondition('id', $aAlreadyLinked, 'NOTIN');
|
||||
}
|
||||
$oHiddenCriteria = $oHiddenFilter->GetCriteria();
|
||||
$aArgs = $oHiddenFilter->GetInternalParams();
|
||||
$sHiddenCriteria = $oHiddenCriteria->Render($aArgs);
|
||||
|
||||
$oLinkSetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
$valuesDef = $oLinkSetDef->GetValuesDef();
|
||||
if ($valuesDef === null)
|
||||
{
|
||||
$oFilter = new DBObjectSearch($this->sLinkedClass);
|
||||
@@ -258,13 +326,26 @@ class UILinksWidgetDirect
|
||||
}
|
||||
$oFilter = DBObjectSearch::FromOQL($valuesDef->GetFilterExpression());
|
||||
}
|
||||
|
||||
if ($oCurrentObj != null)
|
||||
{
|
||||
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
|
||||
|
||||
$aArgs = array_merge($oCurrentObj->ToArgs('this'), $oFilter->GetInternalParams());
|
||||
$oFilter->SetInternalParams($aArgs);
|
||||
}
|
||||
$bOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open');
|
||||
$oBlock = new DisplayBlock($oFilter, 'search', false);
|
||||
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->sInputid}", array('open' => $bOpen));
|
||||
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->sInputid}",
|
||||
array(
|
||||
'result_list_outer_selector' => "SearchResultsToAdd_{$this->sInputid}",
|
||||
'table_id' => "add_{$this->sInputid}",
|
||||
'table_inner_id' => "ResultsToAdd_{$this->sInputid}",
|
||||
'selection_mode' => true,
|
||||
'cssCount' => "#count_{$this->sInputid}",
|
||||
'query_params' => $oFilter->GetInternalParams(),
|
||||
'hidden_criteria' => $sHiddenCriteria,
|
||||
)
|
||||
);
|
||||
$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";
|
||||
@@ -274,16 +355,15 @@ class UILinksWidgetDirect
|
||||
$sHtml .= "</div>\n";
|
||||
$sHtml .= "</form>\n";
|
||||
$oPage->add($sHtml);
|
||||
//$oPage->add_ready_script("$('#SearchFormToAdd_{$this->sAttCode}{$this->sNameSuffix} form').bind('submit.uilinksWizard', oWidget{$this->sInputId}.SearchObjectsToAdd);");
|
||||
//$oPage->add_ready_script("$('#SearchFormToAdd_{$this->sAttCode}{$this->sNameSuffix}').resize(oWidget{$this->siInputId}.UpdateSizes);");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Search for objects to be linked to the current object (i.e "remote" objects)
|
||||
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
|
||||
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of $this->sLinkedClass
|
||||
* @param array $aAlreadyLinked Array of indentifiers of objects which are already linke to the current object (or about to be linked)
|
||||
* @param DBObject $oCurrentObj The object currently being edited... if known...
|
||||
* @throws Exception
|
||||
*/
|
||||
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinked = array(), $oCurrentObj = null)
|
||||
{
|
||||
@@ -291,8 +371,8 @@ class UILinksWidgetDirect
|
||||
{
|
||||
$sRemoteClass = $this->sLinkedClass;
|
||||
}
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
$valuesDef = $oLinksetDef->GetValuesDef();
|
||||
$oLinkSetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
$valuesDef = $oLinkSetDef->GetValuesDef();
|
||||
if ($valuesDef === null)
|
||||
{
|
||||
$oFilter = new DBObjectSearch($sRemoteClass);
|
||||
@@ -309,7 +389,7 @@ class UILinksWidgetDirect
|
||||
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
|
||||
// and already present in the database
|
||||
if (!$oCurrentObj->IsNew())
|
||||
{
|
||||
$oFilter->AddCondition('id', $oCurrentObj->GetKey(), '!=');
|
||||
@@ -321,6 +401,8 @@ class UILinksWidgetDirect
|
||||
}
|
||||
if ($oCurrentObj != null)
|
||||
{
|
||||
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
|
||||
|
||||
$aArgs = array_merge($oCurrentObj->ToArgs('this'), $oFilter->GetInternalParams());
|
||||
$oFilter->SetInternalParams($aArgs);
|
||||
}
|
||||
@@ -328,6 +410,10 @@ class UILinksWidgetDirect
|
||||
$oBlock->Display($oP, "ResultsToAdd_{$this->sInputid}", array('menu' => false, 'cssCount'=> '#count_'.$this->sInputid , 'selection_mode' => true, 'table_id' => 'add_'.$this->sInputid)); // Don't display the 'Actions' menu on the results
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oP
|
||||
* @param $oFullSetFilter
|
||||
*/
|
||||
public function DoAddObjects(WebPage $oP, $oFullSetFilter)
|
||||
{
|
||||
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
|
||||
@@ -355,7 +441,14 @@ class UILinksWidgetDirect
|
||||
}
|
||||
return $aAttribs;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param string $sRealClass
|
||||
* @param array $aValues
|
||||
* @param int $iTempId
|
||||
* @return mixed
|
||||
*/
|
||||
public function GetRow($oPage, $sRealClass, $aValues, $iTempId)
|
||||
{
|
||||
if ($sRealClass == '')
|
||||
@@ -367,7 +460,13 @@ class UILinksWidgetDirect
|
||||
|
||||
return $this->GetObjectRow($oPage, $oLinkObj, $iTempId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param $oLinkObj
|
||||
* @param int $iTempId
|
||||
* @return mixed
|
||||
*/
|
||||
protected function GetObjectRow($oPage, $oLinkObj, $iTempId)
|
||||
{
|
||||
$aAttribs = $this->GetTableConfig();
|
||||
@@ -383,7 +482,7 @@ class UILinksWidgetDirect
|
||||
/**
|
||||
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
|
||||
* @param DBObject $oSourceObj
|
||||
* @param DBSearch $oSearch
|
||||
* @param DBSearch|DBObjectSearch $oSearch
|
||||
*/
|
||||
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
|
||||
{
|
||||
@@ -402,7 +501,6 @@ class UILinksWidgetDirect
|
||||
|
||||
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sSrcClass, $sAttCode);
|
||||
$defaultValue = $oSourceObj->Get($sAttCode);
|
||||
|
||||
// Find the attcode for the same 'context' parameter in the destination class
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,12 +20,12 @@
|
||||
/**
|
||||
* Class UILinksWidget
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
require_once(APPROOT.'/application/webpage.class.inc.php');
|
||||
require_once(APPROOT.'/application/displayblock.class.inc.php');
|
||||
require_once(APPROOT.'application/webpage.class.inc.php');
|
||||
require_once(APPROOT.'application/displayblock.class.inc.php');
|
||||
|
||||
class UILinksWidget
|
||||
{
|
||||
@@ -39,7 +39,22 @@ class UILinksWidget
|
||||
protected $m_sLinkedClass;
|
||||
protected $m_sRemoteClass;
|
||||
protected $m_bDuplicatesAllowed;
|
||||
|
||||
protected $m_aEditableFields;
|
||||
protected $m_aTableConfig;
|
||||
|
||||
/**
|
||||
* UILinksWidget constructor.
|
||||
*
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode
|
||||
* @param int $iInputId
|
||||
* @param string $sNameSuffix
|
||||
* @param bool $bDuplicatesAllowed
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct($sClass, $sAttCode, $iInputId, $sNameSuffix = '', $bDuplicatesAllowed = false)
|
||||
{
|
||||
$this->m_sClass = $sClass;
|
||||
@@ -48,12 +63,15 @@ class UILinksWidget
|
||||
$this->m_iInputId = $iInputId;
|
||||
$this->m_bDuplicatesAllowed = $bDuplicatesAllowed;
|
||||
$this->m_aEditableFields = array();
|
||||
|
||||
|
||||
/** @var AttributeLinkedSetIndirect $oAttDef */
|
||||
$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);
|
||||
|
||||
/** @var AttributeExternalKey $oLinkingAttDef */
|
||||
$oLinkingAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $this->m_sExtKeyToRemote);
|
||||
$this->m_sRemoteClass = $oLinkingAttDef->GetTargetClass();
|
||||
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
||||
$sStateAttCode = MetaModel::GetStateAttributeCode($this->m_sClass);
|
||||
@@ -92,16 +110,25 @@ class UILinksWidget
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A one-row form for editing a link record
|
||||
*
|
||||
* @param WebPage $oP Web page used for the ouput
|
||||
* @param DBObject $oLinkedObj The object to which all the elements of the linked set refer to
|
||||
* @param DBObject $oLinkedObj Remote object
|
||||
* @param mixed $linkObjOrId Either the object linked or a unique number for new link records to add
|
||||
* @param Hash $aArgs Extra context arguments
|
||||
* @return string The HTML fragment of the one-row form
|
||||
* @param array $aArgs Extra context arguments
|
||||
* @param DBObject $oCurrentObj The object to which all the elements of the linked set refer to
|
||||
* @param int $iUniqueId A unique identifier of new links
|
||||
* @param boolean $bReadOnly Display link as editable or read-only. Default is false (editable)
|
||||
*
|
||||
* @return array The HTML fragment of the one-row form
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId = null, $aArgs = array(), $oCurrentObj )
|
||||
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false)
|
||||
{
|
||||
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
|
||||
$aRow = array();
|
||||
@@ -115,16 +142,33 @@ class UILinksWidget
|
||||
$aArgs['prefix'] = $sPrefix;
|
||||
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}{$key}";
|
||||
$aArgs['this'] = $linkObjOrId;
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$key\">";
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"attr_{$sPrefix}id{$sNameSuffix}\" value=\"$key\">";
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId->GetKey().']';
|
||||
$sSafeId = utils::GetSafeId($sFieldId);
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
|
||||
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $linkObjOrId->Get($sFieldCode), '' /* DisplayValue */, $sSafeId, $sNameSuffix, 0, $aArgs);
|
||||
$aFieldsMap[$sFieldCode] = $sSafeId;
|
||||
}
|
||||
|
||||
if($bReadOnly)
|
||||
{
|
||||
$aRow['form::checkbox'] = "";
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$sDisplayValue = $linkObjOrId->GetEditValue($sFieldCode);
|
||||
$aRow[$sFieldCode] = $sDisplayValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"$key\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$key\">";
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId->GetKey().']';
|
||||
$sSafeId = utils::GetSafeId($sFieldId);
|
||||
$sValue = $linkObjOrId->Get($sFieldCode);
|
||||
$sDisplayValue = $linkObjOrId->GetEditValue($sFieldCode);
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
|
||||
$aRow[$sFieldCode] = '<div class="field_container" style="border:none;"><div class="field_data"><div class="field_value">'.
|
||||
cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $sValue, $sDisplayValue, $sSafeId, $sNameSuffix, 0, $aArgs).
|
||||
'</div></div></div>';
|
||||
$aFieldsMap[$sFieldCode] = $sSafeId;
|
||||
}
|
||||
}
|
||||
|
||||
$sState = $linkObjOrId->GetState();
|
||||
}
|
||||
else
|
||||
@@ -135,78 +179,60 @@ class UILinksWidget
|
||||
// New link existing only in memory
|
||||
$oNewLinkObj = $linkObjOrId;
|
||||
$iRemoteObjKey = $oNewLinkObj->Get($this->m_sExtKeyToRemote);
|
||||
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, $iRemoteObjKey);
|
||||
$oNewLinkObj->Set($this->m_sExtKeyToMe, $oCurrentObj); // Setting the extkey with the object also fills the related external fields
|
||||
$linkObjOrId = -$iRemoteObjKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
$iRemoteObjKey = -$linkObjOrId;
|
||||
$iRemoteObjKey = $linkObjOrId;
|
||||
$oNewLinkObj = MetaModel::NewObject($this->m_sLinkedClass);
|
||||
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, -$linkObjOrId);
|
||||
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, $iRemoteObjKey);
|
||||
$oNewLinkObj->Set($this->m_sExtKeyToRemote, $oRemoteObj); // Setting the extkey with the object alsoo fills the related external fields
|
||||
$oNewLinkObj->Set($this->m_sExtKeyToMe, $oCurrentObj); // Setting the extkey with the object also fills the related external fields
|
||||
}
|
||||
$sPrefix .= "[$linkObjOrId][";
|
||||
$sPrefix .= "[-$iUniqueId][";
|
||||
$sNameSuffix = "]"; // To make a tabular form
|
||||
$aArgs['prefix'] = $sPrefix;
|
||||
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}_".(-$linkObjOrId);
|
||||
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}_".($iUniqueId < 0 ? -$iUniqueId : $iUniqueId);
|
||||
$aArgs['this'] = $oNewLinkObj;
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$linkObjOrId\">";
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"attr_{$sPrefix}id{$sNameSuffix}\" value=\"\">";
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"-$iUniqueId\">";
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$linkObjOrId.']';
|
||||
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.-$iUniqueId.']';
|
||||
$sSafeId = utils::GetSafeId($sFieldId);
|
||||
$sValue = $oNewLinkObj->Get($sFieldCode);
|
||||
$sDisplayValue = $oNewLinkObj->GetEditValue($sFieldCode);
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
|
||||
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $oNewLinkObj->Get($sFieldCode) /* TO DO/ call GetDefaultValue($oObject->ToArgs()) */, '' /* DisplayValue */, $sSafeId /* id */, $sNameSuffix, 0, $aArgs);
|
||||
$aRow[$sFieldCode] = '<div class="field_container" style="border:none;"><div class="field_data"><div class="field_value">'.
|
||||
cmdbAbstractObject::GetFormElementForField($oP, $this->m_sLinkedClass, $sFieldCode, $oAttDef, $sValue, $sDisplayValue, $sSafeId /* id */, $sNameSuffix, 0, $aArgs).
|
||||
'</div></div></div>';
|
||||
$aFieldsMap[$sFieldCode] = $sSafeId;
|
||||
}
|
||||
$sState = '';
|
||||
$sJSDaysMin = json_encode(array(Dict::S('DayOfWeek-Sunday-Min'), Dict::S('DayOfWeek-Monday-Min'), Dict::S('DayOfWeek-Tuesday-Min'), Dict::S('DayOfWeek-Wednesday-Min'),
|
||||
Dict::S('DayOfWeek-Thursday-Min'), Dict::S('DayOfWeek-Friday-Min'), Dict::S('DayOfWeek-Saturday-Min')));
|
||||
$sJSMonthsShort = json_encode(array(Dict::S('Month-01-Short'), Dict::S('Month-02-Short'), Dict::S('Month-03-Short'), Dict::S('Month-04-Short'), Dict::S('Month-05-Short'), Dict::S('Month-06-Short'),
|
||||
Dict::S('Month-07-Short'), Dict::S('Month-08-Short'), Dict::S('Month-09-Short'), Dict::S('Month-10-Short'), Dict::S('Month-11-Short'), Dict::S('Month-12-Short')));
|
||||
$iFirstDayOfWeek = (int) Dict::S('Calendar-FirstDayOfWeek');
|
||||
|
||||
$oP->add_script(
|
||||
<<<EOF
|
||||
$(".date-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true,
|
||||
dayNamesMin: $sJSDaysMin,
|
||||
monthNamesShort: $sJSMonthsShort,
|
||||
firstDay: $iFirstDayOfWeek
|
||||
});
|
||||
$(".datetime-pick").datepicker({
|
||||
showOn: 'button',
|
||||
buttonImage: '../images/calendar.png',
|
||||
buttonImageOnly: true,
|
||||
dateFormat: 'yy-mm-dd 00:00:00',
|
||||
constrainInput: false,
|
||||
changeMonth: true,
|
||||
changeYear: true,
|
||||
dayNamesMin: $sJSDaysMin,
|
||||
monthNamesShort: $sJSMonthsShort,
|
||||
firstDay: $iFirstDayOfWeek
|
||||
});
|
||||
// Rows created with ajax call need OnLinkAdded call.
|
||||
// Rows added before loading the form cannot call OnLinkAdded.
|
||||
if ($iUniqueId > 0)
|
||||
{
|
||||
$oP->add_script(
|
||||
<<<EOF
|
||||
PrepareWidgets();
|
||||
oWidget{$this->m_iInputId}.OnLinkAdded($iUniqueId, $iRemoteObjKey);
|
||||
EOF
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$sExtKeyToMeId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToMe);
|
||||
$aFieldsMap[$this->m_sExtKeyToMe] = $sExtKeyToMeId;
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToMeId\" value=\"".$oCurrentObj->GetKey()."\">";
|
||||
|
||||
$sExtKeyToRemoteId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToRemote);
|
||||
$aFieldsMap[$this->m_sExtKeyToRemote] = $sExtKeyToRemoteId;
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToRemoteId\" value=\"$iRemoteObjKey\">";
|
||||
|
||||
if(!$bReadOnly)
|
||||
{
|
||||
$sExtKeyToMeId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToMe);
|
||||
$aFieldsMap[$this->m_sExtKeyToMe] = $sExtKeyToMeId;
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToMeId\" value=\"".$oCurrentObj->GetKey()."\">";
|
||||
|
||||
$sExtKeyToRemoteId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToRemote);
|
||||
$aFieldsMap[$this->m_sExtKeyToRemote] = $sExtKeyToRemoteId;
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToRemoteId\" value=\"$iRemoteObjKey\">";
|
||||
}
|
||||
|
||||
$iFieldsCount = count($aFieldsMap);
|
||||
$sJsonFieldsMap = json_encode($aFieldsMap);
|
||||
|
||||
@@ -227,7 +253,11 @@ EOF
|
||||
|
||||
/**
|
||||
* Display one row of the whole form
|
||||
* @return none
|
||||
* @param WebPage $oP
|
||||
* @param array $aConfig
|
||||
* @param array $aRow
|
||||
* @param int $iRowId
|
||||
* @return string
|
||||
*/
|
||||
protected function DisplayFormRow(WebPage $oP, $aConfig, $aRow, $iRowId)
|
||||
{
|
||||
@@ -245,8 +275,8 @@ EOF
|
||||
/**
|
||||
* Display the table with the form for editing all the links at once
|
||||
* @param WebPage $oP The web page used for the output
|
||||
* @param Hash $aConfig The table's header configuration
|
||||
* @param Hash $aData The tabular data to be displayed
|
||||
* @param array $aConfig The table's header configuration
|
||||
* @param array $aData The tabular data to be displayed
|
||||
* @return string Html fragment representing the form table
|
||||
*/
|
||||
protected function DisplayFormTable(WebPage $oP, $aConfig, $aData)
|
||||
@@ -283,48 +313,65 @@ EOF
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get the HTML fragment corresponding to the linkset editing widget
|
||||
* @param WebPage $oP The web page used for all the output
|
||||
* @param DBObjectSet The initial value of the linked set
|
||||
* @param Hash $aArgs Extra context arguments
|
||||
*
|
||||
* @param WebPage $oPage
|
||||
* @param DBObject|ormLinkSet $oValue
|
||||
* @param array $aArgs Extra context arguments
|
||||
* @param string $sFormPrefix prefix of the fields in the current form
|
||||
* @param DBObject $oCurrentObj the current object to which the linkset is related
|
||||
*
|
||||
* @return string The HTML fragment to be inserted into the page
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
*/
|
||||
public function Display(WebPage $oPage, DBObjectSet $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
|
||||
public function Display(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
|
||||
{
|
||||
$sHtmlValue = '';
|
||||
$sTargetClass = self::GetTargetClass($this->m_sClass, $this->m_sAttCode);
|
||||
$sHtmlValue .= "<div id=\"linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix}\">\n";
|
||||
$sHtmlValue .= "<input type=\"hidden\" id=\"{$sFormPrefix}{$this->m_iInputId}\">\n";
|
||||
$oValue->Rewind();
|
||||
$aForm = array();
|
||||
$iAddedId = 1; // Unique id for new links
|
||||
while($oCurrentLink = $oValue->Fetch())
|
||||
{
|
||||
$aRow = array();
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote));
|
||||
if ($oCurrentLink->IsNew())
|
||||
{
|
||||
$key = -$oLinkedObj->GetKey();
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
$key = $oCurrentLink->GetKey();
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj);
|
||||
}
|
||||
// We try to retrieve the remote object as usual
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote), false /* Must not be found */);
|
||||
// If successful, it means that we can edit its link
|
||||
if($oLinkedObj !== null)
|
||||
{
|
||||
$bReadOnly = false;
|
||||
}
|
||||
// Else we retrieve it without restrictions (silos) and will display its link as readonly
|
||||
else
|
||||
{
|
||||
$bReadOnly = true;
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote), false /* Must not be found */, true);
|
||||
}
|
||||
|
||||
if ($oCurrentLink->IsNew())
|
||||
{
|
||||
$key = -($iAddedId++);
|
||||
}
|
||||
else
|
||||
{
|
||||
$key = $oCurrentLink->GetKey();
|
||||
}
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly);
|
||||
}
|
||||
$sHtmlValue .= $this->DisplayFormTable($oPage, $this->m_aTableConfig, $aForm);
|
||||
$sDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false';
|
||||
// Don't automatically launch the search if the table is huge
|
||||
$bDoSearch = !utils::IsHighCardinality($this->m_sRemoteClass);
|
||||
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
|
||||
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
|
||||
$oPage->add_ready_script(<<<EOF
|
||||
oWidget{$this->m_iInputId} = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}', $sDuplicates, $sWizHelper, '{$this->m_sExtKeyToRemote}');
|
||||
oWidget{$this->m_iInputId} = new LinksWidget('{$this->m_sAttCode}{$this->m_sNameSuffix}', '{$this->m_sClass}', '{$this->m_sAttCode}', '{$this->m_iInputId}', '{$this->m_sNameSuffix}', $sDuplicates, $sWizHelper, '{$this->m_sExtKeyToRemote}', $sJSDoSearch);
|
||||
oWidget{$this->m_iInputId}.Init();
|
||||
$('#{$this->m_iInputId}').bind('update_value', function() { $(this).val(oWidget{$this->m_iInputId}.GetUpdatedValue()); })
|
||||
EOF
|
||||
);
|
||||
$sHtmlValue .= "<span style=\"float:left;\"> <img src=\"../images/tv-item-last.gif\"> <input id=\"{$this->m_sAttCode}{$this->m_sNameSuffix}_btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"oWidget{$this->m_iInputId}.RemoveSelected();\" >";
|
||||
@@ -332,16 +379,27 @@ EOF
|
||||
$sHtmlValue .= "<span style=\"clear:both;\"><p> </p></span>\n";
|
||||
$sHtmlValue .= "</div>\n";
|
||||
$oPage->add_at_the_end("<div id=\"dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}\"></div>"); // To prevent adding forms inside the main form
|
||||
return $sHtmlValue;
|
||||
return $sHtmlValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param string $sAttCode
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected static function GetTargetClass($sClass, $sAttCode)
|
||||
{
|
||||
/** @var AttributeLinkedSet $oAttDef */
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$sLinkedClass = $oAttDef->GetLinkedClass();
|
||||
$sTargetClass = '';
|
||||
switch(get_class($oAttDef))
|
||||
{
|
||||
case 'AttributeLinkedSetIndirect':
|
||||
/** @var AttributeExternalKey $oLinkingAttDef */
|
||||
/** @var AttributeLinkedSetIndirect $oAttDef */
|
||||
$oLinkingAttDef = MetaModel::GetAttributeDef($sLinkedClass, $oAttDef->GetExtKeyToRemote());
|
||||
$sTargetClass = $oLinkingAttDef->GetTargetClass();
|
||||
break;
|
||||
@@ -353,21 +411,60 @@ EOF
|
||||
|
||||
return $sTargetClass;
|
||||
}
|
||||
|
||||
public function GetObjectPickerDialog($oPage, $oCurrentObj)
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param DBObject $oCurrentObj
|
||||
* @param $sJson
|
||||
* @param array $aAlreadyLinkedIds
|
||||
*
|
||||
* @throws DictExceptionMissingString
|
||||
* @throws Exception
|
||||
*/
|
||||
public function GetObjectPickerDialog($oPage, $oCurrentObj, $sJson, $aAlreadyLinkedIds = array(), $aPrefillFormParam = array())
|
||||
{
|
||||
$bOpen = MetaModel::GetConfig()->Get('legacy_search_drawer_open');
|
||||
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
|
||||
|
||||
$oAlreadyLinkedFilter = new DBObjectSearch($this->m_sRemoteClass);
|
||||
if (!$this->m_bDuplicatesAllowed && count($aAlreadyLinkedIds) > 0)
|
||||
{
|
||||
$oAlreadyLinkedFilter->AddCondition('id', $aAlreadyLinkedIds, 'NOTIN');
|
||||
$oAlreadyLinkedExpression = $oAlreadyLinkedFilter->GetCriteria();
|
||||
$sAlreadyLinkedExpression = $oAlreadyLinkedExpression->Render();
|
||||
}
|
||||
else
|
||||
{
|
||||
$sAlreadyLinkedExpression = '';
|
||||
}
|
||||
|
||||
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
|
||||
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
|
||||
|
||||
if(!empty($oCurrentObj))
|
||||
{
|
||||
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
|
||||
$aPrefillFormParam['filter'] = $oFilter;
|
||||
$aPrefillFormParam['dest_class'] = $this->m_sRemoteClass;
|
||||
$oCurrentObj->PrefillForm('search', $aPrefillFormParam);
|
||||
}
|
||||
$oBlock = new DisplayBlock($oFilter, 'search', false);
|
||||
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}", array('open' => $bOpen));
|
||||
$sHtml .= "<form id=\"ObjectsAddForm_{$this->m_sAttCode}{$this->m_sNameSuffix}\" OnSubmit=\"return oWidget{$this->m_iInputId}.DoAddObjects(this.id);\">\n";
|
||||
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}",
|
||||
array(
|
||||
'menu' => false,
|
||||
'result_list_outer_selector' => "SearchResultsToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}",
|
||||
'table_id' => 'add_'.$this->m_sAttCode,
|
||||
'table_inner_id' => "ResultsToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}",
|
||||
'selection_mode' => true,
|
||||
'json' => $sJson,
|
||||
'cssCount' => '#count_'.$this->m_sAttCode.$this->m_sNameSuffix,
|
||||
'query_params' => $oFilter->GetInternalParams(),
|
||||
'hidden_criteria' => $sAlreadyLinkedExpression,
|
||||
));
|
||||
$sHtml .= "<form id=\"ObjectsAddForm_{$this->m_sAttCode}{$this->m_sNameSuffix}\">\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=\"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 .= "<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=\"button\" onclick=\"return oWidget{$this->m_iInputId}.DoAddObjects(this.id);\" value=\"".Dict::S('UI:Button:Add')."\">";
|
||||
$sHtml .= "</div>\n";
|
||||
$sHtml .= "</form>\n";
|
||||
$oPage->add($sHtml);
|
||||
@@ -379,11 +476,17 @@ EOF
|
||||
|
||||
/**
|
||||
* 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 m_sRemoteClass
|
||||
* @param Array $aAlreadyLinkedIds List of IDs of objects of "remote" class already linked, to be filtered out of the search
|
||||
* @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
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinkedIds = array())
|
||||
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinkedIds = array(), $oCurrentObj = null)
|
||||
{
|
||||
if ($sRemoteClass != '')
|
||||
{
|
||||
@@ -397,51 +500,35 @@ EOF
|
||||
}
|
||||
if (!$this->m_bDuplicatesAllowed && count($aAlreadyLinkedIds) > 0)
|
||||
{
|
||||
// Positive IDs correspond to existing link records
|
||||
// negative IDs correspond to "remote" objects to be linked
|
||||
$aLinkIds = array();
|
||||
$aRemoteObjIds = array();
|
||||
foreach($aAlreadyLinkedIds as $iId)
|
||||
{
|
||||
if ($iId > 0)
|
||||
{
|
||||
$aLinkIds[] = $iId;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRemoteObjIds[] = -$iId;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($aLinkIds) >0)
|
||||
{
|
||||
// Search for the links to find to which "remote" object they are linked
|
||||
$oLinkFilter = new DBObjectSearch($this->m_sLinkedClass);
|
||||
$oLinkFilter->AddCondition('id', $aLinkIds, 'IN');
|
||||
$oLinkSet = new CMDBObjectSet($oLinkFilter);
|
||||
while($oLink = $oLinkSet->Fetch())
|
||||
{
|
||||
$aRemoteObjIds[] = $oLink->Get($this->m_sExtKeyToRemote);
|
||||
}
|
||||
}
|
||||
$oFilter->AddCondition('id', $aRemoteObjIds, 'NOTIN');
|
||||
$oFilter->AddCondition('id', $aAlreadyLinkedIds, 'NOTIN');
|
||||
}
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oBlock->Display($oP, "ResultsToAdd_{$this->m_sAttCode}", array('menu' => false, 'cssCount'=> '#count_'.$this->m_sAttCode.$this->m_sNameSuffix , 'selection_mode' => true, 'table_id' => 'add_'.$this->m_sAttCode)); // Don't display the 'Actions' menu on the results
|
||||
}
|
||||
|
||||
public function DoAddObjects(WebPage $oP, $oFullSetFilter, $oCurrentObj)
|
||||
|
||||
/**
|
||||
* @param WebPage $oP
|
||||
* @param int $iMaxAddedId
|
||||
* @param $oFullSetFilter
|
||||
* @param DBObject $oCurrentObj
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function DoAddObjects(WebPage $oP, $iMaxAddedId, $oFullSetFilter, $oCurrentObj)
|
||||
{
|
||||
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
|
||||
|
||||
$iAdditionId = $iMaxAddedId + 1;
|
||||
foreach($aLinkedObjectIds as $iObjectId)
|
||||
{
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId);
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId, false);
|
||||
if (is_object($oLinkedObj))
|
||||
{
|
||||
$aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId, array(), $oCurrentObj ); // Not yet created link get negative Ids
|
||||
$oP->add($this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iObjectId));
|
||||
$aRow = $this->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
|
||||
$oP->add($this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iAdditionId));
|
||||
$iAdditionId++;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -449,11 +536,15 @@ EOF
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
|
||||
*
|
||||
* @param DBObject $oSourceObj
|
||||
* @param DBSearch $oSearch
|
||||
* @param DBSearch|DBObjectSearch $oSearch
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function SetSearchDefaultFromContext($oSourceObj, &$oSearch)
|
||||
{
|
||||
@@ -472,7 +563,6 @@ EOF
|
||||
|
||||
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode))
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sSrcClass, $sAttCode);
|
||||
$defaultValue = $oSourceObj->Get($sAttCode);
|
||||
|
||||
// Find the attcode for the same 'context' parameter in the destination class
|
||||
@@ -486,10 +576,33 @@ EOF
|
||||
|
||||
if (MetaModel::IsValidAttCode($sDestClass, $sAttCode) && !empty($defaultValue))
|
||||
{
|
||||
$oSearch->AddCondition($sAttCode, $defaultValue);
|
||||
// Add Hierarchical condition if hierarchical key
|
||||
$oAttDef = MetaModel::GetAttributeDef($sDestClass, $sAttCode);
|
||||
if (isset($oAttDef) && ($oAttDef->IsExternalKey()))
|
||||
{
|
||||
try
|
||||
{
|
||||
/** @var AttributeExternalKey $oAttDef */
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sTargetClass);
|
||||
if ($sHierarchicalKeyCode !== false)
|
||||
{
|
||||
$oFilter = new DBObjectSearch($sTargetClass);
|
||||
$oFilter->AddCondition('id', $defaultValue);
|
||||
$oHKFilter = new DBObjectSearch($sTargetClass);
|
||||
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
|
||||
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
|
||||
}
|
||||
} catch (Exception $e)
|
||||
{
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oSearch->AddCondition($sAttCode, $defaultValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -58,9 +58,15 @@ class UIPasswordWidget
|
||||
$sConfirmPasswordValue = $aPasswordValues ? $aPasswordValues['confirm'] : '*****';
|
||||
$sChangedValue = (($sPasswordValue != '*****') || ($sConfirmPasswordValue != '*****')) ? 1 : 0;
|
||||
$sHtmlValue = '';
|
||||
$sHtmlValue = '<input type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/> <span class="form_validation" id="v_'.$this->iId.'"></span><span id="fstatus_'.$this->iId.'"></span><br/>';
|
||||
$sHtmlValue .= '<input type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'[confirm]"/> '.Dict::S('UI:PasswordConfirm').' <input id="'.$this->iId.'_reset" type="button" value="'.Dict::S('UI:Button:ResetPassword').'" onClick="ResetPwd(\''.$this->iId.'\');">';
|
||||
$sHtmlValue .= '<div class="field_input_zone field_input_onewaypassword">';
|
||||
$sHtmlValue .= '<input type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$sHtmlValue .= '<input type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'[confirm]"/>';
|
||||
$sHtmlValue .= '<span>'.Dict::S('UI:PasswordConfirm').'</span>';
|
||||
$sHtmlValue .= '<input id="'.$this->iId.'_reset" type="button" value="'.Dict::S('UI:Button:ResetPassword').'" onClick="ResetPwd(\''.$this->iId.'\');">';
|
||||
$sHtmlValue .= '<input type="hidden" id="'.$this->iId.'_changed" name="attr_'.$sCode.'[changed]" value="'.$sChangedValue.'"/>';
|
||||
$sHtmlValue .= '</div>';
|
||||
|
||||
$sHtmlValue .= '<span class="form_validation" id="v_'.$this->iId.'"></span><span class="field_status" id="fstatus_'.$this->iId.'"></span>';
|
||||
|
||||
$oPage->add_ready_script("$('#$this->iId').bind('keyup change', function(evt) { return PasswordFieldChanged('$this->iId') } );"); // Bind to a custom event: validate
|
||||
$oPage->add_ready_script("$('#$this->iId').bind('keyup change validate', function(evt, sFormId) { return ValidatePasswordField('$this->iId', sFormId) } );"); // Bind to a custom event: validate
|
||||
|
||||
115
application/ui.searchformforeignkeys.class.inc.php
Normal file
115
application/ui.searchformforeignkeys.class.inc.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* Copyright (C) 2010-2018 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
require_once(APPROOT.'/application/webpage.class.inc.php');
|
||||
require_once(APPROOT.'/application/displayblock.class.inc.php');
|
||||
|
||||
class UISearchFormForeignKeys
|
||||
{
|
||||
public function __construct($sTargetClass, $iInputId = null)
|
||||
{
|
||||
$this->m_sRemoteClass = $sTargetClass;
|
||||
$this->m_iInputId = $iInputId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
*
|
||||
* @param $sTitle
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function ShowModalSearchForeignKeys($oPage, $sTitle)
|
||||
{
|
||||
$sHtml = "<div class=\"wizContainer\" style=\"vertical-align:top;\">\n";
|
||||
|
||||
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
|
||||
|
||||
$oBlock = new DisplayBlock($oFilter, 'search', false);
|
||||
$sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->m_iInputId}",
|
||||
array(
|
||||
'menu' => false,
|
||||
'result_list_outer_selector' => "SearchResultsToAdd_{$this->m_iInputId}",
|
||||
'table_id' => "add_{$this->m_iInputId}",
|
||||
'table_inner_id' => "ResultsToAdd_{$this->m_iInputId}",
|
||||
'selection_mode' => true,
|
||||
'cssCount' => "#count_{$this->m_iInputId}",
|
||||
'query_params' => $oFilter->GetInternalParams(),
|
||||
));
|
||||
$sHtml .= "<form id=\"ObjectsAddForm_{$this->m_iInputId}\">\n";
|
||||
$sHtml .= "<div id=\"SearchResultsToAdd_{$this->m_iInputId}\" 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->m_iInputId}\" value=\"0\"/>";
|
||||
$sHtml .= "<input type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_{$this->m_iInputId}').dialog('close');\"> <input id=\"btn_ok_{$this->m_iInputId}\" disabled=\"disabled\" type=\"button\" onclick=\"return oForeignKeysWidget{$this->m_iInputId}.DoAddObjects(this.id);\" value=\"".Dict::S('UI:Button:Add')."\">";
|
||||
$sHtml .= "</div>\n";
|
||||
$sHtml .= "</form>\n";
|
||||
$oPage->add($sHtml);
|
||||
$oPage->add_ready_script("$('#dlg_{$this->m_iInputId}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, resizeStop: oForeignKeysWidget{$this->m_iInputId}.UpdateSizes });");
|
||||
$oPage->add_ready_script("$('#dlg_{$this->m_iInputId}').dialog('option', {title:'$sTitle'});");
|
||||
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_iInputId} form').bind('submit.uilinksWizard', oForeignKeysWidget{$this->m_iInputId}.SearchObjectsToAdd);");
|
||||
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_iInputId}').resize(oForeignKeysWidget{$this->m_iInputId}.UpdateSizes);");
|
||||
}
|
||||
|
||||
public function GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter)
|
||||
{
|
||||
try
|
||||
{
|
||||
$aLinkedObjects = utils::ReadMultipleSelectionWithFriendlyname($oFullSetFilter);
|
||||
$oPage->add(json_encode($aLinkedObjects));
|
||||
}
|
||||
catch (CoreException $e)
|
||||
{
|
||||
http_response_code(500);
|
||||
$oPage->add(json_encode(array('error' => $e->GetMessage())));
|
||||
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 m_sRemoteClass
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function ListResultsSearchForeignKeys(WebPage $oP, $sRemoteClass = '')
|
||||
{
|
||||
if ($sRemoteClass != '')
|
||||
{
|
||||
// assert(MetaModel::IsParentClass($this->m_sRemoteClass, $sRemoteClass));
|
||||
$oFilter = new DBObjectSearch($sRemoteClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No remote class specified use the one defined in the linkedset
|
||||
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
|
||||
}
|
||||
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oBlock->Display($oP, "ResultsToAdd_{$this->m_iInputId}",
|
||||
array('menu' => false, 'cssCount' => "#count_{$this->m_iInputId}", 'selection_mode' => true, 'table_id' => "add_{$this->m_iInputId}"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,423 +0,0 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Class UILinksWizard
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class UILinksWizard
|
||||
{
|
||||
protected $m_sClass;
|
||||
protected $m_sLinkageAttr;
|
||||
protected $m_iObjectId;
|
||||
protected $m_sLinkedClass;
|
||||
protected $m_sLinkingAttCode;
|
||||
protected $m_aEditableFields;
|
||||
protected $m_aTableConfig;
|
||||
|
||||
public function __construct($sClass, $sLinkageAttr, $iObjectId, $sLinkedClass = '')
|
||||
{
|
||||
$this->m_sClass = $sClass;
|
||||
$this->m_sLinkageAttr = $sLinkageAttr;
|
||||
$this->m_iObjectId = $iObjectId;
|
||||
$this->m_sLinkedClass = $sLinkedClass; // Will try to guess below, if it's empty
|
||||
$this->m_sLinkingAttCode = ''; // Will be filled once we've found the attribute corresponding to the linked class
|
||||
|
||||
$this->m_aEditableFields = array();
|
||||
$this->m_aTableConfig = array();
|
||||
$this->m_aTableConfig['form::checkbox'] = array( 'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onChange=\"var value = this.checked; $('.selection').each( function() { this.checked = value; } );OnSelectChange();\">", 'description' => Dict::S('UI:SelectAllToggle+'));
|
||||
foreach(MetaModel::GetAttributesList($this->m_sClass) as $sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
if ($oAttDef->IsExternalKey() && ($sAttCode != $this->m_sLinkageAttr))
|
||||
{
|
||||
if (empty($this->m_sLinkedClass))
|
||||
{
|
||||
// This is a class of objects we can manage !
|
||||
// Since nothing was specify, any class will do !
|
||||
$this->m_sLinkedClass = $oAttDef->GetTargetClass();
|
||||
$this->m_sLinkingAttCode = $sAttCode;
|
||||
}
|
||||
else if ($this->m_sLinkedClass == $oAttDef->GetTargetClass())
|
||||
{
|
||||
// This is the class of objects we want to manage !
|
||||
$this->m_sLinkingAttCode = $sAttCode;
|
||||
}
|
||||
}
|
||||
else if ( (!$oAttDef->IsExternalKey()) && (!$oAttDef->IsExternalField()))
|
||||
{
|
||||
$this->m_aEditableFields[] = $sAttCode;
|
||||
$this->m_aTableConfig[$sAttCode] = array( 'label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription());
|
||||
}
|
||||
}
|
||||
if (empty($this->m_sLinkedClass))
|
||||
{
|
||||
throw( new Exception(Dict::Format('UI:Error:IncorrectLinkDefinition_LinkedClass_Class', $sLinkedClass, $sClass)));
|
||||
}
|
||||
foreach(MetaModel::GetZListItems($this->m_sLinkedClass, 'list') as $sFieldCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
|
||||
$this->m_aTableConfig['static::'.$sFieldCode] = array( 'label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription());
|
||||
}
|
||||
}
|
||||
|
||||
public function Display(WebPage $oP, $aExtraParams = array())
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr);
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$oTargetObj = MetaModel::GetObject($sTargetClass, $this->m_iObjectId);
|
||||
|
||||
$oP->set_title("iTop - ".MetaModel::GetName($this->m_sLinkedClass)." objects linked with ".MetaModel::GetName(get_class($oTargetObj)).": ".$oTargetObj->GetRawName());
|
||||
$oP->add("<div class=\"wizContainer\">\n");
|
||||
$oP->add("<form method=\"post\">\n");
|
||||
$oP->add("<div class=\"page_header\">\n");
|
||||
$oP->add("<input type=\"hidden\" id=\"linksToRemove\" name=\"linksToRemove\" value=\"\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"do_modify_links\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"class\" value=\"{$this->m_sClass}\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"linkage\" value=\"{$this->m_sLinkageAttr}\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"object_id\" value=\"{$this->m_iObjectId}\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"linking_attcode\" value=\"{$this->m_sLinkingAttCode}\">\n");
|
||||
$oP->add("<h1>".Dict::Format('UI:ManageObjectsOf_Class_LinkedWith_Class_Instance', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName(get_class($oTargetObj)), "<span class=\"hilite\">".$oTargetObj->GetHyperlink()."</span>")."</h1>\n");
|
||||
$oP->add("</div>\n");
|
||||
$oP->add_script(
|
||||
<<<EOF
|
||||
function OnSelectChange()
|
||||
{
|
||||
var nbChecked = $('.selection:checked').length;
|
||||
if (nbChecked > 0)
|
||||
{
|
||||
$('#btnRemove').removeAttr('disabled');
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#btnRemove').attr('disabled','disabled');
|
||||
}
|
||||
}
|
||||
|
||||
function RemoveSelected()
|
||||
{
|
||||
$('.selection:checked').each(
|
||||
function()
|
||||
{
|
||||
$('#linksToRemove').val($('#linksToRemove').val() + ' ' + this.value);
|
||||
$('#row_'+this.value).remove();
|
||||
}
|
||||
);
|
||||
// Disable the button since all the selected items have been removed
|
||||
$('#btnRemove').attr('disabled','disabled');
|
||||
// Re-run the zebra plugin to properly highlight the remaining lines
|
||||
$('.listResults').trigger('update');
|
||||
|
||||
}
|
||||
|
||||
function AddObjects()
|
||||
{
|
||||
// TO DO: compute the list of objects already linked with the current Object
|
||||
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', { 'operation': 'addObjects',
|
||||
'class': '{$this->m_sClass}',
|
||||
'linkageAttr': '{$this->m_sLinkageAttr}',
|
||||
'linkedClass': '{$this->m_sLinkedClass}',
|
||||
'objectId': '{$this->m_iObjectId}'
|
||||
},
|
||||
function(data)
|
||||
{
|
||||
$('#ModalDlg').html(data);
|
||||
dlgWidth = $(document).width() - 100;
|
||||
$('#ModalDlg').css('width', dlgWidth);
|
||||
$('#ModalDlg').css('left', 50);
|
||||
$('#ModalDlg').css('top', 50);
|
||||
$('#ModalDlg').dialog( 'open' );
|
||||
},
|
||||
'html'
|
||||
);
|
||||
}
|
||||
|
||||
function SearchObjectsToAdd(currentFormId)
|
||||
{
|
||||
var theMap = { 'class': '{$this->m_sClass}',
|
||||
'linkageAttr': '{$this->m_sLinkageAttr}',
|
||||
'linkedClass': '{$this->m_sLinkedClass}',
|
||||
'objectId': '{$this->m_iObjectId}'
|
||||
}
|
||||
if ($('#'+currentFormId+' :input[name=class]').val() != undefined)
|
||||
{
|
||||
theMap.linkedClass = $('#'+currentFormId+' :input[name=class]').val();
|
||||
}
|
||||
// Gather the parameters from the search form
|
||||
$('#'+currentFormId+' :input').each(
|
||||
function(i)
|
||||
{
|
||||
if (this.name != '')
|
||||
{
|
||||
theMap[this.name] = this.value;
|
||||
}
|
||||
}
|
||||
);
|
||||
theMap['operation'] = 'searchObjectsToAdd';
|
||||
|
||||
// Run the query and display the results
|
||||
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap,
|
||||
function(data)
|
||||
{
|
||||
$('#SearchResultsToAdd').html(data);
|
||||
$('#SearchResultsToAdd .listResults').tablesorter( { headers: {0: false}}, widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
|
||||
|
||||
},
|
||||
'html'
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function DoAddObjects(currentFormId)
|
||||
{
|
||||
var theMap = { 'class': '{$this->m_sClass}',
|
||||
'linkageAttr': '{$this->m_sLinkageAttr}',
|
||||
'linkedClass': '{$this->m_sLinkedClass}',
|
||||
'objectId': '{$this->m_iObjectId}'
|
||||
}
|
||||
|
||||
// Gather the parameters from the search form
|
||||
$('#'+currentFormId+' :input').each(
|
||||
function(i)
|
||||
{
|
||||
if ( (this.name != '') && ((this.type != 'checkbox') || (this.checked)) )
|
||||
{
|
||||
//console.log(this.type);
|
||||
arrayExpr = /\[\]$/;
|
||||
if (arrayExpr.test(this.name))
|
||||
{
|
||||
// Array
|
||||
if (theMap[this.name] == undefined)
|
||||
{
|
||||
theMap[this.name] = new Array();
|
||||
}
|
||||
theMap[this.name].push(this.value);
|
||||
}
|
||||
else
|
||||
{
|
||||
theMap[this.name] = this.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
theMap['operation'] = 'doAddObjects';
|
||||
|
||||
// Run the query and display the results
|
||||
$.post( GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', theMap,
|
||||
function(data)
|
||||
{
|
||||
//console.log('Data: ' + data);
|
||||
if (data != '')
|
||||
{
|
||||
$('#empty_row').remove();
|
||||
}
|
||||
$('.listResults tbody').append(data);
|
||||
$('.listResults').trigger('update');
|
||||
$('.listResults').tablesorter( { headers: {0: false}}, widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
|
||||
},
|
||||
'html'
|
||||
);
|
||||
$('#ModalDlg').dialog('close');
|
||||
return false;
|
||||
}
|
||||
|
||||
function InitForm()
|
||||
{
|
||||
// make sure that the form is clean
|
||||
$('.selection').each( function() { this.checked = false; });
|
||||
$('#btnRemove').attr('disabled','disabled');
|
||||
$('#linksToRemove').val('');
|
||||
}
|
||||
|
||||
function SubmitHook()
|
||||
{
|
||||
var the_form = this;
|
||||
SearchObjectsToAdd(the_form.id);
|
||||
return false;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$oP->add_ready_script("InitForm();");
|
||||
$oFilter = new DBObjectSearch($this->m_sClass);
|
||||
$oFilter->AddCondition($this->m_sLinkageAttr, $this->m_iObjectId, '=');
|
||||
$oSet = new DBObjectSet($oFilter);
|
||||
$aForm = array();
|
||||
while($oCurrentLink = $oSet->Fetch())
|
||||
{
|
||||
$aRow = array();
|
||||
$key = $oCurrentLink->GetKey();
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sLinkedClass, $oCurrentLink->Get($this->m_sLinkingAttCode));
|
||||
|
||||
$aForm[$key] = $this->GetFormRow($oP, $oLinkedObj, $oCurrentLink);
|
||||
}
|
||||
//var_dump($aTableLabels);
|
||||
//var_dump($aForm);
|
||||
$this->DisplayFormTable($oP, $this->m_aTableConfig, $aForm);
|
||||
$oP->add("<span style=\"float:left;\"> <img src=\"../images/tv-item-last.gif\"> <input id=\"btnRemove\" type=\"button\" value=\"".Dict::S('UI:RemoveLinkedObjectsOf_Class')."\" onClick=\"RemoveSelected();\" >");
|
||||
$oP->add(" <input id=\"btnAdd\" type=\"button\" value=\"".Dict::Format('UI:AddLinkedObjectsOf_Class', MetaModel::GetName($this->m_sLinkedClass))."\" onClick=\"AddObjects();\"></span>\n");
|
||||
$oP->add("<span style=\"float:right;\"><input id=\"btnCancel\" type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"BackToDetails('".$sTargetClass."', ".$this->m_iObjectId.", '', '');\">");
|
||||
$oP->add(" <input id=\"btnOk\" type=\"submit\" value=\"".Dict::S('UI:Button:Ok')."\"></span>\n");
|
||||
$oP->add("<span style=\"clear:both;\"><p> </p></span>\n");
|
||||
$oP->add("</div>\n");
|
||||
$oP->add("</form>\n");
|
||||
if (isset($aExtraParams['StartWithAdd']) && ($aExtraParams['StartWithAdd']))
|
||||
{
|
||||
$oP->add_ready_script("AddObjects();");
|
||||
}
|
||||
}
|
||||
|
||||
protected function GetFormRow($oP, $oLinkedObj, $currentLink = null )
|
||||
{
|
||||
$aRow = array();
|
||||
if(is_object($currentLink))
|
||||
{
|
||||
$key = $currentLink->GetKey();
|
||||
$sNameSuffix = "[$key]"; // To make a tabular form
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onChange=\"OnSelectChange();\" value=\"$key\">";
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"linkId[$key]\" value=\"$key\">";
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sFieldCode);
|
||||
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sClass, $sFieldCode, $oAttDef, $currentLink->Get($sFieldCode), '' /* DisplayValue */, $key, $sNameSuffix);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// form for creating a new record
|
||||
$sNameSuffix = "[$currentLink]"; // To make a tabular form
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" type=\"checkbox\" onChange=\"OnSelectChange();\" value=\"$currentLink\">";
|
||||
$aRow['form::checkbox'] .= "<input type=\"hidden\" name=\"linkId[$currentLink]\" value=\"$currentLink\">";
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sFieldCode);
|
||||
$aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sClass, $sFieldCode, $oAttDef, '' /* TO DO/ call GetDefaultValue($oObject->ToArgs()) */, '' /* DisplayValue */, '' /* id */, $sNameSuffix);
|
||||
}
|
||||
}
|
||||
foreach(MetaModel::GetZListItems($this->m_sLinkedClass, 'list') as $sFieldCode)
|
||||
{
|
||||
$aRow['static::'.$sFieldCode] = $oLinkedObj->GetAsHTML($sFieldCode);
|
||||
}
|
||||
return $aRow;
|
||||
}
|
||||
|
||||
protected function DisplayFormTable(WebPage $oP, $aConfig, $aData)
|
||||
{
|
||||
$oP->add("<table class=\"listResults\">\n");
|
||||
// Header
|
||||
$oP->add("<thead>\n");
|
||||
$oP->add("<tr>\n");
|
||||
foreach($aConfig as $sName=>$aDef)
|
||||
{
|
||||
$oP->add("<th title=\"".$aDef['description']."\">".$aDef['label']."</th>\n");
|
||||
}
|
||||
$oP->add("</tr>\n");
|
||||
$oP->add("</thead>\n");
|
||||
|
||||
// Content
|
||||
$oP->add("</tbody>\n");
|
||||
if (count($aData) == 0)
|
||||
{
|
||||
$oP->add("<tr id=\"empty_row\"><td colspan=\"".count($aConfig)."\" style=\"text-align:center;\">".Dict::S('UI:Message:EmptyList:UseAdd')."</td></td>");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach($aData as $iRowId => $aRow)
|
||||
{
|
||||
$this->DisplayFormRow($oP, $aConfig, $aRow, $iRowId);
|
||||
}
|
||||
}
|
||||
$oP->add("</tbody>\n");
|
||||
|
||||
// Footer
|
||||
$oP->add("</table>\n");
|
||||
}
|
||||
|
||||
protected function DisplayFormRow(WebPage $oP, $aConfig, $aRow, $iRowId)
|
||||
{
|
||||
$oP->add("<tr id=\"row_$iRowId\">\n");
|
||||
foreach($aConfig as $sName=>$void)
|
||||
{
|
||||
$oP->add("<td>".$aRow[$sName]."</td>\n");
|
||||
}
|
||||
$oP->add("</tr>\n");
|
||||
}
|
||||
|
||||
public function DisplayAddForm(WebPage $oP)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr);
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$oTargetObj = MetaModel::GetObject($sTargetClass, $this->m_iObjectId);
|
||||
$oP->add("<div class=\"wizContainer\">\n");
|
||||
//$oP->add("<div class=\"page_header\">\n");
|
||||
//$oP->add("<h1>".Dict::Format('UI:AddObjectsOf_Class_LinkedWith_Class_Instance', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName(get_class($oTargetObj)), "<span class=\"hilite\">".$oTargetObj->GetHyperlink()."</span>")."</h1>\n");
|
||||
//$oP->add("</div>\n");
|
||||
|
||||
$oFilter = new DBObjectSearch($this->m_sLinkedClass);
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$oBlock = new DisplayBlock($oFilter, 'search', false);
|
||||
$oBlock->Display($oP, 'SearchFormToAdd', array('open' => true));
|
||||
$oP->Add("<form id=\"ObjectsAddForm\" OnSubmit=\"return DoAddObjects(this.id);\">\n");
|
||||
$oP->Add("<div id=\"SearchResultsToAdd\">\n");
|
||||
$oP->Add("<div style=\"height: 100px; background: #fff;border-color:#F6F6F1 #E6E6E1 #E6E6E1 #F6F6F1; border-style:solid; border-width:3px; text-align: center; vertical-align: center;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n");
|
||||
$oP->Add("</div>\n");
|
||||
$oP->add("<input type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#ModalDlg').dialog('close');\"> <input type=\"submit\" value=\"".Dict::S('UI:Button:Add')."\">");
|
||||
$oP->Add("</div>\n");
|
||||
$oP->Add("</form>\n");
|
||||
$oP->add_ready_script("$('#ModalDlg').dialog('option', {title:'".Dict::Format('UI:AddObjectsOf_Class_LinkedWith_Class_Instance', MetaModel::GetName($this->m_sLinkedClass), MetaModel::GetName(get_class($oTargetObj)), "<span class=\"hilite\">".$oTargetObj->GetHyperlink()."</span>")."'});");
|
||||
$oP->add_ready_script("$('div#SearchFormToAdd form').bind('submit.uilinksWizard', SubmitHook);");
|
||||
}
|
||||
|
||||
public function SearchObjectsToAdd(WebPage $oP)
|
||||
{
|
||||
//$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr);
|
||||
|
||||
$oFilter = new DBObjectSearch($this->m_sLinkedClass);
|
||||
$oSet = new CMDBObjectSet($oFilter);
|
||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
$oBlock->Display($oP, 'ResultsToAdd', array('menu' => false, 'selection_mode' => true)); // Don't display the 'Actions' menu on the results
|
||||
}
|
||||
|
||||
public function DoAddObjects(WebPage $oP, $aLinkedObjectIds = array())
|
||||
{
|
||||
//$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr);
|
||||
//$sTargetClass = $oAttDef->GetTargetClass();
|
||||
//$oP->Add("<!-- nb of objects to add: ".count($aLinkedObjectIds)." -->\n"); // Just to make sure it's not empty
|
||||
$aTable = array();
|
||||
foreach($aLinkedObjectIds as $iObjectId)
|
||||
{
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sLinkedClass, $iObjectId);
|
||||
if (is_object($oLinkedObj))
|
||||
{
|
||||
$aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId ); // Not yet created link get negative Ids
|
||||
$this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iObjectId);
|
||||
}
|
||||
else
|
||||
{
|
||||
echo Dict::Format('UI:Error:Object_Class_Id_NotFound', $this->m_sLinkedClass, $iObjectId);
|
||||
}
|
||||
}
|
||||
//var_dump($aTable);
|
||||
//$oP->Add("<!-- end of added list -->\n"); // Just to make sure it's not empty
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* Store and retrieve user's preferences (i.e persistent per user settings)
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
require_once(APPROOT.'/core/dbobject.class.php');
|
||||
@@ -50,7 +50,7 @@ class appUserPreferences extends DBObject
|
||||
self::Load();
|
||||
}
|
||||
$aPrefs = self::$oUserPrefs->Get('preferences');
|
||||
if (isset($aPrefs[$sCode]))
|
||||
if (array_key_exists($sCode, $aPrefs))
|
||||
{
|
||||
return $aPrefs[$sCode];
|
||||
}
|
||||
@@ -72,9 +72,16 @@ class appUserPreferences extends DBObject
|
||||
self::Load();
|
||||
}
|
||||
$aPrefs = self::$oUserPrefs->Get('preferences');
|
||||
$aPrefs[$sCode] = $sValue;
|
||||
self::$oUserPrefs->Set('preferences', $aPrefs);
|
||||
self::Save();
|
||||
if (array_key_exists($sCode, $aPrefs) && ($aPrefs[$sCode] === $sValue))
|
||||
{
|
||||
// Do not write it again
|
||||
}
|
||||
else
|
||||
{
|
||||
$aPrefs[$sCode] = $sValue;
|
||||
self::$oUserPrefs->Set('preferences', $aPrefs);
|
||||
self::Save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -148,7 +155,9 @@ class appUserPreferences extends DBObject
|
||||
{
|
||||
if (self::$oUserPrefs->IsModified())
|
||||
{
|
||||
utils::PushArchiveMode(false);
|
||||
self::$oUserPrefs->DBUpdate();
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,7 +181,9 @@ class appUserPreferences extends DBObject
|
||||
$oObj->Set('preferences', array()); // Default preferences: an empty array
|
||||
try
|
||||
{
|
||||
utils::PushArchiveMode(false);
|
||||
$oObj->DBInsert();
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
@@ -207,7 +218,8 @@ class appUserPreferences extends DBObject
|
||||
*/
|
||||
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null)
|
||||
{
|
||||
utils::PushArchiveMode(false);
|
||||
$this->DBDelete($oDeletionPlan);
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -84,6 +84,26 @@ class WizardHelper
|
||||
$oTargetObj = MetaModel::GetObject($sLinkedAttDef->GetTargetClass(), $aLinkedObject[$sLinkedAttCode]);
|
||||
$oLinkedObj->Set($sLinkedAttCode, $oTargetObj);
|
||||
}
|
||||
elseif($sLinkedAttDef instanceof AttributeDateTime)
|
||||
{
|
||||
$sDateClass = get_class($sLinkedAttDef);
|
||||
$sDate = $aLinkedObject[$sLinkedAttCode];
|
||||
if($sDate !== null && $sDate !== '')
|
||||
{
|
||||
$oDateTimeFormat = $sDateClass::GetFormat();
|
||||
$oDate = $oDateTimeFormat->Parse($sDate);
|
||||
if ($sDateClass == "AttributeDate")
|
||||
{
|
||||
$sDate = $oDate->format('Y-m-d');
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDate = $oDate->format('Y-m-d H:i:s');
|
||||
}
|
||||
}
|
||||
|
||||
$oLinkedObj->Set($sLinkedAttCode, $sDate);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oLinkedObj->Set($sLinkedAttCode, $aLinkedObject[$sLinkedAttCode]);
|
||||
@@ -138,6 +158,22 @@ class WizardHelper
|
||||
$oObj->Set($sAttCode, $value);
|
||||
}
|
||||
}
|
||||
else if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
|
||||
{
|
||||
if ($value != null)
|
||||
{
|
||||
$oDate = $oAttDef->GetFormat()->Parse($value);
|
||||
if ($oDate instanceof DateTime)
|
||||
{
|
||||
$value = $oDate->format($oAttDef->GetInternalFormat());
|
||||
}
|
||||
else
|
||||
{
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
$oObj->Set($sAttCode, $value);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oObj->Set($sAttCode, $value);
|
||||
@@ -254,11 +290,21 @@ class WizardHelper
|
||||
{
|
||||
return $this->m_aData['m_sClass'];
|
||||
}
|
||||
|
||||
public function GetFormPrefix()
|
||||
{
|
||||
return $this->m_aData['m_sFormPrefix'];
|
||||
}
|
||||
|
||||
public function GetFormPrefix()
|
||||
{
|
||||
return $this->m_aData['m_sFormPrefix'];
|
||||
}
|
||||
|
||||
public function GetInitialState()
|
||||
{
|
||||
return isset($this->m_aData['m_sInitialState']) ? $this->m_aData['m_sInitialState'] : null;
|
||||
}
|
||||
|
||||
public function GetStimulus()
|
||||
{
|
||||
return isset($this->m_aData['m_sStimulus']) ? $this->m_aData['m_sStimulus'] : null;
|
||||
}
|
||||
|
||||
public function GetIdForField($sFieldName)
|
||||
{
|
||||
|
||||
18
composer.json
Normal file
18
composer.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"require": {
|
||||
"php": ">=5.6.0",
|
||||
"ext-mysql": "*",
|
||||
"ext-ldap": "*",
|
||||
"ext-mcrypt": "*",
|
||||
"ext-cli": "*",
|
||||
"ext-soap": "*",
|
||||
"ext-json": "*",
|
||||
"ext-zip": "*",
|
||||
"ext-mysqli": "*"
|
||||
},
|
||||
"config": {
|
||||
"platform": {
|
||||
"php": "5.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ abstract class Action extends cmdbAbstractObject
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"category" => "grant_by_profile,core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
@@ -60,7 +60,7 @@ abstract class Action extends cmdbAbstractObject
|
||||
MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'trigger_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description', 'status')); // 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('default_search', array('name', 'description', 'status')); // Criteria of the std search form
|
||||
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ abstract class ActionNotification extends Action
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"category" => "grant_by_profile,core/cmdb",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
@@ -136,7 +136,7 @@ class ActionEmail extends ActionNotification
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb,application",
|
||||
"category" => "grant_by_profile,core/cmdb,application",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
@@ -262,22 +262,34 @@ class ActionEmail extends ActionNotification
|
||||
{
|
||||
$sPrefix = '';
|
||||
}
|
||||
|
||||
if ($oLog)
|
||||
{
|
||||
$oLog->Set('message', $sPrefix . $sRes);
|
||||
}
|
||||
$oLog->DBUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
if ($oLog)
|
||||
{
|
||||
$oLog->Set('message', 'Error: '.$e->getMessage());
|
||||
|
||||
try
|
||||
{
|
||||
$oLog->DBUpdate();
|
||||
}
|
||||
catch (Exception $eSecondTryUpdate)
|
||||
{
|
||||
IssueLog::Error("Failed to process email ".$oLog->GetKey()." - reason: ".$e->getMessage()."\nTrace:\n".$e->getTraceAsString());
|
||||
|
||||
$oLog->Set('message', 'Error: more details in the log for email "'.$oLog->GetKey().'"');
|
||||
$oLog->DBUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($oLog)
|
||||
{
|
||||
$oLog->DBUpdate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
|
||||
@@ -348,7 +360,7 @@ class ActionEmail extends ActionNotification
|
||||
$sTestBody .= "</div>\n";
|
||||
$oEmail->SetBody($sTestBody, 'text/html', $sStyles);
|
||||
$oEmail->SetRecipientTO($this->Get('test_recipient'));
|
||||
$oEmail->SetRecipientFrom($this->Get('test_recipient'));
|
||||
$oEmail->SetRecipientFrom($sFrom);
|
||||
$oEmail->SetReferences($sReference);
|
||||
$oEmail->SetMessageId($sMessageId);
|
||||
}
|
||||
|
||||
98
core/apc-compat.php
Normal file
98
core/apc-compat.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
// Copyright (C) 2016-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
// Emulate the API of APC, over APCU
|
||||
// Note: for PHP < 7, this compatibility used to be provided by APCU itself (if compiled with some options)
|
||||
// for PHP 7+, it can be provided by the mean of apcu_bc, which is not so simple to install
|
||||
// The current emulation aims at skipping this complexity
|
||||
if (!function_exists('apc_store') && function_exists('apcu_store'))
|
||||
{
|
||||
function apc_add($key, $var, $ttl = 0)
|
||||
{
|
||||
return apcu_add($key, $var, $ttl);
|
||||
}
|
||||
function apc_cache_info($cache_type = '', $limited = false)
|
||||
{
|
||||
return apcu_cache_info($limited);
|
||||
}
|
||||
function apc_cas($key, $old, $new)
|
||||
{
|
||||
return apcu_cas($key, $old, $new);
|
||||
}
|
||||
function apc_clear_cache($cache_type = '')
|
||||
{
|
||||
return apcu_clear_cache();
|
||||
}
|
||||
function apc_dec($key, $step = 1, &$success = null)
|
||||
{
|
||||
apcu_dec($key, $step, $success);
|
||||
}
|
||||
function apc_delete($key)
|
||||
{
|
||||
return apcu_delete($key);
|
||||
}
|
||||
function apc_exists($keys)
|
||||
{
|
||||
return apcu_exists($keys);
|
||||
}
|
||||
function apc_fetch($key)
|
||||
{
|
||||
return apcu_fetch($key);
|
||||
}
|
||||
function apc_inc($key, $step = 1, &$success = null)
|
||||
{
|
||||
apcu_inc($key, $step, $success);
|
||||
}
|
||||
function apc_sma_info($limited = false)
|
||||
{
|
||||
return apcu_sma_info($limited);
|
||||
}
|
||||
function apc_store($key, $var, $ttl = 0)
|
||||
{
|
||||
return apcu_store($key, $var, $ttl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns user cache info... beware of the format of the returned structure that may vary (See usages)
|
||||
* @return array
|
||||
*/
|
||||
function apc_cache_info_compat()
|
||||
{
|
||||
if (!function_exists('apc_cache_info')) return array();
|
||||
|
||||
$oFunction = new ReflectionFunction('apc_cache_info');
|
||||
if ($oFunction->getNumberOfParameters() != 2)
|
||||
{
|
||||
// Beware: APCu behaves slightly differently from APC !!
|
||||
// Worse: the compatibility layer integrated into APC differs from apcu-bc (testing the number of parameters is a must)
|
||||
// In CLI mode (PHP > 7) apc_cache_info returns null and outputs an error message.
|
||||
$aCacheUserData = @apc_cache_info();
|
||||
}
|
||||
else
|
||||
{
|
||||
$aCacheUserData = @apc_cache_info('user');
|
||||
}
|
||||
return $aCacheUserData;
|
||||
}
|
||||
|
||||
// Cache emulation
|
||||
if (!function_exists('apc_store'))
|
||||
{
|
||||
require_once(APPROOT.'core/apc-emulation.php');
|
||||
}
|
||||
333
core/apc-emulation.php
Normal file
333
core/apc-emulation.php
Normal file
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
// Copyright (c) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
//
|
||||
|
||||
/**
|
||||
* Date: 27/09/2017
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param string $cache_type
|
||||
* @param bool $limited
|
||||
* @return array|bool
|
||||
*/
|
||||
function apc_cache_info($cache_type = '', $limited = false)
|
||||
{
|
||||
$aInfo = array();
|
||||
$sRootCacheDir = apcFile::GetCacheFileName();
|
||||
$aInfo['cache_list'] = apcFile::GetCacheEntries($sRootCacheDir);
|
||||
return $aInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $key
|
||||
* @param $var
|
||||
* @param int $ttl
|
||||
* @return array|bool
|
||||
*/
|
||||
function apc_store($key, $var = NULL, $ttl = 0)
|
||||
{
|
||||
if (is_array($key))
|
||||
{
|
||||
$aResult = array();
|
||||
foreach($key as $sKey => $value)
|
||||
{
|
||||
$aResult[] = apcFile::StoreOneFile($sKey, $value, $ttl);
|
||||
}
|
||||
return $aResult;
|
||||
}
|
||||
return apcFile::StoreOneFile($key, $var, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key string|array
|
||||
* @return mixed
|
||||
*/
|
||||
function apc_fetch($key)
|
||||
{
|
||||
if (is_array($key))
|
||||
{
|
||||
$aResult = array();
|
||||
foreach($key as $sKey)
|
||||
{
|
||||
$aResult[$sKey] = apcFile::FetchOneFile($sKey);
|
||||
}
|
||||
return $aResult;
|
||||
}
|
||||
return apcFile::FetchOneFile($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cache_type
|
||||
* @return bool
|
||||
*/
|
||||
function apc_clear_cache($cache_type = '')
|
||||
{
|
||||
apcFile::DeleteEntry(utils::GetCachePath());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $key
|
||||
* @return bool|string[]
|
||||
*/
|
||||
function apc_delete($key)
|
||||
{
|
||||
if (empty($key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$bRet1 = apcFile::DeleteEntry(apcFile::GetCacheFileName($key));
|
||||
$bRet2 = apcFile::DeleteEntry(apcFile::GetCacheFileName('-'.$key));
|
||||
return $bRet1 || $bRet2;
|
||||
}
|
||||
|
||||
class apcFile
|
||||
{
|
||||
// Check only once per request
|
||||
static public $aFilesByTime = null;
|
||||
static public $iFileCount = 0;
|
||||
|
||||
/** Get the file name corresponding to the cache entry.
|
||||
* If an empty key is provided, the root of the cache is returned.
|
||||
* @param $sKey
|
||||
* @return string
|
||||
*/
|
||||
static public function GetCacheFileName($sKey = '')
|
||||
{
|
||||
$sPath = str_replace(array(' ', '/', '\\', '.'), '-', $sKey);
|
||||
return utils::GetCachePath().'apc-emul/'.$sPath;
|
||||
}
|
||||
|
||||
/** Get the list of entries from a starting folder.
|
||||
* @param $sEntry string starting folder.
|
||||
* @return array list of entries stored into array of key 'info'
|
||||
*/
|
||||
static public function GetCacheEntries($sEntry)
|
||||
{
|
||||
$aResult = array();
|
||||
if (is_dir($sEntry))
|
||||
{
|
||||
$aFiles = array_diff(scandir($sEntry), array('.', '..'));
|
||||
foreach($aFiles as $sFile)
|
||||
{
|
||||
$sSubFile = $sEntry.'/'.$sFile;
|
||||
$aResult = array_merge($aResult, self::GetCacheEntries($sSubFile));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sKey = basename($sEntry);
|
||||
if (strpos($sKey, '-') === 0)
|
||||
{
|
||||
$sKey = substr($sKey, 1);
|
||||
}
|
||||
$aResult[] = array('info' => $sKey);
|
||||
}
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
/** Delete one cache entry.
|
||||
* @param $sCache
|
||||
* @return bool true if the entry was deleted false if error occurs (like entry did not exist).
|
||||
*/
|
||||
static public function DeleteEntry($sCache)
|
||||
{
|
||||
if (is_dir($sCache))
|
||||
{
|
||||
$aFiles = array_diff(scandir($sCache), array('.', '..'));
|
||||
foreach($aFiles as $sFile)
|
||||
{
|
||||
$sSubFile = $sCache.'/'.$sFile;
|
||||
if (!self::DeleteEntry($sSubFile))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!@rmdir($sCache))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!@unlink($sCache))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
self::ResetFileCount();
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Get one cache entry content.
|
||||
* @param $sKey
|
||||
* @return bool|mixed
|
||||
*/
|
||||
static public function FetchOneFile($sKey)
|
||||
{
|
||||
// Try the 'TTLed' version
|
||||
$sValue = self::ReadCacheLocked(self::GetCacheFileName('-'.$sKey));
|
||||
if ($sValue === false)
|
||||
{
|
||||
$sValue = self::ReadCacheLocked(self::GetCacheFileName($sKey));
|
||||
if ($sValue === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$oRes = @unserialize($sValue);
|
||||
return $oRes;
|
||||
}
|
||||
|
||||
/** Add one cache entry.
|
||||
* @param string $sKey
|
||||
* @param $value
|
||||
* @param int $iTTL time to live
|
||||
* @return bool
|
||||
*/
|
||||
static public function StoreOneFile($sKey, $value, $iTTL)
|
||||
{
|
||||
if (empty($sKey))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@unlink(self::GetCacheFileName($sKey));
|
||||
@unlink(self::GetCacheFileName('-'.$sKey));
|
||||
if ($iTTL > 0)
|
||||
{
|
||||
// hint for ttl management
|
||||
$sKey = '-'.$sKey;
|
||||
}
|
||||
|
||||
$sFilename = self::GetCacheFileName($sKey);
|
||||
// try to create the folder
|
||||
$sDirname = dirname($sFilename);
|
||||
if (!file_exists($sDirname))
|
||||
{
|
||||
if (!@mkdir($sDirname, 0755, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$bRes = !(@file_put_contents($sFilename, serialize($value), LOCK_EX) === false);
|
||||
self::AddFile($sFilename);
|
||||
return $bRes;
|
||||
}
|
||||
|
||||
/** Manage the cache files when adding a new cache entry:
|
||||
* remove older files if the mamximum is reached.
|
||||
* @param $sNewFilename
|
||||
*/
|
||||
static protected function AddFile($sNewFilename)
|
||||
{
|
||||
if (strpos(basename($sNewFilename), '-') !== 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$iMaxFiles = MetaModel::GetConfig()->Get('apc_cache_emulation.max_entries');
|
||||
if ($iMaxFiles == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!self::$aFilesByTime)
|
||||
{
|
||||
self::ListFilesByTime();
|
||||
self::$iFileCount = count(self::$aFilesByTime);
|
||||
if ($iMaxFiles !== 0)
|
||||
{
|
||||
asort(self::$aFilesByTime);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self::$aFilesByTime[$sNewFilename] = time();
|
||||
self::$iFileCount++;
|
||||
}
|
||||
if (self::$iFileCount > $iMaxFiles)
|
||||
{
|
||||
$iFileNbToRemove = self::$iFileCount - $iMaxFiles;
|
||||
foreach(self::$aFilesByTime as $sFileToRemove => $iTime)
|
||||
{
|
||||
@unlink($sFileToRemove);
|
||||
if (--$iFileNbToRemove === 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
self::$aFilesByTime = array_slice(self::$aFilesByTime, self::$iFileCount - $iMaxFiles, null, true);
|
||||
self::$iFileCount = $iMaxFiles;
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the list of files with their associated access time
|
||||
* @param string $sCheck Directory to scan
|
||||
*/
|
||||
static protected function ListFilesByTime($sCheck = null)
|
||||
{
|
||||
if (empty($sCheck))
|
||||
{
|
||||
$sCheck = self::GetCacheFileName();
|
||||
}
|
||||
// Garbage collection
|
||||
$aFiles = array_diff(@scandir($sCheck), array('.', '..'));
|
||||
foreach($aFiles as $sFile)
|
||||
{
|
||||
$sSubFile = $sCheck.'/'.$sFile;
|
||||
if (is_dir($sSubFile))
|
||||
{
|
||||
self::ListFilesByTime($sSubFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strpos(basename($sSubFile), '-') === 0)
|
||||
{
|
||||
self::$aFilesByTime[$sSubFile] = @fileatime($sSubFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Read the content of one cache file under lock protection
|
||||
* @param $sFilename
|
||||
* @return bool|string the content of the cache entry or false if error
|
||||
*/
|
||||
static protected function ReadCacheLocked($sFilename)
|
||||
{
|
||||
$file = @fopen($sFilename, 'r');
|
||||
if ($file === false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
flock($file, LOCK_SH);
|
||||
$sContent = @fread($file, @filesize($sFilename));
|
||||
flock($file, LOCK_UN);
|
||||
fclose($file);
|
||||
return $sContent;
|
||||
}
|
||||
|
||||
static protected function ResetFileCount()
|
||||
{
|
||||
self::$aFilesByTime = null;
|
||||
self::$iFileCount = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -186,7 +186,7 @@ abstract class AsyncTask extends DBObject
|
||||
* @return boolean True if the task record can be deleted
|
||||
*/
|
||||
public function Process()
|
||||
{
|
||||
{
|
||||
// By default: consider that the task is not completed
|
||||
$bRet = false;
|
||||
|
||||
@@ -196,16 +196,15 @@ abstract class AsyncTask extends DBObject
|
||||
{
|
||||
try
|
||||
{
|
||||
$sStatus = $this->DoProcess();
|
||||
if ($this->Get('event_id') != 0)
|
||||
{
|
||||
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
|
||||
$oEventLog->Set('message', $sStatus);
|
||||
$oEventLog->DBUpdate();
|
||||
$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)
|
||||
} catch (Exception $e)
|
||||
{
|
||||
$this->HandleError($e->getMessage(), $e->getCode());
|
||||
}
|
||||
@@ -215,6 +214,7 @@ abstract class AsyncTask extends DBObject
|
||||
// Already done or being handled by another process... skip...
|
||||
$bRet = false;
|
||||
}
|
||||
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
@@ -238,7 +238,20 @@ abstract class AsyncTask extends DBObject
|
||||
{
|
||||
$iRetryDelay = $this->GetRetryDelay($iErrorCode);
|
||||
IssueLog::Info('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage.' - remaining retries: '.$iRemaining.' - next retry in '.$iRetryDelay.'s');
|
||||
|
||||
if ($this->Get('event_id') != 0)
|
||||
{
|
||||
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
|
||||
$oEventLog->Set('message', "$sErrorMessage\nFailed to process async task. Remaining retries: '.$iRemaining.'. Next retry in '.$iRetryDelay.'s'");
|
||||
try
|
||||
{
|
||||
$oEventLog->DBUpdate();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$oEventLog->Set('message', "Failed to process async task. Remaining retries: '.$iRemaining.'. Next retry in '.$iRetryDelay.'s', more details in the log");
|
||||
$oEventLog->DBUpdate();
|
||||
}
|
||||
}
|
||||
$this->Set('remaining_retries', $iRemaining - 1);
|
||||
$this->Set('status', 'planned');
|
||||
$this->Set('started', null);
|
||||
@@ -247,7 +260,20 @@ abstract class AsyncTask extends DBObject
|
||||
else
|
||||
{
|
||||
IssueLog::Error('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage);
|
||||
|
||||
if ($this->Get('event_id') != 0)
|
||||
{
|
||||
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
|
||||
$oEventLog->Set('message', "$sErrorMessage\nFailed to process async task.");
|
||||
try
|
||||
{
|
||||
$oEventLog->DBUpdate();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$oEventLog->Set('message', 'Failed to process async task, more details in the log');
|
||||
$oEventLog->DBUpdate();
|
||||
}
|
||||
}
|
||||
$this->Set('status', 'error');
|
||||
$this->Set('started', null);
|
||||
$this->Set('planned', null);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@ MetaModel::IncludeModule('application/user.preferences.class.inc.php');
|
||||
MetaModel::IncludeModule('application/user.dashboard.class.inc.php');
|
||||
MetaModel::IncludeModule('application/audit.rule.class.inc.php');
|
||||
MetaModel::IncludeModule('application/query.class.inc.php');
|
||||
MetaModel::IncludeModule('setup/moduleinstallation.class.inc.php');
|
||||
|
||||
MetaModel::IncludeModule('core/event.class.inc.php');
|
||||
MetaModel::IncludeModule('core/action.class.inc.php');
|
||||
|
||||
55
core/background.inc.php
Normal file
55
core/background.inc.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
// Copyright (C) 2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* Tasks performed in the background
|
||||
*
|
||||
* @copyright Copyright (C) 2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
|
||||
class ObsolescenceDateUpdater implements iBackgroundProcess
|
||||
{
|
||||
public function GetPeriodicity()
|
||||
{
|
||||
return MetaModel::GetConfig()->Get('obsolescence.date_update_interval'); // 10 mn
|
||||
}
|
||||
|
||||
public function Process($iUnixTimeLimit)
|
||||
{
|
||||
$iCountSet = 0;
|
||||
$iCountReset = 0;
|
||||
$iClasses = 0;
|
||||
foreach (MetaModel::EnumObsoletableClasses() as $sClass)
|
||||
{
|
||||
$oObsoletedToday = new DBObjectSearch($sClass);
|
||||
$oObsoletedToday->AddCondition('obsolescence_flag', 1, '=');
|
||||
$oObsoletedToday->AddCondition('obsolescence_date', null, '=');
|
||||
$sToday = date(AttributeDate::GetSQLFormat());
|
||||
$iCountSet += MetaModel::BulkUpdate($oObsoletedToday, array('obsolescence_date' => $sToday));
|
||||
|
||||
$oObsoletedToday = new DBObjectSearch($sClass);
|
||||
$oObsoletedToday->AddCondition('obsolescence_flag', 1, '!=');
|
||||
$oObsoletedToday->AddCondition('obsolescence_date', null, '!=');
|
||||
$iCountReset += MetaModel::BulkUpdate($oObsoletedToday, array('obsolescence_date' => null));
|
||||
}
|
||||
return "Obsolescence date updated (classes: $iClasses ; set: $iCountSet ; reset: $iCountReset)\n";
|
||||
}
|
||||
}
|
||||
@@ -24,9 +24,16 @@
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
interface iProcess
|
||||
{
|
||||
/**
|
||||
* @param int $iUnixTimeLimit
|
||||
*
|
||||
* @return string status message
|
||||
* @throws \ProcessException
|
||||
* @throws \ProcessFatalException
|
||||
* @throws MySQLHasGoneAwayException
|
||||
*/
|
||||
public function Process($iUnixTimeLimit);
|
||||
}
|
||||
|
||||
@@ -37,13 +44,11 @@ interface iProcess
|
||||
* @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
|
||||
*/
|
||||
/**
|
||||
* @return int repetition rate in seconds
|
||||
*/
|
||||
public function GetPeriodicity();
|
||||
}
|
||||
|
||||
@@ -54,14 +59,30 @@ interface iBackgroundProcess extends iProcess
|
||||
* @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
|
||||
*/
|
||||
/**
|
||||
* @return DateTime exact time at which the process must be run next time
|
||||
*/
|
||||
public function GetNextOccurrence();
|
||||
}
|
||||
|
||||
?>
|
||||
/**
|
||||
* Class ProcessException
|
||||
* Exception for iProcess implementations.<br>
|
||||
* An error happened during the processing but we can go on with the next implementations.
|
||||
*/
|
||||
class ProcessException extends CoreException
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class ProcessFatalException
|
||||
* Exception for iProcess implementations.<br>
|
||||
* A big error occurred, we have to stop the iProcess processing.
|
||||
*/
|
||||
class ProcessFatalException extends CoreException
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -320,7 +320,7 @@ class BulkChange
|
||||
// Returns true if the CSV data specifies that the external key must be left undefined
|
||||
protected function IsNullExternalKeySpec($aRowData, $sAttCode)
|
||||
{
|
||||
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
//$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
foreach ($this->m_aExtKeys[$sAttCode] as $sForeignAttCode => $iCol)
|
||||
{
|
||||
// The foreign attribute is one of our reconciliation key
|
||||
@@ -367,6 +367,7 @@ class BulkChange
|
||||
else
|
||||
{
|
||||
$oReconFilter = new DBObjectSearch($oExtKey->GetTargetClass());
|
||||
|
||||
$aCacheKeys = array();
|
||||
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
|
||||
{
|
||||
@@ -385,7 +386,6 @@ class BulkChange
|
||||
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
|
||||
}
|
||||
$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...
|
||||
@@ -472,6 +472,10 @@ class BulkChange
|
||||
if ($sAttCode == 'id') continue;
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
|
||||
// skip reconciliation keys
|
||||
if (!$oAttDef->IsWritable() && in_array($sAttCode, $this->m_aReconcilKeys)){ continue; }
|
||||
|
||||
$aReasons = array();
|
||||
$iFlags = $oTargetObj->GetAttributeFlags($sAttCode, $aReasons);
|
||||
if ( (($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY) && ( $oTargetObj->Get($sAttCode) != $aRowData[$iCol]) )
|
||||
@@ -528,13 +532,11 @@ class BulkChange
|
||||
{
|
||||
$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, $this->m_bLocalizedValues);
|
||||
$sOrigValue = $oTargetObj->GetOriginalAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter, $this->m_bLocalizedValues);
|
||||
$sInput = $aRowData[$iCol];
|
||||
}
|
||||
if (isset($aErrors[$sAttCode]))
|
||||
{
|
||||
@@ -605,10 +607,6 @@ class BulkChange
|
||||
throw new BulkChangeException('Invalid attribute code', array('class' => get_class($oTargetObj), 'attcode' => $sAttCode));
|
||||
}
|
||||
$oTargetObj->Set($sAttCode, $value);
|
||||
if (!array_key_exists($sAttCode, $this->m_aAttList))
|
||||
{
|
||||
// #@# will be out of the reporting... (counted anyway)
|
||||
}
|
||||
}
|
||||
|
||||
// Reporting on fields
|
||||
@@ -646,6 +644,47 @@ class BulkChange
|
||||
protected function CreateObject(&$aResult, $iRow, $aRowData, CMDBChange $oChange = null)
|
||||
{
|
||||
$oTargetObj = MetaModel::NewObject($this->m_sClass);
|
||||
|
||||
// Populate the cache for hierarchical keys (only if in verify mode)
|
||||
if (is_null($oChange))
|
||||
{
|
||||
// 1. determine if a hierarchical key exists
|
||||
foreach($this->m_aExtKeys as $sAttCode => $aKeyConfig)
|
||||
{
|
||||
$oExtKey = MetaModel::GetAttributeDef(get_class($oTargetObj), $sAttCode);
|
||||
if (!$this->IsNullExternalKeySpec($aRowData, $sAttCode) && MetaModel::IsParentClass(get_class($oTargetObj), $this->m_sClass))
|
||||
{
|
||||
// 2. Populate the cache for further checks
|
||||
$aCacheKeys = array();
|
||||
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
|
||||
{
|
||||
// The foreign attribute is one of our reconciliation key
|
||||
if ($sForeignAttCode == 'id')
|
||||
{
|
||||
$value = $aRowData[$iCol];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isset($this->m_aAttList[$sForeignAttCode]) || !isset($aRowData[$this->m_aAttList[$sForeignAttCode]]))
|
||||
{
|
||||
// the key is not in the import
|
||||
break 2;
|
||||
}
|
||||
$value = $aRowData[$this->m_aAttList[$sForeignAttCode]];
|
||||
}
|
||||
$aCacheKeys[] = $value;
|
||||
}
|
||||
$sCacheKey = implode('_|_', $aCacheKeys); // Unique key for this query...
|
||||
$this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey] = array(
|
||||
'c' => 1,
|
||||
'k' => -1,
|
||||
'oql' => '',
|
||||
'h' => 0, // number of hits on this cache entry
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$aResult[$iRow] = $this->PrepareObject($oTargetObj, $aRowData, $aErrors);
|
||||
|
||||
if (count($aErrors) > 0)
|
||||
@@ -679,7 +718,7 @@ class BulkChange
|
||||
if ($oChange)
|
||||
{
|
||||
$newID = $oTargetObj->DBInsertTrackedNoReload($oChange);
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_NewObj($this->m_sClass, $newID);
|
||||
$aResult[$iRow]["__STATUS__"] = new RowStatus_NewObj();
|
||||
$aResult[$iRow]["finalclass"] = get_class($oTargetObj);
|
||||
$aResult[$iRow]["id"] = new CellStatus_Void($newID);
|
||||
}
|
||||
@@ -828,8 +867,8 @@ class BulkChange
|
||||
$sFormat = $sDateFormat;
|
||||
}
|
||||
$oFormat = new DateTimeFormat($sFormat);
|
||||
$sRegExp = $oFormat->ToRegExpr();
|
||||
if (!preg_match('/'.$sRegExp.'/', $this->m_aData[$iRow][$iCol]))
|
||||
$sRegExp = $oFormat->ToRegExpr('/');
|
||||
if (!preg_match($sRegExp, $this->m_aData[$iRow][$iCol]))
|
||||
{
|
||||
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
|
||||
}
|
||||
@@ -1171,6 +1210,9 @@ EOF
|
||||
|
||||
/**
|
||||
* Display the details of an import
|
||||
* @param iTopWebPage $oPage
|
||||
* @param $iChange
|
||||
* @throws Exception
|
||||
*/
|
||||
static function DisplayImportHistoryDetails(iTopWebPage $oPage, $iChange)
|
||||
{
|
||||
@@ -1306,5 +1348,3 @@ EOF
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
|
||||
@@ -73,8 +73,14 @@ class BulkExportResult extends DBObject
|
||||
MetaModel::Init_AddAttribute(new AttributeString("temp_file_path", array("allowed_values"=>null, "sql"=>"temp_file_path", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLongText("search", array("allowed_values"=>null, "sql"=>"search", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeLongText("status_info", array("allowed_values"=>null, "sql"=>"status_info", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeBoolean("localize_output", array("allowed_values"=>null, "sql"=>"localize_output", "default_value"=>true, "is_null_allowed"=>true, "depends_on"=>array())));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws CoreUnexpectedValue
|
||||
* @throws Exception
|
||||
*/
|
||||
public function ComputeValues()
|
||||
{
|
||||
$this->Set('user_id', UserRights::GetUserId());
|
||||
@@ -112,7 +118,9 @@ class BulkExportResultGC implements iBackgroundProcess
|
||||
}
|
||||
$iProcessed++;
|
||||
@unlink($oResult->Get('temp_file_path'));
|
||||
utils::PushArchiveMode(false);
|
||||
$oResult->DBDelete();
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
return "Cleaned $iProcessed old export results(s).";
|
||||
}
|
||||
@@ -145,13 +153,16 @@ abstract class BulkExport
|
||||
$this->sTmpFile = '';
|
||||
$this->bLocalizeOutput = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the first class capable of exporting the data in the given format
|
||||
* @param string $sFormat The lowercase format (e.g. html, csv, spreadsheet, xlsx, xml, json, pdf...)
|
||||
* @param DBSearch $oSearch The search/filter defining the set of objects to export or null when listing the supported formats
|
||||
* @return iBulkExport|NULL
|
||||
*/
|
||||
|
||||
/**
|
||||
* Find the first class capable of exporting the data in the given format
|
||||
*
|
||||
* @param string $sFormatCode The lowercase format (e.g. html, csv, spreadsheet, xlsx, xml, json, pdf...)
|
||||
* @param DBSearch $oSearch The search/filter defining the set of objects to export or null when listing the supported formats
|
||||
*
|
||||
* @return BulkExport|null
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
static public function FindExporter($sFormatCode, $oSearch = null)
|
||||
{
|
||||
foreach(get_declared_classes() as $sPHPClass)
|
||||
@@ -172,12 +183,17 @@ abstract class BulkExport
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the exporter corresponding to the given persistent token
|
||||
* @param int $iPersistentToken The identifier of the BulkExportResult object storing the information
|
||||
* @return iBulkExport|NULL
|
||||
*/
|
||||
|
||||
/**
|
||||
* Find the exporter corresponding to the given persistent token
|
||||
*
|
||||
* @param int $iPersistentToken The identifier of the BulkExportResult object storing the information
|
||||
*
|
||||
* @return iBulkExport|null
|
||||
* @throws ArchivedObjectException
|
||||
* @throws CoreException
|
||||
* @throws ReflectionException
|
||||
*/
|
||||
static public function FindExporterFromToken($iPersistentToken = null)
|
||||
{
|
||||
$oBulkExporter = null;
|
||||
@@ -186,7 +202,7 @@ abstract class BulkExport
|
||||
{
|
||||
$sFormatCode = $oInfo->Get('format');
|
||||
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
|
||||
|
||||
|
||||
$oBulkExporter = self::FindExporter($sFormatCode, $oSearch);
|
||||
if ($oBulkExporter)
|
||||
{
|
||||
@@ -194,13 +210,21 @@ abstract class BulkExport
|
||||
$oBulkExporter->SetObjectList($oSearch);
|
||||
$oBulkExporter->SetChunkSize($oInfo->Get('chunk_size'));
|
||||
$oBulkExporter->SetStatusInfo(json_decode($oInfo->Get('status_info'), true));
|
||||
|
||||
$oBulkExporter->SetLocalizeOutput($oInfo->Get('localize_output'));
|
||||
|
||||
|
||||
$oBulkExporter->sTmpFile = $oInfo->Get('temp_file_path');
|
||||
$oBulkExporter->oBulkExportResult = $oInfo;
|
||||
}
|
||||
}
|
||||
return $oBulkExporter;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $data
|
||||
* @throws Exception
|
||||
*/
|
||||
public function AppendToTmpFile($data)
|
||||
{
|
||||
if ($this->sTmpFile == '')
|
||||
@@ -219,10 +243,10 @@ abstract class BulkExport
|
||||
{
|
||||
return $this->sTmpFile;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Lists all possible export formats. The output is a hash array in the form: 'format_code' => 'localized format label'
|
||||
* @return multitype:string
|
||||
* @return array :string
|
||||
*/
|
||||
static public function FindSupportedFormats()
|
||||
{
|
||||
@@ -248,6 +272,14 @@ abstract class BulkExport
|
||||
{
|
||||
$this->iChunkSize = $iChunkSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $bLocalizeOutput
|
||||
*/
|
||||
public function SetLocalizeOutput($bLocalizeOutput)
|
||||
{
|
||||
$this->bLocalizeOutput = $bLocalizeOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
@@ -286,13 +318,21 @@ abstract class BulkExport
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetHeader()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
abstract public function GetNextChunk(&$aStatus);
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetFooter()
|
||||
{
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function SaveState()
|
||||
@@ -302,11 +342,15 @@ abstract class BulkExport
|
||||
$this->oBulkExportResult = new BulkExportResult();
|
||||
$this->oBulkExportResult->Set('format', $this->sFormatCode);
|
||||
$this->oBulkExportResult->Set('search', $this->oSearch->serialize());
|
||||
$this->oBulkExportResult->Set('chunk_size', $this->iChunkSize);
|
||||
$this->oBulkExportResult->Set('temp_file_path', $this->sTmpFile);
|
||||
}
|
||||
$this->oBulkExportResult->Set('chunk_size', $this->iChunkSize);
|
||||
$this->oBulkExportResult->Set('temp_file_path', $this->sTmpFile);
|
||||
$this->oBulkExportResult->Set('localize_output', $this->bLocalizeOutput);
|
||||
}
|
||||
$this->oBulkExportResult->Set('status_info', json_encode($this->GetStatusInfo()));
|
||||
return $this->oBulkExportResult->DBWrite();
|
||||
utils::PushArchiveMode(false);
|
||||
$ret = $this->oBulkExportResult->DBWrite();
|
||||
utils::PopArchiveMode();
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function Cleanup()
|
||||
@@ -318,7 +362,9 @@ abstract class BulkExport
|
||||
{
|
||||
@unlink($sFilename);
|
||||
}
|
||||
utils::PushArchiveMode(false);
|
||||
$this->oBulkExportResult->DBDelete();
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,13 +394,21 @@ abstract class BulkExport
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetMimeType()
|
||||
{
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function GetFileExtension()
|
||||
{
|
||||
|
||||
return '';
|
||||
}
|
||||
public function GetCharacterSet()
|
||||
{
|
||||
@@ -381,6 +435,11 @@ abstract class BulkExport
|
||||
return $this->aStatusInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sExtension
|
||||
* @return string
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function MakeTmpFile($sExtension)
|
||||
{
|
||||
if(!is_dir(APPROOT."data/bulk_export"))
|
||||
@@ -394,7 +453,6 @@ abstract class BulkExport
|
||||
}
|
||||
|
||||
$iNum = rand();
|
||||
$sFileName = '';
|
||||
do
|
||||
{
|
||||
$iNum++;
|
||||
@@ -412,7 +470,11 @@ abstract class BulkExport
|
||||
// The built-in exports
|
||||
require_once(APPROOT.'core/tabularbulkexport.class.inc.php');
|
||||
require_once(APPROOT.'core/htmlbulkexport.class.inc.php');
|
||||
require_once(APPROOT.'core/pdfbulkexport.class.inc.php');
|
||||
if (extension_loaded('gd'))
|
||||
{
|
||||
// PDF export - via TCPDF - requires GD
|
||||
require_once(APPROOT.'core/pdfbulkexport.class.inc.php');
|
||||
}
|
||||
require_once(APPROOT.'core/csvbulkexport.class.inc.php');
|
||||
require_once(APPROOT.'core/excelbulkexport.class.inc.php');
|
||||
require_once(APPROOT.'core/spreadsheetbulkexport.class.inc.php');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Class cmdbObject
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -231,6 +231,202 @@ abstract class CMDBObject extends DBObject
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sAttCode
|
||||
* @param $original Original value
|
||||
* @param $value Current value
|
||||
*/
|
||||
protected function RecordAttChange($sAttCode, $original, $value)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($oAttDef->IsExternalField()) return;
|
||||
if ($oAttDef->IsLinkSet()) return;
|
||||
if ($oAttDef->GetTrackingLevel() == ATTRIBUTE_TRACKING_NONE) return;
|
||||
|
||||
if ($oAttDef instanceOf AttributeOneWayPassword)
|
||||
{
|
||||
// One Way encrypted passwords' history is stored -one way- encrypted
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeOneWayPassword");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (is_null($original))
|
||||
{
|
||||
$original = '';
|
||||
}
|
||||
$oMyChangeOp->Set("prev_pwd", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeEncryptedString)
|
||||
{
|
||||
// Encrypted string history is stored encrypted
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeEncrypted");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (is_null($original))
|
||||
{
|
||||
$original = '';
|
||||
}
|
||||
$oMyChangeOp->Set("prevstring", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeBlob)
|
||||
{
|
||||
// Data blobs
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeBlob");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (is_null($original))
|
||||
{
|
||||
$original = new ormDocument();
|
||||
}
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeStopWatch)
|
||||
{
|
||||
// Stop watches - record changes for sub items only (they are visible, the rest is not visible)
|
||||
//
|
||||
foreach ($oAttDef->ListSubItems() as $sSubItemAttCode => $oSubItemAttDef)
|
||||
{
|
||||
$item_value = $oAttDef->GetSubItemValue($oSubItemAttDef->Get('item_code'), $value, $this);
|
||||
$item_original = $oAttDef->GetSubItemValue($oSubItemAttDef->Get('item_code'), $original, $this);
|
||||
|
||||
if ($item_value != $item_original)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sSubItemAttCode);
|
||||
|
||||
$oMyChangeOp->Set("oldvalue", $item_original);
|
||||
$oMyChangeOp->Set("newvalue", $item_value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeCaseLog)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCaseLog");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
$oMyChangeOp->Set("lastentry", $value->GetLatestEntryIndex());
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeLongText)
|
||||
{
|
||||
// Data blobs
|
||||
if ($oAttDef->GetFormat() == 'html')
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeLongText");
|
||||
}
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (!is_null($original) && ($original instanceof ormCaseLog))
|
||||
{
|
||||
$original = $original->GetText();
|
||||
}
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeText)
|
||||
{
|
||||
// Data blobs
|
||||
if ($oAttDef->GetFormat() == 'html')
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeText");
|
||||
}
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (!is_null($original) && ($original instanceof ormCaseLog))
|
||||
{
|
||||
$original = $original->GetText();
|
||||
}
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeBoolean)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original ? 1 : 0);
|
||||
$oMyChangeOp->Set("newvalue", $value ? 1 : 0);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeHierarchicalKey)
|
||||
{
|
||||
// Hierarchical keys
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original);
|
||||
$oMyChangeOp->Set("newvalue", $value[$sAttCode]);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeCustomFields)
|
||||
{
|
||||
// Custom fields
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCustomFields");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("prevdata", json_encode($original->GetValues()));
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeURL)
|
||||
{
|
||||
// URLs
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeURL");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original);
|
||||
$oMyChangeOp->Set("newvalue", $value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scalars
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original);
|
||||
$oMyChangeOp->Set("newvalue", $value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aValues
|
||||
* @param array $aOrigValues
|
||||
*/
|
||||
protected function RecordAttChanges(array $aValues, array $aOrigValues)
|
||||
{
|
||||
parent::RecordAttChanges($aValues, $aOrigValues);
|
||||
@@ -239,11 +435,6 @@ abstract class CMDBObject extends DBObject
|
||||
//
|
||||
foreach ($aValues as $sAttCode=> $value)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($oAttDef->IsExternalField()) continue;
|
||||
if ($oAttDef->IsLinkSet()) continue;
|
||||
if ($oAttDef->GetTrackingLevel() == ATTRIBUTE_TRACKING_NONE) continue;
|
||||
|
||||
if (array_key_exists($sAttCode, $aOrigValues))
|
||||
{
|
||||
$original = $aOrigValues[$sAttCode];
|
||||
@@ -252,185 +443,7 @@ abstract class CMDBObject extends DBObject
|
||||
{
|
||||
$original = null;
|
||||
}
|
||||
|
||||
if ($oAttDef instanceOf AttributeOneWayPassword)
|
||||
{
|
||||
// One Way encrypted passwords' history is stored -one way- encrypted
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeOneWayPassword");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (is_null($original))
|
||||
{
|
||||
$original = '';
|
||||
}
|
||||
$oMyChangeOp->Set("prev_pwd", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeEncryptedString)
|
||||
{
|
||||
// Encrypted string history is stored encrypted
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeEncrypted");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (is_null($original))
|
||||
{
|
||||
$original = '';
|
||||
}
|
||||
$oMyChangeOp->Set("prevstring", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeBlob)
|
||||
{
|
||||
// Data blobs
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeBlob");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (is_null($original))
|
||||
{
|
||||
$original = new ormDocument();
|
||||
}
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeStopWatch)
|
||||
{
|
||||
// Stop watches - record changes for sub items only (they are visible, the rest is not visible)
|
||||
//
|
||||
foreach ($oAttDef->ListSubItems() as $sSubItemAttCode => $oSubItemAttDef)
|
||||
{
|
||||
$item_value = $oAttDef->GetSubItemValue($oSubItemAttDef->Get('item_code'), $value, $this);
|
||||
$item_original = $oAttDef->GetSubItemValue($oSubItemAttDef->Get('item_code'), $original, $this);
|
||||
|
||||
if ($item_value != $item_original)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sSubItemAttCode);
|
||||
|
||||
$oMyChangeOp->Set("oldvalue", $item_original);
|
||||
$oMyChangeOp->Set("newvalue", $item_value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeCaseLog)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCaseLog");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
$oMyChangeOp->Set("lastentry", $value->GetLatestEntryIndex());
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeLongText)
|
||||
{
|
||||
// Data blobs
|
||||
if ($oAttDef->GetFormat() == 'html')
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeLongText");
|
||||
}
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (!is_null($original) && ($original instanceof ormCaseLog))
|
||||
{
|
||||
$original = $original->GetText();
|
||||
}
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeText)
|
||||
{
|
||||
// Data blobs
|
||||
if ($oAttDef->GetFormat() == 'html')
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeHTML");
|
||||
}
|
||||
else
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeText");
|
||||
}
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
|
||||
if (!is_null($original) && ($original instanceof ormCaseLog))
|
||||
{
|
||||
$original = $original->GetText();
|
||||
}
|
||||
$oMyChangeOp->Set("prevdata", $original);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeBoolean)
|
||||
{
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original ? 1 : 0);
|
||||
$oMyChangeOp->Set("newvalue", $value ? 1 : 0);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeHierarchicalKey)
|
||||
{
|
||||
// Hierarchical keys
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original);
|
||||
$oMyChangeOp->Set("newvalue", $value[$sAttCode]);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeCustomFields)
|
||||
{
|
||||
// Custom fields
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeCustomFields");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("prevdata", json_encode($original->GetValues()));
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
elseif ($oAttDef instanceOf AttributeURL)
|
||||
{
|
||||
// URLs
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeURL");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original);
|
||||
$oMyChangeOp->Set("newvalue", $value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Scalars
|
||||
//
|
||||
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeScalar");
|
||||
$oMyChangeOp->Set("objclass", get_class($this));
|
||||
$oMyChangeOp->Set("objkey", $this->GetKey());
|
||||
$oMyChangeOp->Set("attcode", $sAttCode);
|
||||
$oMyChangeOp->Set("oldvalue", $original);
|
||||
$oMyChangeOp->Set("newvalue", $value);
|
||||
$iId = $oMyChangeOp->DBInsertNoReload();
|
||||
}
|
||||
$this->RecordAttChange($sAttCode, $original, $value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,6 +609,34 @@ abstract class CMDBObject extends DBObject
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function DBArchive()
|
||||
{
|
||||
// Note: do the job anyway, so as to repair any DB discrepancy
|
||||
$bOriginal = $this->Get('archive_flag');
|
||||
parent::DBArchive();
|
||||
|
||||
if (!$bOriginal)
|
||||
{
|
||||
utils::PushArchiveMode(false);
|
||||
$this->RecordAttChange('archive_flag', false, true);
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
}
|
||||
|
||||
public function DBUnarchive()
|
||||
{
|
||||
// Note: do the job anyway, so as to repair any DB discrepancy
|
||||
$bOriginal = $this->Get('archive_flag');
|
||||
parent::DBUnarchive();
|
||||
|
||||
if ($bOriginal)
|
||||
{
|
||||
utils::PushArchiveMode(false);
|
||||
$this->RecordAttChange('archive_flag', true, false);
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -649,5 +690,5 @@ class CMDBObjectSet extends DBObjectSet
|
||||
$oRetSet->AddObjectExtended($aObjectsByClassAlias);
|
||||
}
|
||||
return $oRetSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2018 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* DB Server abstraction
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -29,22 +29,76 @@ require_once(APPROOT.'core/kpi.class.inc.php');
|
||||
|
||||
class MySQLException extends CoreException
|
||||
{
|
||||
public function __construct($sIssue, $aContext, $oException = null)
|
||||
/**
|
||||
* MySQLException constructor.
|
||||
*
|
||||
* @param string $sIssue
|
||||
* @param array $aContext
|
||||
* @param \Exception $oException
|
||||
* @param \mysqli $oMysqli to use when working with a custom mysqli instance
|
||||
*/
|
||||
public function __construct($sIssue, $aContext, $oException = null, $oMysqli = null)
|
||||
{
|
||||
if ($oException != null)
|
||||
{
|
||||
$aContext['mysql_error'] = $oException->getCode();
|
||||
$aContext['mysql_errno'] = $oException->getMessage();
|
||||
$aContext['mysql_errno'] = $oException->getCode();
|
||||
$this->code = $oException->getCode();
|
||||
$aContext['mysql_error'] = $oException->getMessage();
|
||||
}
|
||||
else if ($oMysqli != null)
|
||||
{
|
||||
$aContext['mysql_errno'] = $oMysqli->errno;
|
||||
$this->code = $oMysqli->errno;
|
||||
$aContext['mysql_error'] = $oMysqli->error;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aContext['mysql_error'] = CMDBSource::GetError();
|
||||
$aContext['mysql_errno'] = CMDBSource::GetErrNo();
|
||||
$this->code = CMDBSource::GetErrNo();
|
||||
$aContext['mysql_error'] = CMDBSource::GetError();
|
||||
}
|
||||
parent::__construct($sIssue, $aContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class MySQLQueryHasNoResultException
|
||||
*
|
||||
* @since 2.5
|
||||
*/
|
||||
class MySQLQueryHasNoResultException extends MySQLException
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Class MySQLHasGoneAwayException
|
||||
*
|
||||
* @since 2.5
|
||||
* @see itop bug 1195
|
||||
* @see https://dev.mysql.com/doc/refman/5.7/en/gone-away.html
|
||||
*/
|
||||
class MySQLHasGoneAwayException extends MySQLException
|
||||
{
|
||||
/**
|
||||
* can not be a constant before PHP 5.6 (http://php.net/manual/fr/language.oop5.constants.php)
|
||||
*
|
||||
* @return int[]
|
||||
*/
|
||||
public static function getErrorCodes()
|
||||
{
|
||||
return array(
|
||||
2006,
|
||||
2013
|
||||
);
|
||||
}
|
||||
|
||||
public function __construct($sIssue, $aContext)
|
||||
{
|
||||
parent::__construct($sIssue, $aContext, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* CMDBSource
|
||||
@@ -58,37 +112,136 @@ class CMDBSource
|
||||
protected static $m_sDBUser;
|
||||
protected static $m_sDBPwd;
|
||||
protected static $m_sDBName;
|
||||
/**
|
||||
* @var boolean
|
||||
* @since 2.5 #1260 MySQL TLS first implementation
|
||||
*/
|
||||
protected static $m_bDBTlsEnabled;
|
||||
/**
|
||||
* @var string
|
||||
* @since 2.5 #1260 MySQL TLS first implementation
|
||||
*/
|
||||
protected static $m_sDBTlsCA;
|
||||
|
||||
/** @var mysqli $m_oMysqli */
|
||||
protected static $m_oMysqli;
|
||||
|
||||
public static function Init($sServer, $sUser, $sPwd, $sSource = '')
|
||||
/**
|
||||
* SQL charset & collation declaration for text columns
|
||||
*
|
||||
* Using a function instead of a constant or attribute to avoid crash in the setup for older PHP versions (cannot
|
||||
* use expression as value)
|
||||
*
|
||||
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-column.html
|
||||
* @since 2.5 #1001 switch to utf8mb4
|
||||
*/
|
||||
public static function GetSqlStringColumnDefinition()
|
||||
{
|
||||
return ' CHARACTER SET '.DEFAULT_CHARACTER_SET.' COLLATE '.DEFAULT_COLLATION;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Config $oConfig
|
||||
*
|
||||
* @throws \MySQLException
|
||||
* @uses \CMDBSource::Init()
|
||||
* @uses \CMDBSource::SetCharacterSet()
|
||||
*/
|
||||
public static function InitFromConfig($oConfig)
|
||||
{
|
||||
$sServer = $oConfig->Get('db_host');
|
||||
$sUser = $oConfig->Get('db_user');
|
||||
$sPwd = $oConfig->Get('db_pwd');
|
||||
$sSource = $oConfig->Get('db_name');
|
||||
$bTlsEnabled = $oConfig->Get('db_tls.enabled');
|
||||
$sTlsCA = $oConfig->Get('db_tls.ca');
|
||||
|
||||
self::Init($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA);
|
||||
|
||||
$sCharacterSet = DEFAULT_CHARACTER_SET;
|
||||
$sCollation = DEFAULT_COLLATION;
|
||||
self::SetCharacterSet($sCharacterSet, $sCollation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sServer
|
||||
* @param string $sUser
|
||||
* @param string $sPwd
|
||||
* @param string $sSource database to use
|
||||
* @param bool $bTlsEnabled
|
||||
* @param string $sTlsCA
|
||||
*
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public static function Init(
|
||||
$sServer, $sUser, $sPwd, $sSource = '', $bTlsEnabled = false, $sTlsCA = null
|
||||
)
|
||||
{
|
||||
self::$m_sDBHost = $sServer;
|
||||
self::$m_sDBUser = $sUser;
|
||||
self::$m_sDBPwd = $sPwd;
|
||||
self::$m_sDBName = $sSource;
|
||||
self::$m_oMysqli = null;
|
||||
self::$m_bDBTlsEnabled = empty($bTlsEnabled) ? false : $bTlsEnabled;
|
||||
self::$m_sDBTlsCA = empty($sTlsCA) ? null : $sTlsCA;
|
||||
|
||||
self::$m_oMysqli = self::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sDbHost
|
||||
* @param string $sUser
|
||||
* @param string $sPwd
|
||||
* @param string $sSource database to use
|
||||
* @param bool $bTlsEnabled
|
||||
* @param string $sTlsCa
|
||||
* @param bool $bCheckTlsAfterConnection If true then verify after connection if it is encrypted
|
||||
*
|
||||
* @return \mysqli
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public static function GetMysqliInstance(
|
||||
$sDbHost, $sUser, $sPwd, $sSource = '', $bTlsEnabled = false, $sTlsCa = null, $bCheckTlsAfterConnection = false
|
||||
) {
|
||||
$oMysqli = null;
|
||||
|
||||
$sServer = null;
|
||||
$iPort = null;
|
||||
self::InitServerAndPort($sDbHost, $sServer, $iPort);
|
||||
|
||||
$iFlags = null;
|
||||
|
||||
// *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 !!!
|
||||
mysqli_report(MYSQLI_REPORT_STRICT);
|
||||
|
||||
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
|
||||
{
|
||||
$aConnectInfo = explode(':', self::$m_sDBHost);
|
||||
if (count($aConnectInfo) > 1)
|
||||
$oMysqli = new mysqli();
|
||||
$oMysqli->init();
|
||||
|
||||
if ($bTlsEnabled)
|
||||
{
|
||||
// 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);
|
||||
$iFlags = (empty($sTlsCa))
|
||||
? MYSQLI_CLIENT_SSL_DONT_VERIFY_SERVER_CERT
|
||||
: MYSQLI_CLIENT_SSL;
|
||||
$sTlsCert = null; // not implemented
|
||||
$sTlsCaPath = null; // not implemented
|
||||
$sTlsCipher = null; // not implemented
|
||||
$oMysqli->ssl_set($bTlsEnabled, $sTlsCert, $sTlsCa, $sTlsCaPath, $sTlsCipher);
|
||||
}
|
||||
$oMysqli->real_connect($sServer, $sUser, $sPwd, '', $iPort, ini_get("mysqli.default_socket"), $iFlags);
|
||||
}
|
||||
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);
|
||||
throw new MySQLException('Could not connect to the DB server', array('host' => $sServer, 'user' => $sUser), $e);
|
||||
}
|
||||
|
||||
if ($bTlsEnabled
|
||||
&& $bCheckTlsAfterConnection
|
||||
&& !self::IsOpenedDbConnectionUsingTls($oMysqli))
|
||||
{
|
||||
throw new MySQLException("Connection to the database is not encrypted whereas it was opened using TLS parameters",
|
||||
null, null, $oMysqli);
|
||||
}
|
||||
|
||||
if (!empty($sSource))
|
||||
@@ -96,16 +249,104 @@ class CMDBSource
|
||||
try
|
||||
{
|
||||
mysqli_report(MYSQLI_REPORT_STRICT); // Errors, in the next query, will throw mysqli_sql_exception
|
||||
self::$m_oMysqli->query("USE `$sSource`");
|
||||
$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);
|
||||
throw new MySQLException('Could not select DB',
|
||||
array('host' => $sServer, 'user' => $sUser, 'db_name' => $sSource), $e);
|
||||
}
|
||||
}
|
||||
|
||||
return $oMysqli;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sDbHost initial value ("p:domain:port" syntax)
|
||||
* @param string $sServer server variable to update
|
||||
* @param int $iPort port variable to update
|
||||
*/
|
||||
public static function InitServerAndPort($sDbHost, &$sServer, &$iPort)
|
||||
{
|
||||
$aConnectInfo = explode(':', $sDbHost);
|
||||
|
||||
$bUsePersistentConnection = false;
|
||||
if (strcasecmp($aConnectInfo[0], 'p') == 0)
|
||||
{
|
||||
// we might have "p:" prefix to use persistent connections (see http://php.net/manual/en/mysqli.persistconns.php)
|
||||
$bUsePersistentConnection = true;
|
||||
$sServer = $aConnectInfo[0].':'.$aConnectInfo[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sServer = $aConnectInfo[0];
|
||||
}
|
||||
|
||||
$iConnectInfoCount = count($aConnectInfo);
|
||||
if ($bUsePersistentConnection && ($iConnectInfoCount == 3))
|
||||
{
|
||||
$iPort = $aConnectInfo[2];
|
||||
}
|
||||
else if (!$bUsePersistentConnection && ($iConnectInfoCount == 2))
|
||||
{
|
||||
$iPort = $aConnectInfo[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
$iPort = 3306;
|
||||
}
|
||||
}
|
||||
|
||||
public static function SetCharacterSet($sCharset = 'utf8', $sCollation = 'utf8_general_ci')
|
||||
/**
|
||||
* <p>A DB connection can be opened transparently (no errors thrown) without being encrypted, whereas the TLS
|
||||
* parameters were used.<br>
|
||||
* This method can be called to ensure that the DB connection really uses TLS.
|
||||
*
|
||||
* <p>We're using this object connection : {@link self::$m_oMysqli}
|
||||
*
|
||||
* @param \mysqli $oMysqli
|
||||
*
|
||||
* @return boolean true if the connection was really established using TLS
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @uses IsMySqlVarNonEmpty
|
||||
*/
|
||||
private static function IsOpenedDbConnectionUsingTls($oMysqli)
|
||||
{
|
||||
if (self::$m_oMysqli == null)
|
||||
{
|
||||
self::$m_oMysqli = $oMysqli;
|
||||
}
|
||||
|
||||
$bNonEmptySslVersionVar = self::IsMySqlVarNonEmpty('ssl_version');
|
||||
$bNonEmptySslCipherVar = self::IsMySqlVarNonEmpty('ssl_cipher');
|
||||
|
||||
return ($bNonEmptySslVersionVar && $bNonEmptySslCipherVar);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sVarName
|
||||
*
|
||||
* @return bool
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @uses SHOW STATUS queries
|
||||
*/
|
||||
private static function IsMySqlVarNonEmpty($sVarName)
|
||||
{
|
||||
try
|
||||
{
|
||||
$sResult = self::QueryToScalar("SHOW SESSION STATUS LIKE '$sVarName'", 1);
|
||||
}
|
||||
catch (MySQLQueryHasNoResultException $e)
|
||||
{
|
||||
$sResult = null;
|
||||
}
|
||||
|
||||
return (!empty($sResult));
|
||||
}
|
||||
|
||||
public static function SetCharacterSet($sCharset = DEFAULT_CHARACTER_SET, $sCollation = DEFAULT_COLLATION)
|
||||
{
|
||||
if (strlen($sCharset) > 0)
|
||||
{
|
||||
@@ -164,7 +405,12 @@ class CMDBSource
|
||||
$aVersions = self::QueryToCol('SELECT Version() as version', 'version');
|
||||
return $aVersions[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $sSource
|
||||
*
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public static function SelectDB($sSource)
|
||||
{
|
||||
if (!((bool)self::$m_oMysqli->query("USE `$sSource`")))
|
||||
@@ -174,9 +420,15 @@ class CMDBSource
|
||||
self::$m_sDBName = $sSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sSource
|
||||
*
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public static function CreateDB($sSource)
|
||||
{
|
||||
self::Query("CREATE DATABASE `$sSource` CHARACTER SET utf8 COLLATE utf8_unicode_ci");
|
||||
self::Query("CREATE DATABASE `$sSource` CHARACTER SET ".DEFAULT_CHARACTER_SET." COLLATE ".DEFAULT_COLLATION);
|
||||
self::SelectDB($sSource);
|
||||
}
|
||||
|
||||
@@ -207,6 +459,14 @@ class CMDBSource
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \mysqli
|
||||
*/
|
||||
public static function GetMysqli()
|
||||
{
|
||||
return self::$m_oMysqli;
|
||||
}
|
||||
|
||||
public static function GetErrNo()
|
||||
{
|
||||
if (self::$m_oMysqli->errno != 0)
|
||||
@@ -236,15 +496,19 @@ class CMDBSource
|
||||
public static function DBPwd() {return self::$m_sDBPwd;}
|
||||
public static function DBName() {return self::$m_sDBName;}
|
||||
|
||||
/**
|
||||
* Quote variable and protect against SQL injection attacks
|
||||
* Code found in the PHP documentation: quote_smart($value)
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param bool $bAlways should be set to true when the purpose is to create a IN clause,
|
||||
* otherwise and if there is a mix of strings and numbers, the clause would always be false
|
||||
* @param string $cQuoteStyle
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
public static function Quote($value, $bAlways = false, $cQuoteStyle = "'")
|
||||
{
|
||||
// Quote variable and protect against SQL injection attacks
|
||||
// Code found in the PHP documentation: quote_smart($value)
|
||||
|
||||
// bAlways should be set to true when the purpose is to create a IN clause,
|
||||
// otherwise and if there is a mix of strings and numbers, the clause
|
||||
// would always be false
|
||||
|
||||
if (is_null($value))
|
||||
{
|
||||
return 'NULL';
|
||||
@@ -273,6 +537,13 @@ class CMDBSource
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sSQLQuery
|
||||
*
|
||||
* @return \mysqli_result
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public static function Query($sSQLQuery)
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
@@ -287,19 +558,35 @@ class CMDBSource
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSQLQuery);
|
||||
if ($oResult === false)
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSQLQuery));
|
||||
$aContext = array('query' => $sSQLQuery);
|
||||
|
||||
$iMySqlErrorNo = self::$m_oMysqli->errno;
|
||||
$aMySqlHasGoneAwayErrorCodes = MySQLHasGoneAwayException::getErrorCodes();
|
||||
if (in_array($iMySqlErrorNo, $aMySqlHasGoneAwayErrorCodes))
|
||||
{
|
||||
throw new MySQLHasGoneAwayException(self::GetError(), $aContext);
|
||||
}
|
||||
|
||||
throw new MySQLException('Failed to issue SQL query', $aContext);
|
||||
}
|
||||
|
||||
return $oResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sTable
|
||||
*
|
||||
* @return int
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public static function GetNextInsertId($sTable)
|
||||
{
|
||||
$sSQL = "SHOW TABLE STATUS LIKE '$sTable'";
|
||||
$oResult = self::Query($sSQL);
|
||||
$aRow = $oResult->fetch_assoc();
|
||||
$iNextInsertId = $aRow['Auto_increment'];
|
||||
return $iNextInsertId;
|
||||
|
||||
return $aRow['Auto_increment'];
|
||||
}
|
||||
|
||||
public static function GetInsertId()
|
||||
@@ -326,7 +613,15 @@ class CMDBSource
|
||||
self::Query($sSQLQuery);
|
||||
}
|
||||
|
||||
public static function QueryToScalar($sSql)
|
||||
/**
|
||||
* @param string $sSql
|
||||
* @param int $iCol beginning at 0
|
||||
*
|
||||
* @return string corresponding cell content on the first line
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLQueryHasNoResultException
|
||||
*/
|
||||
public static function QueryToScalar($sSql, $iCol = 0)
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
try
|
||||
@@ -343,20 +638,28 @@ class CMDBSource
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||
}
|
||||
|
||||
|
||||
if ($aRow = $oResult->fetch_array(MYSQLI_BOTH))
|
||||
{
|
||||
$res = $aRow[0];
|
||||
$res = $aRow[$iCol];
|
||||
}
|
||||
else
|
||||
{
|
||||
$oResult->free();
|
||||
throw new MySQLException('Found no result for query', array('query' => $sSql));
|
||||
throw new MySQLQueryHasNoResultException('Found no result for query', array('query' => $sSql));
|
||||
}
|
||||
$oResult->free();
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $sSql
|
||||
*
|
||||
* @return array
|
||||
* @throws \MySQLException if query cannot be processed
|
||||
*/
|
||||
public static function QueryToArray($sSql)
|
||||
{
|
||||
$aData = array();
|
||||
@@ -384,6 +687,13 @@ class CMDBSource
|
||||
return $aData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sSql
|
||||
* @param int $col
|
||||
*
|
||||
* @return array
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public static function QueryToCol($sSql, $col)
|
||||
{
|
||||
$aColumn = array();
|
||||
@@ -395,6 +705,12 @@ class CMDBSource
|
||||
return $aColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sSql
|
||||
*
|
||||
* @return array
|
||||
* @throws \MySQLException if query cannot be processed
|
||||
*/
|
||||
public static function ExplainQuery($sSql)
|
||||
{
|
||||
$aData = array();
|
||||
@@ -410,8 +726,8 @@ class CMDBSource
|
||||
{
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
|
||||
}
|
||||
|
||||
$aNames = self::GetColumns($oResult);
|
||||
|
||||
$aNames = self::GetColumns($oResult, $sSql);
|
||||
|
||||
$aData[] = $aNames;
|
||||
while ($aRow = $oResult->fetch_array(MYSQLI_ASSOC))
|
||||
@@ -422,6 +738,12 @@ class CMDBSource
|
||||
return $aData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sSql
|
||||
*
|
||||
* @return string
|
||||
* @throws \MySQLException if query cannot be processed
|
||||
*/
|
||||
public static function TestQuery($sSql)
|
||||
{
|
||||
try
|
||||
@@ -459,7 +781,14 @@ class CMDBSource
|
||||
return $oResult->fetch_array(MYSQLI_ASSOC);
|
||||
}
|
||||
|
||||
public static function GetColumns($oResult)
|
||||
/**
|
||||
* @param mysqli_result $oResult
|
||||
* @param string $sSql
|
||||
*
|
||||
* @return string[]
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public static function GetColumns($oResult, $sSql)
|
||||
{
|
||||
$aNames = array();
|
||||
for ($i = 0; $i < (($___mysqli_tmp = $oResult->field_count) ? $___mysqli_tmp : 0) ; $i++)
|
||||
@@ -541,17 +870,35 @@ class CMDBSource
|
||||
return ($aFieldData["Type"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sTable
|
||||
* @param string $sField
|
||||
*
|
||||
* @return bool|string
|
||||
* @see \AttributeDefinition::GetSQLColumns()
|
||||
*/
|
||||
public static function GetFieldSpec($sTable, $sField)
|
||||
{
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
if (empty($aTableInfo)) return false;
|
||||
if (!array_key_exists($sField, $aTableInfo["Fields"])) return false;
|
||||
$aFieldData = $aTableInfo["Fields"][$sField];
|
||||
|
||||
$sRet = $aFieldData["Type"];
|
||||
|
||||
$sColumnCharset = $aFieldData["Charset"];
|
||||
$sColumnCollation = $aFieldData["Collation"];
|
||||
if (!empty($sColumnCharset))
|
||||
{
|
||||
$sRet .= ' CHARACTER SET '.$sColumnCharset;
|
||||
$sRet .= ' COLLATE '.$sColumnCollation;
|
||||
}
|
||||
|
||||
if ($aFieldData["Null"] == 'NO')
|
||||
{
|
||||
$sRet .= ' NOT NULL';
|
||||
}
|
||||
|
||||
if (is_numeric($aFieldData["Default"]))
|
||||
{
|
||||
if (strtolower(substr($aFieldData["Type"], 0, 5)) == 'enum(')
|
||||
@@ -569,10 +916,11 @@ class CMDBSource
|
||||
{
|
||||
$sRet .= ' DEFAULT '.self::Quote($aFieldData["Default"]);
|
||||
}
|
||||
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public static function HasIndex($sTable, $sIndexId, $aFields = null)
|
||||
public static function HasIndex($sTable, $sIndexId, $aFields = null, $aLength = null)
|
||||
{
|
||||
$aTableInfo = self::GetTableInfo($sTable);
|
||||
if (empty($aTableInfo)) return false;
|
||||
@@ -586,9 +934,24 @@ class CMDBSource
|
||||
|
||||
// Compare the columns
|
||||
$sSearchedIndex = implode(',', $aFields);
|
||||
$sExistingIndex = implode(',', $aTableInfo['Indexes'][$sIndexId]);
|
||||
$aColumnNames = array();
|
||||
$aSubParts = array();
|
||||
foreach($aTableInfo['Indexes'][$sIndexId] as $aIndexDef)
|
||||
{
|
||||
$aColumnNames[] = $aIndexDef['Column_name'];
|
||||
$aSubParts[] = $aIndexDef['Sub_part'];
|
||||
}
|
||||
$sExistingIndex = implode(',', $aColumnNames);
|
||||
|
||||
return ($sSearchedIndex == $sExistingIndex);
|
||||
if (is_null($aLength))
|
||||
{
|
||||
return ($sSearchedIndex == $sExistingIndex);
|
||||
}
|
||||
|
||||
$sSearchedLength = implode(',', $aLength);
|
||||
$sExistingLength = implode(',', $aSubParts);
|
||||
|
||||
return ($sSearchedIndex == $sExistingIndex) && ($sSearchedLength == $sExistingLength);
|
||||
}
|
||||
|
||||
// Returns an array of (fieldname => array of field info)
|
||||
@@ -608,35 +971,49 @@ class CMDBSource
|
||||
{
|
||||
self::$m_aTablesInfo = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sTableName
|
||||
*
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
private static function _TableInfoCacheInit($sTableName)
|
||||
{
|
||||
if (isset(self::$m_aTablesInfo[strtolower($sTableName)])
|
||||
&& (self::$m_aTablesInfo[strtolower($sTableName)] != null)) return;
|
||||
|
||||
try
|
||||
&& (self::$m_aTablesInfo[strtolower($sTableName)] != null))
|
||||
{
|
||||
// Check if the table exists
|
||||
$aFields = self::QueryToArray("SHOW COLUMNS FROM `$sTableName`");
|
||||
// Note: without backticks, you get an error with some table names (e.g. "group")
|
||||
foreach ($aFields as $aFieldData)
|
||||
{
|
||||
$sFieldName = $aFieldData["Field"];
|
||||
self::$m_aTablesInfo[strtolower($sTableName)]["Fields"][$sFieldName] =
|
||||
array
|
||||
(
|
||||
"Name"=>$aFieldData["Field"],
|
||||
"Type"=>$aFieldData["Type"],
|
||||
"Null"=>$aFieldData["Null"],
|
||||
"Key"=>$aFieldData["Key"],
|
||||
"Default"=>$aFieldData["Default"],
|
||||
"Extra"=>$aFieldData["Extra"]
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
catch(MySQLException $e)
|
||||
|
||||
// Create array entry, if table does not exist / has no columns
|
||||
self::$m_aTablesInfo[strtolower($sTableName)] = null;
|
||||
|
||||
// Get table informations
|
||||
// We were using SHOW COLUMNS FROM... but this don't return charset and collation info !
|
||||
// so since 2.5 and #1001 (switch to utf8mb4) we're using INFORMATION_SCHEMA !
|
||||
$aMapping = array(
|
||||
"Name" => "COLUMN_NAME",
|
||||
"Type" => "COLUMN_TYPE",
|
||||
"Null" => "IS_NULLABLE",
|
||||
"Key" => "COLUMN_KEY",
|
||||
"Default" => "COLUMN_DEFAULT",
|
||||
"Extra" => "EXTRA",
|
||||
"Charset" => "CHARACTER_SET_NAME",
|
||||
"Collation" => "COLLATION_NAME",
|
||||
"CharMaxLength" => "CHARACTER_MAXIMUM_LENGTH",
|
||||
);
|
||||
$sColumns = implode(', ', $aMapping);
|
||||
$sDBName = self::$m_sDBName;
|
||||
$aFields = self::QueryToArray("SELECT $sColumns FROM information_schema.`COLUMNS` WHERE table_schema = '$sDBName' AND table_name = '$sTableName';");
|
||||
foreach ($aFields as $aFieldData)
|
||||
{
|
||||
// Table does not exist
|
||||
self::$m_aTablesInfo[strtolower($sTableName)] = null;
|
||||
$aFields = array();
|
||||
foreach($aMapping as $sKey => $sColumn)
|
||||
{
|
||||
$aFields[$sKey] = $aFieldData[$sColumn];
|
||||
}
|
||||
$sFieldName = $aFieldData["COLUMN_NAME"];
|
||||
self::$m_aTablesInfo[strtolower($sTableName)]["Fields"][$sFieldName] = $aFields;
|
||||
}
|
||||
|
||||
if (!is_null(self::$m_aTablesInfo[strtolower($sTableName)]))
|
||||
@@ -645,32 +1022,57 @@ class CMDBSource
|
||||
$aMyIndexes = array();
|
||||
foreach ($aIndexes as $aIndexColumn)
|
||||
{
|
||||
$aMyIndexes[$aIndexColumn['Key_name']][$aIndexColumn['Seq_in_index']-1] = $aIndexColumn['Column_name'];
|
||||
$aMyIndexes[$aIndexColumn['Key_name']][$aIndexColumn['Seq_in_index']-1] = $aIndexColumn;
|
||||
}
|
||||
self::$m_aTablesInfo[strtolower($sTableName)]["Indexes"] = $aMyIndexes;
|
||||
}
|
||||
}
|
||||
//public static function EnumTables()
|
||||
//{
|
||||
// self::_TablesInfoCacheInit();
|
||||
// return array_keys(self::$m_aTablesInfo);
|
||||
//}
|
||||
|
||||
public static function GetTableInfo($sTable)
|
||||
{
|
||||
self::_TableInfoCacheInit($sTable);
|
||||
|
||||
// perform a case insensitive match because on Windows the table names become lowercase :-(
|
||||
//foreach(self::$m_aTablesInfo as $sTableName => $aInfo)
|
||||
//{
|
||||
// if (strtolower($sTableName) == strtolower($sTable))
|
||||
// {
|
||||
// return $aInfo;
|
||||
// }
|
||||
//}
|
||||
return self::$m_aTablesInfo[strtolower($sTable)];
|
||||
//return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sTableName
|
||||
*
|
||||
* @return string query to upgrade table charset and collation if needed, null if not
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @since 2.5 #1001 switch to utf8mb4
|
||||
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-table.html
|
||||
*/
|
||||
public static function DBCheckTableCharsetAndCollation($sTableName)
|
||||
{
|
||||
$sDBName = self::DBName();
|
||||
$sTableInfoQuery = "SELECT C.character_set_name, T.table_collation
|
||||
FROM information_schema.`TABLES` T inner join information_schema.`COLLATION_CHARACTER_SET_APPLICABILITY` C
|
||||
ON T.table_collation = C.collation_name
|
||||
WHERE T.table_schema = '$sDBName'
|
||||
AND T.table_name = '$sTableName';";
|
||||
$aTableInfo = self::QueryToArray($sTableInfoQuery);
|
||||
$sTableCharset = $aTableInfo[0]['character_set_name'];
|
||||
$sTableCollation = $aTableInfo[0]['table_collation'];
|
||||
|
||||
if ((DEFAULT_CHARACTER_SET == $sTableCharset) && (DEFAULT_COLLATION == $sTableCollation))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return 'ALTER TABLE `'.$sTableName.'` '.self::GetSqlStringColumnDefinition().';';
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sTable
|
||||
*
|
||||
* @return array
|
||||
* @throws \MySQLException if query cannot be processed
|
||||
*/
|
||||
public static function DumpTable($sTable)
|
||||
{
|
||||
$sSql = "SELECT * FROM `$sTable`";
|
||||
@@ -726,6 +1128,7 @@ class CMDBSource
|
||||
}
|
||||
catch(MySQLException $e)
|
||||
{
|
||||
$iCode = self::GetErrNo();
|
||||
return "Current user not allowed to see his own privileges (could not access to the database 'mysql' - $iCode)";
|
||||
}
|
||||
|
||||
@@ -784,4 +1187,28 @@ class CMDBSource
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string query to upgrade database charset and collation if needed, null if not
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @since 2.5 #1001 switch to utf8mb4
|
||||
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-database.html
|
||||
*/
|
||||
public static function DBCheckCharsetAndCollation()
|
||||
{
|
||||
$sDBName = CMDBSource::DBName();
|
||||
$sDBInfoQuery = "SELECT DEFAULT_CHARACTER_SET_NAME, DEFAULT_COLLATION_NAME
|
||||
FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '$sDBName';";
|
||||
$aDBInfo = CMDBSource::QueryToArray($sDBInfoQuery);
|
||||
$sDBCharset = $aDBInfo[0]['DEFAULT_CHARACTER_SET_NAME'];
|
||||
$sDBCollation = $aDBInfo[0]['DEFAULT_COLLATION_NAME'];
|
||||
|
||||
if ((DEFAULT_CHARACTER_SET == $sDBCharset) && (DEFAULT_COLLATION == $sDBCollation))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return 'ALTER DATABASE'.CMDBSource::GetSqlStringColumnDefinition().';';
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2016 Combodo SARL
|
||||
// Copyright (C) 2016-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -29,13 +29,13 @@
|
||||
*
|
||||
* if (ContextTag::Check('GUI:Portal'))
|
||||
*
|
||||
* @copyright Copyright (C) 2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2016-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class ContextTag
|
||||
{
|
||||
protected static $aStack;
|
||||
protected static $aStack = array();
|
||||
|
||||
/**
|
||||
* Store a context tag on the stack
|
||||
|
||||
@@ -69,6 +69,13 @@ class CoreException extends Exception
|
||||
parent::__construct($sMessage, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string code and message for log purposes
|
||||
*/
|
||||
public function getInfoLog()
|
||||
{
|
||||
return 'error_code='.$this->getCode().', message="'.$this->getMessage().'"';
|
||||
}
|
||||
public function getHtmlDesc($sHighlightHtmlBegin = '<b>', $sHighlightHtmlEnd = '</b>')
|
||||
{
|
||||
return $this->getMessage();
|
||||
@@ -112,4 +119,11 @@ class SecurityException extends CoreException
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
/**
|
||||
* Throwned when querying on an object that exists in the database but is archived
|
||||
*
|
||||
* @see N.1108
|
||||
*/
|
||||
class ArchivedObjectException extends CoreException
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,3 +1,258 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.5">
|
||||
<user_rights>
|
||||
<profiles>
|
||||
<profile id="1024" _delta="define">
|
||||
<name>REST Services User</name>
|
||||
<description>Only users having this profile are allowed to use the REST Web Services (unless 'secure_rest_services' is set to false in the configuration file).</description>
|
||||
<groups />
|
||||
</profile>
|
||||
</profiles>
|
||||
</user_rights>
|
||||
<meta>
|
||||
<classes>
|
||||
<class id="User" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>core,grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="contactid" xsi:type="AttributeExternalKey">
|
||||
<target_class>Person</target_class>
|
||||
</field>
|
||||
<field id="last_name" xsi:type="AttributeExternalField"/>
|
||||
<field id="first_name" xsi:type="AttributeExternalField"/>
|
||||
<field id="email" xsi:type="AttributeExternalField"/>
|
||||
<field id="org_id" xsi:type="AttributeExternalField"/>
|
||||
<field id="login" xsi:type="AttributeString"/>
|
||||
<field id="language" xsi:type="AttributeApplicationLanguage"/>
|
||||
<field id="status" xsi:type="AttributeEnum"/>
|
||||
<field id="profile_list" xsi:type="AttributeLinkedSetIndirect"/>
|
||||
<field id="allowed_org_list" xsi:type="AttributeLinkedSetIndirect"/>
|
||||
<field id="finalclass" xsi:type="AttributeFinalClass"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
<field id="contactid_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="contactid_obsolescence_flag" xsi:type="AttributeExternalField"/>
|
||||
<field id="org_id_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="org_id_obsolescence_flag" xsi:type="AttributeExternalField"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="URP_Profiles" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>addon/userrights,grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString"/>
|
||||
<field id="description" xsi:type="AttributeString"/>
|
||||
<field id="user_list" xsi:type="AttributeLinkedSetIndirect"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="URP_UserProfile" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>addon/userrights,grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="userid" xsi:type="AttributeExternalKey">
|
||||
<target_class>User</target_class>
|
||||
</field>
|
||||
<field id="userlogin" xsi:type="AttributeExternalField"/>
|
||||
<field id="profileid" xsi:type="AttributeExternalKey">
|
||||
<target_class>URP_Profiles</target_class>
|
||||
</field>
|
||||
<field id="profile" xsi:type="AttributeExternalField"/>
|
||||
<field id="reason" xsi:type="AttributeString"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
<field id="userid_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="userid_finalclass_recall" xsi:type="AttributeExternalField"/>
|
||||
<field id="profileid_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="URP_UserOrg" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>addon/userrights,grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="userid" xsi:type="AttributeExternalKey">
|
||||
<target_class>User</target_class>
|
||||
</field>
|
||||
<field id="userlogin" xsi:type="AttributeExternalField"/>
|
||||
<field id="allowed_org_id" xsi:type="AttributeExternalKey">
|
||||
<target_class>Organization</target_class>
|
||||
</field>
|
||||
<field id="allowed_org_name" xsi:type="AttributeExternalField"/>
|
||||
<field id="reason" xsi:type="AttributeString"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
<field id="userid_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="userid_finalclass_recall" xsi:type="AttributeExternalField"/>
|
||||
<field id="allowed_org_id_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="allowed_org_id_obsolescence_flag" xsi:type="AttributeExternalField"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="Action" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>grant_by_profile,core/cmdb</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString"/>
|
||||
<field id="description" xsi:type="AttributeString"/>
|
||||
<field id="status" xsi:type="AttributeEnum"/>
|
||||
<field id="trigger_list" xsi:type="AttributeLinkedSetIndirect"/>
|
||||
<field id="finalclass" xsi:type="AttributeFinalClass"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="Trigger" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>grant_by_profile,core/cmdb</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="description" xsi:type="AttributeString"/>
|
||||
<field id="action_list" xsi:type="AttributeLinkedSetIndirect"/>
|
||||
<field id="finalclass" xsi:type="AttributeFinalClass"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="SynchroDataSource" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>core/cmdb,view_in_gui,grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString"/>
|
||||
<field id="description" xsi:type="AttributeText"/>
|
||||
<field id="status" xsi:type="AttributeEnum"/>
|
||||
<field id="user_id" xsi:type="AttributeExternalKey">
|
||||
<target_class>User</target_class>
|
||||
</field>
|
||||
<field id="notify_contact_id" xsi:type="AttributeExternalKey">
|
||||
<target_class>Contact</target_class>
|
||||
</field>
|
||||
<field id="scope_class" xsi:type="AttributeClass"/>
|
||||
<field id="database_table_name" xsi:type="AttributeString"/>
|
||||
<field id="scope_restriction" xsi:type="AttributeString"/>
|
||||
<field id="full_load_periodicity" xsi:type="AttributeDuration"/>
|
||||
<field id="reconciliation_policy" xsi:type="AttributeEnum"/>
|
||||
<field id="action_on_zero" xsi:type="AttributeEnum"/>
|
||||
<field id="action_on_one" xsi:type="AttributeEnum"/>
|
||||
<field id="action_on_multiple" xsi:type="AttributeEnum"/>
|
||||
<field id="delete_policy" xsi:type="AttributeEnum"/>
|
||||
<field id="delete_policy_update" xsi:type="AttributeString"/>
|
||||
<field id="delete_policy_retention" xsi:type="AttributeDuration"/>
|
||||
<field id="attribute_list" xsi:type="AttributeLinkedSet"/>
|
||||
<field id="user_delete_policy" xsi:type="AttributeEnum"/>
|
||||
<field id="url_icon" xsi:type="AttributeURL"/>
|
||||
<field id="url_application" xsi:type="AttributeString"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
<field id="user_id_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="user_id_finalclass_recall" xsi:type="AttributeExternalField"/>
|
||||
<field id="notify_contact_id_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="notify_contact_id_finalclass_recall" xsi:type="AttributeExternalField"/>
|
||||
<field id="notify_contact_id_obsolescence_flag" xsi:type="AttributeExternalField"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="SynchroAttribute" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>core/cmdb,view_in_gui,grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="sync_source_id" xsi:type="AttributeExternalKey">
|
||||
<target_class>SynchroDataSource</target_class>
|
||||
</field>
|
||||
<field id="sync_source_name" xsi:type="AttributeExternalField"/>
|
||||
<field id="attcode" xsi:type="AttributeString"/>
|
||||
<field id="update" xsi:type="AttributeBoolean"/>
|
||||
<field id="reconcile" xsi:type="AttributeBoolean"/>
|
||||
<field id="update_policy" xsi:type="AttributeEnum"/>
|
||||
<field id="finalclass" xsi:type="AttributeFinalClass"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
<field id="sync_source_id_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="AuditRule" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>application, grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString"/>
|
||||
<field id="description" xsi:type="AttributeString"/>
|
||||
<field id="query" xsi:type="AttributeOQL"/>
|
||||
<field id="valid_flag" xsi:type="AttributeEnum"/>
|
||||
<field id="category_id" xsi:type="AttributeExternalKey">
|
||||
<target_class>AuditCategory</target_class>
|
||||
</field>
|
||||
<field id="category_name" xsi:type="AttributeExternalField"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
<field id="category_id_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="AuditCategory" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>application, grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString"/>
|
||||
<field id="description" xsi:type="AttributeString"/>
|
||||
<field id="definition_set" xsi:type="AttributeOQL"/>
|
||||
<field id="rules_list" xsi:type="AttributeLinkedSet"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="Query" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>core/cmdb,view_in_gui,application,grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString"/>
|
||||
<field id="description" xsi:type="AttributeText"/>
|
||||
<field id="fields" xsi:type="AttributeText"/>
|
||||
<field id="finalclass" xsi:type="AttributeFinalClass"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="lnkTriggerAction" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>grant_by_profile,core/cmdb,application</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="action_id" xsi:type="AttributeExternalKey">
|
||||
<target_class>Action</target_class>
|
||||
</field>
|
||||
<field id="action_name" xsi:type="AttributeExternalField"/>
|
||||
<field id="trigger_id" xsi:type="AttributeExternalKey">
|
||||
<target_class>Trigger</target_class>
|
||||
</field>
|
||||
<field id="trigger_name" xsi:type="AttributeExternalField"/>
|
||||
<field id="order" xsi:type="AttributeInteger"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
<field id="action_id_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="action_id_finalclass_recall" xsi:type="AttributeExternalField"/>
|
||||
<field id="trigger_id_friendlyname" xsi:type="AttributeExternalField"/>
|
||||
<field id="trigger_id_finalclass_recall" xsi:type="AttributeExternalField"/>
|
||||
</fields>
|
||||
</class>
|
||||
</classes>
|
||||
</meta>
|
||||
</itop_design>
|
||||
@@ -413,10 +413,16 @@ EOF
|
||||
/**
|
||||
* Get the regular expression to (approximately) validate a date/time for the current format
|
||||
* The validation does not take into account the number of days in a month (i.e. June 31st will pass, as well as Feb 30th!)
|
||||
* @param string $sDelimiter Surround the regexp (and escape) if needed
|
||||
* @return string The regular expression in PCRE syntax
|
||||
*/
|
||||
public function ToRegExpr()
|
||||
public function ToRegExpr($sDelimiter = null)
|
||||
{
|
||||
return '^'.$this->Transform('regexpr', "\\%s", false /* escape all */, '.?*$^()[]/:').'$';
|
||||
$sRet = '^'.$this->Transform('regexpr', "\\%s", false /* escape all */, '.?*$^()[]:').'$';
|
||||
if ($sDelimiter !== null)
|
||||
{
|
||||
$sRet = $sDelimiter.str_replace($sDelimiter, '\\'.$sDelimiter, $sRet).$sDelimiter;
|
||||
}
|
||||
return $sRet;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -74,8 +74,8 @@ abstract class DBObject implements iDisplay
|
||||
private static $m_aBulkInsertCols = array(); // class => array of ('table' => array of <sql_column>)
|
||||
private static $m_bBulkInsert = false;
|
||||
|
||||
private $m_bIsInDB = false; // true IIF the object is mapped to a DB record
|
||||
private $m_iKey = null;
|
||||
protected $m_bIsInDB = false; // true IIF the object is mapped to a DB record
|
||||
protected $m_iKey = null;
|
||||
private $m_aCurrValues = array();
|
||||
protected $m_aOrigValues = array();
|
||||
|
||||
@@ -116,7 +116,7 @@ abstract class DBObject implements iDisplay
|
||||
// set default values
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
$this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue($this);
|
||||
$this->m_aCurrValues[$sAttCode] = $this->GetDefaultValue($sAttCode);
|
||||
$this->m_aOrigValues[$sAttCode] = null;
|
||||
if ($oAttDef->IsExternalField() || ($oAttDef instanceof AttributeFriendlyName))
|
||||
{
|
||||
@@ -159,18 +159,13 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$sRet = '';
|
||||
$sClass = get_class($this);
|
||||
$sRootClass = MetaModel::GetRootClass($sClass);
|
||||
$iPKey = $this->GetKey();
|
||||
$sRet .= "<b title=\"$sRootClass\">$sClass</b>::$iPKey<br/>\n";
|
||||
$sRet .= "<ul class=\"treeview\">\n";
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
$sRet .= "<li>".$oAttDef->GetLabel()." = ".$this->GetAsHtml($sAttCode)."</li>\n";
|
||||
}
|
||||
$sRet .= "</ul>";
|
||||
return $sRet;
|
||||
$sRet = '';
|
||||
$sClass = get_class($this);
|
||||
$sRootClass = MetaModel::GetRootClass($sClass);
|
||||
$iPKey = $this->GetKey();
|
||||
$sFriendlyname = $this->Get('friendlyname');
|
||||
$sRet .= "<b title=\"$sRootClass\">$sClass</b>::$iPKey ($sFriendlyname)<br/>\n";
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
// Restore initial values... mmmm, to be discussed
|
||||
@@ -192,10 +187,14 @@ abstract class DBObject implements iDisplay
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $bAllowAllData DEPRECATED: the reload must never fail!
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function Reload($bAllowAllData = false)
|
||||
{
|
||||
assert($this->m_bIsInDB);
|
||||
$aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey, false /* must be found */, $bAllowAllData/* in the future $this->m_bAllowAllData ??*/);
|
||||
$aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey, false /* must be found */, true /* AllowAllData */);
|
||||
if (empty($aRow))
|
||||
{
|
||||
throw new CoreException("Failed to reload object of class '".get_class($this)."', id = ".$this->m_iKey);
|
||||
@@ -208,23 +207,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
if (!$oAttDef->IsLinkSet()) continue;
|
||||
|
||||
// Load the link information
|
||||
$sLinkClass = $oAttDef->GetLinkedClass();
|
||||
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
||||
|
||||
// The class to target is not the current class, because if this is a derived class,
|
||||
// it may differ from the target class, then things start to become confusing
|
||||
$oRemoteExtKeyAtt = MetaModel::GetAttributeDef($sLinkClass, $sExtKeyToMe);
|
||||
$sMyClass = $oRemoteExtKeyAtt->GetTargetClass();
|
||||
|
||||
$oMyselfSearch = new DBObjectSearch($sMyClass);
|
||||
$oMyselfSearch->AddCondition('id', $this->m_iKey, '=');
|
||||
|
||||
$oLinkSearch = new DBObjectSearch($sLinkClass);
|
||||
$oLinkSearch->AddCondition_PointingTo($oMyselfSearch, $sExtKeyToMe);
|
||||
$oLinks = new DBObjectSet($oLinkSearch);
|
||||
|
||||
$this->m_aCurrValues[$sAttCode] = $oLinks;
|
||||
$this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue($this);
|
||||
$this->m_aOrigValues[$sAttCode] = clone $this->m_aCurrValues[$sAttCode];
|
||||
$this->m_aLoadedAtt[$sAttCode] = true;
|
||||
}
|
||||
@@ -348,7 +331,14 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
return $bFullyLoaded;
|
||||
}
|
||||
|
||||
|
||||
protected function _Set($sAttCode, $value)
|
||||
{
|
||||
$this->m_aCurrValues[$sAttCode] = $value;
|
||||
$this->m_aTouchedAtt[$sAttCode] = true;
|
||||
unset($this->m_aModifiedAtt[$sAttCode]);
|
||||
}
|
||||
|
||||
public function Set($sAttCode, $value)
|
||||
{
|
||||
if ($sAttCode == 'finalclass')
|
||||
@@ -356,8 +346,15 @@ abstract class DBObject implements iDisplay
|
||||
// Ignore it - this attribute is set upon object creation and that's it
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
|
||||
if (!$oAttDef->IsWritable())
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
throw new Exception("Attempting to set the value on the read-only attribute $sClass::$sAttCode");
|
||||
}
|
||||
|
||||
if ($this->m_bIsInDB && !$this->m_bFullyLoaded && !$this->m_bDirty)
|
||||
{
|
||||
// First time Set is called... ensure that the object gets fully loaded
|
||||
@@ -380,7 +377,7 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef)
|
||||
{
|
||||
if (($oDef->IsExternalField() || ($oDef instanceof AttributeFriendlyName)) && ($oDef->GetKeyAttCode() == $sAttCode))
|
||||
if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sAttCode))
|
||||
{
|
||||
$this->m_aCurrValues[$sCode] = $value->Get($oDef->GetExtAttCode());
|
||||
$this->m_aLoadedAtt[$sCode] = true;
|
||||
@@ -393,48 +390,28 @@ abstract class DBObject implements iDisplay
|
||||
// Invalidate the corresponding fields so that they get reloaded in case they are needed (See Get())
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef)
|
||||
{
|
||||
if (($oDef->IsExternalField() || ($oDef instanceof AttributeFriendlyName)) && ($oDef->GetKeyAttCode() == $sAttCode))
|
||||
if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sAttCode))
|
||||
{
|
||||
$this->m_aCurrValues[$sCode] = $oDef->GetDefaultValue($this);
|
||||
$this->m_aCurrValues[$sCode] = $this->GetDefaultValue($sCode);
|
||||
unset($this->m_aLoadedAtt[$sCode]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if($oAttDef->IsLinkSet())
|
||||
if ($oAttDef->IsLinkSet() && ($value != null))
|
||||
{
|
||||
if (is_null($value))
|
||||
{
|
||||
// Normalize
|
||||
$value = DBObjectSet::FromScratch($oAttDef->GetLinkedClass());
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((get_class($value) != 'DBObjectSet') && !is_subclass_of($value, 'DBObjectSet'))
|
||||
{
|
||||
throw new CoreUnexpectedValue("expecting a set of persistent objects (found a '".get_class($value)."'), setting default value (empty list)");
|
||||
}
|
||||
}
|
||||
|
||||
$oObjectSet = $value;
|
||||
$sSetClass = $oObjectSet->GetClass();
|
||||
$sLinkClass = $oAttDef->GetLinkedClass();
|
||||
// not working fine :-( if (!is_subclass_of($sSetClass, $sLinkClass))
|
||||
if ($sSetClass != $sLinkClass)
|
||||
{
|
||||
throw new CoreUnexpectedValue("expecting a set of '$sLinkClass' objects (found a set of '$sSetClass'), setting default value (empty list)");
|
||||
}
|
||||
$realvalue = clone $this->m_aCurrValues[$sAttCode];
|
||||
$realvalue->UpdateFromCompleteList($value);
|
||||
}
|
||||
|
||||
$realvalue = $oAttDef->MakeRealValue($value, $this);
|
||||
|
||||
$this->m_aCurrValues[$sAttCode] = $realvalue;
|
||||
$this->m_aTouchedAtt[$sAttCode] = true;
|
||||
unset($this->m_aModifiedAtt[$sAttCode]);
|
||||
else
|
||||
{
|
||||
$realvalue = $oAttDef->MakeRealValue($value, $this);
|
||||
}
|
||||
$this->_Set($sAttCode, $realvalue);
|
||||
|
||||
foreach (MetaModel::ListMetaAttributes(get_class($this), $sAttCode) as $sMetaAttCode => $oMetaAttDef)
|
||||
{
|
||||
$this->Set($sMetaAttCode, $oMetaAttDef->MapValue($this));
|
||||
$this->_Set($sMetaAttCode, $oMetaAttDef->MapValue($this));
|
||||
}
|
||||
|
||||
// The object has changed, reset caches
|
||||
@@ -538,7 +515,7 @@ abstract class DBObject implements iDisplay
|
||||
else
|
||||
{
|
||||
// Not loaded... is it related to an external key?
|
||||
if ($oAttDef->IsExternalField() || ($oAttDef instanceof AttributeFriendlyName))
|
||||
if ($oAttDef->IsExternalField())
|
||||
{
|
||||
// Let's get the object and compute all of the corresponding attributes
|
||||
// (i.e not only the requested attribute)
|
||||
@@ -560,7 +537,7 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef)
|
||||
{
|
||||
if (($oDef->IsExternalField() || ($oDef instanceof AttributeFriendlyName)) && ($oDef->GetKeyAttCode() == $sExtKeyAttCode))
|
||||
if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sExtKeyAttCode))
|
||||
{
|
||||
if ($oRemote)
|
||||
{
|
||||
@@ -568,7 +545,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->m_aCurrValues[$sCode] = $oDef->GetDefaultValue($this);
|
||||
$this->m_aCurrValues[$sCode] = $this->GetDefaultValue($sCode);
|
||||
}
|
||||
$this->m_aLoadedAtt[$sCode] = true;
|
||||
}
|
||||
@@ -578,7 +555,7 @@ abstract class DBObject implements iDisplay
|
||||
$value = $this->m_aCurrValues[$sAttCode];
|
||||
}
|
||||
|
||||
if ($value instanceof DBObjectSet)
|
||||
if ($value instanceof ormLinkSet)
|
||||
{
|
||||
$value->Rewind();
|
||||
}
|
||||
@@ -591,9 +568,23 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
throw new CoreException("Unknown attribute code '$sAttCode' for the class ".get_class($this));
|
||||
}
|
||||
return $this->m_aOrigValues[$sAttCode];
|
||||
$aOrigValues = $this->m_aOrigValues;
|
||||
return isset($aOrigValues[$sAttCode]) ? $aOrigValues[$sAttCode] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default value of the $sAttCode. By default, returns the default value of the AttributeDefinition.
|
||||
* Overridable.
|
||||
*
|
||||
* @param $sAttCode
|
||||
* @return mixed
|
||||
*/
|
||||
public function GetDefaultValue($sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
return $oAttDef->GetDefaultValue($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns data loaded by the mean of a dynamic and explicit JOIN
|
||||
*/
|
||||
@@ -738,7 +729,9 @@ abstract class DBObject implements iDisplay
|
||||
else
|
||||
{
|
||||
$sHtmlLabel = htmlentities($this->Get($sAttCode.'_friendlyname'), ENT_QUOTES, 'UTF-8');
|
||||
return $this->MakeHyperLink($sTargetClass, $iTargetKey, $sHtmlLabel);
|
||||
$bArchived = $this->IsArchived($sAttCode);
|
||||
$bObsolete = $this->IsObsolete($sAttCode);
|
||||
return $this->MakeHyperLink($sTargetClass, $iTargetKey, $sHtmlLabel, null, true, $bArchived, $bObsolete);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -817,10 +810,12 @@ abstract class DBObject implements iDisplay
|
||||
* @param string $sHtmlLabel Label with HTML entities escaped (< escaped as <)
|
||||
* @param null $sUrlMakerClass
|
||||
* @param bool|true $bWithNavigationContext
|
||||
* @param bool|false $bArchived
|
||||
* @param bool|false $bObsolete
|
||||
* @return string
|
||||
* @throws DictExceptionMissingString
|
||||
*/
|
||||
public static function MakeHyperLink($sObjClass, $sObjKey, $sHtmlLabel = '', $sUrlMakerClass = null, $bWithNavigationContext = true)
|
||||
public static function MakeHyperLink($sObjClass, $sObjKey, $sHtmlLabel = '', $sUrlMakerClass = null, $bWithNavigationContext = true, $bArchived = false, $bObsolete = false)
|
||||
{
|
||||
if ($sObjKey <= 0) return '<em>'.Dict::S('UI:UndefinedObject').'</em>'; // Objects built in memory have negative IDs
|
||||
|
||||
@@ -843,19 +838,58 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
$sHint = MetaModel::GetName($sObjClass)."::$sObjKey";
|
||||
$sUrl = ApplicationContext::MakeObjectUrl($sObjClass, $sObjKey, $sUrlMakerClass, $bWithNavigationContext);
|
||||
if (strlen($sUrl) > 0)
|
||||
|
||||
$bClickable = !$bArchived || utils::IsArchiveMode();
|
||||
if ($bArchived)
|
||||
{
|
||||
return "<a href=\"$sUrl\" title=\"$sHint\">$sHtmlLabel</a>";
|
||||
$sSpanClass = 'archived';
|
||||
$sFA = 'fa-archive object-archived';
|
||||
$sHint = Dict::S('ObjectRef:Archived');
|
||||
}
|
||||
elseif ($bObsolete)
|
||||
{
|
||||
$sSpanClass = 'obsolete';
|
||||
$sFA = 'fa-eye-slash object-obsolete';
|
||||
$sHint = Dict::S('ObjectRef:Obsolete');
|
||||
}
|
||||
else
|
||||
{
|
||||
return $sHtmlLabel;
|
||||
$sSpanClass = '';
|
||||
$sFA = '';
|
||||
}
|
||||
if ($sFA == '')
|
||||
{
|
||||
$sIcon = '';
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($bClickable)
|
||||
{
|
||||
$sIcon = "<span class=\"object-ref-icon fa $sFA fa-1x fa-fw\"></span>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sIcon = "<span class=\"object-ref-icon-disabled fa $sFA fa-1x fa-fw\"></span>";
|
||||
}
|
||||
}
|
||||
|
||||
if ($bClickable && (strlen($sUrl) > 0))
|
||||
{
|
||||
$sHLink = "<a class=\"object-ref-link\" href=\"$sUrl\">$sIcon$sHtmlLabel</a>";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHLink = $sIcon.$sHtmlLabel;
|
||||
}
|
||||
$sRet = "<span class=\"object-ref $sSpanClass\" title=\"$sHint\">$sHLink</span>";
|
||||
return $sRet;
|
||||
}
|
||||
|
||||
public function GetHyperlink($sUrlMakerClass = null, $bWithNavigationContext = true)
|
||||
{
|
||||
return self::MakeHyperLink(get_class($this), $this->GetKey(), $this->GetName(), $sUrlMakerClass, $bWithNavigationContext);
|
||||
$bArchived = $this->IsArchived();
|
||||
$bObsolete = $this->IsObsolete();
|
||||
return self::MakeHyperLink(get_class($this), $this->GetKey(), $this->GetName(), $sUrlMakerClass, $bWithNavigationContext, $bArchived, $bObsolete);
|
||||
}
|
||||
|
||||
public static function ComputeStandardUIPage($sClass)
|
||||
@@ -1015,24 +1049,24 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of flags (OPT_ATT_HIDDEN, OPT_ATT_READONLY, OPT_ATT_MANDATORY...)
|
||||
* for the given attribute in the current state of the object
|
||||
* @param $sAttCode string $sAttCode The code of the attribute
|
||||
* @param $aReasons array To store the reasons why the attribute is read-only (info about the synchro replicas)
|
||||
* @param $sTargetState string The target state in which to evalutate the flags, if empty the current state will be used
|
||||
* @return integer Flags: the binary combination of the flags applicable to this attribute
|
||||
*/
|
||||
*
|
||||
* @param string $sAttCode $sAttCode The code of the attribute
|
||||
* @param array $aReasons To store the reasons why the attribute is read-only (info about the synchro replicas)
|
||||
* @param string $sTargetState The target state in which to evalutate the flags, if empty the current state will be
|
||||
* used
|
||||
*
|
||||
* @return integer the binary combination of flags (OPT_ATT_HIDDEN, OPT_ATT_READONLY, OPT_ATT_MANDATORY...) for the
|
||||
* given attribute in the given state of the object
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
|
||||
{
|
||||
$iFlags = 0; // By default (if no life cycle) no flag at all
|
||||
|
||||
$aReadOnlyAtts = $this->GetReadOnlyAttributes();
|
||||
if ($aReadOnlyAtts != null)
|
||||
if (($aReadOnlyAtts != null) && (in_array($sAttCode, $aReadOnlyAtts)))
|
||||
{
|
||||
if (in_array($sAttCode, $aReadOnlyAtts))
|
||||
{
|
||||
return OPT_ATT_READONLY;
|
||||
}
|
||||
return OPT_ATT_READONLY;
|
||||
}
|
||||
|
||||
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
|
||||
@@ -1056,6 +1090,80 @@ abstract class DBObject implements iDisplay
|
||||
return $iFlags | $iSynchroFlags; // Combine both sets of flags
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sAttCode
|
||||
* @param array $aReasons To store the reasons why the attribute is read-only (info about the synchro replicas)
|
||||
*
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function IsAttributeReadOnlyForCurrentState($sAttCode, &$aReasons = array())
|
||||
{
|
||||
$iAttFlags = $this->GetAttributeFlags($sAttCode, $aReasons);
|
||||
|
||||
return ($iAttFlags & OPT_ATT_READONLY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of flags (OPT_ATT_HIDDEN, OPT_ATT_READONLY, OPT_ATT_MANDATORY...)
|
||||
* for the given attribute in a transition
|
||||
* @param $sAttCode string $sAttCode The code of the attribute
|
||||
* @param $sStimulus string The stimulus code to apply
|
||||
* @param $aReasons array To store the reasons why the attribute is read-only (info about the synchro replicas)
|
||||
* @param $sOriginState string The state from which to apply $sStimulus, if empty current state will be used
|
||||
* @return integer Flags: the binary combination of the flags applicable to this attribute
|
||||
*/
|
||||
public function GetTransitionFlags($sAttCode, $sStimulus, &$aReasons = array(), $sOriginState = '')
|
||||
{
|
||||
$iFlags = 0; // By default (if no lifecycle) no flag at all
|
||||
|
||||
$sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this));
|
||||
// If no state attribute, there is no lifecycle
|
||||
if (empty($sStateAttCode))
|
||||
{
|
||||
return $iFlags;
|
||||
}
|
||||
|
||||
// Retrieving current state if necessary
|
||||
if ($sOriginState === '')
|
||||
{
|
||||
$sOriginState = $this->Get($sStateAttCode);
|
||||
}
|
||||
|
||||
// Retrieving attribute flags
|
||||
$iAttributeFlags = $this->GetAttributeFlags($sAttCode, $aReasons, $sOriginState);
|
||||
|
||||
// Retrieving transition flags
|
||||
$iTransitionFlags = MetaModel::GetTransitionFlags(get_class($this), $sOriginState, $sStimulus, $sAttCode);
|
||||
|
||||
// Merging transition flags with attribute flags
|
||||
$iFlags = $iTransitionFlags | $iAttributeFlags;
|
||||
|
||||
return $iFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of attribute codes (with their flags) when $sStimulus is applied on the object in the $sOriginState state.
|
||||
* Note: Attributes (and flags) from the target state and the transition are combined.
|
||||
*
|
||||
* @param $sStimulus string
|
||||
* @param $sOriginState string Default is current state
|
||||
* @return array
|
||||
*/
|
||||
public function GetTransitionAttributes($sStimulus, $sOriginState = null)
|
||||
{
|
||||
$sObjClass = get_class($this);
|
||||
|
||||
// Defining current state as origin state if not specified
|
||||
if($sOriginState === null)
|
||||
{
|
||||
$sOriginState = $this->GetState();
|
||||
}
|
||||
|
||||
$aAttributes = MetaModel::GetTransitionAttributes($sObjClass, $sStimulus, $sOriginState);
|
||||
|
||||
return $aAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of flags (OPT_ATT_HIDDEN, OPT_ATT_READONLY, OPT_ATT_MANDATORY...)
|
||||
* for the given attribute for the current state of the object considered as an INITIAL state
|
||||
@@ -1117,16 +1225,17 @@ abstract class DBObject implements iDisplay
|
||||
if ($oAtt->IsHierarchicalKey())
|
||||
{
|
||||
// This check cannot be deactivated since otherwise the user may break things by a CSV import of a bulk modify
|
||||
if ($toCheck == $this->GetKey())
|
||||
$aValues = $oAtt->GetAllowedValues(array('this' => $this));
|
||||
if (!array_key_exists($toCheck, $aValues))
|
||||
{
|
||||
return "An object can not be its own parent in a hierarchy (".$oAtt->Getlabel()." = $toCheck)";
|
||||
return "Value not allowed [$toCheck]";
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ($oAtt->IsScalar())
|
||||
{
|
||||
$aValues = $oAtt->GetAllowedValues($this->ToArgsForQuery());
|
||||
if (count($aValues) > 0)
|
||||
if (is_array($aValues) && (count($aValues) > 0))
|
||||
{
|
||||
if (!array_key_exists($toCheck, $aValues))
|
||||
{
|
||||
@@ -1167,7 +1276,7 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
$aChanges = $this->ListChanges();
|
||||
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
foreach($aChanges as $sAttCode => $value)
|
||||
{
|
||||
$res = $this->CheckValue($sAttCode);
|
||||
if ($res !== true)
|
||||
@@ -1407,42 +1516,23 @@ abstract class DBObject implements iDisplay
|
||||
return true;
|
||||
}
|
||||
|
||||
// used only by insert
|
||||
protected function OnObjectKeyReady()
|
||||
{
|
||||
// Meant to be overloaded
|
||||
}
|
||||
|
||||
// used both by insert/update
|
||||
private function DBWriteLinks()
|
||||
{
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef)
|
||||
foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if (!$oAttDef->IsLinkSet()) continue;
|
||||
if (!array_key_exists($sAttCode, $this->m_aTouchedAtt)) continue;
|
||||
if (array_key_exists($sAttCode, $this->m_aModifiedAtt) && ($this->m_aModifiedAtt[$sAttCode] == false)) continue;
|
||||
|
||||
// Note: any change to this algorithm must be reproduced into the implementation of AttributeLinkSet::Equals()
|
||||
$sExtKeyToMe = $oAttDef->GetExtKeyToMe();
|
||||
$sAdditionalKey = null;
|
||||
if ($oAttDef->IsIndirect() && !$oAttDef->DuplicatesAllowed())
|
||||
{
|
||||
$sAdditionalKey = $oAttDef->GetExtKeyToRemote();
|
||||
}
|
||||
$oComparator = new DBObjectSetComparator($this->m_aOrigValues[$sAttCode], $this->Get($sAttCode), array($sExtKeyToMe), $sAdditionalKey);
|
||||
$aChanges = $oComparator->GetDifferences();
|
||||
|
||||
foreach($aChanges['added'] as $oLink)
|
||||
{
|
||||
// Make sure that the objects in the set point to "this"
|
||||
$oLink->Set($oAttDef->GetExtKeyToMe(), $this->m_iKey);
|
||||
$id = $oLink->DBWrite();
|
||||
}
|
||||
|
||||
foreach($aChanges['modified'] as $oLink)
|
||||
{
|
||||
// Objects in the set either remain attached or have been detached -> leave the link as is
|
||||
$oLink->DBWrite();
|
||||
}
|
||||
|
||||
foreach($aChanges['removed'] as $oLink)
|
||||
{
|
||||
$oLink->DBDelete();
|
||||
}
|
||||
|
||||
$oLinkSet = $this->m_aCurrValues[$sAttCode];
|
||||
$oLinkSet->DBWrite($this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1644,11 +1734,21 @@ abstract class DBObject implements iDisplay
|
||||
$this->DBInsertSingleTable($sParentClass);
|
||||
}
|
||||
|
||||
$this->DBWriteLinks();
|
||||
$this->OnObjectKeyReady();
|
||||
|
||||
$this->DBWriteLinks();
|
||||
$this->WriteExternalAttributes();
|
||||
|
||||
$this->m_bIsInDB = true;
|
||||
$this->m_bDirty = false;
|
||||
foreach ($this->m_aCurrValues as $sAttCode => $value)
|
||||
{
|
||||
if (is_object($value))
|
||||
{
|
||||
$value = clone $value;
|
||||
}
|
||||
$this->m_aOrigValues[$sAttCode] = $value;
|
||||
}
|
||||
|
||||
$this->AfterInsert();
|
||||
|
||||
@@ -1873,7 +1973,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($oAttDef->IsExternalKey()) $bHasANewExternalKeyValue = true;
|
||||
if ($oAttDef->IsDirectField())
|
||||
if ($oAttDef->IsBasedOnDBColumns())
|
||||
{
|
||||
$aDBChanges[$sAttCode] = $aChanges[$sAttCode];
|
||||
}
|
||||
@@ -1931,6 +2031,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
$oFilter = new DBObjectSearch(get_class($this));
|
||||
$oFilter->AddCondition('id', $this->m_iKey, '=');
|
||||
$oFilter->AllowAllData();
|
||||
|
||||
$sSQL = $oFilter->MakeUpdateQuery($aDBChanges);
|
||||
CMDBSource::Query($sSQL);
|
||||
@@ -2303,8 +2404,7 @@ abstract class DBObject implements iDisplay
|
||||
*/
|
||||
public function Reset($sAttCode)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$this->Set($sAttCode, $oAttDef->GetDefaultValue($this));
|
||||
$this->Set($sAttCode, $this->GetDefaultValue($sAttCode));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2840,7 +2940,7 @@ abstract class DBObject implements iDisplay
|
||||
$oSearch->AllowAllData();
|
||||
}
|
||||
$oSet = new CMDBObjectSet($oSearch);
|
||||
if ($oSet->Count() > 0)
|
||||
if ($oSet->CountExceeds(0))
|
||||
{
|
||||
$aDependentObjects[$sRemoteClass][$sExtKeyAttCode] = array(
|
||||
'attribute' => $oExtKeyAttDef,
|
||||
@@ -3308,8 +3408,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
throw new Exception("Unknown attribute ".get_class($this)."::".$sAttCode);
|
||||
}
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$this->Set($sAttCode, $oAttDef->GetDefaultValue());
|
||||
$this->Set($sAttCode, $this->GetDefaultValue($sAttCode));
|
||||
break;
|
||||
|
||||
case 'nullify':
|
||||
@@ -3393,7 +3492,7 @@ abstract class DBObject implements iDisplay
|
||||
throw new Exception('Missing argument #1: source attribute');
|
||||
}
|
||||
$sSourceKeyAttCode = $aParams[0];
|
||||
if (!MetaModel::IsValidAttCode(get_class($oObjectToRead), $sSourceKeyAttCode))
|
||||
if (($sSourceKeyAttCode != 'id') && !MetaModel::IsValidAttCode(get_class($oObjectToRead), $sSourceKeyAttCode))
|
||||
{
|
||||
throw new Exception("Unknown attribute ".get_class($oObjectToRead)."::".$sSourceKeyAttCode);
|
||||
}
|
||||
@@ -3470,5 +3569,165 @@ abstract class DBObject implements iDisplay
|
||||
throw new Exception("Invalid verb");
|
||||
}
|
||||
}
|
||||
|
||||
public function IsArchived($sKeyAttCode = null)
|
||||
{
|
||||
$bRet = false;
|
||||
$sFlagAttCode = is_null($sKeyAttCode) ? 'archive_flag' : $sKeyAttCode.'_archive_flag';
|
||||
if (MetaModel::IsValidAttCode(get_class($this), $sFlagAttCode) && $this->Get($sFlagAttCode))
|
||||
{
|
||||
$bRet = true;
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
public function IsObsolete($sKeyAttCode = null)
|
||||
{
|
||||
$bRet = false;
|
||||
$sFlagAttCode = is_null($sKeyAttCode) ? 'obsolescence_flag' : $sKeyAttCode.'_obsolescence_flag';
|
||||
if (MetaModel::IsValidAttCode(get_class($this), $sFlagAttCode) && $this->Get($sFlagAttCode))
|
||||
{
|
||||
$bRet = true;
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $bArchive
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function DBWriteArchiveFlag($bArchive)
|
||||
{
|
||||
if (!MetaModel::IsArchivable(get_class($this)))
|
||||
{
|
||||
throw new Exception(get_class($this).' is not an archivable class');
|
||||
}
|
||||
|
||||
$iFlag = $bArchive ? 1 : 0;
|
||||
$sDate = $bArchive ? '"'.date(AttributeDate::GetSQLFormat()).'"' : 'null';
|
||||
|
||||
$sClass = get_class($this);
|
||||
$sArchiveRoot = MetaModel::GetAttributeOrigin($sClass, 'archive_flag');
|
||||
$sRootTable = MetaModel::DBGetTable($sArchiveRoot);
|
||||
$sRootKey = MetaModel::DBGetKey($sArchiveRoot);
|
||||
$aJoins = array("`$sRootTable`");
|
||||
$aUpdates = array();
|
||||
foreach (MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL) as $sParentClass)
|
||||
{
|
||||
if (!MetaModel::IsValidAttCode($sParentClass, 'archive_flag')) continue;
|
||||
|
||||
$sTable = MetaModel::DBGetTable($sParentClass);
|
||||
$aUpdates[] = "`$sTable`.`archive_flag` = $iFlag";
|
||||
if ($sParentClass == $sArchiveRoot)
|
||||
{
|
||||
if (!$bArchive || $this->Get('archive_date') == '')
|
||||
{
|
||||
// Erase or set the date (do not change it)
|
||||
$aUpdates[] = "`$sTable`.`archive_date` = $sDate";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sKey = MetaModel::DBGetKey($sParentClass);
|
||||
$aJoins[] = "`$sTable` ON `$sTable`.`$sKey` = `$sRootTable`.`$sRootKey`";
|
||||
}
|
||||
}
|
||||
$sJoins = implode(' INNER JOIN ', $aJoins);
|
||||
$sValues = implode(', ', $aUpdates);
|
||||
$sUpdateQuery = "UPDATE $sJoins SET $sValues WHERE `$sRootTable`.`$sRootKey` = ".$this->GetKey();
|
||||
CMDBSource::Query($sUpdateQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be called to repair the database (tables consistency)
|
||||
* The archive_date will be preserved
|
||||
* @throws Exception
|
||||
*/
|
||||
public function DBArchive()
|
||||
{
|
||||
$this->DBWriteArchiveFlag(true);
|
||||
$this->m_aCurrValues['archive_flag'] = true;
|
||||
$this->m_aOrigValues['archive_flag'] = true;
|
||||
}
|
||||
|
||||
public function DBUnarchive()
|
||||
{
|
||||
$this->DBWriteArchiveFlag(false);
|
||||
$this->m_aCurrValues['archive_flag'] = false;
|
||||
$this->m_aOrigValues['archive_flag'] = false;
|
||||
$this->m_aCurrValues['archive_date'] = null;
|
||||
$this->m_aOrigValues['archive_date'] = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param string $sClass Needs to be an instanciable class
|
||||
* @returns $oObj
|
||||
**/
|
||||
public static function MakeDefaultInstance($sClass)
|
||||
{
|
||||
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
|
||||
$oObj = MetaModel::NewObject($sClass);
|
||||
if (!empty($sStateAttCode))
|
||||
{
|
||||
$sTargetState = MetaModel::GetDefaultState($sClass);
|
||||
$oObj->Set($sStateAttCode, $sTargetState);
|
||||
}
|
||||
return $oObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete a new object with data from context
|
||||
* @param array $aContextParam Context used for creation form prefilling
|
||||
*
|
||||
*/
|
||||
public function PrefillCreationForm(&$aContextParam)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete an object after a state transition with data from context
|
||||
* @param array $aContextParam Context used for creation form prefilling
|
||||
*
|
||||
*/
|
||||
public function PrefillTransitionForm(&$aContextParam)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete a filter ($aContextParam['filter']) data from context
|
||||
* (Called on source object)
|
||||
* @param array $aContextParam Context used for creation form prefilling
|
||||
*
|
||||
*/
|
||||
public function PrefillSearchForm(&$aContextParam)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefill a creation / stimulus change / search form according to context, current state of an object, stimulus.. $sOperation
|
||||
* @param string $sOperation Operation identifier
|
||||
* @param array $aContextParam Context used for creation form prefilling
|
||||
*
|
||||
*/
|
||||
public function PrefillForm($sOperation, &$aContextParam)
|
||||
{
|
||||
switch($sOperation){
|
||||
case 'creation_from_0':
|
||||
case 'creation_from_extkey':
|
||||
case 'creation_from_editinplace':
|
||||
$this->PrefillCreationForm($aContextParam);
|
||||
break;
|
||||
case 'state_change':
|
||||
$this->PrefillTransitionForm($aContextParam);
|
||||
break;
|
||||
case 'search':
|
||||
$this->PrefillSearchForm($aContextParam);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
63
core/dbobjectiterator.php
Normal file
63
core/dbobjectiterator.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
|
||||
/**
|
||||
* A set of persistent objects, could be heterogeneous as long as the objects in the set have a common ancestor class
|
||||
*
|
||||
* @package iTopORM
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
interface iDBObjectSetIterator extends Countable
|
||||
{
|
||||
/**
|
||||
* The class of the objects of the collection (at least a common ancestor)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetClass();
|
||||
|
||||
/**
|
||||
* The total number of objects in the collection
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function Count();
|
||||
|
||||
/**
|
||||
* Reset the cursor to the first item in the collection. Equivalent to Seek(0)
|
||||
*
|
||||
* @return DBObject The fetched object or null when at the end
|
||||
*/
|
||||
public function Rewind();
|
||||
|
||||
/**
|
||||
* Position the cursor to the given 0-based position
|
||||
*
|
||||
* @param int $iRow
|
||||
*/
|
||||
public function Seek($iPosition);
|
||||
|
||||
/**
|
||||
* Fetch the object at the current position in the collection and move the cursor to the next position.
|
||||
*
|
||||
* @return DBObject The fetched object or null when at the end
|
||||
*/
|
||||
public function Fetch();
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -16,11 +16,12 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
require_once('dbobjectiterator.php');
|
||||
|
||||
/**
|
||||
* Object set management
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -30,31 +31,66 @@
|
||||
*
|
||||
* @package iTopORM
|
||||
*/
|
||||
class DBObjectSet
|
||||
class DBObjectSet implements iDBObjectSetIterator
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $m_aAddedIds; // Ids of objects added (discrete lists)
|
||||
/**
|
||||
* @var hash array of (row => array of (classalias) => object/null) storing the objects added "in memory"
|
||||
*/
|
||||
protected $m_aAddedObjects;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $m_aArgs;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $m_aAttToLoad;
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $m_aOrderBy;
|
||||
/**
|
||||
* @var bool True when the filter has been used OR the set is built step by step (AddObject...)
|
||||
*/
|
||||
public $m_bLoaded;
|
||||
/**
|
||||
* @var int Total number of rows for the query without LIMIT. null if unknown yet
|
||||
*/
|
||||
protected $m_iNumTotalDBRows;
|
||||
/**
|
||||
* @var int Total number of rows LOADED in $this->m_oSQLResult via a SQL query. 0 by default
|
||||
*/
|
||||
protected $m_iNumLoadedDBRows;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $m_iCurrRow;
|
||||
/**
|
||||
* @var DBSearch
|
||||
*/
|
||||
protected $m_oFilter;
|
||||
/**
|
||||
* @var mysqli_result
|
||||
*/
|
||||
protected $m_oSQLResult;
|
||||
protected $m_bSort;
|
||||
|
||||
/**
|
||||
* Create a new set based on a Search definition.
|
||||
*
|
||||
* @param DBSearch $oFilter The search filter defining the objects which are part of the set (multiple columns/objects per row are supported)
|
||||
* @param hash $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
||||
* @param hash $aArgs Values to substitute for the search/query parameters (if any). Format: param_name => value
|
||||
* @param hash $aExtendedDataSpec
|
||||
* @param array $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
||||
* @param array $aArgs Values to substitute for the search/query parameters (if any). Format: param_name => value
|
||||
* @param array $aExtendedDataSpec
|
||||
* @param int $iLimitCount Maximum number of rows to load (i.e. equivalent to MySQL's LIMIT start, count)
|
||||
* @param int $iLimitStart Index of the first row to load (i.e. equivalent to MySQL's LIMIT start, count)
|
||||
* @param bool $bSort if false no order by is done
|
||||
*/
|
||||
public function __construct(DBSearch $oFilter, $aOrderBy = array(), $aArgs = array(), $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0)
|
||||
public function __construct(DBSearch $oFilter, $aOrderBy = array(), $aArgs = array(), $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bSort = true)
|
||||
{
|
||||
$this->m_oFilter = $oFilter->DeepClone();
|
||||
$this->m_aAddedIds = array();
|
||||
@@ -64,11 +100,12 @@ class DBObjectSet
|
||||
$this->m_aExtendedDataSpec = $aExtendedDataSpec;
|
||||
$this->m_iLimitCount = $iLimitCount;
|
||||
$this->m_iLimitStart = $iLimitStart;
|
||||
$this->m_bSort = $bSort;
|
||||
|
||||
$this->m_iNumTotalDBRows = null; // Total number of rows for the query without LIMIT. null if unknown yet
|
||||
$this->m_iNumLoadedDBRows = 0; // Total number of rows LOADED in $this->m_oSQLResult via a SQL query. 0 by default
|
||||
$this->m_bLoaded = false; // true when the filter has been used OR the set is built step by step (AddObject...)
|
||||
$this->m_aAddedObjects = array(); // array of (row => array of (classalias) => object/null) storing the objects added "in memory"
|
||||
$this->m_iNumTotalDBRows = null;
|
||||
$this->m_iNumLoadedDBRows = 0;
|
||||
$this->m_bLoaded = false;
|
||||
$this->m_aAddedObjects = array();
|
||||
$this->m_iCurrRow = 0;
|
||||
$this->m_oSQLResult = null;
|
||||
}
|
||||
@@ -123,10 +160,21 @@ class DBObjectSet
|
||||
$this->m_iCurrRow = 0;
|
||||
$this->m_oSQLResult = null;
|
||||
}
|
||||
|
||||
public function SetShowObsoleteData($bShow)
|
||||
{
|
||||
$this->m_oFilter->SetShowObsoleteData($bShow);
|
||||
}
|
||||
|
||||
public function GetShowObsoleteData()
|
||||
{
|
||||
return $this->m_oFilter->GetShowObsoleteData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the subset of attributes to load (for each class of objects) before performing the SQL query for retrieving the rows from the DB
|
||||
*
|
||||
* @param hash $aAttToLoad Format: alias => array of attribute_codes
|
||||
*
|
||||
* @param array $aAttToLoad Format: alias => array of attribute_codes
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@@ -150,11 +198,25 @@ class DBObjectSet
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad);
|
||||
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad] = $oAttDef;
|
||||
if ($oAttDef->IsExternalKey())
|
||||
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
|
||||
{
|
||||
// Add the external key friendly name anytime
|
||||
$oFriendlyNameAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_friendlyname');
|
||||
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_friendlyname'] = $oFriendlyNameAttDef;
|
||||
|
||||
if (MetaModel::IsArchivable($oAttDef->GetTargetClass(EXTKEY_ABSOLUTE)))
|
||||
{
|
||||
// Add the archive flag if necessary
|
||||
$oArchiveFlagAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_archive_flag');
|
||||
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_archive_flag'] = $oArchiveFlagAttDef;
|
||||
}
|
||||
|
||||
if (MetaModel::IsObsoletable($oAttDef->GetTargetClass(EXTKEY_ABSOLUTE)))
|
||||
{
|
||||
// Add the obsolescence flag if necessary
|
||||
$oObsoleteFlagAttDef = MetaModel::GetAttributeDef($sClass, $sAttToLoad.'_obsolescence_flag');
|
||||
$aAttToLoadWithAttDef[$sClassAlias][$sAttToLoad.'_obsolescence_flag'] = $oObsoleteFlagAttDef;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,6 +224,20 @@ class DBObjectSet
|
||||
$oFriendlyNameAttDef = MetaModel::GetAttributeDef($sClass, 'friendlyname');
|
||||
$aAttToLoadWithAttDef[$sClassAlias]['friendlyname'] = $oFriendlyNameAttDef;
|
||||
|
||||
if (MetaModel::IsArchivable($sClass))
|
||||
{
|
||||
// Add the archive flag if necessary
|
||||
$oArchiveFlagAttDef = MetaModel::GetAttributeDef($sClass, 'archive_flag');
|
||||
$aAttToLoadWithAttDef[$sClassAlias]['archive_flag'] = $oArchiveFlagAttDef;
|
||||
}
|
||||
|
||||
if (MetaModel::IsObsoletable($sClass))
|
||||
{
|
||||
// Add the obsolescence flag if necessary
|
||||
$oObsoleteFlagAttDef = MetaModel::GetAttributeDef($sClass, 'obsolescence_flag');
|
||||
$aAttToLoadWithAttDef[$sClassAlias]['obsolescence_flag'] = $oObsoleteFlagAttDef;
|
||||
}
|
||||
|
||||
// Make sure that the final class is requested anytime, whatever the specification (needed for object construction!)
|
||||
if (!MetaModel::IsStandaloneClass($sClass) && !array_key_exists('finalclass', $aAttToLoadWithAttDef[$sClassAlias]))
|
||||
{
|
||||
@@ -192,7 +268,7 @@ class DBObjectSet
|
||||
*
|
||||
* @param string $sClass The class (or an ancestor) for the objects to be added in this set
|
||||
*
|
||||
* @return DBObject The empty set
|
||||
* @return DBObjectSet The empty set
|
||||
*/
|
||||
static public function FromScratch($sClass)
|
||||
{
|
||||
@@ -264,8 +340,14 @@ class DBObjectSet
|
||||
}
|
||||
|
||||
return self::FromArray($sTargetClass, $aTargets);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: After calling this method, the set cursor will be at the end of the set. You might want to rewind it.
|
||||
*
|
||||
* @param bool $bWithId
|
||||
* @return array
|
||||
*/
|
||||
public function ToArray($bWithId = true)
|
||||
{
|
||||
$aRet = array();
|
||||
@@ -332,8 +414,15 @@ class DBObjectSet
|
||||
$iRow++;
|
||||
}
|
||||
return $aRet;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: After calling this method, the set cursor will be at the end of the set. You might want to rewind it.
|
||||
*
|
||||
* @param string $sAttCode
|
||||
* @param bool $bWithId
|
||||
* @return array
|
||||
*/
|
||||
public function GetColumnAsArray($sAttCode, $bWithId = true)
|
||||
{
|
||||
$aRet = array();
|
||||
@@ -364,6 +453,7 @@ class DBObjectSet
|
||||
{
|
||||
// Make sure that we carry on the parameters of the set with the filter
|
||||
$oFilter = $this->m_oFilter->DeepClone();
|
||||
$oFilter->SetShowObsoleteData(true);
|
||||
// Note: the arguments found within a set can be object (but not in a filter)
|
||||
// That's why PrepareQueryArguments must be invoked there
|
||||
$oFilter->SetInternalParams(array_merge($oFilter->GetInternalParams(), $this->m_aArgs));
|
||||
@@ -445,8 +535,8 @@ class DBObjectSet
|
||||
|
||||
/**
|
||||
* Sets the sort order for loading the rows from the DB. Changing the order by causes a Reload.
|
||||
*
|
||||
* @param hash $aOrderBy Format: field_code => boolean (true = ascending, false = descending)
|
||||
*
|
||||
* @param hash $aOrderBy Format: [alias.]attcode => boolean (true = ascending, false = descending)
|
||||
*/
|
||||
public function SetOrderBy($aOrderBy)
|
||||
{
|
||||
@@ -461,6 +551,34 @@ class DBObjectSet
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sort order for loading the rows from the DB. Changing the order by causes a Reload.
|
||||
*
|
||||
* @param hash $aAliases Format: alias => boolean (true = ascending, false = descending). If omitted, then it defaults to all the selected classes
|
||||
*/
|
||||
public function SetOrderByClasses($aAliases = null)
|
||||
{
|
||||
if ($aAliases === null)
|
||||
{
|
||||
$aAliases = array();
|
||||
foreach ($this->GetSelectedClasses() as $sAlias => $sClass)
|
||||
{
|
||||
$aAliases[$sAlias] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$aAttributes = array();
|
||||
foreach ($aAliases as $sAlias => $bClassDirection)
|
||||
{
|
||||
foreach (MetaModel::GetOrderByDefault($this->m_oFilter->GetClassName($sAlias)) as $sAttCode => $bAttributeDirection)
|
||||
{
|
||||
$bDirection = $bClassDirection ? $bAttributeDirection : !$bAttributeDirection;
|
||||
$aAttributes[$sAlias.'.'.$sAttCode] = $bDirection;
|
||||
}
|
||||
}
|
||||
$this->SetOrderBy($aAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 'count' limit for loading the rows from the DB
|
||||
*
|
||||
@@ -486,10 +604,15 @@ class DBObjectSet
|
||||
*
|
||||
* Limitation: the sort order has no effect on objects added in-memory
|
||||
*
|
||||
* @return hash Format: field_code => boolean (true = ascending, false = descending)
|
||||
* @return array Format: field_code => boolean (true = ascending, false = descending)
|
||||
*/
|
||||
public function GetRealSortOrder()
|
||||
{
|
||||
if (!$this->m_bSort)
|
||||
{
|
||||
// No order by
|
||||
return array();
|
||||
}
|
||||
// Get the class default sort order if not specified with the API
|
||||
//
|
||||
if (empty($this->m_aOrderBy))
|
||||
@@ -511,14 +634,7 @@ class DBObjectSet
|
||||
// Note: it is mandatory to set this value now, to protect against reentrance
|
||||
$this->m_bLoaded = true;
|
||||
|
||||
if ($this->m_iLimitCount > 0)
|
||||
{
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery($this->GetRealSortOrder(), $this->m_aArgs, $this->m_aAttToLoad, $this->m_aExtendedDataSpec, $this->m_iLimitCount, $this->m_iLimitStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery($this->GetRealSortOrder(), $this->m_aArgs, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
|
||||
}
|
||||
$sSQL = $this->_makeSelectQuery($this->m_aAttToLoad);
|
||||
|
||||
if (is_object($this->m_oSQLResult))
|
||||
{
|
||||
@@ -526,23 +642,74 @@ class DBObjectSet
|
||||
$this->m_oSQLResult->free();
|
||||
$this->m_oSQLResult = null;
|
||||
}
|
||||
$this->m_iNumTotalDBRows = null;
|
||||
|
||||
$this->m_oSQLResult = CMDBSource::Query($sSQL);
|
||||
|
||||
try
|
||||
{
|
||||
$this->m_oSQLResult = CMDBSource::Query($sSQL);
|
||||
} catch (MySQLException $e)
|
||||
{
|
||||
// 1116 = ER_TOO_MANY_TABLES
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html#error_er_too_many_tables
|
||||
if ($e->getCode() != 1116)
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// N.689 Workaround for the 61 max joins in MySQL : full lazy load !
|
||||
$aAttToLoad = array();
|
||||
foreach($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass)
|
||||
{
|
||||
$aAttToLoad[$sClassAlias] = array();
|
||||
$bIsAbstractClass = MetaModel::IsAbstract($sClass);
|
||||
$bIsClassWithChildren = MetaModel::HasChildrenClasses($sClass);
|
||||
if ($bIsAbstractClass || $bIsClassWithChildren)
|
||||
{
|
||||
// we need finalClass field at least to be able to instantiate the real corresponding object !
|
||||
$aAttToLoad[$sClassAlias]['finalclass'] = MetaModel::GetAttributeDef($sClass, 'finalclass');
|
||||
}
|
||||
}
|
||||
$sSQL = $this->_makeSelectQuery($aAttToLoad);
|
||||
$this->m_oSQLResult = CMDBSource::Query($sSQL); // may fail again
|
||||
}
|
||||
|
||||
if ($this->m_oSQLResult === false) return;
|
||||
|
||||
if (($this->m_iLimitCount == 0) && ($this->m_iLimitStart == 0))
|
||||
|
||||
if ((($this->m_iLimitCount == 0) || ($this->m_iLimitCount > $this->m_oSQLResult->num_rows)) && ($this->m_iLimitStart == 0))
|
||||
{
|
||||
$this->m_iNumTotalDBRows = $this->m_oSQLResult->num_rows;
|
||||
}
|
||||
|
||||
$this->m_iNumLoadedDBRows = $this->m_oSQLResult->num_rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* The total number of rows in this set. Independently of the SetLimit used for loading the set and taking into account the rows added in-memory.
|
||||
*
|
||||
* May actually perform the SQL query SELECT COUNT... if the set was not previously loaded, or loaded with a SetLimit
|
||||
*
|
||||
* @param string[] $aAttToLoad
|
||||
*
|
||||
* @return string SQL query
|
||||
*/
|
||||
private function _makeSelectQuery($aAttToLoad)
|
||||
{
|
||||
if ($this->m_iLimitCount > 0)
|
||||
{
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery($this->GetRealSortOrder(), $this->m_aArgs, $aAttToLoad,
|
||||
$this->m_aExtendedDataSpec, $this->m_iLimitCount, $this->m_iLimitStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery($this->GetRealSortOrder(), $this->m_aArgs, $aAttToLoad,
|
||||
$this->m_aExtendedDataSpec);
|
||||
}
|
||||
|
||||
return $sSQL;
|
||||
}
|
||||
|
||||
/**
|
||||
* The total number of rows in this set. Independently of the SetLimit used for loading the set and taking into
|
||||
* account the rows added in-memory.
|
||||
*
|
||||
* May actually perform the SQL query SELECT COUNT... if the set was not previously loaded, or loaded with a
|
||||
* SetLimit
|
||||
*
|
||||
* @return int The total number of rows for this set.
|
||||
*/
|
||||
public function Count()
|
||||
@@ -552,14 +719,83 @@ class DBObjectSet
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
if (!$resQuery) return 0;
|
||||
|
||||
|
||||
$aRow = CMDBSource::FetchArray($resQuery);
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
$this->m_iNumTotalDBRows = $aRow['COUNT'];
|
||||
$this->m_iNumTotalDBRows = intval($aRow['COUNT']);
|
||||
}
|
||||
|
||||
return $this->m_iNumTotalDBRows + count($this->m_aAddedObjects); // Does it fix Trac #887 ??
|
||||
}
|
||||
|
||||
|
||||
/** Check if the count exceeds a given limit
|
||||
* @param $iLimit
|
||||
*
|
||||
* @return bool
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public function CountExceeds($iLimit)
|
||||
{
|
||||
if (is_null($this->m_iNumTotalDBRows))
|
||||
{
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
if ($resQuery)
|
||||
{
|
||||
$aRow = CMDBSource::FetchArray($resQuery);
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
$iCount = intval($aRow['COUNT']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCount = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCount = $this->m_iNumTotalDBRows;
|
||||
}
|
||||
|
||||
return ($iCount > $iLimit);
|
||||
}
|
||||
|
||||
/** Count only up to the given limit
|
||||
* @param $iLimit
|
||||
*
|
||||
* @return int
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*/
|
||||
public function CountWithLimit($iLimit)
|
||||
{
|
||||
if (is_null($this->m_iNumTotalDBRows))
|
||||
{
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
if ($resQuery)
|
||||
{
|
||||
$aRow = CMDBSource::FetchArray($resQuery);
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
$iCount = intval($aRow['COUNT']);
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCount = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCount = $this->m_iNumTotalDBRows;
|
||||
}
|
||||
|
||||
return $iCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of rows available in memory (loaded from DB + added in memory)
|
||||
*
|
||||
@@ -849,22 +1085,6 @@ class DBObjectSet
|
||||
return $oComparator->SetsAreEquivalent();
|
||||
}
|
||||
|
||||
protected function GetObjectAt($iIndex)
|
||||
{
|
||||
if (!$this->m_bLoaded) $this->Load();
|
||||
|
||||
// Save the current position for iteration
|
||||
$iCurrPos = $this->m_iCurrRow;
|
||||
|
||||
$this->Seek($iIndex);
|
||||
$oObject = $this->Fetch();
|
||||
|
||||
// Restore the current position for iteration
|
||||
$this->Seek($this->m_iCurrRow);
|
||||
|
||||
return $oObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a new set (in memory) made of objects of the given set which are NOT present in the current set
|
||||
*
|
||||
@@ -1126,19 +1346,27 @@ class DBObjectSetComparator
|
||||
protected $aIDs1;
|
||||
protected $aIDs2;
|
||||
protected $aExcludedColumns;
|
||||
|
||||
/**
|
||||
* @var iDBObjectSetIterator
|
||||
*/
|
||||
protected $oSet1;
|
||||
/**
|
||||
* @var iDBObjectSetIterator
|
||||
*/
|
||||
protected $oSet2;
|
||||
|
||||
protected $sAdditionalKeyColumn;
|
||||
protected $aAdditionalKeys;
|
||||
|
||||
/**
|
||||
* Initializes the comparator
|
||||
* @param DBObjectSet $oSet1 The first set of objects to compare, or null
|
||||
* @param DBObjectSet $oSet2 The second set of objects to compare, or null
|
||||
* @param iDBObjectSetIterator $oSet1 The first set of objects to compare, or null
|
||||
* @param iDBObjectSetIterator $oSet2 The second set of objects to compare, or null
|
||||
* @param array $aExcludedColumns The list of columns (= attribute codes) to exclude from the comparison
|
||||
* @param string $sAdditionalKeyColumn The attribute code of an additional column to be considered as a key indentifying the object (useful for n:n links)
|
||||
*/
|
||||
public function __construct($oSet1, $oSet2, $aExcludedColumns = array(), $sAdditionalKeyColumn = null)
|
||||
public function __construct(iDBObjectSetIterator $oSet1, iDBObjectSetIterator $oSet2, $aExcludedColumns = array(), $sAdditionalKeyColumn = null)
|
||||
{
|
||||
$this->aFingerprints1 = null;
|
||||
$this->aFingerprints2 = null;
|
||||
@@ -1164,9 +1392,6 @@ class DBObjectSetComparator
|
||||
|
||||
if ($this->oSet1 !== null)
|
||||
{
|
||||
$aAliases = $this->oSet1->GetSelectedClasses();
|
||||
if (count($aAliases) > 1) throw new Exception('DBObjectSetComparator does not support Sets with more than one column. $oSet1: ('.print_r($aAliases, true).')');
|
||||
|
||||
$this->oSet1->Rewind();
|
||||
while($oObj = $this->oSet1->Fetch())
|
||||
{
|
||||
@@ -1182,9 +1407,6 @@ class DBObjectSetComparator
|
||||
|
||||
if ($this->oSet2 !== null)
|
||||
{
|
||||
$aAliases = $this->oSet2->GetSelectedClasses();
|
||||
if (count($aAliases) > 1) throw new Exception('DBObjectSetComparator does not support Sets with more than one column. $oSet2: ('.print_r($aAliases, true).')');
|
||||
|
||||
$this->oSet2->Rewind();
|
||||
while($oObj = $this->oSet2->Fetch())
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2015-2016 Combodo SARL
|
||||
// Copyright (C) 2015-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* A union of DBObjectSearches
|
||||
*
|
||||
* @copyright Copyright (C) 2015-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2015-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -55,6 +55,40 @@ class DBUnionSearch extends DBSearch
|
||||
$this->ComputeSelectedClasses();
|
||||
}
|
||||
|
||||
public function AllowAllData()
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->AllowAllData();
|
||||
}
|
||||
}
|
||||
public function IsAllDataAllowed()
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
if ($oSearch->IsAllDataAllowed() === false) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function SetArchiveMode($bEnable)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->SetArchiveMode($bEnable);
|
||||
}
|
||||
parent::SetArchiveMode($bEnable);
|
||||
}
|
||||
|
||||
public function SetShowObsoleteData($bShow)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->SetShowObsoleteData($bShow);
|
||||
}
|
||||
parent::SetShowObsoleteData($bShow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the lowest common ancestor for each of the selected class
|
||||
*/
|
||||
@@ -109,7 +143,7 @@ class DBUnionSearch extends DBSearch
|
||||
|
||||
/**
|
||||
* Limited to the selected classes
|
||||
*/
|
||||
*/
|
||||
public function GetClassName($sAlias)
|
||||
{
|
||||
if (array_key_exists($sAlias, $this->aSelectedClasses))
|
||||
@@ -187,6 +221,23 @@ class DBUnionSearch extends DBSearch
|
||||
$this->ComputeSelectedClasses();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change any alias of the query tree
|
||||
*
|
||||
* @param $sOldName
|
||||
* @param $sNewName
|
||||
* @return bool True if the alias has been found and changed
|
||||
*/
|
||||
public function RenameAlias($sOldName, $sNewName)
|
||||
{
|
||||
$bRet = false;
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$bRet = $oSearch->RenameAlias($sOldName, $sNewName) || $bRet;
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
public function IsAny()
|
||||
{
|
||||
$bIsAny = true;
|
||||
@@ -282,19 +333,33 @@ class DBUnionSearch extends DBSearch
|
||||
}
|
||||
}
|
||||
|
||||
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS)
|
||||
/**
|
||||
* @param DBObjectSearch $oFilter
|
||||
* @param $sExtKeyAttCode
|
||||
* @param int $iOperatorCode
|
||||
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
|
||||
* @throws CoreException
|
||||
* @throws CoreWarning
|
||||
*/
|
||||
public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode);
|
||||
$oSearch->AddCondition_PointingTo($oFilter, $sExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
|
||||
}
|
||||
}
|
||||
|
||||
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS)
|
||||
/**
|
||||
* @param DBObjectSearch $oFilter
|
||||
* @param $sForeignExtKeyAttCode
|
||||
* @param int $iOperatorCode
|
||||
* @param null $aRealiasingMap array of <old-alias> => <new-alias>, for each alias that has changed
|
||||
*/
|
||||
public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, $iOperatorCode = TREE_OPERATOR_EQUALS, &$aRealiasingMap = null)
|
||||
{
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$oSearch->AddCondition_ReferencedBy($oFilter, $sForeignExtKeyAttCode, $iOperatorCode);
|
||||
$oSearch->AddCondition_ReferencedBy($oFilter, $sForeignExtKeyAttCode, $iOperatorCode, $aRealiasingMap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,12 +422,12 @@ class DBUnionSearch extends DBSearch
|
||||
/**
|
||||
* Overloads for query building
|
||||
*/
|
||||
public function ToOQL($bDevelopParams = false, $aContextParams = null)
|
||||
public function ToOQL($bDevelopParams = false, $aContextParams = null, $bWithAllowAllFlag = false)
|
||||
{
|
||||
$aSubQueries = array();
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$aSubQueries[] = $oSearch->ToOQL($bDevelopParams, $aContextParams);
|
||||
$aSubQueries[] = $oSearch->ToOQL($bDevelopParams, $aContextParams, $bWithAllowAllFlag);
|
||||
}
|
||||
$sRet = implode(' UNION ', $aSubQueries);
|
||||
return $sRet;
|
||||
@@ -409,15 +474,17 @@ class DBUnionSearch extends DBSearch
|
||||
throw new Exception('MakeUpdateQuery is not implemented for the unions!');
|
||||
}
|
||||
|
||||
protected function MakeSQLQuery($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr = null, $aSelectedClasses = null)
|
||||
public function GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr = null, $aSelectedClasses = null, $aSelectExpr = null)
|
||||
{
|
||||
if (count($this->aSearches) == 1)
|
||||
{
|
||||
return $this->aSearches[0]->MakeSQLQuery($aAttToLoad, $bGetCount, $aModifierProperties, $aGroupByExpr);
|
||||
return $this->aSearches[0]->GetSQLQueryStructure($aAttToLoad, $bGetCount, $aGroupByExpr, $aSelectExpr);
|
||||
}
|
||||
|
||||
$aSQLQueries = array();
|
||||
$aAliases = array_keys($this->aSelectedClasses);
|
||||
$aQueryAttToLoad = null;
|
||||
$aUnionQuerySelectExpr = array();
|
||||
foreach ($this->aSearches as $iSearch => $oSearch)
|
||||
{
|
||||
$aSearchAliases = array_keys($oSearch->GetSelectedClasses());
|
||||
@@ -431,19 +498,30 @@ class DBUnionSearch extends DBSearch
|
||||
$aSearchSelectedClasses[$sSearchAlias] = $this->aSelectedClasses[$sAlias];
|
||||
}
|
||||
|
||||
if (is_null($aAttToLoad))
|
||||
if ($bGetCount)
|
||||
{
|
||||
$aQueryAttToLoad = null;
|
||||
// Select only ids for the count to allow optimization of joins
|
||||
foreach($aSearchAliases as $sSearchAlias)
|
||||
{
|
||||
$aQueryAttToLoad[$sSearchAlias] = array();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// (Eventually) Transform the aliases
|
||||
$aQueryAttToLoad = array();
|
||||
foreach ($aAttToLoad as $sAlias => $aAttributes)
|
||||
if (is_null($aAttToLoad))
|
||||
{
|
||||
$iColumn = array_search($sAlias, $aAliases);
|
||||
$sQueryAlias = ($iColumn === false) ? $sAlias : $aSearchAliases[$iColumn];
|
||||
$aQueryAttToLoad[$sQueryAlias] = $aAttributes;
|
||||
$aQueryAttToLoad = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
// (Eventually) Transform the aliases
|
||||
$aQueryAttToLoad = array();
|
||||
foreach($aAttToLoad as $sAlias => $aAttributes)
|
||||
{
|
||||
$iColumn = array_search($sAlias, $aAliases);
|
||||
$sQueryAlias = ($iColumn === false) ? $sAlias : $aSearchAliases[$iColumn];
|
||||
$aQueryAttToLoad[$sQueryAlias] = $aAttributes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,11 +546,53 @@ class DBUnionSearch extends DBSearch
|
||||
$aQueryGroupByExpr[$sExpressionAlias] = $oExpression->Translate($aTranslationData, false, false);
|
||||
}
|
||||
}
|
||||
$oSubQuery = $oSearch->MakeSQLQuery($aQueryAttToLoad, false, $aModifierProperties, $aQueryGroupByExpr, $aSearchSelectedClasses);
|
||||
|
||||
if (is_null($aSelectExpr))
|
||||
{
|
||||
$aQuerySelectExpr = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aQuerySelectExpr = array();
|
||||
$aTranslationData = array();
|
||||
$aQueryColumns = array_keys($oSearch->GetSelectedClasses());
|
||||
foreach($aAliases as $iColumn => $sAlias)
|
||||
{
|
||||
$sQueryAlias = $aQueryColumns[$iColumn];
|
||||
$aTranslationData[$sAlias]['*'] = $sQueryAlias;
|
||||
}
|
||||
foreach($aSelectExpr as $sExpressionAlias => $oExpression)
|
||||
{
|
||||
$oExpression->Browse(function ($oNode) use (&$aQuerySelectExpr, &$aTranslationData)
|
||||
{
|
||||
if ($oNode instanceof FieldExpression)
|
||||
{
|
||||
$sAlias = $oNode->GetParent()."__".$oNode->GetName();
|
||||
if (!key_exists($sAlias, $aQuerySelectExpr))
|
||||
{
|
||||
$aQuerySelectExpr[$sAlias] = $oNode->Translate($aTranslationData, false, false);
|
||||
}
|
||||
$aTranslationData[$oNode->GetParent()][$oNode->GetName()] = new FieldExpression($sAlias);
|
||||
}
|
||||
});
|
||||
// Only done for the first select as aliases are named after the first query
|
||||
if (!array_key_exists($sExpressionAlias, $aUnionQuerySelectExpr))
|
||||
{
|
||||
$aUnionQuerySelectExpr[$sExpressionAlias] = $oExpression->Translate($aTranslationData, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
$oSubQuery = $oSearch->GetSQLQueryStructure($aQueryAttToLoad, false, $aQueryGroupByExpr, $aSearchSelectedClasses, $aQuerySelectExpr);
|
||||
if (count($aSearchAliases) > 1)
|
||||
{
|
||||
// Necessary to make sure that selected columns will match throughout all the queries
|
||||
// (default order of selected fields depending on the order of JOINS)
|
||||
$oSubQuery->SortSelectedFields();
|
||||
}
|
||||
$aSQLQueries[] = $oSubQuery;
|
||||
}
|
||||
|
||||
$oSQLQuery = new SQLUnionQuery($aSQLQueries, $aGroupByExpr);
|
||||
$oSQLQuery = new SQLUnionQuery($aSQLQueries, $aGroupByExpr, $aUnionQuerySelectExpr);
|
||||
//MyHelpers::var_dump_html($oSQLQuery, true);
|
||||
//MyHelpers::var_dump_html($oSQLQuery->RenderSelect(), true);
|
||||
if (self::$m_bDebugQuery) $oSQLQuery->DisplayHtml();
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
<?php
|
||||
// Copyright (C) 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/>
|
||||
/**
|
||||
* Copyright (c) 2010-2018 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Design document and associated nodes
|
||||
@@ -57,6 +60,9 @@ class DesignDocument extends DOMDocument
|
||||
|
||||
/**
|
||||
* Overload of the standard API
|
||||
*
|
||||
* @param $filename
|
||||
* @param int $options
|
||||
*/
|
||||
public function load($filename, $options = 0)
|
||||
{
|
||||
@@ -65,6 +71,11 @@ class DesignDocument extends DOMDocument
|
||||
|
||||
/**
|
||||
* Overload of the standard API
|
||||
*
|
||||
* @param $filename
|
||||
* @param int $options
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function save($filename, $options = 0)
|
||||
{
|
||||
@@ -84,18 +95,18 @@ class DesignDocument extends DOMDocument
|
||||
{
|
||||
return $sXml;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "<pre>\n";
|
||||
echo htmlentities($sXml);
|
||||
echo "</pre>\n";
|
||||
}
|
||||
|
||||
echo "<pre>\n";
|
||||
echo htmlentities($sXml);
|
||||
echo "</pre>\n";
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote and escape strings for use within an XPath expression
|
||||
* Usage: DesignDocument::GetNodes('class[@id='.DesignDocument::XPathQuote($sId).']');
|
||||
* @param $sValue The value to be quoted
|
||||
* @param string $sValue The value to be quoted
|
||||
* @return string to be used within an XPath
|
||||
*/
|
||||
public static function XPathQuote($sValue)
|
||||
@@ -115,7 +126,7 @@ class DesignDocument extends DOMDocument
|
||||
/**
|
||||
* Extracts some nodes from the DOM
|
||||
* @param string $sXPath A XPath expression
|
||||
* @param DesignNode|null $oContextNode The node to start the search from
|
||||
* @param DesignElement $oContextNode The node to start the search from
|
||||
* @return \DOMNodeList
|
||||
*/
|
||||
public function GetNodes($sXPath, $oContextNode = null)
|
||||
@@ -134,7 +145,7 @@ class DesignDocument extends DOMDocument
|
||||
|
||||
/**
|
||||
* An alternative to getNodePath, that gives the id of nodes instead of the position within the children
|
||||
* @param $oNode The node to describe
|
||||
* @param DesignElement $oNode The node to describe
|
||||
* @return string
|
||||
*/
|
||||
public static function GetItopNodePath($oNode)
|
||||
@@ -166,8 +177,11 @@ class DesignElement extends \DOMElement
|
||||
|
||||
/**
|
||||
* Create an HTML representation of the DOM, for debugging purposes
|
||||
*
|
||||
* @param bool|false $bReturnRes Echoes or returns the HTML representation
|
||||
*
|
||||
* @return mixed void or the HTML representation of the DOM
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function Dump($bReturnRes = false)
|
||||
{
|
||||
@@ -180,19 +194,16 @@ class DesignElement extends \DOMElement
|
||||
{
|
||||
return $sXml;
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "<pre>\n";
|
||||
echo htmlentities($sXml);
|
||||
echo "</pre>\n";
|
||||
}
|
||||
echo "<pre>\n";
|
||||
echo htmlentities($sXml);
|
||||
echo "</pre>\n";
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node directly under the given node
|
||||
* @param $sTagName
|
||||
* @param bool|true $bMustExist
|
||||
* @return null
|
||||
* @return \MFElement
|
||||
* @throws DOMFormatException
|
||||
*/
|
||||
public function GetUniqueElement($sTagName, $bMustExist = true)
|
||||
@@ -216,7 +227,7 @@ class DesignElement extends \DOMElement
|
||||
/**
|
||||
* Returns the node directly under the current node, or null if missing
|
||||
* @param $sTagName
|
||||
* @return null
|
||||
* @return \MFElement
|
||||
* @throws DOMFormatException
|
||||
*/
|
||||
public function GetOptionalElement($sTagName)
|
||||
@@ -252,9 +263,12 @@ class DesignElement extends \DOMElement
|
||||
|
||||
/**
|
||||
* Get the TEXT value from a child node
|
||||
*
|
||||
* @param string $sTagName
|
||||
* @param string|null $sDefault
|
||||
*
|
||||
* @return string
|
||||
* @throws \DOMFormatException
|
||||
*/
|
||||
public function GetChildText($sTagName, $sDefault = null)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* Class Dict
|
||||
* Management of localizable strings
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2018 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -65,6 +65,11 @@ class Dict
|
||||
protected static $m_aData = array();
|
||||
protected static $m_sApplicationPrefix = null;
|
||||
|
||||
/**
|
||||
* @param $sLanguageCode
|
||||
*
|
||||
* @throws \DictExceptionUnknownLanguage
|
||||
*/
|
||||
public static function SetDefaultLanguage($sLanguageCode)
|
||||
{
|
||||
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
|
||||
@@ -74,6 +79,11 @@ class Dict
|
||||
self::$m_sDefaultLanguage = $sLanguageCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sLanguageCode
|
||||
*
|
||||
* @throws \DictExceptionUnknownLanguage
|
||||
*/
|
||||
public static function SetUserLanguage($sLanguageCode)
|
||||
{
|
||||
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
|
||||
@@ -108,11 +118,12 @@ class Dict
|
||||
|
||||
/**
|
||||
* Returns a localised string from the dictonary
|
||||
*
|
||||
* @param string $sStringCode The code identifying the dictionary entry
|
||||
* @param string $sDefault Default value if there is no match in the dictionary
|
||||
* @param bool $bUserLanguageOnly True to allow the use of the default language as a fallback, false otherwise
|
||||
* @throws DictExceptionMissingString
|
||||
* @return unknown|Ambigous <>|string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function S($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
|
||||
{
|
||||
@@ -122,7 +133,7 @@ class Dict
|
||||
|
||||
if (!array_key_exists(self::GetUserLanguage(), self::$m_aData))
|
||||
{
|
||||
// It may happen, when something happens before the dictionnaries get loaded
|
||||
// It may happen, when something happens before the dictionaries get loaded
|
||||
return $sStringCode;
|
||||
}
|
||||
$aCurrentDictionary = self::$m_aData[self::GetUserLanguage()];
|
||||
@@ -153,25 +164,12 @@ class Dict
|
||||
}
|
||||
// Could not find the string...
|
||||
//
|
||||
switch (self::$m_iErrorMode)
|
||||
if (is_null($sDefault))
|
||||
{
|
||||
case DICT_ERR_STRING:
|
||||
if (is_null($sDefault))
|
||||
{
|
||||
return $sStringCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
return $sDefault;
|
||||
}
|
||||
break;
|
||||
|
||||
case DICT_ERR_EXCEPTION:
|
||||
default:
|
||||
throw new DictExceptionMissingString(self::$m_sCurrentLanguage, $sStringCode);
|
||||
break;
|
||||
return $sStringCode;
|
||||
}
|
||||
return 'bug!';
|
||||
|
||||
return $sDefault;
|
||||
}
|
||||
|
||||
|
||||
@@ -283,6 +281,9 @@ class Dict
|
||||
|
||||
/**
|
||||
* Clone a string in every language (if it exists in that language)
|
||||
*
|
||||
* @param $sSourceCode
|
||||
* @param $sDestCode
|
||||
*/
|
||||
public static function CloneString($sSourceCode, $sDestCode)
|
||||
{
|
||||
@@ -355,5 +356,38 @@ class Dict
|
||||
// No need to actually load the strings since it's only used to know the list of languages
|
||||
// at setup time !!
|
||||
}
|
||||
|
||||
/**
|
||||
* Export all the dictionary entries - of the given language - whose code matches the given prefix
|
||||
* missing entries in the current language will be replaced by entries in the default language
|
||||
* @param string $sStartingWith
|
||||
* @return string[]
|
||||
*/
|
||||
public static function ExportEntries($sStartingWith)
|
||||
{
|
||||
self::InitLangIfNeeded(self::GetUserLanguage());
|
||||
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
|
||||
$aEntries = array();
|
||||
$iLength = strlen($sStartingWith);
|
||||
|
||||
// First prefill the array with entries from the default language
|
||||
foreach(self::$m_aData[self::$m_sDefaultLanguage] as $sCode => $sEntry)
|
||||
{
|
||||
if (substr($sCode, 0, $iLength) == $sStartingWith)
|
||||
{
|
||||
$aEntries[$sCode] = $sEntry;
|
||||
}
|
||||
}
|
||||
|
||||
// Now put (overwrite) the entries for the user language
|
||||
foreach(self::$m_aData[self::GetUserLanguage()] as $sCode => $sEntry)
|
||||
{
|
||||
if (substr($sCode, 0, $iLength) == $sStartingWith)
|
||||
{
|
||||
$aEntries[$sCode] = $sEntry;
|
||||
}
|
||||
}
|
||||
return $aEntries;
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1438,22 +1438,22 @@ class DisplayableGraph extends SimpleGraph
|
||||
}
|
||||
$aExcludedByClass[get_class($oObj)][] = $oObj->GetKey();
|
||||
}
|
||||
$sSftShort = Dict::S('UI:ElementsDisplayed');
|
||||
$sSearchToggle = Dict::S('UI:Search:Toggle');
|
||||
$oP->add("<div class=\"not-printable\">\n");
|
||||
$oP->add("<div id=\"ds_flash\" class=\"SearchDrawer\" style=\"display:none;\">\n");
|
||||
if (!$oP->IsPrintableVersion())
|
||||
{
|
||||
$oP->add_ready_script(
|
||||
$oP->add(
|
||||
<<<EOF
|
||||
$( "#tabbedContent_0" ).tabs({ heightStyle: "fill" });
|
||||
<div id="ds_flash" class="search_box">
|
||||
<form id="dh_flash" class="search_form_handler closed">
|
||||
<h2 class="sf_title"><span class="sft_long">$sSftShort</span><span class="sft_short">$sSftShort</span><span class="sft_toggler fa fa-caret-down pull-right" title="$sSearchToggle"></span></h2>
|
||||
<div id="dh_flash_criterion_outer" class="sf_criterion_area"><div class="sf_criterion_row">
|
||||
EOF
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$("#dh_flash").click( function() {
|
||||
$("#ds_flash").slideToggle('normal', function() { $("#ds_flash").parent().resize(); $("#dh_flash").trigger('toggle_complete'); } );
|
||||
$("#dh_flash").toggleClass('open');
|
||||
$("#dh_flash > .sf_title").click( function() {
|
||||
$("#dh_flash").toggleClass('closed');
|
||||
});
|
||||
$('#ReloadMovieBtn').button().button('disable');
|
||||
EOF
|
||||
@@ -1476,9 +1476,8 @@ EOF
|
||||
$idx++;
|
||||
}
|
||||
$oP->add("<p style=\"text-align:right\"><button type=\"button\" id=\"ReloadMovieBtn\" onClick=\"DoReload()\">".Dict::S('UI:Button:Refresh')."</button></p>");
|
||||
$oP->add("</div></div></form>");
|
||||
$oP->add("</div>\n");
|
||||
$oP->add("<div class=\"HRDrawer\"></div>\n");
|
||||
$oP->add("<div id=\"dh_flash\" class=\"DrawerHandle\">".Dict::S('UI:ElementsDisplayed')."</div>\n");
|
||||
$oP->add("</div>\n"); // class="not-printable"
|
||||
|
||||
$aAdditionalContexts = array();
|
||||
|
||||
@@ -57,6 +57,7 @@ class EMail
|
||||
{
|
||||
$this->m_aData = array();
|
||||
$this->m_oMessage = Swift_Message::newInstance();
|
||||
$this->SetRecipientFrom(MetaModel::GetConfig()->Get('email_default_sender_address'), MetaModel::GetConfig()->Get('email_default_sender_label'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -195,20 +196,29 @@ class EMail
|
||||
|
||||
$aFailedRecipients = array();
|
||||
$this->m_oMessage->setMaxLineLength(0);
|
||||
IssueLog::Info(__METHOD__.' '.$this->m_oMessage->getMaxLineLength());
|
||||
IssueLog::Info(__METHOD__.' '.$this->m_oMessage->toString());
|
||||
$iSent = $oMailer->send($this->m_oMessage, $aFailedRecipients);
|
||||
if ($iSent === 0)
|
||||
$oKPI = new ExecutionKPI();
|
||||
try
|
||||
{
|
||||
// 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;
|
||||
$iSent = $oMailer->send($this->m_oMessage, $aFailedRecipients);
|
||||
if ($iSent === 0)
|
||||
{
|
||||
// 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.');
|
||||
$oKPI->ComputeStats('Email Sent', 'Error received');
|
||||
return EMAIL_SEND_ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aIssues = array();
|
||||
$oKPI->ComputeStats('Email Sent', 'Succeded');
|
||||
return EMAIL_SEND_OK;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception $e)
|
||||
{
|
||||
$aIssues = array();
|
||||
return EMAIL_SEND_OK;
|
||||
$oKPI->ComputeStats('Email Sent', 'Error received');
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,6 +261,11 @@ IssueLog::Info(__METHOD__.' '.$this->m_oMessage->toString());
|
||||
|
||||
public function Send(&$aIssues, $bForceSynchronous = false, $oLog = null)
|
||||
{
|
||||
//select a default sender if none is provided.
|
||||
if(empty($this->m_aData['from']['address']) && !empty($this->m_aData['to'])){
|
||||
$this->SetRecipientFrom($this->m_aData['to']);
|
||||
}
|
||||
|
||||
if ($bForceSynchronous)
|
||||
{
|
||||
return $this->SendSynchronous($aIssues, $oLog);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2013 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -264,8 +264,8 @@ class EventIssue extends Event
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not a string
|
||||
$aPost[$sKey] = (string) $sValue;
|
||||
// Not a string (avoid warnings in case the value cannot be easily casted into a string)
|
||||
$aPost[$sKey] = @(string) $sValue;
|
||||
}
|
||||
}
|
||||
$this->Set('arguments_post', $aPost);
|
||||
@@ -408,4 +408,30 @@ class EventLoginUsage extends Event
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
class EventOnObject extends Event
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "core/cmdb,view_in_gui",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_event_onobject",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"display_template" => "",
|
||||
"order_by_default" => array('date' => false)
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeString("obj_class", array("allowed_values"=>null, "sql"=>"obj_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeInteger("obj_key", array("allowed_values"=>null, "sql"=>"obj_key", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'obj_class', 'obj_key', 'message')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'obj_class', 'obj_key', 'message')); // Attributes to be displayed for a list
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,10 +188,10 @@ EOF
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
||||
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
|
||||
}
|
||||
else if ($value instanceOf ormCustomFieldsValue)
|
||||
else if ($value instanceOf ormDocument)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
|
||||
$sRet = $oAttDef->GetAsCSV($value, "\n", '', $oObj);
|
||||
$sRet = $oAttDef->GetAsCSV($value, '', '', $oObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
110
core/expressioncache.class.inc.php
Normal file
110
core/expressioncache.class.inc.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
// Copyright (c) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
//
|
||||
|
||||
class ExpressionCache
|
||||
{
|
||||
static private $aCache = array();
|
||||
|
||||
static public function GetCachedExpression($sClass, $sAttCode)
|
||||
{
|
||||
// read current cache
|
||||
@include_once (static::GetCacheFileName());
|
||||
|
||||
$oExpr = null;
|
||||
$sKey = static::GetKey($sClass, $sAttCode);
|
||||
if (array_key_exists($sKey, static::$aCache))
|
||||
{
|
||||
$oExpr = static::$aCache[$sKey];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (class_exists('ExpressionCacheData'))
|
||||
{
|
||||
if (array_key_exists($sKey, ExpressionCacheData::$aCache))
|
||||
{
|
||||
$sVal = ExpressionCacheData::$aCache[$sKey];
|
||||
$oExpr = unserialize($sVal);
|
||||
static::$aCache[$sKey] = $oExpr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $oExpr;
|
||||
}
|
||||
|
||||
|
||||
static public function Warmup()
|
||||
{
|
||||
$sFilePath = static::GetCacheFileName();
|
||||
|
||||
if (!is_file($sFilePath))
|
||||
{
|
||||
$content = <<<EOF
|
||||
<?php
|
||||
// Copyright (c) 2010-2017 Combodo SARL
|
||||
// Generated Expression Cache file
|
||||
|
||||
class ExpressionCacheData
|
||||
{
|
||||
static \$aCache = array(
|
||||
EOF;
|
||||
|
||||
foreach(MetaModel::GetClasses() as $sClass)
|
||||
{
|
||||
$content .= static::GetSerializedExpression($sClass, 'friendlyname');
|
||||
if (MetaModel::IsObsoletable($sClass))
|
||||
{
|
||||
$content .= static::GetSerializedExpression($sClass, 'obsolescence_flag');
|
||||
}
|
||||
}
|
||||
|
||||
$content .= <<<EOF
|
||||
);
|
||||
}
|
||||
EOF;
|
||||
|
||||
file_put_contents($sFilePath, $content);
|
||||
}
|
||||
}
|
||||
|
||||
static private function GetSerializedExpression($sClass, $sAttCode)
|
||||
{
|
||||
$sKey = static::GetKey($sClass, $sAttCode);
|
||||
$oExpr = DBObjectSearch::GetPolymorphicExpression($sClass, $sAttCode);
|
||||
return "'".$sKey."' => '".serialize($oExpr)."',\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sClass
|
||||
* @param $sAttCode
|
||||
* @return string
|
||||
*/
|
||||
static private function GetKey($sClass, $sAttCode)
|
||||
{
|
||||
return $sClass.'::'.$sAttCode;
|
||||
}
|
||||
|
||||
public static function GetCacheFileName()
|
||||
{
|
||||
return utils::GetCachePath().'expressioncache.php';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,20 @@
|
||||
<?php
|
||||
// Copyright (C) 2016-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
/**
|
||||
* Base class for all possible implementations of HTML Sanitization
|
||||
*/
|
||||
@@ -135,11 +151,17 @@ class HTMLPurifierSanitizer extends HTMLSanitizer
|
||||
class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
{
|
||||
protected $oDoc;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @see https://www.itophub.io/wiki/page?id=2_5_0%3Aadmin%3Arich_text_limitations
|
||||
*/
|
||||
protected static $aTagsWhiteList = array(
|
||||
'html' => array(),
|
||||
'body' => array(),
|
||||
'a' => array('href', 'name', 'style'),
|
||||
'a' => array('href', 'name', 'style', 'target', 'title'),
|
||||
'p' => array('style'),
|
||||
'blockquote' => array('style'),
|
||||
'br' => array(),
|
||||
'span' => array('style'),
|
||||
'div' => array('style'),
|
||||
@@ -148,7 +170,7 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
'u' => array(),
|
||||
'em' => array(),
|
||||
'strong' => array(),
|
||||
'img' => array('src','style'),
|
||||
'img' => array('src', 'style', 'alt', 'title'),
|
||||
'ul' => array('style'),
|
||||
'ol' => array('style'),
|
||||
'li' => array('style'),
|
||||
@@ -159,19 +181,18 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
'nav' => array('style'),
|
||||
'section' => array('style'),
|
||||
'code' => array('style'),
|
||||
'table' => array('style', 'width'),
|
||||
'table' => array('style', 'width', 'summary', 'align', 'border', 'cellpadding', 'cellspacing'),
|
||||
'thead' => array('style'),
|
||||
'tbody' => array('style'),
|
||||
'tr' => array('style'),
|
||||
'td' => array('style', 'colspan'),
|
||||
'th' => array('style'),
|
||||
'tr' => array('style', 'colspan', 'rowspan'),
|
||||
'td' => array('style', 'colspan', 'rowspan'),
|
||||
'th' => array('style', 'colspan', 'rowspan'),
|
||||
'fieldset' => array('style'),
|
||||
'legend' => array('style'),
|
||||
'font' => array('face', 'color', 'style', 'size'),
|
||||
'big' => array(),
|
||||
'small' => array(),
|
||||
'tt' => array(),
|
||||
'code' => array(),
|
||||
'kbd' => array(),
|
||||
'samp' => array(),
|
||||
'var' => array(),
|
||||
@@ -182,22 +203,64 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
'q' => array(),
|
||||
'hr' => array('style'),
|
||||
'pre' => array(),
|
||||
'center' => array(),
|
||||
);
|
||||
|
||||
|
||||
protected static $aAttrsWhiteList = array(
|
||||
'href' => '/^(http:|https:)/i',
|
||||
'src' => '/^(http:|https:|data:)/i',
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @see https://www.itophub.io/wiki/page?id=2_5_0%3Aadmin%3Arich_text_limitations
|
||||
*/
|
||||
protected static $aStylesWhiteList = array(
|
||||
'background-color', 'color', 'float', 'font', 'font-style', 'font-size', 'font-family', 'padding', 'margin', 'border', 'cellpadding', 'cellspacing', 'bordercolor', 'border-collapse', 'width', 'height',
|
||||
'background-color',
|
||||
'border',
|
||||
'border-collapse',
|
||||
'bordercolor',
|
||||
'cellpadding',
|
||||
'cellspacing',
|
||||
'color',
|
||||
'float',
|
||||
'font',
|
||||
'font-family',
|
||||
'font-size',
|
||||
'font-style',
|
||||
'height',
|
||||
'margin',
|
||||
'padding',
|
||||
'text-align',
|
||||
'width',
|
||||
);
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// Building href validation pattern from url and email validation patterns as the patterns are not used the same way in HTML content than in standard attributes value.
|
||||
// eg. "foo@bar.com" vs "mailto:foo@bar.com?subject=Title&body=Hello%20world"
|
||||
if (!array_key_exists('href', self::$aAttrsWhiteList))
|
||||
{
|
||||
// Regular urls
|
||||
$sUrlPattern = utils::GetConfig()->Get('url_validation_pattern');
|
||||
// Mailto urls
|
||||
$sMailtoPattern = '(mailto:(' . utils::GetConfig()->Get('email_validation_pattern') . ')(?:\?(?:subject|body)=([a-zA-Z0-9+\$_.-]*)(?:&(?:subject|body)=([a-zA-Z0-9+\$_.-]*))?)?)';
|
||||
|
||||
$sPattern = $sUrlPattern . '|' . $sMailtoPattern;
|
||||
$sPattern = '/'.str_replace('/', '\/', $sPattern).'/i';
|
||||
self::$aAttrsWhiteList['href'] = $sPattern;
|
||||
}
|
||||
}
|
||||
|
||||
public function DoSanitize($sHTML)
|
||||
{
|
||||
$this->oDoc = new DOMDocument();
|
||||
$this->oDoc->preserveWhitespace = true;
|
||||
|
||||
// MS outlook implements empty lines by the mean of <p><o:p></o:p></p>
|
||||
// We have to transform that into <p><br></p> (which is how Thunderbird implements empty lines)
|
||||
// Unfortunately, DOMDocument::loadHTML does not take the tag namespaces into account (once loaded there is no way to know if the tag did have a namespace)
|
||||
// therefore we have to do the transformation upfront
|
||||
$sHTML = preg_replace('@<o:p>\s*</o:p>@', '<br>', $sHTML);
|
||||
|
||||
@$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified
|
||||
|
||||
$this->CleanNode($this->oDoc);
|
||||
|
||||
@@ -464,7 +464,7 @@ EOF
|
||||
oEditor.on( 'instanceReady', function() {
|
||||
if(!CKEDITOR.env.iOS && $('#'+oEditor.id+'_toolbox .editor_magnifier').length == 0)
|
||||
{
|
||||
$('#'+oEditor.id+'_toolbox').append('<span class="editor_magnifier" title="$sToggleFullScreen" style="display:block;width:12px;height:11px;border:1px #A6A6A6 solid;cursor:pointer; background-image:url($sAppRootUrl/images/full-screen.png)"> </span>');
|
||||
$('#'+oEditor.id+'_toolbox').append('<span class="editor_magnifier" title="$sToggleFullScreen" style="display:block;width:12px;height:11px;border:1px #A6A6A6 solid;cursor:pointer; background-image:url(\\'$sAppRootUrl/images/full-screen.png\\')"> </span>');
|
||||
$('#'+oEditor.id+'_toolbox .editor_magnifier').on('click', function() {
|
||||
oEditor.execCommand('maximize');
|
||||
if ($(this).closest('.cke_maximized').length != 0)
|
||||
@@ -473,12 +473,15 @@ EOF
|
||||
}
|
||||
});
|
||||
}
|
||||
oEditor.widgets.registered.uploadimage.onUploaded = function( upload ) {
|
||||
var oData = JSON.parse(upload.xhr.responseText);
|
||||
this.replaceWith( '<img src="' + upload.url + '" ' +
|
||||
'width="' + oData.width + '" ' +
|
||||
'height="' + oData.height + '">' );
|
||||
}
|
||||
if (oEditor.widgets.registered.uploadimage)
|
||||
{
|
||||
oEditor.widgets.registered.uploadimage.onUploaded = function( upload ) {
|
||||
var oData = JSON.parse(upload.xhr.responseText);
|
||||
this.replaceWith( '<img src="' + upload.url + '" ' +
|
||||
'width="' + oData.width + '" ' +
|
||||
'height="' + oData.height + '">' );
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
EOF
|
||||
@@ -494,10 +497,10 @@ EOF
|
||||
*/
|
||||
class InlineImageGC implements iBackgroundProcess
|
||||
{
|
||||
public function GetPeriodicity()
|
||||
{
|
||||
return 3600; // Runs every 3600 seconds
|
||||
}
|
||||
public function GetPeriodicity()
|
||||
{
|
||||
return 3600; // Runs every hour
|
||||
}
|
||||
|
||||
public function Process($iTimeLimit)
|
||||
{
|
||||
|
||||
93
core/introspection.class.inc.php
Normal file
93
core/introspection.class.inc.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
// Copyright (C) 2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
/**
|
||||
* Usage:
|
||||
* require_once(...'introspection.class.inc.php');
|
||||
*/
|
||||
|
||||
require_once('attributedef.class.inc.php');
|
||||
|
||||
class Introspection
|
||||
{
|
||||
protected $aAttributeHierarchy = array(); // class => child classes
|
||||
protected $aAttributes = array();
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->InitAttributes();
|
||||
}
|
||||
|
||||
protected function InitAttributes()
|
||||
{
|
||||
foreach(get_declared_classes() as $sPHPClass)
|
||||
{
|
||||
$oRefClass = new ReflectionClass($sPHPClass);
|
||||
if ($sPHPClass == 'AttributeDefinition' || $oRefClass->isSubclassOf('AttributeDefinition'))
|
||||
{
|
||||
if ($oParentClass = $oRefClass->getParentClass())
|
||||
{
|
||||
$sParentClass = $oParentClass->getName();
|
||||
if (!array_key_exists($sParentClass, $this->aAttributeHierarchy))
|
||||
{
|
||||
$this->aAttributeHierarchy[$sParentClass] = array();
|
||||
}
|
||||
$this->aAttributeHierarchy[$sParentClass][] = $sPHPClass;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sParentClass = null;
|
||||
}
|
||||
$this->aAttributes[$sPHPClass] = array(
|
||||
'parent' => $sParentClass,
|
||||
'LoadInObject' => $sPHPClass::LoadInObject(),
|
||||
'LoadFromDB' => $sPHPClass::LoadFromDB(),
|
||||
'IsBasedOnDBColumns' => $sPHPClass::IsBasedOnDBColumns(),
|
||||
'IsBasedOnOQLExpression' => $sPHPClass::IsBasedOnOQLExpression(),
|
||||
'IsExternalField' => $sPHPClass::IsExternalField(),
|
||||
'IsScalar' => $sPHPClass::IsScalar(),
|
||||
'IsLinkset' => $sPHPClass::IsLinkset(),
|
||||
'IsHierarchicalKey' => $sPHPClass::IsHierarchicalKey(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
public function GetAttributes()
|
||||
{
|
||||
return $this->aAttributes;
|
||||
}
|
||||
public function GetAttributeHierarchy()
|
||||
{
|
||||
return $this->aAttributeHierarchy;
|
||||
}
|
||||
public function EnumAttributeCharacteristics()
|
||||
{
|
||||
return array(
|
||||
'LoadInObject' => 'Is the value stored in the object itself?',
|
||||
'LoadFromDB' => 'Is the value read from the DB?',
|
||||
'IsBasedOnDBColumns' => 'Is this a value stored within one or several columns?',
|
||||
'IsBasedOnOQLExpression' => 'Is this a value computed after other attributes, by the mean of an OQL expression?',
|
||||
'IsExternalField' => 'Is this a value stored on a related object (external key)?',
|
||||
'IsScalar' => 'Is this a value that makes sense in a SQL/OQL expression?',
|
||||
'IsLinkset' => 'Is this a collection (1-N or N-N)?',
|
||||
'IsHierarchicalKey' => 'Is this attribute an external key pointing to the host class?',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2012 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -19,7 +19,7 @@
|
||||
/**
|
||||
* File logging
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2012 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -69,81 +69,54 @@ class FileLog
|
||||
}
|
||||
}
|
||||
|
||||
class SetupLog
|
||||
abstract class LogAPI
|
||||
{
|
||||
protected static $m_oFileLog;
|
||||
|
||||
public static function Enable($sTargetFile)
|
||||
{
|
||||
self::$m_oFileLog = new FileLog($sTargetFile);
|
||||
static::$m_oFileLog = new FileLog($sTargetFile);
|
||||
}
|
||||
|
||||
public static function Error($sText)
|
||||
{
|
||||
self::$m_oFileLog->Error($sText);
|
||||
if (static::$m_oFileLog)
|
||||
{
|
||||
static::$m_oFileLog->Error($sText);
|
||||
}
|
||||
}
|
||||
public static function Warning($sText)
|
||||
{
|
||||
self::$m_oFileLog->Warning($sText);
|
||||
if (static::$m_oFileLog)
|
||||
{
|
||||
static::$m_oFileLog->Warning($sText);
|
||||
}
|
||||
}
|
||||
public static function Info($sText)
|
||||
{
|
||||
self::$m_oFileLog->Info($sText);
|
||||
if (static::$m_oFileLog)
|
||||
{
|
||||
static::$m_oFileLog->Info($sText);
|
||||
}
|
||||
}
|
||||
public static function Ok($sText)
|
||||
{
|
||||
self::$m_oFileLog->Ok($sText);
|
||||
if (static::$m_oFileLog)
|
||||
{
|
||||
static::$m_oFileLog->Ok($sText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class IssueLog
|
||||
class SetupLog extends LogAPI
|
||||
{
|
||||
protected static $m_oFileLog;
|
||||
|
||||
public static function Enable($sTargetFile)
|
||||
{
|
||||
self::$m_oFileLog = new FileLog($sTargetFile);
|
||||
}
|
||||
public static function Error($sText)
|
||||
{
|
||||
self::$m_oFileLog->Error($sText);
|
||||
}
|
||||
public static function Warning($sText)
|
||||
{
|
||||
self::$m_oFileLog->Warning($sText);
|
||||
}
|
||||
public static function Info($sText)
|
||||
{
|
||||
self::$m_oFileLog->Info($sText);
|
||||
}
|
||||
public static function Ok($sText)
|
||||
{
|
||||
self::$m_oFileLog->Ok($sText);
|
||||
}
|
||||
protected static $m_oFileLog = null;
|
||||
}
|
||||
|
||||
class ToolsLog
|
||||
class IssueLog extends LogAPI
|
||||
{
|
||||
protected static $m_oFileLog;
|
||||
|
||||
public static function Enable($sTargetFile)
|
||||
{
|
||||
self::$m_oFileLog = new FileLog($sTargetFile);
|
||||
}
|
||||
public static function Error($sText)
|
||||
{
|
||||
self::$m_oFileLog->Error($sText);
|
||||
}
|
||||
public static function Warning($sText)
|
||||
{
|
||||
self::$m_oFileLog->Warning($sText);
|
||||
}
|
||||
public static function Info($sText)
|
||||
{
|
||||
self::$m_oFileLog->Info($sText);
|
||||
}
|
||||
public static function Ok($sText)
|
||||
{
|
||||
self::$m_oFileLog->Ok($sText);
|
||||
}
|
||||
protected static $m_oFileLog = null;
|
||||
}
|
||||
|
||||
class ToolsLog extends LogAPI
|
||||
{
|
||||
protected static $m_oFileLog = null;
|
||||
}
|
||||
?>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -89,8 +89,7 @@ class ModuleDesign extends \Combodo\iTop\DesignDocument
|
||||
}
|
||||
else
|
||||
{
|
||||
var_dump($aFiles);
|
||||
$aAvailable = array();
|
||||
$aAvailable = array();
|
||||
foreach ($aFiles as $sFile)
|
||||
{
|
||||
$aAvailable[] = "'".basename($sFile, '.xml')."'";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2013-2016 Combodo SARL
|
||||
// Copyright (C) 2013-2018 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -24,32 +24,58 @@
|
||||
* Relies on MySQL locks because the API sem_get is not always present in the
|
||||
* installed PHP.
|
||||
*
|
||||
* @copyright Copyright (C) 2013-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2013-2018 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
class iTopMutex
|
||||
{
|
||||
protected $sName;
|
||||
protected $hDBLink;
|
||||
/** @var bool */
|
||||
protected $bLocked; // Whether or not this instance of the Mutex is locked
|
||||
|
||||
/** @var \mysqli */
|
||||
protected $hDBLink;
|
||||
protected $sDBHost;
|
||||
protected $sDBUser;
|
||||
protected $sDBPwd;
|
||||
protected $sDBName;
|
||||
protected $sDBSubname;
|
||||
protected $bDBTlsEnabled;
|
||||
protected $sDBTlsCA;
|
||||
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)
|
||||
public function __construct(
|
||||
$sName, $sDBHost = null, $sDBUser = null, $sDBPwd = null, $bDBTlsEnabled = false, $sDBTlsCA = 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)
|
||||
$oConfig = MetaModel::GetConfig();
|
||||
if ($oConfig === null)
|
||||
{
|
||||
$oConfig = utils::GetConfig(); // Will return an empty config when called during the setup
|
||||
}
|
||||
$this->sDBHost = is_null($sDBHost) ? $oConfig->Get('db_host') : $sDBHost;
|
||||
$this->sDBUser = is_null($sDBUser) ? $oConfig->Get('db_user') : $sDBUser;
|
||||
$this->sDBPwd = is_null($sDBPwd) ? $oConfig->Get('db_pwd') : $sDBPwd;
|
||||
$this->sDBName = $oConfig->Get('db_name');
|
||||
$sDBSubname = $oConfig->Get('db_subname');
|
||||
|
||||
$this->bDBTlsEnabled = is_null($bDBTlsEnabled) ? $oConfig->Get('db_tls.enabled') : $bDBTlsEnabled;
|
||||
$this->sDBTlsCA = is_null($sDBTlsCA) ? $oConfig->Get('db_tls.ca') : $sDBTlsCA;
|
||||
|
||||
$this->sName = $sName;
|
||||
if (substr($sName, -strlen($this->sDBName.$sDBSubname)) != $this->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->sName .= $this->sDBName.$sDBSubname;
|
||||
}
|
||||
|
||||
|
||||
// Limit the length of the name for MySQL > 5.7.5
|
||||
$this->sName = 'itop.'.md5($this->sName);
|
||||
|
||||
$this->bLocked = false; // Not yet locked
|
||||
|
||||
if (!array_key_exists($this->sName, self::$aAcquiredLocks))
|
||||
@@ -57,12 +83,9 @@ class iTopMutex
|
||||
self::$aAcquiredLocks[$this->sName] = 0;
|
||||
}
|
||||
|
||||
// It is a MUST to create a dedicated session each time a lock is required, because
|
||||
// It is MANDATORY 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);
|
||||
$this->InitMySQLSession();
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
@@ -75,7 +98,9 @@ class iTopMutex
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire the mutex
|
||||
* Acquire the mutex. Uses a MySQL lock. <b>Warn</b> : can have an abnormal behavior on MySQL clusters (see R-016204)
|
||||
*
|
||||
* @see https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_get-lock
|
||||
*/
|
||||
public function Lock()
|
||||
{
|
||||
@@ -193,26 +218,26 @@ class iTopMutex
|
||||
self::$aAcquiredLocks[$this->sName]--;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function InitMySQLSession($sHost, $sUser, $sPwd)
|
||||
/**
|
||||
* Initialize database connection. Mandatory attributes must be already set !
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \MySQLException
|
||||
*/
|
||||
public function InitMySQLSession()
|
||||
{
|
||||
$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);
|
||||
}
|
||||
$sServer = $this->sDBHost;
|
||||
$sUser = $this->sDBUser;
|
||||
$sPwd = $this->sDBPwd;
|
||||
$sSource = $this->sDBName;
|
||||
$bTlsEnabled = $this->bDBTlsEnabled;
|
||||
$sTlsCA = $this->sDBTlsCA;
|
||||
|
||||
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
|
||||
|
||||
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().')');
|
||||
throw new Exception("Could not connect to the DB server (host=$sServer, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
* echo "Ok, '$sOQL' is a valid query";
|
||||
* }
|
||||
*/
|
||||
if (!class_exists('CoreException', false))
|
||||
{
|
||||
class CoreException extends Exception
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
//if (!class_exists('CoreException', false))
|
||||
//{
|
||||
// class CoreException extends Exception
|
||||
// {
|
||||
//
|
||||
// }
|
||||
//}
|
||||
|
||||
require_once(__DIR__.'/expression.class.inc.php');
|
||||
require_once(__DIR__.'/oqlquery.class.inc.php');
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2015 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -20,7 +20,7 @@
|
||||
/**
|
||||
* Wrapper to execute the parser, lexical analyzer and normalization of an OQL query
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2015 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@@ -83,6 +83,10 @@ class OqlInterpreter
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OqlQuery
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function ParseQuery()
|
||||
{
|
||||
$oRes = $this->Parse();
|
||||
@@ -93,6 +97,9 @@ class OqlInterpreter
|
||||
return $oRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Expression
|
||||
*/
|
||||
public function ParseExpression()
|
||||
{
|
||||
$oRes = $this->Parse();
|
||||
@@ -103,5 +110,3 @@ class OqlInterpreter
|
||||
return $oRes;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -283,10 +283,10 @@ abstract class OqlQuery
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
*
|
||||
* @param ModelReflection $oModelReflection MetaModel to consider
|
||||
* @param string $sSourceQuery
|
||||
*/
|
||||
abstract public function Check(ModelReflection $oModelReflection, $sSourceQuery);
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2016 Combodo SARL
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -23,7 +23,7 @@ define('CASELOG_SEPARATOR', "\n".'========== %1$s : %2$s (%3$d) ============'."\
|
||||
/**
|
||||
* Class to store a "case log" in a structured way, keeping track of its successive entries
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2016 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2017 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
class ormCaseLog {
|
||||
@@ -191,8 +191,15 @@ class ormCaseLog {
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->m_sLog;
|
||||
if($this->IsEmpty()) return '';
|
||||
|
||||
return $this->m_sLog;
|
||||
}
|
||||
|
||||
public function IsEmpty()
|
||||
{
|
||||
return ($this->m_sLog === null);
|
||||
}
|
||||
|
||||
public function ClearModifiedFlag()
|
||||
{
|
||||
@@ -388,8 +395,9 @@ class ormCaseLog {
|
||||
if (($bEditMode) && (count($aIndex) > 0) && $this->m_bModified)
|
||||
{
|
||||
// Don't display the first element, that is still considered as editable
|
||||
$iPos = $aIndex[0]['separator_length'] + $aIndex[0]['text_length'];
|
||||
array_shift($aIndex);
|
||||
$aLastEntry = end($aIndex);
|
||||
$iPos = $aLastEntry['separator_length'] + $aLastEntry['text_length'];
|
||||
array_pop($aIndex);
|
||||
}
|
||||
for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
|
||||
{
|
||||
@@ -519,57 +527,36 @@ class ormCaseLog {
|
||||
if ($this->m_bModified)
|
||||
{
|
||||
$aLatestEntry = end($this->m_aIndex);
|
||||
if ($aLatestEntry['user_name'] != $sOnBehalfOf)
|
||||
if ($aLatestEntry['user_name'] == $sOnBehalfOf)
|
||||
{
|
||||
$bMergeEntries = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$bMergeEntries = true;
|
||||
// Append the new text to the previous one
|
||||
$sPreviousText = substr($this->m_sLog, $aLatestEntry['separator_length'], $aLatestEntry['text_length']);
|
||||
$sText = $sPreviousText."\n".$sText;
|
||||
|
||||
// Cleanup the previous entry
|
||||
array_pop($this->m_aIndex);
|
||||
$this->m_sLog = substr($this->m_sLog, $aLatestEntry['separator_length'] + $aLatestEntry['text_length']);
|
||||
}
|
||||
}
|
||||
|
||||
if ($bMergeEntries)
|
||||
{
|
||||
$aLatestEntry = end($this->m_aIndex);
|
||||
$this->m_sLog = substr($this->m_sLog, $aLatestEntry['separator_length']);
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
|
||||
$iSepLength = strlen($sSeparator);
|
||||
$iTextlength = strlen($sText."\n");
|
||||
$this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
|
||||
$this->m_aIndex[] = array(
|
||||
'user_name' => $sOnBehalfOf,
|
||||
'user_id' => $iUserId,
|
||||
'date' => time(),
|
||||
'text_length' => $aLatestEntry['text_length'] + $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
'format' => 'html',
|
||||
);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
|
||||
$iSepLength = strlen($sSeparator);
|
||||
$iTextlength = strlen($sText);
|
||||
$this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
|
||||
$this->m_aIndex[] = array(
|
||||
'user_name' => $sOnBehalfOf,
|
||||
'user_id' => $iUserId,
|
||||
'date' => time(),
|
||||
'text_length' => $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
'format' => 'html',
|
||||
);
|
||||
}
|
||||
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
|
||||
$iSepLength = strlen($sSeparator);
|
||||
$iTextlength = strlen($sText);
|
||||
$this->m_sLog = $sSeparator.$sText.$this->m_sLog; // Latest entry printed first
|
||||
$this->m_aIndex[] = array(
|
||||
'user_name' => $sOnBehalfOf,
|
||||
'user_id' => $iUserId,
|
||||
'date' => time(),
|
||||
'text_length' => $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
'format' => 'html',
|
||||
);
|
||||
$this->m_bModified = true;
|
||||
}
|
||||
|
||||
|
||||
public function AddLogEntryFromJSON($oJson, $bCheckUserId = true)
|
||||
{
|
||||
$sText = HTMLSanitizer::Sanitize(isset($oJson->message) ? $oJson->message : '');
|
||||
|
||||
if (isset($oJson->user_id))
|
||||
{
|
||||
if (!UserRights::IsAdministrator())
|
||||
@@ -616,10 +603,16 @@ class ormCaseLog {
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: what is the default format ? text ?
|
||||
// The default is HTML
|
||||
$sFormat = 'html';
|
||||
}
|
||||
|
||||
|
||||
$sText = isset($oJson->message) ? $oJson->message : '';
|
||||
if ($sFormat == 'html')
|
||||
{
|
||||
$sText = HTMLSanitizer::Sanitize($sText);
|
||||
}
|
||||
|
||||
$sDate = date(AttributeDateTime::GetInternalFormat(), $iDate);
|
||||
|
||||
$sSeparator = sprintf(CASELOG_SEPARATOR, $sDate, $sOnBehalfOf, $iUserId);
|
||||
@@ -639,12 +632,12 @@ class ormCaseLog {
|
||||
}
|
||||
|
||||
|
||||
public function GetModifiedEntry()
|
||||
public function GetModifiedEntry($sFormat = 'text')
|
||||
{
|
||||
$sModifiedEntry = '';
|
||||
if ($this->m_bModified)
|
||||
{
|
||||
$sModifiedEntry = $this->GetLatestEntry();
|
||||
$sModifiedEntry = $this->GetLatestEntry($sFormat);
|
||||
}
|
||||
return $sModifiedEntry;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ class ormCustomFieldsValue
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this->oHostObject), $this->sAttCode);
|
||||
$oHandler = $oAttDef->GetHandler($this->GetValues());
|
||||
return 'template...verb='.$sVerb.' sur "'.json_encode($this->aCurrentValues).'"';
|
||||
return $oHandler->GetForTemplate($this->aCurrentValues, $sVerb, $bLocalize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,6 +51,8 @@ class ormDocument
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
if($this->IsEmpty()) return '';
|
||||
|
||||
return MyHelpers::beautifulstr($this->m_data, 100, true);
|
||||
}
|
||||
|
||||
@@ -115,21 +117,29 @@ class ormDocument
|
||||
*/
|
||||
public function GetDownloadLink($sClass, $Id, $sAttCode)
|
||||
{
|
||||
return "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode\">".htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8')."</a>\n";
|
||||
return "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/ajax.document.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode\">".htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8')."</a>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an URL to display a document like an image
|
||||
* @return string
|
||||
*/
|
||||
public function GetDisplayURL($sClass, $Id, $sAttCode)
|
||||
{
|
||||
return utils::GetAbsoluteUrlAppRoot() . "pages/ajax.render.php?operation=display_document&class=$sClass&id=$Id&field=$sAttCode";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an URL to download a document like an image (uses HTTP caching)
|
||||
* @return string
|
||||
*/
|
||||
*/
|
||||
public function GetDownloadURL($sClass, $Id, $sAttCode)
|
||||
{
|
||||
// Compute a signature to reset the cache anytime the data changes (this is acceptable if used only with icon files)
|
||||
$sSignature = md5($this->GetData());
|
||||
return utils::GetAbsoluteUrlAppRoot()."pages/ajax.document.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode&s=$sSignature&cache=86400";
|
||||
return utils::GetAbsoluteUrlAppRoot() . "pages/ajax.document.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode&s=$sSignature&cache=86400";
|
||||
}
|
||||
|
||||
|
||||
public function IsPreviewAvailable()
|
||||
{
|
||||
$bRet = false;
|
||||
@@ -176,7 +186,7 @@ class ormDocument
|
||||
{
|
||||
$oPage->TrashUnexpectedOutput();
|
||||
$oPage->SetContentType($oDocument->GetMimeType());
|
||||
//$oPage->SetContentDisposition($sContentDisposition,$oDocument->GetFileName());
|
||||
$oPage->SetContentDisposition($sContentDisposition,$oDocument->GetFileName());
|
||||
$oPage->add($oDocument->GetData());
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user