mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-16 00:44:10 +01:00
Compare commits
555 Commits
3.0.0-beta
...
3.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bac2918ceb | ||
|
|
604837c770 | ||
|
|
f32c283c9c | ||
|
|
27d06a712b | ||
|
|
b15050487c | ||
|
|
46f232d561 | ||
|
|
63976df2e1 | ||
|
|
3514e21772 | ||
|
|
3463b1715a | ||
|
|
0287500feb | ||
|
|
e4cbaf7096 | ||
|
|
73c6b4be20 | ||
|
|
fde2103659 | ||
|
|
24500216f6 | ||
|
|
2039b872d3 | ||
|
|
b368c593e5 | ||
|
|
c9c731a2a5 | ||
|
|
8204723b5b | ||
|
|
f93218a80f | ||
|
|
4f5a9c898c | ||
|
|
7ce5712b71 | ||
|
|
90b41e0b81 | ||
|
|
9d2c89f118 | ||
|
|
61137a6f65 | ||
|
|
a26c8fbd48 | ||
|
|
0080a2e733 | ||
|
|
992ee3a74b | ||
|
|
e45013891c | ||
|
|
ea043960ff | ||
|
|
d0f83046cd | ||
|
|
7f4fddb378 | ||
|
|
9cd076131f | ||
|
|
a71cb97db3 | ||
|
|
0c80a4e430 | ||
|
|
779211e638 | ||
|
|
4c99f497cc | ||
|
|
d1e2be97d2 | ||
|
|
93c6cfffda | ||
|
|
b50ba0ad49 | ||
|
|
0205cdf713 | ||
|
|
39fc59a8b2 | ||
|
|
107c9adf60 | ||
|
|
143c30b099 | ||
|
|
d29880b1b8 | ||
|
|
3edfc2016d | ||
|
|
5f80be75ed | ||
|
|
0d4796ae2b | ||
|
|
2d156bd77b | ||
|
|
5908ec5197 | ||
|
|
d122dbfdd6 | ||
|
|
46d58e6512 | ||
|
|
6cf781da33 | ||
|
|
0f2cbaf186 | ||
|
|
7ddb47dc83 | ||
|
|
304e379c01 | ||
|
|
93a138606f | ||
|
|
70074ee1cb | ||
|
|
d28ccb264f | ||
|
|
9f95d45f51 | ||
|
|
8ab38854a8 | ||
|
|
bceb570d84 | ||
|
|
138fa569f2 | ||
|
|
1f65ab5a92 | ||
|
|
7cfac7300b | ||
|
|
ede720c9b9 | ||
|
|
684efee5d2 | ||
|
|
e347989dcb | ||
|
|
e1051e7a47 | ||
|
|
7e8eb26866 | ||
|
|
6775a3e928 | ||
|
|
faa38155e5 | ||
|
|
558bbc3357 | ||
|
|
cd7f9e478f | ||
|
|
e3586cff65 | ||
|
|
106127e6b7 | ||
|
|
2299983db3 | ||
|
|
2fab7de34b | ||
|
|
c0ab79971c | ||
|
|
03ed9c9deb | ||
|
|
7152277760 | ||
|
|
14d05da9f6 | ||
|
|
69a0bd0c34 | ||
|
|
c09cee994a | ||
|
|
50ee0b3eed | ||
|
|
84169a864c | ||
|
|
9f27cf2b84 | ||
|
|
f78986009f | ||
|
|
4fdbec72d4 | ||
|
|
94d31827e7 | ||
|
|
45a8eb93e7 | ||
|
|
6ac3539373 | ||
|
|
a5d8425fe7 | ||
|
|
3608c6b8ab | ||
|
|
2b1e583dc7 | ||
|
|
3081aa473a | ||
|
|
809ea2eb49 | ||
|
|
2f98a3e7ac | ||
|
|
c737fc46ff | ||
|
|
619c0d34e8 | ||
|
|
bdc7ed6f8e | ||
|
|
d9819d9c2a | ||
|
|
f24f8a2f34 | ||
|
|
3890b1a020 | ||
|
|
2e08cff9ee | ||
|
|
968a0e5f3a | ||
|
|
4694e8152e | ||
|
|
4b731dd336 | ||
|
|
5e0c7c211b | ||
|
|
815870fe6b | ||
|
|
33ceab86fb | ||
|
|
fe1bf3bfc1 | ||
|
|
3727223db3 | ||
|
|
5b96d9f778 | ||
|
|
162b15236d | ||
|
|
83e98ef2b8 | ||
|
|
d783954d13 | ||
|
|
7207dc657c | ||
|
|
f1cce8e430 | ||
|
|
3da49e6603 | ||
|
|
5048421bfa | ||
|
|
b9c5f2c523 | ||
|
|
ec75195a2f | ||
|
|
e971d628dd | ||
|
|
bbd50ba73b | ||
|
|
ba71c1bcd5 | ||
|
|
16be8fd3d3 | ||
|
|
cacae0a2b3 | ||
|
|
b1ed15f31c | ||
|
|
788caf9c50 | ||
|
|
1728dcc40c | ||
|
|
35165568af | ||
|
|
cb5554ddb1 | ||
|
|
6a5840ed82 | ||
|
|
4a67819f87 | ||
|
|
81c39c35cd | ||
|
|
4caf52f1ae | ||
|
|
0c5b845c8a | ||
|
|
cdfdb1f2ca | ||
|
|
f29a8792af | ||
|
|
b494ff2ce6 | ||
|
|
a65c55fc48 | ||
|
|
01b94f42fe | ||
|
|
df1e19dc43 | ||
|
|
cbef9bb267 | ||
|
|
9ad341f73a | ||
|
|
03e9bcd47a | ||
|
|
55effea0a3 | ||
|
|
dfaa973359 | ||
|
|
2e45b20fc4 | ||
|
|
e89090f0ec | ||
|
|
c9d02826a0 | ||
|
|
47db04bcb7 | ||
|
|
a49c451ae4 | ||
|
|
25c3704990 | ||
|
|
3000659e86 | ||
|
|
ce36c00b83 | ||
|
|
2a3e6384d9 | ||
|
|
dd7e73e413 | ||
|
|
1709082e39 | ||
|
|
41f6e85673 | ||
|
|
3ef3166bd5 | ||
|
|
0d26211dbe | ||
|
|
3b99006159 | ||
|
|
299ad7e753 | ||
|
|
94a2421186 | ||
|
|
923c3a27a3 | ||
|
|
c1a1186bf7 | ||
|
|
b360514646 | ||
|
|
541794ca9c | ||
|
|
5fbcae1f55 | ||
|
|
0f6e0e5085 | ||
|
|
3541a9e1db | ||
|
|
1f95a4ee21 | ||
|
|
af5a0d5006 | ||
|
|
84280a3b5f | ||
|
|
e3bce622e5 | ||
|
|
ef9392a8d5 | ||
|
|
15087ab848 | ||
|
|
7bb7445c91 | ||
|
|
6a4ce3c3b1 | ||
|
|
7df27de5c1 | ||
|
|
b4fc647845 | ||
|
|
de053eed72 | ||
|
|
17612f88d3 | ||
|
|
e14845728c | ||
|
|
4e80fc0f76 | ||
|
|
fcfcf85e0d | ||
|
|
f0715baf7d | ||
|
|
2733e7966f | ||
|
|
52327d1086 | ||
|
|
81cf3df5e2 | ||
|
|
45b5c39af7 | ||
|
|
4463e91d85 | ||
|
|
ce87bf9e23 | ||
|
|
dbc3da7bc3 | ||
|
|
ebc9fa684a | ||
|
|
606bdc1909 | ||
|
|
7495fb9af4 | ||
|
|
75dbad7406 | ||
|
|
c95064b76d | ||
|
|
f0933eaf1e | ||
|
|
b9ea7d4913 | ||
|
|
9c75a705f3 | ||
|
|
3381c085f4 | ||
|
|
b89d181125 | ||
|
|
c3a2713bba | ||
|
|
99b73fe1ee | ||
|
|
151f42ceef | ||
|
|
627c0070c1 | ||
|
|
3a56824cde | ||
|
|
9b6f7d94f4 | ||
|
|
9a6d40e9db | ||
|
|
64e8aa5fee | ||
|
|
a839b1c4ae | ||
|
|
b58df7150f | ||
|
|
1cd230a543 | ||
|
|
fc47e031b6 | ||
|
|
477128ad53 | ||
|
|
3b4ba2aafd | ||
|
|
70f67068f4 | ||
|
|
4fec344799 | ||
|
|
cc5f7608e3 | ||
|
|
468de06fe1 | ||
|
|
aa20289dfe | ||
|
|
4449fdbbc5 | ||
|
|
04a690caff | ||
|
|
0156eb0dda | ||
|
|
e8448332f4 | ||
|
|
e8e6ceb29e | ||
|
|
1f42f897d8 | ||
|
|
aa66bec783 | ||
|
|
1da52a8517 | ||
|
|
cbd2181862 | ||
|
|
102528195b | ||
|
|
7def094291 | ||
|
|
ffb3edced5 | ||
|
|
17e8c53236 | ||
|
|
910638d93f | ||
|
|
d005ff0099 | ||
|
|
4fdf8803a5 | ||
|
|
06af788480 | ||
|
|
7aa1719514 | ||
|
|
81c0951c2a | ||
|
|
89ecdeb13b | ||
|
|
c6211cde09 | ||
|
|
739001eca4 | ||
|
|
7243da3576 | ||
|
|
c9d547030f | ||
|
|
4923ac7015 | ||
|
|
c1c264d6d8 | ||
|
|
0940741568 | ||
|
|
041a938fc0 | ||
|
|
839048fab2 | ||
|
|
4180a41f27 | ||
|
|
8ada56fc53 | ||
|
|
6c5ca614e0 | ||
|
|
4ddee0b624 | ||
|
|
135ddbf37d | ||
|
|
a43adcd202 | ||
|
|
e8e170fb06 | ||
|
|
5ac5d649aa | ||
|
|
ec0c98bb0f | ||
|
|
decb802df4 | ||
|
|
cacc3a3085 | ||
|
|
0fd2ea6a47 | ||
|
|
ab7e73ef9b | ||
|
|
31e7a5383c | ||
|
|
64afa07ff5 | ||
|
|
656bbfe46d | ||
|
|
426f275c03 | ||
|
|
b55ba2ac7f | ||
|
|
693a861e7d | ||
|
|
0ee6c60e94 | ||
|
|
a663e9fded | ||
|
|
b3bf516b20 | ||
|
|
c2408b74cd | ||
|
|
6855c2f83a | ||
|
|
b11bf30881 | ||
|
|
64736f1463 | ||
|
|
930b224ca2 | ||
|
|
c87de1024d | ||
|
|
24e6840a8b | ||
|
|
06ed048983 | ||
|
|
937313495d | ||
|
|
9493e31a5d | ||
|
|
92b61c7491 | ||
|
|
3a4c4cd33d | ||
|
|
9aa399894c | ||
|
|
bde5dc825d | ||
|
|
8578d18e7f | ||
|
|
9b0e55210d | ||
|
|
e530cbb4f2 | ||
|
|
ddb8378fe6 | ||
|
|
47db23d91c | ||
|
|
fc1f701bf6 | ||
|
|
ece31855af | ||
|
|
d57ef77758 | ||
|
|
365c7bb89e | ||
|
|
bf2c4dee1b | ||
|
|
11b812b5fc | ||
|
|
b073e4385c | ||
|
|
c37c46a4a8 | ||
|
|
f592d94af3 | ||
|
|
f9359431fe | ||
|
|
25e560fdaa | ||
|
|
0ce805b192 | ||
|
|
6bf25f90bc | ||
|
|
30d36fca81 | ||
|
|
ffd0bbb1a4 | ||
|
|
3db20e8028 | ||
|
|
ed12f98075 | ||
|
|
19eef5bd72 | ||
|
|
7b2bcd1055 | ||
|
|
108c1fcb2b | ||
|
|
ced29dea8e | ||
|
|
20a07d61c6 | ||
|
|
682c821d0e | ||
|
|
5f382ee053 | ||
|
|
dc81630b16 | ||
|
|
f84c095b22 | ||
|
|
9bfa60d272 | ||
|
|
3e51d010d2 | ||
|
|
b190d0e385 | ||
|
|
0bd64ef700 | ||
|
|
9665c7c947 | ||
|
|
dcf872bcd5 | ||
|
|
6254617814 | ||
|
|
fc4d19be5e | ||
|
|
88d6b63e12 | ||
|
|
60b24bad64 | ||
|
|
cde2f333b4 | ||
|
|
427ec288ef | ||
|
|
18041527c5 | ||
|
|
f58e2ce6c0 | ||
|
|
6312063cd3 | ||
|
|
d0c1f650d1 | ||
|
|
4511883baf | ||
|
|
1e6a4621ea | ||
|
|
2a0fc227de | ||
|
|
05e98be6c7 | ||
|
|
fe7fa2035d | ||
|
|
a00f4156ea | ||
|
|
4d37e2267f | ||
|
|
5055397024 | ||
|
|
2bb142e8ee | ||
|
|
93f273a287 | ||
|
|
04e7616c84 | ||
|
|
219b970703 | ||
|
|
408ca6d427 | ||
|
|
ae323d393d | ||
|
|
76c139253e | ||
|
|
2a50a543bf | ||
|
|
ee54d6869b | ||
|
|
bd753d65a4 | ||
|
|
2fe8f0e43b | ||
|
|
94cb5d3439 | ||
|
|
0ed2388cf8 | ||
|
|
b91dee6c0c | ||
|
|
02b09c2535 | ||
|
|
10cfb373f2 | ||
|
|
69578d5d07 | ||
|
|
97d6d413bb | ||
|
|
4f27f3ac37 | ||
|
|
4771908f42 | ||
|
|
d1751a042e | ||
|
|
99a0e0c58e | ||
|
|
7e0d5d64ce | ||
|
|
3f8f57fa9a | ||
|
|
434ce57b7e | ||
|
|
c1eb928893 | ||
|
|
97cf0a2534 | ||
|
|
ec02657113 | ||
|
|
600a629734 | ||
|
|
9acdb45482 | ||
|
|
e51bd4c95f | ||
|
|
71b3eb0ec7 | ||
|
|
c14c851e94 | ||
|
|
011ac0222c | ||
|
|
00580856ae | ||
|
|
9b6dabf5fb | ||
|
|
eb2a615bd2 | ||
|
|
cfc3a82c44 | ||
|
|
b23dac591e | ||
|
|
de004af288 | ||
|
|
f48cdd51f4 | ||
|
|
10d303f43f | ||
|
|
6a7c953344 | ||
|
|
0432727ace | ||
|
|
d0a4e541c3 | ||
|
|
d515816488 | ||
|
|
25a5ec4bcf | ||
|
|
4fbe391243 | ||
|
|
c9ef543e2e | ||
|
|
cd5286d03b | ||
|
|
e6d61d1ebd | ||
|
|
f916f9cde8 | ||
|
|
be06adc794 | ||
|
|
271c51f1f4 | ||
|
|
28e9f95f7c | ||
|
|
d4da1b99d8 | ||
|
|
28ecb77bf1 | ||
|
|
85effd0fb0 | ||
|
|
c89c47d671 | ||
|
|
6de4511601 | ||
|
|
00acf271e2 | ||
|
|
0e03e78490 | ||
|
|
99cbb6c996 | ||
|
|
0e36070f0a | ||
|
|
bc349253cf | ||
|
|
f3ed286ee0 | ||
|
|
efcd0654c7 | ||
|
|
d413ebff2d | ||
|
|
33f94b7886 | ||
|
|
817aded5aa | ||
|
|
5e61388b65 | ||
|
|
9e9cdd3d56 | ||
|
|
d7d430e2a3 | ||
|
|
09c4ba4044 | ||
|
|
3f795c7555 | ||
|
|
c451e61972 | ||
|
|
afc0a02058 | ||
|
|
a5bf059e23 | ||
|
|
9f0e2c0a3a | ||
|
|
827a108a38 | ||
|
|
4b50f5e1db | ||
|
|
910bbe1160 | ||
|
|
5b742c97c9 | ||
|
|
16a2137777 | ||
|
|
e8316782aa | ||
|
|
edcf9e6a1e | ||
|
|
9addc4a7ca | ||
|
|
5ed71ecb96 | ||
|
|
7db97c6804 | ||
|
|
f039d54a51 | ||
|
|
dab0e372d0 | ||
|
|
dc8c6ed7a9 | ||
|
|
18e341b0b7 | ||
|
|
2722f305e0 | ||
|
|
469e2e6e0e | ||
|
|
05301d4191 | ||
|
|
3df5febd3e | ||
|
|
dfd1d5fe35 | ||
|
|
d289457c0c | ||
|
|
f52b3bff0d | ||
|
|
b6b17733bf | ||
|
|
fc2b00699e | ||
|
|
b00b08d08b | ||
|
|
c02ea05883 | ||
|
|
61a0028d02 | ||
|
|
c1c2d027c3 | ||
|
|
5269096ecd | ||
|
|
e6511e049a | ||
|
|
e4c68936a0 | ||
|
|
74fbd12709 | ||
|
|
4db5d4c08d | ||
|
|
3511867ba3 | ||
|
|
c40394638a | ||
|
|
cc2862837a | ||
|
|
e27c6b1cd7 | ||
|
|
ae00686e58 | ||
|
|
7934f9b9dc | ||
|
|
7f2eef4a24 | ||
|
|
8a65a592f3 | ||
|
|
7d6b019cfa | ||
|
|
5e48400cb1 | ||
|
|
252562ace9 | ||
|
|
c9c32b0de1 | ||
|
|
770ac8ffe5 | ||
|
|
db137d3816 | ||
|
|
dcfdb2d0a9 | ||
|
|
0cbf34ba5a | ||
|
|
77768e8400 | ||
|
|
5621eb2191 | ||
|
|
f1037147a9 | ||
|
|
bea52d5fb9 | ||
|
|
2bf970932e | ||
|
|
b6fac4b411 | ||
|
|
d8a77c22a3 | ||
|
|
b75a495336 | ||
|
|
ed3c387712 | ||
|
|
bbadc1f0be | ||
|
|
a141db4737 | ||
|
|
cb67dd1f1c | ||
|
|
af653608ef | ||
|
|
9dac061b04 | ||
|
|
ccf1bba235 | ||
|
|
312a5b246b | ||
|
|
ddd6bf22af | ||
|
|
903de43589 | ||
|
|
2d67594ccf | ||
|
|
0c7eee7f9c | ||
|
|
65f9f86bcc | ||
|
|
efaf53e568 | ||
|
|
81a2a9278c | ||
|
|
e15d4bfab6 | ||
|
|
e23c41232d | ||
|
|
e25d070a38 | ||
|
|
499c97b914 | ||
|
|
c472b8ad3b | ||
|
|
94ceb98b9e | ||
|
|
b1de8683f0 | ||
|
|
cff2105908 | ||
|
|
e11d5c142c | ||
|
|
f77b3bedaf | ||
|
|
3654df1cae | ||
|
|
f3d2e89c68 | ||
|
|
dd46536f7c | ||
|
|
e249907a9c | ||
|
|
4ded943a43 | ||
|
|
bd52f4fefb | ||
|
|
f57785e422 | ||
|
|
77880c3675 | ||
|
|
bb369e68b5 | ||
|
|
2a2d68e204 | ||
|
|
b49fbf1a97 | ||
|
|
308bff7875 | ||
|
|
9444e61856 | ||
|
|
2f114701a1 | ||
|
|
5128db05cf | ||
|
|
9675202bb2 | ||
|
|
cbe42cd727 | ||
|
|
3559425fc1 | ||
|
|
a246528eec | ||
|
|
7fbbf1088a | ||
|
|
fa4a5e2990 | ||
|
|
b662a7e3c6 | ||
|
|
cdc452282e | ||
|
|
d8c4251c03 | ||
|
|
72d1ab5cbc | ||
|
|
18b8e7093a | ||
|
|
aedb9ccbba | ||
|
|
67fa156c0e | ||
|
|
9437e689fd | ||
|
|
a79459bc53 | ||
|
|
500bd15843 | ||
|
|
3e8dd2f4a5 | ||
|
|
d0fade9ce1 | ||
|
|
51a49dfce8 | ||
|
|
db8462ccc9 | ||
|
|
066b71686d | ||
|
|
be633001a5 | ||
|
|
84426c6634 | ||
|
|
dbaf924171 | ||
|
|
8adf743cc7 | ||
|
|
75450ded1d | ||
|
|
bcca6ac720 | ||
|
|
865f9f4f67 | ||
|
|
a7e54d4bad | ||
|
|
2beb795f9a | ||
|
|
6847d8a5c7 | ||
|
|
06985d3cf2 | ||
|
|
2fe4265223 | ||
|
|
ed719e13c7 | ||
|
|
2d705c6697 | ||
|
|
c98ad106c4 |
@@ -36,18 +36,24 @@ clearstatcache();
|
||||
$oiTopComposer = new iTopComposer();
|
||||
$aDeniedButStillPresent = $oiTopComposer->ListDeniedButStillPresent();
|
||||
|
||||
echo "\n";
|
||||
foreach ($aDeniedButStillPresent as $sDir)
|
||||
{
|
||||
if (! preg_match('#[tT]ests?/?$#', $sDir))
|
||||
if (false === iTopComposer::IsTestDir($sDir))
|
||||
{
|
||||
echo "\nfound INVALID denied test dir: '$sDir'\n";
|
||||
echo "ERROR found INVALID denied test dir: '$sDir'\n";
|
||||
throw new \Exception("$sDir must end with /Test/ or /test/");
|
||||
}
|
||||
|
||||
if (false === file_exists($sDir)) {
|
||||
echo "INFO $sDir is in denied list, but not existing on disk => skipping !\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
SetupUtils::rrmdir($sDir);
|
||||
echo "Remove denied test dir: '$sDir'\n";
|
||||
echo "OK Remove denied test dir: '$sDir'\n";
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
|
||||
@@ -25,8 +25,8 @@ require_once (__DIR__.DIRECTORY_SEPARATOR.'update.classes.inc.php');
|
||||
/** @var \FileVersionUpdater[] $aFilesUpdaters */
|
||||
$aFilesUpdaters = array(
|
||||
new iTopVersionFileUpdater(),
|
||||
new CssVariablesFileUpdater(),
|
||||
new DatamodelsModulesFiles(),
|
||||
new ConstantFileUpdater('ITOP_CORE_VERSION', 'approot.inc.php'),
|
||||
);
|
||||
|
||||
if (count($argv) === 1)
|
||||
|
||||
@@ -69,6 +69,40 @@ abstract class AbstractSingleFileVersionUpdater extends FileVersionUpdater
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°4714
|
||||
*/
|
||||
class ConstantFileUpdater extends AbstractSingleFileVersionUpdater {
|
||||
/** @var string */
|
||||
private $sConstantName;
|
||||
|
||||
/**
|
||||
* @param $sConstantName constant to search, for example `ITOP_CORE_VERSION`
|
||||
* @param $sFileToUpdate file containing constant definition
|
||||
*/
|
||||
public function __construct($sConstantName, $sFileToUpdate)
|
||||
{
|
||||
$this->sConstantName = $sConstantName;
|
||||
parent::__construct($sFileToUpdate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function UpdateFileContent($sVersionLabel, $sFileContent, $sFileFullPath)
|
||||
{
|
||||
$sConstantSearchPattern = <<<REGEXP
|
||||
/define\('{$this->sConstantName}', ?'[^']+'\);/
|
||||
REGEXP;
|
||||
|
||||
return preg_replace(
|
||||
$sConstantSearchPattern,
|
||||
"define('{$this->sConstantName}', '{$sVersionLabel}');",
|
||||
$sFileContent
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class iTopVersionFileUpdater extends AbstractSingleFileVersionUpdater
|
||||
{
|
||||
public function __construct()
|
||||
@@ -89,26 +123,6 @@ class iTopVersionFileUpdater extends AbstractSingleFileVersionUpdater
|
||||
}
|
||||
}
|
||||
|
||||
class CssVariablesFileUpdater extends AbstractSingleFileVersionUpdater
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct('css/css-variables.scss');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function UpdateFileContent($sVersionLabel, $sFileContent, $sFileFullPath)
|
||||
{
|
||||
return preg_replace(
|
||||
'/(\$version: "v)[^"]*(";)/',
|
||||
'${1}'.$sVersionLabel.'${2}',
|
||||
$sFileContent
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractGlobFileVersionUpdater extends FileVersionUpdater
|
||||
{
|
||||
protected $sGlobPattern;
|
||||
|
||||
11
README.md
11
README.md
@@ -78,18 +78,19 @@ We would like to give a special thank you 🤗 to the people from the community
|
||||
|
||||
- Alves, David
|
||||
- Beck, Pedro
|
||||
- Beer, Christian (a.k.a [@ChristianBeer](https://www.github.com/ChristianBeer))
|
||||
- Bilger, Jean-François
|
||||
- Bostoen, Jeffrey (a.k.a @jbostoen)
|
||||
- Bostoen, Jeffrey (a.k.a [@jbostoen](https://www.github.com/jbostoen))
|
||||
- Cardoso, Anderson
|
||||
- Cassaro, Bruno
|
||||
- Casteleyn, Thomas (a.k.a @Hipska)
|
||||
- Casteleyn, Thomas (a.k.a [@Hipska](https://www.github.com/Hipska))
|
||||
- Castro, Randall Badilla
|
||||
- Colantoni, Maria Laura
|
||||
- Couronné, Guy
|
||||
- Dvořák, Lukáš
|
||||
- Goethals, Stefan
|
||||
- Gumble, David
|
||||
- Kaltefleiter, Lars (a.k.a @larhip)
|
||||
- Kaltefleiter, Lars (a.k.a [@larhip](https://www.github.com/larhip))
|
||||
- Khamit, Shamil
|
||||
- Kincel, Martin
|
||||
- Konečný, Kamil
|
||||
@@ -98,12 +99,12 @@ We would like to give a special thank you 🤗 to the people from the community
|
||||
- Lazcano, Federico
|
||||
- Lucas, Jonathan
|
||||
- Malik, Remie
|
||||
- Mindêllo de Andrade, Lucas (a.k.a @rokam)
|
||||
- Mindêllo de Andrade, Lucas (a.k.a [@rokam](https://www.github.com/rokam))
|
||||
- Raenker, Martin
|
||||
- Rosenke, Stephan
|
||||
- Seki, Shoji
|
||||
- Shilov, Vladimir
|
||||
- Stukalov, Ilya (a.k.a @ilya-stukalov)
|
||||
- Stukalov, Ilya (a.k.a [@ilya](https://www.github.com/ilya)-stukalov)
|
||||
- Tulio, Marco
|
||||
- Turrubiates, Miguel
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ define('PORTAL_PROFILE_NAME', 'Portal user');
|
||||
class UserRightsBaseClassGUI extends cmdbAbstractObject
|
||||
{
|
||||
// Whenever something changes, reload the privileges
|
||||
|
||||
|
||||
protected function AfterInsert()
|
||||
{
|
||||
UserRights::FlushPrivileges();
|
||||
@@ -59,7 +59,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
}
|
||||
|
||||
protected static $m_aCacheProfiles = null;
|
||||
|
||||
|
||||
public static function DoCreateProfile($sName, $sDescription)
|
||||
{
|
||||
if (is_null(self::$m_aCacheProfiles))
|
||||
@@ -71,7 +71,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
{
|
||||
self::$m_aCacheProfiles[$oProfile->Get('name')] = $oProfile->GetKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sCacheKey = $sName;
|
||||
if (isset(self::$m_aCacheProfiles[$sCacheKey]))
|
||||
@@ -82,10 +82,10 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
$oNewObj->Set('name', $sName);
|
||||
$oNewObj->Set('description', $sDescription);
|
||||
$iId = $oNewObj->DBInsertNoReload();
|
||||
self::$m_aCacheProfiles[$sCacheKey] = $iId;
|
||||
self::$m_aCacheProfiles[$sCacheKey] = $iId;
|
||||
return $iId;
|
||||
}
|
||||
|
||||
|
||||
function GetGrantAsHtml($oUserRights, $sClass, $sAction)
|
||||
{
|
||||
$bGrant = $oUserRights->GetProfileActionGrant($this->GetKey(), $sClass, $sAction);
|
||||
@@ -102,7 +102,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
return '<span style="background-color: #ffdddd;">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function DoShowGrantSumary($oPage)
|
||||
{
|
||||
if ($this->GetRawName() == "Administrator")
|
||||
@@ -114,7 +114,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
|
||||
// Note: for sure, we assume that the instance is derived from UserRightsProfile
|
||||
$oUserRights = UserRights::GetModuleInstance();
|
||||
|
||||
|
||||
$aDisplayData = array();
|
||||
foreach (MetaModel::GetClasses('bizmodel,grant_by_profile') as $sClass)
|
||||
{
|
||||
@@ -123,12 +123,12 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
{
|
||||
$bGrant = $oUserRights->GetClassStimulusGrant($this->GetKey(), $sClass, $sStimulusCode);
|
||||
if ($bGrant === true)
|
||||
{
|
||||
{
|
||||
$aStimuli[] = '<span title="'.$sStimulusCode.': '.htmlentities($oStimulus->GetDescription(), ENT_QUOTES, 'UTF-8').'">'.htmlentities($oStimulus->GetLabel(), ENT_QUOTES, 'UTF-8').'</span>';
|
||||
}
|
||||
}
|
||||
$sStimuli = implode(', ', $aStimuli);
|
||||
|
||||
|
||||
$aDisplayData[] = array(
|
||||
'class' => MetaModel::GetName($sClass),
|
||||
'read' => $this->GetGrantAsHtml($oUserRights, $sClass, 'r'),
|
||||
@@ -140,7 +140,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
'stimuli' => $sStimuli,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
$aDisplayConfig = array();
|
||||
$aDisplayConfig['class'] = array('label' => Dict::S('UI:UserManagement:Class'), 'description' => Dict::S('UI:UserManagement:Class+'));
|
||||
$aDisplayConfig['read'] = array('label' => Dict::S('UI:UserManagement:Action:Read'), 'description' => Dict::S('UI:UserManagement:Action:Read+'));
|
||||
@@ -198,7 +198,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
|
||||
* @param $aReasons array To store the reasons why the attribute is read-only (info about the synchro replicas)
|
||||
* @param $sTargetState string The target state in which to evalutate the flags, if empty the current state will be used
|
||||
* @return integer Flags: the binary combination of the flags applicable to this attribute
|
||||
*/
|
||||
*/
|
||||
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
|
||||
{
|
||||
$iFlags = parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
|
||||
@@ -404,7 +404,7 @@ class URP_UserOrg extends UserRightsBaseClassGUI
|
||||
{
|
||||
if (!UserRights::IsLoggedIn() || UserRights::IsAdministrator()) { return; }
|
||||
|
||||
$oUser = UserRights::GetUserObject();
|
||||
$oUser = UserRights::GetUserObject();
|
||||
$oAddon = UserRights::GetModuleInstance();
|
||||
$aOrgs = $oAddon->GetUserOrgs($oUser, '');
|
||||
if (count($aOrgs) > 0)
|
||||
@@ -528,7 +528,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
$oSearch->AllowAllData();
|
||||
$oCondition = new BinaryExpression(new FieldExpression('userid'), '=', new VariableExpression('userid'));
|
||||
$oSearch->AddConditionExpression($oCondition);
|
||||
|
||||
|
||||
$oUserOrgSet = new DBObjectSet($oSearch, array(), array('userid' => $iUser));
|
||||
while ($oUserOrg = $oUserOrgSet->Fetch())
|
||||
{
|
||||
@@ -738,8 +738,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
// load and cache permissions for the current user on the given class
|
||||
//
|
||||
$iUser = $oUser->GetKey();
|
||||
$aTest = @$this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode];
|
||||
if (is_array($aTest)) return $aTest;
|
||||
if (isset($this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode])){
|
||||
$aTest = $this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode];
|
||||
if (is_array($aTest)) return $aTest;
|
||||
}
|
||||
|
||||
$sAction = self::$m_aActionCodes[$iActionCode];
|
||||
|
||||
@@ -905,8 +907,8 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
|
||||
/**
|
||||
* Find out which attribute is corresponding the the dimension 'owner org'
|
||||
* returns null if no such attribute has been found (no filtering should occur)
|
||||
*/
|
||||
* returns null if no such attribute has been found (no filtering should occur)
|
||||
*/
|
||||
public static function GetOwnerOrganizationAttCode($sClass)
|
||||
{
|
||||
$sAttCode = null;
|
||||
|
||||
@@ -1101,7 +1101,9 @@ class JSButtonItem extends JSPopupMenuItem
|
||||
* @api
|
||||
* @package Extensibility
|
||||
* @since 2.0
|
||||
* @deprecated since 3.0.0 use iPageUIBlockExtension instead
|
||||
* @deprecated 3.0.0 If you need to include:
|
||||
* * JS/CSS files/snippets, use {@see \iBackofficeLinkedScriptsExtension}, {@see \iBackofficeLinkedStylesheetsExtension}, etc instead
|
||||
* * HTML (and optionally JS/CSS), use {@see \iPageUIBlockExtension} to manipulate {@see \Combodo\iTop\Application\UI\Base\UIBlock} instead
|
||||
*/
|
||||
interface iPageUIExtension
|
||||
{
|
||||
@@ -1252,6 +1254,151 @@ abstract class AbstractPageUIBlockExtension implements iPageUIBlockExtension
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add script (JS) files to the backoffice pages
|
||||
*
|
||||
* @see \iTopWebPage::$a_linked_scripts
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeLinkedScriptsExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::$a_linked_scripts Each script will be included using this property
|
||||
* @return array An array of absolute URLs to the files to include
|
||||
*/
|
||||
public function GetLinkedScriptsAbsUrls(): array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add inline script (JS) to the backoffice pages' head.
|
||||
* Will be executed first, BEFORE the DOM interpretation.
|
||||
*
|
||||
* @see \iTopWebPage::$a_early_scripts
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeEarlyScriptExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::$a_early_scripts
|
||||
* @return string
|
||||
*/
|
||||
public function GetEarlyScript(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add inline script (JS) to the backoffice pages that will be executed immediately, without waiting for the DOM to be ready.
|
||||
*
|
||||
* @see \iTopWebPage::$a_scripts
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeScriptExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::$a_scripts
|
||||
* @return string
|
||||
*/
|
||||
public function GetScript(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add inline script (JS) to the backoffice pages that will be executed right when the DOM is ready.
|
||||
*
|
||||
* @see \iTopWebPage::$a_init_scripts
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeInitScriptExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::$a_init_scripts
|
||||
* @return string
|
||||
*/
|
||||
public function GetInitScript(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add inline script (JS) to the backoffice pages that will be executed slightly AFTER the DOM is ready (just after the init. scripts).
|
||||
*
|
||||
* @see \iTopWebPage::$a_ready_scripts
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeReadyScriptExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::$a_ready_scripts
|
||||
* @return string
|
||||
*/
|
||||
public function GetReadyScript(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add stylesheets (CSS) to the backoffice pages
|
||||
*
|
||||
* @see \iTopWebPage::$a_linked_stylesheets
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeLinkedStylesheetsExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::$a_linked_stylesheets
|
||||
* @return array An array of absolute URLs to the files to include
|
||||
*/
|
||||
public function GetLinkedStylesheetsAbsUrls(): array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add inline style (CSS) to the backoffice pages' head.
|
||||
*
|
||||
* @see \iTopWebPage::$a_styles
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeStyleExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::$a_styles
|
||||
* @return string
|
||||
*/
|
||||
public function GetStyle(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add Dict entries
|
||||
*
|
||||
* @see \iTopWebPage::$a_dict_entries
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeDictEntriesExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::a_dict_entries
|
||||
* @return array
|
||||
*/
|
||||
public function GetDictEntries(): array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add Dict entries prefixes
|
||||
*
|
||||
* @see \iTopWebPage::$a_dict_entries_prefixes
|
||||
* @api
|
||||
* @since 3.0.0
|
||||
*/
|
||||
interface iBackofficeDictEntriesPrefixesExtension
|
||||
{
|
||||
/**
|
||||
* @see \iTopWebPage::a_dict_entries_prefixes
|
||||
* @return array
|
||||
*/
|
||||
public function GetDictEntriesPrefixes(): array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implement this interface to add content to any enhanced portal page
|
||||
*
|
||||
|
||||
@@ -251,7 +251,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param \DBObject $oObj
|
||||
* @param \cmdbAbstractObject $oObj
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws \Exception
|
||||
@@ -285,6 +285,7 @@ JS
|
||||
);
|
||||
|
||||
$oObj->Reload();
|
||||
$oObj->SetDisplayMode(static::ENUM_DISPLAY_MODE_VIEW);
|
||||
$oObj->DisplayDetails($oPage, false);
|
||||
}
|
||||
|
||||
@@ -390,7 +391,8 @@ JS
|
||||
$oSingletonFilter = new DBObjectSearch(get_class($this));
|
||||
$oSingletonFilter->AddCondition('id', $this->GetKey(), '=');
|
||||
$oBlock = new MenuBlock($oSingletonFilter, 'details', false);
|
||||
$oActionMenuBlock = $oBlock->GetRenderContent($oPage);
|
||||
$sActionMenuId = utils::Sanitize(uniqid('', true), '', utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
|
||||
$oActionMenuBlock = $oBlock->GetRenderContent($oPage, [], $sActionMenuId);
|
||||
$aHeaderBlocks['toolbar'][$oActionMenuBlock->GetId()] = $oActionMenuBlock;
|
||||
}
|
||||
|
||||
@@ -648,11 +650,17 @@ HTML
|
||||
if ($oAttDef instanceof AttributeDashboard) {
|
||||
if (!$this->IsNew()) {
|
||||
$sHostContainerInEditionUrlParam = ($bEditMode) ? '&host_container_in_edition=true' : '';
|
||||
$oPage->AddAjaxTab($oAttDef->GetLabel(),
|
||||
utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=dashboard&class='.get_class($this).'&id='.$this->GetKey().'&attcode='.$oAttDef->GetCode().$sHostContainerInEditionUrlParam,
|
||||
true,
|
||||
$oPage->AddAjaxTab(
|
||||
'Class:'.$sClass.'/Attribute:'.$sAttCode,
|
||||
AjaxTab::ENUM_TAB_PLACEHOLDER_DASHBOARD);
|
||||
utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=dashboard&class='
|
||||
.get_class($this)
|
||||
.'&id='.$this->GetKey()
|
||||
.'&attcode='.$oAttDef->GetCode()
|
||||
.$sHostContainerInEditionUrlParam,
|
||||
true,
|
||||
$oAttDef->GetLabel(),
|
||||
AjaxTab::ENUM_TAB_PLACEHOLDER_DASHBOARD
|
||||
);
|
||||
// Add graphs dependencies
|
||||
WebResourcesHelper::EnableC3JSToWebPage($oPage);
|
||||
}
|
||||
@@ -936,6 +944,7 @@ HTML
|
||||
$aFieldsComments = (isset($aExtraParams['fieldsComments'])) ? $aExtraParams['fieldsComments'] : array();
|
||||
$aExtraFlags = (isset($aExtraParams['fieldsFlags'])) ? $aExtraParams['fieldsFlags'] : array();
|
||||
|
||||
$bHasFieldsWithRichTextEditor = false;
|
||||
foreach ($aDetailsStruct as $sTab => $aCols) {
|
||||
ksort($aCols);
|
||||
$oPage->SetCurrentTab($sTab);
|
||||
@@ -960,6 +969,10 @@ HTML
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($oAttDef instanceof AttributeText) && ($oAttDef->GetFormat() === 'html')) {
|
||||
$bHasFieldsWithRichTextEditor = true;
|
||||
}
|
||||
|
||||
$sAttDefClass = get_class($oAttDef);
|
||||
$sAttLabel = MetaModel::GetLabel($sClass, $sAttCode);
|
||||
|
||||
@@ -1023,7 +1036,7 @@ HTML
|
||||
// Attribute description
|
||||
$sDescription = $oAttDef->GetDescription();
|
||||
$sDescriptionForHTMLTag = utils::HtmlEntities($sDescription);
|
||||
$sDescriptionHTMLTag = (empty($sDescriptionForHTMLTag) || $sDescription === $oAttDef->GetLabel()) ? '' : 'class="ibo-has-description" data-tooltip-content="'.$sDescriptionForHTMLTag.'"';
|
||||
$sDescriptionHTMLTag = (empty($sDescriptionForHTMLTag) || $sDescription === $oAttDef->GetLabel()) ? '' : 'class="ibo-has-description" data-tooltip-content="'.$sDescriptionForHTMLTag.'" data-tooltip-max-width="600px"';
|
||||
|
||||
$val = array(
|
||||
'label' => '<span '.$sDescriptionHTMLTag.' >'.$oAttDef->GetLabel().'</span>',
|
||||
@@ -1040,7 +1053,7 @@ HTML
|
||||
// Attribute description
|
||||
$sDescription = $oAttDef->GetDescription();
|
||||
$sDescriptionForHTMLTag = utils::HtmlEntities($sDescription);
|
||||
$sDescriptionHTMLTag = (empty($sDescriptionForHTMLTag) || $sDescription === $oAttDef->GetLabel()) ? '' : 'class="ibo-has-description" data-tooltip-content="'.$sDescriptionForHTMLTag.'"';
|
||||
$sDescriptionHTMLTag = (empty($sDescriptionForHTMLTag) || $sDescription === $oAttDef->GetLabel()) ? '' : 'class="ibo-has-description" data-tooltip-content="'.$sDescriptionForHTMLTag.' "data-tooltip-max-width="600px"';
|
||||
|
||||
$val = array(
|
||||
'label' => '<span '.$sDescriptionHTMLTag.' >'.$oAttDef->GetLabel().'</span>',
|
||||
@@ -1100,6 +1113,11 @@ HTML
|
||||
}
|
||||
}
|
||||
|
||||
// Fields with CKEditor need to have the highlight.js lib loaded even if they are in read-only, as it is needed to format code snippets
|
||||
if ($bHasFieldsWithRichTextEditor) {
|
||||
WebResourcesHelper::EnableCKEditorToWebPage($oPage);
|
||||
}
|
||||
|
||||
return $aFieldsMap;
|
||||
}
|
||||
|
||||
@@ -1728,6 +1746,9 @@ HTML
|
||||
* @param array $aParams
|
||||
*
|
||||
* @throws \Exception
|
||||
* only used in old and deprecated export.php
|
||||
*
|
||||
* @internal Only to be used by `/webservices/export.php` : this is a legacy method that produces wrong HTML (no TR on table body rows)
|
||||
*/
|
||||
public static function DisplaySetAsHTMLSpreadsheet(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array())
|
||||
{
|
||||
@@ -1748,6 +1769,8 @@ HTML
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \Exception
|
||||
*
|
||||
* @internal Only to be used by `/webservices/export.php` : this is a legacy method that produces wrong HTML (no TR on table body rows)
|
||||
*/
|
||||
public static function GetSetAsHTMLSpreadsheet(DBObjectSet $oSet, $aParams = array())
|
||||
{
|
||||
@@ -2164,7 +2187,7 @@ HTML;
|
||||
$aEventsList[] = 'validate';
|
||||
$aEventsList[] = 'keyup';
|
||||
$aEventsList[] = 'change';
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_password\"><input title=\"$sHelpText\" type=\"password\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" value=\"".htmlentities($value,
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_password ibo-input-wrapper ibo-input-password-wrapper\" data-validation=\"untouched\"><input class=\"ibo-input ibo-input-password\" title=\"$sHelpText\" type=\"password\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" value=\"".htmlentities($value,
|
||||
ENT_QUOTES, 'UTF-8')."\" id=\"$iId\"/></div>{$sValidationSpan}{$sReloadSpan}";
|
||||
break;
|
||||
|
||||
@@ -2193,7 +2216,11 @@ HTML;
|
||||
$sStyle = 'style="'.implode('; ', $aStyles).'"';
|
||||
}
|
||||
|
||||
$aTextareaCssClasses = [];
|
||||
|
||||
if ($oAttDef->GetEditClass() == 'OQLExpression') {
|
||||
$aTextareaCssClasses[] = 'ibo-query-oql';
|
||||
$aTextareaCssClasses[] = 'ibo-is-code';
|
||||
// N°3227 button to open predefined queries dialog
|
||||
$sPredefinedBtnId = 'predef_btn_'.$sFieldPrefix.$sAttCode.$sNameSuffix;
|
||||
$sSearchQueryLbl = Dict::S('UI:Edit:SearchQuery');
|
||||
@@ -2263,14 +2290,16 @@ JS
|
||||
} else {
|
||||
$sAdditionalStuff = '';
|
||||
}
|
||||
|
||||
// Ok, the text area is drawn here
|
||||
$sTextareCssClassesAsString = implode(' ', $aTextareaCssClasses);
|
||||
$sHTMLValue = <<<HTML
|
||||
{$sAdditionalStuff}
|
||||
<div class="field_input_zone field_input_text ibo-input-wrapper ibo-input-text-wrapper" data-validation="untouched">
|
||||
<div class="f_i_text_header">
|
||||
<span class="fullscreen_button" title="{$sFullscreenLabelForHtml}"></span>
|
||||
</div>
|
||||
<textarea class="ibo-input ibo-input-text" title="{$sHelpText}" name="attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}" rows="8" cols="40" id="{$iId}" {$sStyle} >{$sEditValueForHtml}</textarea>
|
||||
<textarea class="ibo-input ibo-input-text {$sTextareCssClassesAsString}" title="{$sHelpText}" name="attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}" rows="8" cols="40" id="{$iId}" {$sStyle} >{$sEditValueForHtml}</textarea>
|
||||
</div>
|
||||
{$sValidationSpan}{$sReloadSpan}
|
||||
HTML;
|
||||
@@ -2292,8 +2321,7 @@ EOF
|
||||
);
|
||||
break;
|
||||
|
||||
// TODO 3.0.0: Isn't this part obsolete now that we have the activity panel or should we keep it for devs using it in custom extensions or maybe *transition forms* as a caselog can be MUST_PROMPT?
|
||||
// used for bulk modify in 3.0
|
||||
// In 3.0 not used for activity panel but kept for bulk modify and bulk-event extension
|
||||
case 'CaseLog':
|
||||
$sInputType = self::ENUM_INPUT_TYPE_HTML_EDITOR;
|
||||
$aStyles = array();
|
||||
@@ -2310,16 +2338,15 @@ EOF
|
||||
$sStyle = 'style="'.implode('; ', $aStyles).'"';
|
||||
}
|
||||
|
||||
$sHeader = '<div class="caselog_input_header"></div>'; // will be hidden in CSS (via :empty) if it remains empty
|
||||
$sHeader = '<div class="ibo-caselog-entry-form--actions"><div class="""ibo-caselog-entry-form--actions" data-role="ibo-caselog-entry-form--action-buttons--extra-actions"></div></div>'; // will be hidden in CSS (via :empty) if it remains empty
|
||||
$sEditValue = is_object($value) ? $value->GetModifiedEntry('html') : '';
|
||||
$sPreviousLog = is_object($value) ? $value->GetAsHTML($oPage, true /* bEditMode */,
|
||||
array('AttributeText', 'RenderWikiHtml')) : '';
|
||||
$sPreviousLog = is_object($value) ? $value->GetAsHTML($oPage, true /* bEditMode */, array('AttributeText', 'RenderWikiHtml')) : '';
|
||||
$iEntriesCount = is_object($value) ? count($value->GetIndex()) : 0;
|
||||
$sHidden = "<input type=\"hidden\" id=\"{$iId}_count\" value=\"$iEntriesCount\"/>"; // To know how many entries the case log already contains
|
||||
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_caselog caselog\" $sStyle>$sHeader<textarea class=\"htmlEditor\" style=\"border:0;width:100%\" title=\"$sHelpText\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" rows=\"8\" cols=\"40\" id=\"$iId\">".htmlentities($sEditValue,
|
||||
ENT_QUOTES,
|
||||
'UTF-8')."</textarea>$sPreviousLog</div>{$sValidationSpan}{$sReloadSpan}$sHidden";
|
||||
$sHTMLValue = "$sHeader<div class=\"ibo-caselog-entry-form--text-input\" $sStyle data-role=\"ibo-caselog-entry-form--text-input\">";
|
||||
$sHTMLValue .= "<textarea class=\"htmlEditor ibo-input-richtext-placeholder\" style=\"border:0;width:100%\" title=\"$sHelpText\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" rows=\"8\" cols=\"40\" id=\"$iId\">".htmlentities($sEditValue,ENT_QUOTES,'UTF-8')."</textarea>";
|
||||
$sHTMLValue .= "$sPreviousLog</div>{$sValidationSpan}{$sReloadSpan}$sHidden";
|
||||
|
||||
// Note: This should be refactored for all types of attribute (see at the end of this function) but as we are doing this for a maintenance release, we are scheduling it for the next main release in to order to avoid regressions as much as possible.
|
||||
$sNullValue = $oAttDef->GetNullValue();
|
||||
@@ -2414,7 +2441,6 @@ EOF
|
||||
<span class="fas fa-trash"></span>
|
||||
</button>
|
||||
</div>
|
||||
<br/>
|
||||
<input title="{$sHelpText}" name="attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}[fcontents]" type="file" id="file_{$iId}" onChange="UpdateFileName('{$iId}', this.value)"/>
|
||||
{$sValidationSpan}{$sReloadSpan}
|
||||
HTML;
|
||||
@@ -3611,7 +3637,7 @@ EOF
|
||||
// - Attribute description
|
||||
$sDescription = $oAttDef->GetDescription();
|
||||
$sDescriptionForHTMLAttributes = utils::HtmlEntities($sDescription);
|
||||
$sDescriptionHTMLAttributes = (empty($sDescriptionForHTMLAttributes) || $sDescription === $oAttDef->GetLabel()) ? '' : 'class="ibo-has-description" data-tooltip-content="'.$sDescriptionForHTMLAttributes.'"';
|
||||
$sDescriptionHTMLAttributes = (empty($sDescriptionForHTMLAttributes) || $sDescription === $oAttDef->GetLabel()) ? '' : 'class="ibo-has-description" data-tooltip-content="'.$sDescriptionForHTMLAttributes.'" data-tooltip-max-width="600px"';
|
||||
|
||||
// - Fullscreen toggler for large fields
|
||||
$sFullscreenTogglerTooltip = Dict::S('UI:ToggleFullScreen');
|
||||
@@ -3638,7 +3664,7 @@ HTML;
|
||||
$sDownloadUrl = $oDocument->GetDownloadURL(get_class($this), $this->GetKey(), $sAttCode);
|
||||
|
||||
$sDisplayValue = <<<HTML
|
||||
{$sFieldAsHtml}<br>
|
||||
{$sFieldAsHtml}
|
||||
<a href="{$sDisplayUrl}" target="_blank">{$sDisplayLabel}</a> / <a href="{$sDownloadUrl}">{$sDownloadLabel}</a>
|
||||
HTML;
|
||||
} else {
|
||||
@@ -4217,13 +4243,20 @@ HTML;
|
||||
if (!is_null($oImage->GetData()))
|
||||
{
|
||||
$aSize = utils::GetImageSize($oImage->GetData());
|
||||
$oImage = utils::ResizeImageToFit(
|
||||
$oImage,
|
||||
$aSize[0],
|
||||
$aSize[1],
|
||||
$oAttDef->Get('storage_max_width'),
|
||||
$oAttDef->Get('storage_max_height')
|
||||
);
|
||||
if (is_array($aSize) && $aSize[0] > 0 && $aSize[1] > 0)
|
||||
{
|
||||
$oImage = utils::ResizeImageToFit(
|
||||
$oImage,
|
||||
$aSize[0],
|
||||
$aSize[1],
|
||||
$oAttDef->Get('storage_max_width'),
|
||||
$oAttDef->Get('storage_max_height')
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
IssueLog::Warning($sClass . ':' . $this->GetKey() . '/' . $sAttCode . ': Image could not be resized. Mimetype: ' . $oImage->GetMimeType() . ', filename: ' . $oImage->GetFileName());
|
||||
}
|
||||
}
|
||||
$aOtherData = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}", null, 'raw_data');
|
||||
if (is_array($aOtherData))
|
||||
@@ -5291,7 +5324,7 @@ EOF
|
||||
if ($bPreview) {
|
||||
if (count($aObjects) == 1) {
|
||||
$oObj = $aObjects[0];
|
||||
$sTitle = Dict::Format('UI:Delete:ConfirmDeletionOf_Name', $oObj->GetName());
|
||||
$sTitle = Dict::Format('UI:Delete:ConfirmDeletionOf_Name', $oObj->GetRawName());
|
||||
} else {
|
||||
$sTitle = Dict::Format('UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class', count($aObjects),
|
||||
MetaModel::GetName($sClass));
|
||||
@@ -5492,7 +5525,7 @@ EOF
|
||||
//
|
||||
if (count($aObjects) == 1) {
|
||||
$oObj = $aObjects[0];
|
||||
$sTitle = Dict::Format('UI:Title:DeletionOf_Object', $oObj->GetName());
|
||||
$sTitle = Dict::Format('UI:Title:DeletionOf_Object', $oObj->GetRawName());
|
||||
} else {
|
||||
$sTitle = Dict::Format('UI:Title:BulkDeletionOf_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass));
|
||||
}
|
||||
@@ -5651,7 +5684,7 @@ EOF
|
||||
oOwnershipLockModal.text(data.popup_message);
|
||||
oOwnershipLockModal.dialog('open');
|
||||
}
|
||||
$('.object-details form .ibo-toolbar .ibo-button:not([name="cancel"])').prop('disabled', true);
|
||||
$('.ibo-object-details .ibo-toolbar .ibo-button:not([name="cancel"])').prop('disabled', true);
|
||||
clearInterval(hOwnershipLockHandlerInterval);
|
||||
}
|
||||
else if ((data.operation == 'lost') || (data.operation == 'expired'))
|
||||
@@ -5662,7 +5695,7 @@ EOF
|
||||
oOwnershipLockModal.text(data.popup_message);
|
||||
oOwnershipLockModal.dialog('open');
|
||||
}
|
||||
$('.object-details form .ibo-toolbar .ibo-button:not([name="cancel"])').prop('disabled', true);
|
||||
$('.ibo-object-details .ibo-toolbar .ibo-button:not([name="cancel"])').prop('disabled', true);
|
||||
clearInterval(hOwnershipLockHandlerInterval);
|
||||
}
|
||||
}, 'json');
|
||||
|
||||
@@ -583,7 +583,7 @@ JS
|
||||
$oPage->add('<div id="select_dashlet" class="ibo-dashboard--available-dashlets--list" data-role="ibo-dashboard--available-dashlets--list">');
|
||||
$aAvailableDashlets = $this->GetAvailableDashlets();
|
||||
foreach ($aAvailableDashlets as $sDashletClass => $aInfo) {
|
||||
$oPage->add('<span dashlet_class="'.$sDashletClass.'" class="ibo-dashboard-editor--available-dashlet-icon dashlet_icon ui-widget-content ui-corner-all" data-role="ibo-dashboard-editor--available-dashlet-icon" id="dashlet_'.$sDashletClass.'" title="'.$aInfo['label'].'"><img src="'.$sUrl.$aInfo['icon'].'" /></span>');
|
||||
$oPage->add('<span dashlet_class="'.$sDashletClass.'" class="ibo-dashboard-editor--available-dashlet-icon dashlet_icon ui-widget-content ui-corner-all" data-role="ibo-dashboard-editor--available-dashlet-icon" id="dashlet_'.$sDashletClass.'" data-tooltip-content="'.$aInfo['label'].'" title="'.$aInfo['label'].'"><img src="'.$sUrl.$aInfo['icon'].'" /></span>');
|
||||
}
|
||||
$oPage->add('</div>');
|
||||
|
||||
@@ -860,28 +860,29 @@ class RuntimeDashboard extends Dashboard
|
||||
{
|
||||
$bCustomized = false;
|
||||
|
||||
if (!appUserPreferences::GetPref('display_original_dashboard_'.$sDashBoardId, false))
|
||||
{
|
||||
$sDashboardFileSanitized = utils::RealPath($sDashboardFile, APPROOT);
|
||||
if (false === $sDashboardFileSanitized) {
|
||||
throw new SecurityException('Invalid dashboard file !');
|
||||
}
|
||||
|
||||
if (!appUserPreferences::GetPref('display_original_dashboard_'.$sDashBoardId, false)) {
|
||||
// Search for an eventual user defined dashboard
|
||||
$oUDSearch = new DBObjectSearch('UserDashboard');
|
||||
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
$oUDSearch->AddCondition('menu_code', $sDashBoardId, '=');
|
||||
$oUDSet = new DBObjectSet($oUDSearch);
|
||||
if ($oUDSet->Count() > 0)
|
||||
{
|
||||
if ($oUDSet->Count() > 0) {
|
||||
// Assuming there is at most one couple {user, menu}!
|
||||
$oUserDashboard = $oUDSet->Fetch();
|
||||
$sDashboardDefinition = $oUserDashboard->Get('contents');
|
||||
$bCustomized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFile);
|
||||
} else {
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFile);
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
|
||||
}
|
||||
|
||||
if ($sDashboardDefinition !== false)
|
||||
@@ -889,7 +890,7 @@ class RuntimeDashboard extends Dashboard
|
||||
$oDashboard = new RuntimeDashboard($sDashBoardId);
|
||||
$oDashboard->FromXml($sDashboardDefinition);
|
||||
$oDashboard->SetCustomFlag($bCustomized);
|
||||
$oDashboard->SetDefinitionFile($sDashboardFile);
|
||||
$oDashboard->SetDefinitionFile($sDashboardFileSanitized);
|
||||
} else {
|
||||
$oDashboard = null;
|
||||
}
|
||||
@@ -1065,11 +1066,11 @@ EOF
|
||||
dashboard.html(data);
|
||||
dashboard.unblock();
|
||||
if ($('#ibo-dashboard-selector$sDivId input').prop("checked")) {
|
||||
$('#ibo-dashboard-selector$sDivId').data('tooltip-content', '$sSwitchToStandard');
|
||||
$('#ibo-dashboard-selector$sDivId').attr('data-tooltip-content', '$sSwitchToStandard');
|
||||
} else {
|
||||
$('#ibo-dashboard-selector$sDivId').data('tooltip-content', '$sSwitchToCustom');
|
||||
$('#ibo-dashboard-selector$sDivId').attr('data-tooltip-content', '$sSwitchToCustom');
|
||||
}
|
||||
CombodoTooltip.InitAllNonInstantiatedTooltips($('#ibo-dashboard-selector$sDivId').parent());
|
||||
CombodoTooltip.InitAllNonInstantiatedTooltips($('#ibo-dashboard-selector$sDivId').parent(), true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1008,7 +1008,8 @@ HTML;
|
||||
|
||||
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletObjectList:Prop-Query'), $this->aProperties['query']);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-queryoql");
|
||||
$oField->AddCSSClass("ibo-query-oql");
|
||||
$oField->AddCSSClass("ibo-is-code");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerBooleanField('menu', Dict::S('UI:DashletObjectList:Prop-Menu'), $this->aProperties['menu']);
|
||||
@@ -1045,7 +1046,8 @@ HTML;
|
||||
|
||||
$oField = new DesignerHiddenField('query', Dict::S('UI:DashletObjectList:Prop-Query'), $sOQL);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-queryoql");
|
||||
$oField->AddCSSClass("ibo-query-oql");
|
||||
$oField->AddCSSClass("ibo-is-code");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
$oField = new DesignerBooleanField('menu', Dict::S('UI:DashletObjectList:Prop-Menu'), $this->aProperties['menu']);
|
||||
@@ -1395,7 +1397,8 @@ abstract class DashletGroupBy extends Dashlet
|
||||
|
||||
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletGroupBy:Prop-Query'), $this->aProperties['query']);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-queryoql");
|
||||
$oField->AddCSSClass("ibo-query-oql");
|
||||
$oField->AddCSSClass("ibo-is-code");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
try {
|
||||
@@ -1652,7 +1655,8 @@ abstract class DashletGroupBy extends Dashlet
|
||||
|
||||
$oField = new DesignerHiddenField('query', Dict::S('UI:DashletGroupBy:Prop-Query'), $sOQL);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-queryoql");
|
||||
$oField->AddCSSClass("ibo-query-oql");
|
||||
$oField->AddCSSClass("ibo-is-code");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
if (!is_null($sOQL)) {
|
||||
@@ -2225,7 +2229,8 @@ class DashletHeaderDynamic extends Dashlet
|
||||
|
||||
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletHeaderDynamic:Prop-Query'), $this->aProperties['query']);
|
||||
$oField->SetMandatory();
|
||||
$oField->AddCSSClass("ibo-queryoql");
|
||||
$oField->AddCSSClass("ibo-query-oql");
|
||||
$oField->AddCSSClass("ibo-is-code");
|
||||
$oForm->AddField($oField);
|
||||
|
||||
try
|
||||
|
||||
@@ -174,6 +174,8 @@ class DisplayBlock
|
||||
/** string Max. height of the list, if not specified will occupy all the available height no matter the pagination */
|
||||
'localize_values',
|
||||
/** param for export.php */
|
||||
'refresh_action',
|
||||
/**to add refresh button in datatable*/
|
||||
], DataTableUIBlockFactory::GetAllowedParams()),
|
||||
'list_search' => array_merge([
|
||||
'update_history',
|
||||
@@ -537,8 +539,10 @@ class DisplayBlock
|
||||
* @throws DictExceptionMissingString
|
||||
* @throws MySQLException
|
||||
* @throws Exception
|
||||
*
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°3129 add type hinting to $aExtraParams
|
||||
*/
|
||||
public function GetRenderContent(WebPage $oPage, array $aExtraParams = [], string $sId = null)
|
||||
public function GetRenderContent(WebPage $oPage, array $aExtraParams, string $sId)
|
||||
{
|
||||
$sHtml = '';
|
||||
$oBlock = null;
|
||||
@@ -851,7 +855,7 @@ JS
|
||||
{
|
||||
$oField = new FieldExpression($sFilterCode, $oFilter->GetClassAlias());
|
||||
$sListExpr = '('.implode(', ', CMDBSource::Quote($condition)).')';
|
||||
$sOQLCondition = $oField->Render()." IN $sListExpr";
|
||||
$sOQLCondition = $oField->RenderExpression()." IN $sListExpr";
|
||||
$oNewCondition = Expression::FromOQL($sOQLCondition);
|
||||
return $oNewCondition;
|
||||
}
|
||||
@@ -1043,13 +1047,18 @@ JS
|
||||
$sCountLabel = $aCount['label'];
|
||||
$oPill = PillFactory::MakeForState($sClass, $sStateValue)
|
||||
->SetTooltip($sStateLabel)
|
||||
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--count\">$sCountLabel</span><span class=\"ibo-dashlet-header-dynamic--label ibo-text-truncated-with-ellipsis\">$sStateLabel</span>");
|
||||
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--count\">$sCountLabel</span><span class=\"ibo-dashlet-header-dynamic--label ibo-text-truncated-with-ellipsis\">".utils::HtmlEntities($sStateLabel)."</span>");
|
||||
if ($sHyperlink != '-') {
|
||||
$oPill->SetUrl($sHyperlink);
|
||||
}
|
||||
$oBlock->AddSubBlock($oPill);
|
||||
}
|
||||
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
|
||||
if(isset($aExtraParams['query_params']['this->object()'])){
|
||||
$aExtraParams['query_params']['this->class'] = get_class($aExtraParams['query_params']['this->object()']);
|
||||
$aExtraParams['query_params']['this->id'] = $aExtraParams['query_params']['this->object()']->GetKey();
|
||||
unset($aExtraParams['query_params']['this->object()']);
|
||||
}
|
||||
$aRefreshParams = ['filter' => $this->m_oFilter->ToOQL(), "extra_params" => json_encode($aExtraParams)];
|
||||
$oBlock->SetJSRefresh(
|
||||
"$('#".$oBlock->GetId()."').block();
|
||||
@@ -1728,7 +1737,24 @@ class HistoryBlock extends DisplayBlock
|
||||
$this->iLimitCount = $iCount;
|
||||
}
|
||||
|
||||
public function GetRenderContent(WebPage $oPage, array $aExtraParams = [], string $sId = null)
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param array $aExtraParams
|
||||
* @param string $sId
|
||||
*
|
||||
* @return string
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
*
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $aExtraParams and add type hinting for PHP 8.0 compatibility
|
||||
* (var is unused, and all calls were already made using a default value)
|
||||
*/
|
||||
public function GetRenderContent(WebPage $oPage, array $aExtraParams, string $sId)
|
||||
{
|
||||
$sHtml = '';
|
||||
$bTruncated = false;
|
||||
@@ -1869,11 +1895,10 @@ class MenuBlock extends DisplayBlock
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
* @throws \ReflectionException
|
||||
*
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value and add type hinting on $aExtraParams for PHP 8.0 compatibility
|
||||
*/
|
||||
public function GetRenderContent(WebPage $oPage, array $aExtraParams = [], string $sId = null)
|
||||
public function GetRenderContent(WebPage $oPage, array $aExtraParams, string $sId)
|
||||
{
|
||||
$oRenderBlock = new UIContentBlock();
|
||||
|
||||
@@ -1884,7 +1909,7 @@ class MenuBlock extends DisplayBlock
|
||||
|
||||
$sClass = $this->m_oFilter->GetClass();
|
||||
$oSet = new CMDBObjectSet($this->m_oFilter);
|
||||
$sRefreshAction = $aExtraParams['sRefreshAction'] ?? '';
|
||||
$sRefreshAction = $aExtraParams['refresh_action'] ?? '';
|
||||
|
||||
/** @var array $aRegularActions Any action other than a transition */
|
||||
$aRegularActions = [];
|
||||
@@ -2220,8 +2245,14 @@ class MenuBlock extends DisplayBlock
|
||||
}
|
||||
if ($oPopupMenuItemsBlock->HasSubBlocks()) {
|
||||
$oRenderBlock->AddSubBlock($oPopupMenuItemsBlock);
|
||||
} else {
|
||||
foreach ($oPopupMenuItemsBlock->GetJsFilesRelPaths() as $sJsPath) {
|
||||
$oRenderBlock->AddJsFileRelPath($sJsPath);
|
||||
}
|
||||
foreach ($oPopupMenuItemsBlock->GetCssFilesRelPaths() as $sCssPath) {
|
||||
$oRenderBlock->AddCssFileRelPath($sCssPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract favorite actions from their menus
|
||||
$aFavoriteRegularActions = [];
|
||||
$aFavoriteTransitionActions = [];
|
||||
@@ -2332,6 +2363,8 @@ class MenuBlock extends DisplayBlock
|
||||
|
||||
$sTarget = isset($aAction['target']) ? $aAction['target'] : '';
|
||||
$oActionButton = ButtonUIBlockFactory::MakeLinkNeutral($sUrl, $sLabel, $sIconClass, $sTarget, utils::Sanitize($sActionId, '', utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER));
|
||||
// ResourceId should not be sanitized
|
||||
$oActionButton->AddDataAttribute('resource-id', $sActionId);
|
||||
$oActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']);
|
||||
if (empty($sLabel)) {
|
||||
if (empty($aAction['tooltip'])) {
|
||||
|
||||
@@ -234,9 +234,9 @@ class DesignerForm
|
||||
.$this->EndRow();
|
||||
|
||||
if (is_null($aRow['label'])) {
|
||||
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_value ibo-field--value" colspan="2">'.$aRow['value'];
|
||||
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_value" colspan="2">'.$aRow['value'];
|
||||
} else {
|
||||
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_label ibo-field--label">'.$aRow['label'].'</td><td class="prop_value ibo-field--value">'.$aRow['value'];
|
||||
$sReturn .= $this->StartRow($sFieldId).'<td class="prop_label">'.$aRow['label'].'</td><td class="prop_value">'.$aRow['value'];
|
||||
}
|
||||
if (!($oField instanceof DesignerFormSelectorField) && !($oField instanceof DesignerMultipleSubFormField)) {
|
||||
$sReturn .= $sValidationFields;
|
||||
@@ -354,7 +354,7 @@ EOF
|
||||
<<<EOF
|
||||
$('#$sDialogId').dialog({
|
||||
height: 'auto',
|
||||
maxHeight: $(window).height() - 8,
|
||||
maxHeight: $(window).height() * 0.9,
|
||||
width: $iDialogWidth,
|
||||
modal: true,
|
||||
autoOpen: $sAutoOpen,
|
||||
@@ -710,7 +710,10 @@ class DesignerFormField
|
||||
$this->bMandatory = false;
|
||||
$this->bReadOnly = false;
|
||||
$this->bAutoApply = false;
|
||||
$this->aCSSClasses = array('ibo-input');
|
||||
$this->aCSSClasses = [];
|
||||
if (ContextTag::Check(ContextTag::TAG_CONSOLE)) {
|
||||
$this->aCSSClasses[] = 'ibo-input';
|
||||
}
|
||||
$this->bDisplayed = true;
|
||||
$this->aWidgetExtraParams = array();
|
||||
}
|
||||
@@ -1065,7 +1068,10 @@ class DesignerLongTextField extends DesignerTextField
|
||||
public function __construct($sCode, $sLabel = '', $defaultValue = '')
|
||||
{
|
||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||
$this->aCSSClasses[] = 'ibo-input-text';
|
||||
|
||||
if (ContextTag::Check(ContextTag::TAG_CONSOLE)) {
|
||||
$this->aCSSClasses[] = 'ibo-input-text';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1199,7 +1205,10 @@ class DesignerComboField extends DesignerFormField
|
||||
$this->bMultipleSelection = false;
|
||||
$this->bOtherChoices = false;
|
||||
$this->sNullLabel = Dict::S('UI:SelectOne');
|
||||
$this->aCSSClasses[] = 'ibo-input-select';
|
||||
|
||||
if (ContextTag::Check(ContextTag::TAG_CONSOLE)) {
|
||||
$this->aCSSClasses[] = 'ibo-input-select';
|
||||
}
|
||||
|
||||
$this->bAutoApply = true;
|
||||
$this->bSorted = true; // Sorted by default
|
||||
@@ -1356,7 +1365,9 @@ class DesignerBooleanField extends DesignerFormField
|
||||
{
|
||||
parent::__construct($sCode, $sLabel, $defaultValue);
|
||||
$this->bAutoApply = true;
|
||||
$this->aCSSClasses[] = 'ibo-input-checkbox';
|
||||
if (ContextTag::Check(ContextTag::TAG_CONSOLE)) {
|
||||
$this->aCSSClasses[] = 'ibo-input-checkbox';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1503,7 +1514,8 @@ class DesignerIconSelectionField extends DesignerFormField
|
||||
$sPostUploadTo = ($this->sUploadUrl == null) ? 'null' : "'{$this->sUploadUrl}'";
|
||||
if (!$this->IsReadOnly()) {
|
||||
$sDefaultValue = ($this->defaultValue !== '') ? $this->defaultValue : $this->aAllowedValues[$idx]['value'];
|
||||
$sValue = "<span class=\"ibo-input-select-wrapper\"><input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$sDefaultValue}\"/></span>";
|
||||
$sCSSClasses = ContextTag::Check(ContextTag::TAG_CONSOLE) ? 'class="ibo-input-select-wrapper"' : '';
|
||||
$sValue = "<span $sCSSClasses><input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"{$sDefaultValue}\"/></span>";
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#$sId').icon_select({current_idx: $idx, items: $sJSItems, post_upload_to: $sPostUploadTo});
|
||||
@@ -1681,7 +1693,9 @@ class DesignerFormSelectorField extends DesignerFormField
|
||||
$this->defaultRealValue = $defaultValue;
|
||||
$this->aSubForms = array();
|
||||
$this->bSorted = true;
|
||||
$this->aCSSClasses[] = 'ibo-input-select';
|
||||
if (ContextTag::Check(ContextTag::TAG_CONSOLE)) {
|
||||
$this->aCSSClasses[] = 'ibo-input-select';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -483,6 +483,7 @@ class LoginWebPage extends NiceWebPage
|
||||
$iResponse = $oLoginFSMExtensionInstance->LoginAction($sLoginState, $iErrorCode);
|
||||
if ($iResponse == self::LOGIN_FSM_RETURN)
|
||||
{
|
||||
Session::WriteClose();
|
||||
return $iErrorCode; // Asked to exit FSM, generally login OK
|
||||
}
|
||||
if ($iResponse == self::LOGIN_FSM_ERROR)
|
||||
|
||||
@@ -77,17 +77,28 @@ function _MaintenanceHtmlMessage($sMessage)
|
||||
*/
|
||||
function _MaintenanceJsonMessage($sTitle, $sMessage)
|
||||
{
|
||||
@include_once(APPROOT."/application/ajaxwebpage.class.inc.php");
|
||||
if (class_exists('ajax_page'))
|
||||
if (class_exists('JsonPage'))
|
||||
{
|
||||
$oP = new ajax_page($sTitle);
|
||||
$oP = new JsonPage($sTitle);
|
||||
$oP->add_header('Access-Control-Allow-Origin: *');
|
||||
$oP->SetContentType('application/json');
|
||||
$oP->add('{"code":100, "message":"'.$sMessage.'"}');
|
||||
|
||||
$aMessage = [
|
||||
'code' => 100,
|
||||
'message' =>$sMessage
|
||||
];
|
||||
|
||||
$oP->AddData($aMessage);
|
||||
$oP->Output();
|
||||
}
|
||||
else
|
||||
{
|
||||
_MaintenanceTextMessage($sMessage);
|
||||
} else {
|
||||
@include_once(APPROOT."/application/ajaxwebpage.class.inc.php");
|
||||
if (class_exists('ajax_page')) {
|
||||
$oP = new ajax_page($sTitle);
|
||||
$oP->add_header('Access-Control-Allow-Origin: *');
|
||||
$oP->SetContentType('application/json');
|
||||
$oP->add('{"code":100, "message":"'.$sMessage.'"}');
|
||||
$oP->Output();
|
||||
} else {
|
||||
_MaintenanceTextMessage($sMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Helper\WebResourcesHelper;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Title\Title;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
|
||||
|
||||
require_once(APPROOT.'/application/utils.inc.php');
|
||||
@@ -1126,16 +1127,20 @@ class OQLMenuNode extends MenuNode
|
||||
{
|
||||
$sUsageId = utils::GetSafeId($sUsageId);
|
||||
$oSearch = DBObjectSearch::FromOQL($sOql);
|
||||
|
||||
$sClass= $oSearch->GetClass();
|
||||
$sIcon = MetaModel::GetClassIcon($sClass, false);
|
||||
if ($bSearchPane) {
|
||||
$aParams = array_merge(['open' => $bSearchOpen, 'table_id' => $sUsageId, 'submit_on_load' => false], $aExtraParams);
|
||||
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
|
||||
$oBlock->Display($oPage, 0);
|
||||
$oPage->add("<div class='sf_results_area ibo-add-margin-top-250' data-target='search_results'>");
|
||||
}
|
||||
|
||||
$oPage->add("<div class='sf_results_area' data-target='search_results'>");
|
||||
$oTitle = TitleUIBlockFactory::MakeForPage($sTitle);
|
||||
$oPage->AddUiBlock($oTitle);
|
||||
else {
|
||||
$oPage->add("<div class='sf_results_area' data-target='search_results'>");
|
||||
}
|
||||
$aExtraParams['panel_class'] =$sClass;
|
||||
$aExtraParams['panel_title'] = $sTitle;
|
||||
$aExtraParams['panel_icon'] = $sIcon;
|
||||
|
||||
$aParams = array_merge(array('table_id' => $sUsageId), $aExtraParams);
|
||||
$oBlock = new DisplayBlock($oSearch, 'list', false /* Asynchronous */, $aParams);
|
||||
|
||||
@@ -126,7 +126,7 @@ class QueryOQL extends Query
|
||||
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
|
||||
{
|
||||
$aFieldsMap = parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
|
||||
$oPage->add_script("$('[name=\"attr_oql\"]').addClass('ibo-queryoql'); $('[data-attribute-code=\"oql\"]').addClass('ibo-queryoql');");
|
||||
$oPage->add_script("$('[name=\"attr_oql\"]').addClass('ibo-query-oql ibo-is-code'); $('[data-attribute-code=\"oql\"]').addClass('ibo-query-oql ibo-is-code');");
|
||||
|
||||
if (!$bEditMode) {
|
||||
$sFields = trim($this->Get('fields'));
|
||||
|
||||
@@ -288,7 +288,8 @@ class ShortcutOQL extends Shortcut
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
// Note: the title gets deleted by the validation mechanism
|
||||
$("#attr_auto_reload_sec").tooltip({items: 'input', content: '$sRateTitle'});
|
||||
$("#attr_auto_reload_sec").attr('data-tooltip-content', '$sRateTitle');
|
||||
CombodoTooltip.InitTooltipFromMarkup($("#attr_auto_reload_sec"));
|
||||
$("#attr_auto_reload_sec").prop('disabled', !$('#attr_auto_reload').is(':checked'));
|
||||
|
||||
$('#attr_auto_reload').change( function(ev) {
|
||||
|
||||
@@ -41,36 +41,31 @@ register_shutdown_function(function()
|
||||
$sReservedMemory = null;
|
||||
if (!is_null($err = error_get_last()) && ($err['type'] == E_ERROR))
|
||||
{
|
||||
// Remove stack trace from MySQLException
|
||||
// Remove stack trace from MySQLException (since 2.7.2 see N°3174)
|
||||
$sMessage = $err['message'];
|
||||
if (strpos($sMessage, 'MySQLException') !== false)
|
||||
{
|
||||
if (strpos($sMessage, 'MySQLException') !== false) {
|
||||
$iStackTracePos = strpos($sMessage, 'Stack trace:');
|
||||
if ($iStackTracePos !== false)
|
||||
{
|
||||
if ($iStackTracePos !== false) {
|
||||
$sMessage = substr($sMessage, 0, $iStackTracePos);
|
||||
}
|
||||
}
|
||||
IssueLog::error($sMessage, null, $err);
|
||||
if (strpos($err['message'], 'Allowed memory size of') !== false)
|
||||
{
|
||||
// Log additional info but message from $err (since 2.7.6 N°4174)
|
||||
$aErrToLog = $err;
|
||||
unset($aErrToLog['message']);
|
||||
IssueLog::error($sMessage, null, $aErrToLog);
|
||||
if (strpos($err['message'], 'Allowed memory size of') !== false) {
|
||||
$sLimit = ini_get('memory_limit');
|
||||
echo "<p>iTop: Allowed memory size of $sLimit exhausted, contact your administrator to increase 'memory_limit' in php.ini</p>\n";
|
||||
}
|
||||
elseif (strpos($err['message'], 'Maximum execution time') !== false)
|
||||
{
|
||||
} elseif (strpos($err['message'], 'Maximum execution time') !== false) {
|
||||
$sLimit = ini_get('max_execution_time');
|
||||
echo "<p>iTop: Maximum execution time of $sLimit exceeded, contact your administrator to increase 'max_execution_time' in php.ini</p>\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
echo "<p>iTop: An error occurred, check server error log for more information.</p>\n";
|
||||
}
|
||||
}
|
||||
});
|
||||
$oKPI = new ExecutionKPI();
|
||||
Session::Start();
|
||||
Session::WriteClose();
|
||||
$oKPI->ComputeAndReport("Session Start");
|
||||
|
||||
$sSwitchEnv = utils::ReadParam('switch_env', null);
|
||||
|
||||
@@ -29,7 +29,7 @@ class ThemeHandlerService
|
||||
{
|
||||
}
|
||||
|
||||
public function CompileTheme($sThemeId, $bSetup = false, $sSetupCompilationTimestamp="", $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null){
|
||||
return ThemeHandler::CompileTheme($sThemeId, $bSetup, $sSetupCompilationTimestamp="", $aThemeParameters, $aImportsPaths, $sWorkingPath);
|
||||
public function CompileTheme($sThemeId, $bSetup = false, $sSetupCompilationTimestamp = "", $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null){
|
||||
return ThemeHandler::CompileTheme($sThemeId, $bSetup, $sSetupCompilationTimestamp, $aThemeParameters, $aImportsPaths, $sWorkingPath);
|
||||
}
|
||||
}
|
||||
@@ -196,16 +196,19 @@ class privUITransactionSession
|
||||
*/
|
||||
class privUITransactionFile
|
||||
{
|
||||
/** @var int Value to use when no user logged */
|
||||
const UNAUTHENTICATED_USER_ID = -666;
|
||||
|
||||
/**
|
||||
* @return int
|
||||
* @throws \SecurityException if no connected user
|
||||
* @return int current user id, or {@see self::UNAUTHENTICATED_USER_ID} if no user logged
|
||||
*
|
||||
* @since 2.6.5 2.7.6 3.0.0 N°4289 method creation
|
||||
*/
|
||||
private static function GetCurrentUserId() {
|
||||
private static function GetCurrentUserId()
|
||||
{
|
||||
$iCurrentUserId = UserRights::GetConnectedUserId();
|
||||
if ('' === $iCurrentUserId) {
|
||||
throw new SecurityException('Cannot creation transaction_id when no user logged');
|
||||
$iCurrentUserId = static::UNAUTHENTICATED_USER_ID;
|
||||
}
|
||||
|
||||
return $iCurrentUserId;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Combodo\iTop;
|
||||
|
||||
use AttributeDate;
|
||||
use AttributeDateTime;
|
||||
use Dict;
|
||||
use Exception;
|
||||
@@ -55,6 +56,10 @@ class TwigExtension
|
||||
{
|
||||
return AttributeDateTime::GetFormat()->Format($sDate);
|
||||
}
|
||||
if (preg_match('@^\d\d\d\d-\d\d-\d\d$@', trim($sDate)))
|
||||
{
|
||||
return AttributeDate::GetFormat()->Format($sDate);
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
|
||||
@@ -61,6 +61,8 @@ class UIExtKeyWidget
|
||||
protected $sAttCode;
|
||||
protected $bSearchMode;
|
||||
|
||||
//public function __construct($sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sNameSuffix = '', $sFieldPrefix = '', $sFormPrefix = '')
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param string $sAttCode
|
||||
@@ -80,18 +82,13 @@ class UIExtKeyWidget
|
||||
* @throws \Exception
|
||||
*
|
||||
* @since 3.0.0 N°3750 new $sInputType parameter
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°3129 Add default value for $aArgs for PHP 8.0 compat
|
||||
*/
|
||||
public static function DisplayFromAttCode(
|
||||
$oPage, $sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName = '', $sFormPrefix = '',
|
||||
$aArgs = [], $bSearchMode = false, &$sInputType = ''
|
||||
)
|
||||
{
|
||||
// we will only use key & name, so let's reduce fields loaded !
|
||||
$aAttToLoad = [
|
||||
$sClass => [], // nothing, id and friendlyname are automatically added by the API
|
||||
];
|
||||
$oAllowedValues->OptimizeColumnLoad($aAttToLoad);
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$iMaxComboLength = $oAttDef->GetMaximumComboLength();
|
||||
@@ -196,7 +193,7 @@ class UIExtKeyWidget
|
||||
|
||||
$bIsAutocomplete = $oAllowedValues->CountExceeds($iMaxComboLength);
|
||||
$sWrapperCssClass = $bIsAutocomplete ? 'ibo-input-select-autocomplete-wrapper' : 'ibo-input-select-wrapper';
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey ibo-input-wrapper ibo-input-select-wrapper--with-buttons $sWrapperCssClass\" data-attcode=\"".$this->sAttCode."\" data-validation=\"untouched\">";
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey ibo-input-wrapper ibo-input-select-wrapper--with-buttons $sWrapperCssClass\" data-attcode=\"".$this->sAttCode."\" data-validation=\"untouched\" data-accessibility-selectize-label=\"$sTitle\">";
|
||||
|
||||
// We just need to compare the number of entries with MaxComboLength, so no need to get the real count.
|
||||
if (!$bIsAutocomplete) {
|
||||
@@ -254,7 +251,7 @@ class UIExtKeyWidget
|
||||
$aOption['picture_url'] = $oImage->GetDisplayURL($sClassAllowed, $oObj->GetKey(), $sObjectImageAttCode);
|
||||
$aOption['initials'] = '';
|
||||
} else {
|
||||
$aOption['initials'] = utils::ToAcronym($oObj->Get('friendlyname'));
|
||||
$aOption['initials'] = utils::FormatInitialsForMedallion(utils::ToAcronym($oObj->Get('friendlyname')));
|
||||
}
|
||||
}
|
||||
array_push($aOptions, $aOption);
|
||||
@@ -704,7 +701,7 @@ JS
|
||||
HTML
|
||||
);
|
||||
|
||||
$sDialogTitleSanitized = utils::HtmlToText($sTitle);
|
||||
$sDialogTitleSanitized = addslashes(utils::HtmlToText($sTitle));
|
||||
$oPage->add_ready_script(<<<JS
|
||||
$('#ac_dlg_{$this->iId}').dialog({
|
||||
width: $(window).width()*0.8,
|
||||
@@ -776,9 +773,11 @@ JS
|
||||
*
|
||||
* @throws CoreException
|
||||
* @throws OQLException
|
||||
*
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $oObj for PHP 8.0 compatibility
|
||||
*/
|
||||
public function AutoComplete(
|
||||
WebPage $oP, $sFilter, $oObj = null, $sContains = '', $sOutputFormat = self::ENUM_OUTPUT_FORMAT_CSV, $sOperation = null
|
||||
WebPage $oP, $sFilter, $oObj, $sContains, $sOutputFormat = self::ENUM_OUTPUT_FORMAT_CSV, $sOperation = null
|
||||
)
|
||||
{
|
||||
if (is_null($sFilter)) {
|
||||
@@ -824,13 +823,13 @@ JS
|
||||
|
||||
$aJsonMap = array();
|
||||
foreach ($aValues as $sKey => $aValue) {
|
||||
$aElt = ['value' => $sKey, 'label' => utils::HtmlEntities($aValue['label']), 'obsolescence_flag' => $aValue['obsolescence_flag']];
|
||||
$aElt = ['value' => $sKey, 'label' => utils::EscapeHtml($aValue['label']), 'obsolescence_flag' => $aValue['obsolescence_flag']];
|
||||
if ($aValue['additional_field'] != '') {
|
||||
$aElt['additional_field'] = utils::HtmlEntities($aValue['additional_field']);
|
||||
$aElt['additional_field'] = utils::EscapeHtml($aValue['additional_field']);
|
||||
}
|
||||
|
||||
if (array_key_exists('initials', $aValue)) {
|
||||
$aElt['initials'] = $aValue['initials'];
|
||||
$aElt['initials'] = utils::FormatInitialsForMedallion($aValue['initials']);
|
||||
if (array_key_exists('picture_url', $aValue)) {
|
||||
$aElt['picture_url'] = $aValue['picture_url'];
|
||||
}
|
||||
@@ -913,8 +912,8 @@ JS
|
||||
$oClassForm = FormUIBlockFactory::MakeStandard();
|
||||
$oBlock->AddSubBlock($oClassForm);
|
||||
$oClassForm->AddSubBlock(cmdbAbstractObject::DisplayBlockSelectClassToCreate( $sClassLabel, $this->sTargetClass, $aPossibleClasses));
|
||||
|
||||
$oPage->add_ready_script("$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true, title: '$sDialogTitle'});\n");
|
||||
$sDialogTitleEscaped = addslashes($sDialogTitle);
|
||||
$oPage->add_ready_script("$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true, title: '$sDialogTitleEscaped'});\n");
|
||||
$oPage->add_ready_script("$('#ac_create_{$this->iId} form').removeAttr('onsubmit');");
|
||||
$oPage->add_ready_script("$('#ac_create_{$this->iId} form').on('submit.uilinksWizard', oACWidget_{$this->iId}.DoSelectObjectClass);");
|
||||
}
|
||||
@@ -1008,16 +1007,46 @@ JS
|
||||
|
||||
if ($bHasChildLeafs)
|
||||
{
|
||||
$oPage->add('<div class="treecontrol" id="treecontrolid"><a href="?#">'.Dict::S("UI:Treeview:CollapseAll").'</a> | <a href="?#">'.Dict::S("UI:Treeview:ExpandAll").'</a></div>');
|
||||
$oPage->add('<span class="treecontrol ibo-button-group" id="treecontrolid"><a class="ibo-button ibo-is-regular ibo-is-neutral" href="?#">'.Dict::S("UI:Treeview:CollapseAll").'</a><a class="ibo-button ibo-is-regular ibo-is-neutral" href="?#">'.Dict::S("UI:Treeview:ExpandAll").'</a></span>');
|
||||
}
|
||||
|
||||
$oPage->add("<input type=\"button\" class=\"ibo-button ibo-is-regular ibo-is-neutral\" id=\"btn_cancel_{$this->iId}\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_tree_{$this->iId}').dialog('close');\"> ");
|
||||
$oPage->add("<input type=\"button\" class=\"ibo-button ibo-is-regular ibo-is-primary\" id=\"btn_ok_{$this->iId}\" value=\"".Dict::S('UI:Button:Ok')."\" onClick=\"oACWidget_{$this->iId}.DoHKOk();\">");
|
||||
|
||||
|
||||
$oPage->add('</div></div>');
|
||||
|
||||
|
||||
$sOkButtonLabel = Dict::S('UI:Button:Ok');
|
||||
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
|
||||
$oPage->add_ready_script("\$('#tree_$this->iId ul').treeview({ control: '#treecontrolid', persist: 'false'});\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");
|
||||
$oPage->add_ready_script(<<<JS
|
||||
$('#dlg_tree_$this->iId').dialog({
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
autoOpen: true,
|
||||
modal: true,
|
||||
title: '$sDialogTitle',
|
||||
open: function() {
|
||||
$(this).css("max-height", parseInt($(window).height()*0.7)+'px');
|
||||
},
|
||||
buttons: [
|
||||
{
|
||||
text: "$sCancelButtonLabel",
|
||||
click: function() { $(this).dialog( "close" ); }
|
||||
},
|
||||
{
|
||||
text: "$sOkButtonLabel",
|
||||
class: "ibo-is-primary",
|
||||
click: function() {
|
||||
oACWidget_{$this->iId}.DoHKOk();
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
resizeStop: oACWidget_{$this->iId}.OnHKResize,
|
||||
close: oACWidget_{$this->iId}.OnHKClose
|
||||
});
|
||||
|
||||
$('#dlg_tree_$this->iId + .ui-dialog-buttonpane .ui-dialog-buttonset').prepend($('#treecontrolid'));
|
||||
|
||||
JS
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -75,9 +75,15 @@ class UILinksWidgetDirect
|
||||
* @param array $aArgs
|
||||
* @param string $sFormPrefix
|
||||
* @param DBObject $oCurrentObj
|
||||
*
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $aArgs for PHP 8.0 compatibility (handling wrong values at method start)
|
||||
*/
|
||||
public function Display(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj)
|
||||
public function Display(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj)
|
||||
{
|
||||
if (empty($aArgs)) {
|
||||
$aArgs = [];
|
||||
}
|
||||
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
switch($oLinksetDef->GetEditMode())
|
||||
{
|
||||
@@ -127,8 +133,10 @@ class UILinksWidgetDirect
|
||||
* @param string $sFormPrefix
|
||||
* @param DBObject $oCurrentObj
|
||||
* @param bool $bDisplayMenu
|
||||
*
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $aArgs for PHP 8.0 compatibility (protected method, always called with default value)
|
||||
*/
|
||||
protected function DisplayAsBlock(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $bDisplayMenu)
|
||||
protected function DisplayAsBlock(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $bDisplayMenu)
|
||||
{
|
||||
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
$sTargetClass = $oLinksetDef->GetLinkedClass();
|
||||
@@ -197,7 +205,6 @@ class UILinksWidgetDirect
|
||||
|
||||
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);
|
||||
@@ -229,8 +236,10 @@ class UILinksWidgetDirect
|
||||
* @param string $sFormPrefix
|
||||
* @param DBObject $oCurrentObj
|
||||
* @param array $aButtons
|
||||
*
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $aArgs for PHP 8.0 compatibility (protected method, caller already handles it)
|
||||
*/
|
||||
protected function DisplayEditInPlace(WebPage $oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, $aButtons = array('create', 'delete'))
|
||||
protected function DisplayEditInPlace(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $aButtons = array('create', 'delete'))
|
||||
{
|
||||
$aAttribs = $this->GetTableConfig();
|
||||
$oValue->Rewind();
|
||||
@@ -297,7 +306,7 @@ class UILinksWidgetDirect
|
||||
}
|
||||
$oHiddenCriteria = $oHiddenFilter->GetCriteria();
|
||||
$aArgs = $oHiddenFilter->GetInternalParams();
|
||||
$sHiddenCriteria = $oHiddenCriteria->Render($aArgs);
|
||||
$sHiddenCriteria = $oHiddenCriteria->RenderExpression(false, $aArgs);
|
||||
|
||||
$oLinkSetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
|
||||
$valuesDef = $oLinkSetDef->GetValuesDef();
|
||||
@@ -474,6 +483,33 @@ HTML
|
||||
}
|
||||
return $oPage->GetTableRow($aRow, $aAttribs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param WebPage $oPage
|
||||
* @param $sRealClass
|
||||
* @param $aValues
|
||||
* @param int $iTempId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetFormRow($oPage, $sRealClass, $aValues, $iTempId)
|
||||
{
|
||||
if ($sRealClass == '')
|
||||
{
|
||||
$sRealClass = $this->sLinkedClass;
|
||||
}
|
||||
$oLinkObj = new $sRealClass();
|
||||
$oLinkObj->UpdateObjectFromPostedForm($this->sInputid);
|
||||
|
||||
$aAttribs = $this->GetTableConfig();
|
||||
$aRow = array();
|
||||
$aRow[] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.($iTempId).'"/>';
|
||||
foreach($this->aZlist as $sLinkedAttCode)
|
||||
{
|
||||
$aRow[] = $oLinkObj->GetAsHTML($sLinkedAttCode);
|
||||
}
|
||||
return $aRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the default search parameters based on 1) a 'current' object and 2) the silos defined by the context
|
||||
|
||||
@@ -375,6 +375,7 @@ JS
|
||||
|
||||
$oValue->Rewind();
|
||||
$aForm = array();
|
||||
$iMaxAddedId = 0;
|
||||
$iAddedId = -1; // Unique id for new links
|
||||
while ($oCurrentLink = $oValue->Fetch())
|
||||
{
|
||||
@@ -395,8 +396,12 @@ JS
|
||||
} else {
|
||||
$key = $oCurrentLink->GetKey();
|
||||
}
|
||||
|
||||
$iMaxAddedId = max($iMaxAddedId, $key);
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly);
|
||||
}
|
||||
$oBlock->iMaxAddedId = (int) $iMaxAddedId;
|
||||
|
||||
$oDataTable = DataTableUIBlockFactory::MakeForForm("{$this->m_sAttCode}{$this->m_sNameSuffix}", $this->m_aTableConfig, $aForm);
|
||||
$oDataTable->SetOptions(['select_mode' => 'custom']);
|
||||
$oBlock->AddSubBlock($oDataTable);
|
||||
|
||||
@@ -59,7 +59,7 @@ class UIPasswordWidget
|
||||
$sConfirmPasswordValue = $aPasswordValues ? $aPasswordValues['confirm'] : '*****';
|
||||
$sChangedValue = (($sPasswordValue != '*****') || ($sConfirmPasswordValue != '*****')) ? 1 : 0;
|
||||
$sHtmlValue = '';
|
||||
$sHtmlValue .= '<div class="field_input_zone field_input_onewaypassword ibo-input-wrapper">';
|
||||
$sHtmlValue .= '<div class="field_input_zone field_input_onewaypassword ibo-input-wrapper ibo-input-one-way-password-wrapper">';
|
||||
$sHtmlValue .= '<input class="ibo-input" type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$sHtmlValue .= '<div class="ibo-input-wrapper ibo-input-wrapper--with-buttons"><input class="ibo-input" type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'[confirm]"/>';
|
||||
$sHtmlValue .= '<div class="ibo-input-select--action-buttons"><div class="ibo-input-select--action-button ibo-input-select--action-button--create" data-tooltip-content="'.Dict::S('UI:PasswordConfirm').'"><i class="fas fa-question-circle"></i></div></div></div>';
|
||||
|
||||
@@ -2118,11 +2118,21 @@ class utils
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the relative (to MODULESROOT) path of the root directory of the module containing the file where the call to
|
||||
* this function is made
|
||||
* or an empty string if no such module is found (or not called within a module file)
|
||||
* @param number $iCallDepth The depth of the module in the callstack. Zero when called directly from within the module
|
||||
* @return string
|
||||
* **Warning** : returned result can be invalid as we're using backtrace to find the module dir name
|
||||
*
|
||||
* @param int $iCallDepth The depth of the module in the callstack. Zero when called directly from within the module
|
||||
*
|
||||
* @return string the relative (to MODULESROOT) path of the root directory of the module containing the file where the call to
|
||||
* this function is made
|
||||
* or an empty string if no such module is found (or not called within a module file)
|
||||
*
|
||||
* @uses \debug_backtrace()
|
||||
*
|
||||
* @since 3.0.0 Before writing model.*.php file, compiler will now always delete it.
|
||||
* If you have symlinks enabled, base dir will be original module dir, but since this behavior change this won't be true anymore for model.*.php
|
||||
* In consequence the backtrace analysis won't be possible for this file
|
||||
* See N°4854
|
||||
* @link https://www.itophub.io/wiki/page?id=3_0_0%3Arelease%3A3_0_whats_new#compiler_always_generate_new_model_php compiler behavior change documentation
|
||||
*/
|
||||
public static function GetCurrentModuleDir($iCallDepth)
|
||||
{
|
||||
@@ -2147,9 +2157,14 @@ class utils
|
||||
}
|
||||
|
||||
/**
|
||||
* **Warning** : as this method uses {@see GetCurrentModuleDir} it produces hazardous results.
|
||||
* You should better uses directly {@see GetAbsoluteUrlModulesRoot} and add the module dir name yourself ! See N°4573
|
||||
*
|
||||
* @return string the base URL for all files in the current module from which this method is called
|
||||
* or an empty string if no such module is found (or not called within a module file)
|
||||
* @throws \Exception
|
||||
*
|
||||
* @uses GetCurrentModuleDir
|
||||
*/
|
||||
public static function GetCurrentModuleUrl()
|
||||
{
|
||||
@@ -2404,43 +2419,19 @@ class utils
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string eg : '2_7_0' ITOP_VERSION is '2.7.1-dev'
|
||||
* @return string eg : '2_7_0' if iTop core version is '2.7.5-2'
|
||||
* @throws \ApplicationException if constant value is invalid
|
||||
* @uses ITOP_CORE_VERSION
|
||||
*/
|
||||
public static function GetItopVersionWikiSyntax() {
|
||||
$sMinorVersion = self::GetItopMinorVersion();
|
||||
public static function GetItopVersionWikiSyntax($sItopVersion = ITOP_CORE_VERSION)
|
||||
{
|
||||
$aExplodedVersion = explode('.', $sItopVersion);
|
||||
|
||||
return str_replace('.', '_', $sMinorVersion).'_0';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sPatchVersion if non provided, will call GetItopPatchVersion
|
||||
*
|
||||
* @return string eg 2.7 if ITOP_VERSION is '2.7.0-dev'
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function GetItopMinorVersion($sPatchVersion = null) {
|
||||
if (is_null($sPatchVersion)) {
|
||||
$sPatchVersion = self::GetItopPatchVersion();
|
||||
}
|
||||
$aExplodedVersion = explode('.', $sPatchVersion);
|
||||
|
||||
if (count($aExplodedVersion) < 2) {
|
||||
throw new Exception('iTop version is wrongfully configured!');
|
||||
}
|
||||
if (($aExplodedVersion[0] == '') || ($aExplodedVersion[1] == '')) {
|
||||
throw new Exception('iTop version is wrongfully configured!');
|
||||
if ((false === isset($aExplodedVersion[0])) || (false === isset($aExplodedVersion[1]))) {
|
||||
throw new ApplicationException('iTop version is wrongfully configured!');
|
||||
}
|
||||
|
||||
return sprintf('%d.%d', $aExplodedVersion[0], $aExplodedVersion[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string eg '2.7.0' if ITOP_VERSION is '2.7.0-dev'
|
||||
*/
|
||||
public static function GetItopPatchVersion() {
|
||||
$aExplodedVersion = explode('-', ITOP_VERSION);
|
||||
|
||||
return $aExplodedVersion[0];
|
||||
return "{$aExplodedVersion[0]}_{$aExplodedVersion[1]}_0";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3059,6 +3050,20 @@ HTML;
|
||||
return $aMentionedObjects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: This method is not ideal, but other solutions seemed even less ideal:
|
||||
* * Add a "$sMaxLength" param. to utils::ToAcronym(): Does not work for every use cases (see corresponding ticket) as in some parts utils::ToAcronym isn't necessarly meant to be used in a medallion.
|
||||
*
|
||||
* @param string $sInitials
|
||||
*
|
||||
* @return string Truncates $sInitials so it can fit in medallions
|
||||
* @since 3.0.1 N°4913
|
||||
*/
|
||||
public static function FormatInitialsForMedallion(string $sInitials): string
|
||||
{
|
||||
return mb_substr($sInitials, 0, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sUrl
|
||||
* @param string $sParamName
|
||||
|
||||
@@ -4,8 +4,25 @@ define('APPROOT', dirname(__FILE__).'/');
|
||||
define('APPCONF', APPROOT.'conf/');
|
||||
|
||||
/**
|
||||
* iTop framework Version
|
||||
* iTop Datamodel XML format version
|
||||
*
|
||||
* It was also used in iTop 3.0.0 to get iTop core version (instead of {@see ITOP_VERSION} which gives the application version).
|
||||
* To address this need you should now use {@see ITOP_CORE_VERSION}
|
||||
*
|
||||
* @see ITOP_CORE_VERSION to get full iTop core version
|
||||
*/
|
||||
define('ITOP_DESIGN_LATEST_VERSION', '3.0');
|
||||
|
||||
/**
|
||||
* Constant containing the iTop core version, whatever application was built
|
||||
*
|
||||
* Note that in iTop 3.0.0 we used {@see ITOP_DESIGN_LATEST_VERSION} to get core version.
|
||||
* When releasing, both constants should be updated : see `.make/release/update-versions.php` for that !
|
||||
*
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°4714 constant creation
|
||||
* @used-by utils::GetItopVersionWikiSyntax()
|
||||
* @used-by iTopModulesPhpVersionIntegrationTest
|
||||
*/
|
||||
define('ITOP_CORE_VERSION', '3.0.1');
|
||||
|
||||
require_once APPROOT.'bootstrap.inc.php';
|
||||
|
||||
@@ -16,16 +16,16 @@
|
||||
"pelago/emogrifier": "3.1.0",
|
||||
"scssphp/scssphp": "1.0.6",
|
||||
"swiftmailer/swiftmailer": "5.4.12",
|
||||
"symfony/console": "3.4.*",
|
||||
"symfony/dotenv": "3.4.*",
|
||||
"symfony/framework-bundle": "3.4.*",
|
||||
"symfony/console": "~3.4.47",
|
||||
"symfony/dotenv": "~3.4.47",
|
||||
"symfony/framework-bundle": "~3.4.47",
|
||||
"symfony/polyfill-php70": "1.*",
|
||||
"symfony/twig-bundle": "3.4.*",
|
||||
"symfony/yaml": "3.4.*"
|
||||
"symfony/twig-bundle": "~3.4.47",
|
||||
"symfony/yaml": "~3.4.47"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/stopwatch": "3.4.*",
|
||||
"symfony/web-profiler-bundle": "3.4.*"
|
||||
"symfony/stopwatch": "~3.4.47",
|
||||
"symfony/web-profiler-bundle": "~3.4.47"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-libsodium": "Required to use the AttributeEncryptedString.",
|
||||
|
||||
905
composer.lock
generated
905
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -118,6 +118,39 @@ abstract class Action extends cmdbAbstractObject
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function AfterInsert()
|
||||
{
|
||||
parent::AfterInsert();
|
||||
$this->DoCheckIfHasTrigger();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function AfterUpdate()
|
||||
{
|
||||
parent::AfterUpdate();
|
||||
$this->DoCheckIfHasTrigger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Action has at least 1 trigger linked. Otherwise, it adds a warning.
|
||||
* @return void
|
||||
* @since 3.0.0
|
||||
*/
|
||||
protected function DoCheckIfHasTrigger()
|
||||
{
|
||||
$oTriggersSet = $this->Get('trigger_list');
|
||||
if ($oTriggersSet->Count() === 0) {
|
||||
$this->m_aCheckWarnings[] = Dict::S('Action:WarningNoTriggerLinked');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,6 +200,17 @@ abstract class ActionNotification extends Action
|
||||
*/
|
||||
class ActionEmail extends ActionNotification
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.1
|
||||
*/
|
||||
const ENUM_HEADER_NAME_MESSAGE_ID = 'Message-ID';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.1
|
||||
*/
|
||||
const ENUM_HEADER_NAME_REFERENCES = 'References';
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@@ -174,13 +218,13 @@ class ActionEmail extends ActionNotification
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "grant_by_profile,core/cmdb,application",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_action_email",
|
||||
"db_key_field" => "id",
|
||||
"category" => "grant_by_profile,core/cmdb,application",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_action_email",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
@@ -367,8 +411,7 @@ class ActionEmail extends ActionNotification
|
||||
{
|
||||
$this->m_iRecipients = 0;
|
||||
$this->m_aMailErrors = array();
|
||||
$bRes = false; // until we do succeed in sending the email
|
||||
|
||||
|
||||
// Determine recipients
|
||||
//
|
||||
$sTo = $this->FindRecipients('to', $aContextArgs);
|
||||
@@ -382,37 +425,48 @@ class ActionEmail extends ActionNotification
|
||||
|
||||
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
|
||||
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
|
||||
|
||||
|
||||
$oObj = $aContextArgs['this->object()'];
|
||||
$sMessageId = sprintf('iTop_%s_%d_%f@%s.openitop.org', get_class($oObj), $oObj->GetKey(), microtime(true /* get as float*/), MetaModel::GetEnvironmentId());
|
||||
$sReference = '<'.$sMessageId.'>';
|
||||
$sMessageId = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_MESSAGE_ID);
|
||||
$sReference = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_REFERENCES);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
|
||||
throw $e;
|
||||
}
|
||||
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
|
||||
|
||||
if (!is_null($oLog))
|
||||
{
|
||||
catch (Exception $e) {
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
throw $e;
|
||||
}
|
||||
finally {
|
||||
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
|
||||
}
|
||||
|
||||
if (!is_null($oLog)) {
|
||||
// Note: we have to secure this because those values are calculated
|
||||
// inside the try statement, and we would like to keep track of as
|
||||
// many data as we could while some variables may still be undefined
|
||||
if (isset($sTo)) $oLog->Set('to', $sTo);
|
||||
if (isset($sCC)) $oLog->Set('cc', $sCC);
|
||||
if (isset($sBCC)) $oLog->Set('bcc', $sBCC);
|
||||
if (isset($sFrom)) $oLog->Set('from', empty($sFromLabel) ? $sFrom : "$sFromLabel <$sFrom>");
|
||||
if (isset($sSubject)) $oLog->Set('subject', $sSubject);
|
||||
if (isset($sBody)) $oLog->Set('body', $sBody);
|
||||
if (isset($sTo)) {
|
||||
$oLog->Set('to', $sTo);
|
||||
}
|
||||
if (isset($sCC)) {
|
||||
$oLog->Set('cc', $sCC);
|
||||
}
|
||||
if (isset($sBCC)) {
|
||||
$oLog->Set('bcc', $sBCC);
|
||||
}
|
||||
if (isset($sFrom)) {
|
||||
$oLog->Set('from', $sFrom);
|
||||
}
|
||||
if (isset($sSubject)) {
|
||||
$oLog->Set('subject', $sSubject);
|
||||
}
|
||||
if (isset($sBody)) {
|
||||
$oLog->Set('body', $sBody);
|
||||
}
|
||||
}
|
||||
$sStyles = file_get_contents(APPROOT.'css/email.css');
|
||||
$sStyles .= MetaModel::GetConfig()->Get('email_css');
|
||||
|
||||
$oEmail = new EMail();
|
||||
|
||||
if ($this->IsBeingTested())
|
||||
{
|
||||
if ($this->IsBeingTested()) {
|
||||
$oEmail->SetSubject('TEST['.$sSubject.']');
|
||||
$sTestBody = $sBody;
|
||||
$sTestBody .= "<div style=\"border: dashed;\">\n";
|
||||
@@ -422,8 +476,8 @@ class ActionEmail extends ActionNotification
|
||||
$sTestBody .= "<li>TO: $sTo</li>\n";
|
||||
$sTestBody .= "<li>CC: $sCC</li>\n";
|
||||
$sTestBody .= "<li>BCC: $sBCC</li>\n";
|
||||
$sTestBody .= empty($sFromLabel) ? "<li>From: $sFrom</li>\n": "<li>From: $sFromLabel <$sFrom></li>\n";
|
||||
$sTestBody .= empty($sReplyToLabel) ? "<li>Reply-To: $sReplyTo</li>\n": "<li>Reply-To: $sReplyToLabel <$sReplyTo></li>\n";
|
||||
$sTestBody .= empty($sFromLabel) ? "<li>From: $sFrom</li>\n" : "<li>From: $sFromLabel <$sFrom></li>\n";
|
||||
$sTestBody .= empty($sReplyToLabel) ? "<li>Reply-To: $sReplyTo</li>\n" : "<li>Reply-To: $sReplyToLabel <$sReplyTo></li>\n";
|
||||
$sTestBody .= "<li>References: $sReference</li>\n";
|
||||
$sTestBody .= "</ul>\n";
|
||||
$sTestBody .= "</p>\n";
|
||||
@@ -433,9 +487,9 @@ class ActionEmail extends ActionNotification
|
||||
$oEmail->SetRecipientFrom($sFrom, $sFromLabel);
|
||||
$oEmail->SetReferences($sReference);
|
||||
$oEmail->SetMessageId($sMessageId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Note: N°4849 We pass the "References" identifier instead of the "Message-ID" on purpose as we want notifications emails to group around the triggering iTop object, not just the users' replies to the notification
|
||||
$oEmail->SetInReplyTo($sReference);
|
||||
} else {
|
||||
$oEmail->SetSubject($sSubject);
|
||||
$oEmail->SetBody($sBody, 'text/html', $sStyles);
|
||||
$oEmail->SetRecipientTO($sTo);
|
||||
@@ -445,6 +499,8 @@ class ActionEmail extends ActionNotification
|
||||
$oEmail->SetRecipientReplyTo($sReplyTo, $sReplyToLabel);
|
||||
$oEmail->SetReferences($sReference);
|
||||
$oEmail->SetMessageId($sMessageId);
|
||||
// Note: N°4849 We pass the "References" identifier instead of the "Message-ID" on purpose as we want notifications emails to group around the triggering iTop object, not just the users' replies to the notification
|
||||
$oEmail->SetInReplyTo($sReference);
|
||||
}
|
||||
|
||||
if (isset($aContextArgs['attachments']))
|
||||
@@ -471,26 +527,64 @@ class ActionEmail extends ActionNotification
|
||||
{
|
||||
case EMAIL_SEND_OK:
|
||||
return "Sent";
|
||||
|
||||
|
||||
case EMAIL_SEND_PENDING:
|
||||
return "Pending";
|
||||
|
||||
|
||||
case EMAIL_SEND_ERROR:
|
||||
return "Errors: ".implode(', ', $aErrors);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_array($this->m_aMailErrors) && count($this->m_aMailErrors) > 0)
|
||||
{
|
||||
} else {
|
||||
if (is_array($this->m_aMailErrors) && count($this->m_aMailErrors) > 0) {
|
||||
$sError = implode(', ', $this->m_aMailErrors);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sError = 'Unknown reason';
|
||||
}
|
||||
|
||||
return 'Notification was not sent: '.$sError;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBObject $oObject
|
||||
* @param string $sHeaderName {@see \ActionEmail::ENUM_HEADER_NAME_REFERENCES}, {@see \ActionEmail::ENUM_HEADER_NAME_MESSAGE_ID}
|
||||
*
|
||||
* @return string The formatted identifier for $sHeaderName based on $oObject
|
||||
* @throws \Exception
|
||||
* @since 3.0.1 N°4849
|
||||
*/
|
||||
protected function GenerateIdentifierForHeaders(DBObject $oObject, string $sHeaderName): string
|
||||
{
|
||||
$sObjClass = get_class($oObject);
|
||||
$sObjId = $oObject->GetKey();
|
||||
$sAppName = utils::Sanitize(ITOP_APPLICATION_SHORT, '', utils::ENUM_SANITIZATION_FILTER_VARIABLE_NAME);
|
||||
|
||||
switch ($sHeaderName) {
|
||||
case static::ENUM_HEADER_NAME_MESSAGE_ID:
|
||||
case static::ENUM_HEADER_NAME_REFERENCES:
|
||||
// Prefix
|
||||
$sPrefix = sprintf('%s_%s_%d', $sAppName, $sObjClass, $sObjId);
|
||||
if ($sHeaderName === static::ENUM_HEADER_NAME_MESSAGE_ID) {
|
||||
$sPrefix .= sprintf('_%f', microtime(true /* get as float*/));
|
||||
}
|
||||
// Suffix
|
||||
$sSuffix = sprintf('@%s.openitop.org', MetaModel::GetEnvironmentId());
|
||||
// Identifier
|
||||
$sIdentifier = $sPrefix.$sSuffix;
|
||||
if ($sHeaderName === static::ENUM_HEADER_NAME_REFERENCES) {
|
||||
$sIdentifier = "<$sIdentifier>";
|
||||
}
|
||||
|
||||
return $sIdentifier;
|
||||
}
|
||||
|
||||
// Requested header name invalid
|
||||
$sErrorMessage = sprintf('%s: Could not generate identifier for header "%s", only %s are supported', static::class, $sHeaderName, implode(' / ', [static::ENUM_HEADER_NAME_MESSAGE_ID, static::ENUM_HEADER_NAME_REFERENCES]));
|
||||
IssueLog::Error($sErrorMessage, LogChannels::NOTIFICATIONS, [
|
||||
'Object' => $sObjClass.'::'.$sObjId.' ('.$oObject->GetRawName().')',
|
||||
'Action' => get_class($this).'::'.$this->GetKey().' ('.$this->GetRawName().')',
|
||||
]);
|
||||
throw new Exception($sErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
42
core/apc-service.class.inc.php
Normal file
42
core/apc-service.class.inc.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Class ApcService
|
||||
* @since 2.7.6 N°4125
|
||||
*/
|
||||
class ApcService {
|
||||
public function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $function_name
|
||||
* @return bool
|
||||
* @see function_exists()
|
||||
*/
|
||||
public function function_exists($function_name) {
|
||||
return function_exists($function_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|array $key
|
||||
* @return mixed
|
||||
* @see apc_fetch()
|
||||
*/
|
||||
function apc_fetch($key)
|
||||
{
|
||||
return apc_fetch($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|string $key
|
||||
* @param $var
|
||||
* @param int $ttl
|
||||
* @return array|bool
|
||||
* @see apc_store()
|
||||
*/
|
||||
function apc_store($key, $var = NULL, $ttl = 0)
|
||||
{
|
||||
return apc_store($key, $var, $ttl);
|
||||
}
|
||||
}
|
||||
?>
|
||||
@@ -4159,6 +4159,7 @@ class AttributeText extends AttributeString
|
||||
{
|
||||
$sValue = parent::GetAsHTML($sValue, $oHostObject, $bLocalize);
|
||||
$sValue = self::RenderWikiHtml($sValue);
|
||||
$sValue = nl2br($sValue);
|
||||
|
||||
return "<div $sStyle>$sValue</div>";
|
||||
}
|
||||
@@ -5716,7 +5717,7 @@ class AttributeMetaEnum extends AttributeEnum
|
||||
$aLocalizedValues = array();
|
||||
foreach($aRawValues as $sKey => $sValue)
|
||||
{
|
||||
$aLocalizedValues[$sKey] = Str::pure2html($this->GetValueLabel($sKey));
|
||||
$aLocalizedValues[$sKey] = $this->GetValueLabel($sKey);
|
||||
}
|
||||
|
||||
return $aLocalizedValues;
|
||||
@@ -8129,6 +8130,25 @@ class AttributeImage extends AttributeBlob
|
||||
return "Image";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @see AttributeBlob::MakeRealValue()
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
$oDoc = parent::MakeRealValue($proposedValue, $oHostObj);
|
||||
|
||||
if (($oDoc instanceof ormDocument)
|
||||
&& (false === $oDoc->IsEmpty())
|
||||
&& ($oDoc->GetMimeType() === 'image/svg+xml')) {
|
||||
$sCleanSvg = HTMLSanitizer::Sanitize($oDoc->GetData(), 'svg_sanitizer');
|
||||
$oDoc = new ormDocument($sCleanSvg, $oDoc->GetMimeType(), $oDoc->GetFileName());
|
||||
}
|
||||
|
||||
// The validation of the MIME Type is done by CheckFormat below
|
||||
return $oDoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the supplied ormDocument actually contains an image
|
||||
* {@inheritDoc}
|
||||
@@ -10832,7 +10852,7 @@ class AttributeClassAttCodeSet extends AttributeSet
|
||||
}
|
||||
}
|
||||
|
||||
$sLabelForHtmlAttribute = MetaModel::GetLabel($sAttClass, $sAttCode)." ($sAttCode)";
|
||||
$sLabelForHtmlAttribute = utils::HtmlEntities(MetaModel::GetLabel($sAttClass, $sAttCode)." ($sAttCode)");
|
||||
$aLocalizedValues[] = '<span class="attribute-set-item" data-code="'.$sAttCode.'" data-label="'.$sLabelForHtmlAttribute.'" data-description="" data-tooltip-content="'.$sLabelForHtmlAttribute.'">'.$sAttCode.'</span>';
|
||||
} catch (Exception $e)
|
||||
{
|
||||
@@ -11025,7 +11045,7 @@ class AttributeQueryAttCodeSet extends AttributeSet
|
||||
$aLocalizedValues = array();
|
||||
foreach ($value as $sAttCode) {
|
||||
if (isset($aAllowedAttributes[$sAttCode])) {
|
||||
$sLabelForHtmlAttribute = $aAllowedAttributes[$sAttCode];
|
||||
$sLabelForHtmlAttribute = utils::HtmlEntities($aAllowedAttributes[$sAttCode]);
|
||||
$aLocalizedValues[] = '<span class="attribute-set-item" data-code="'.$sAttCode.'" data-label="'.$sLabelForHtmlAttribute.'" data-description="" data-tooltip-content="'.$sLabelForHtmlAttribute.'">'.$sAttCode.'</span>';
|
||||
}
|
||||
}
|
||||
@@ -11574,19 +11594,20 @@ class AttributeTagSet extends AttributeSet
|
||||
$sTooltipContent = $sTagLabel;
|
||||
$sTooltipHtmlEnabled = 'false';
|
||||
} else {
|
||||
$sTagLabelEscaped = utils::EscapeHtml($sTagLabel);
|
||||
$sTooltipContent = <<<HTML
|
||||
<h4>$sTagLabel</h4>
|
||||
<h4>$sTagLabelEscaped</h4>
|
||||
<div>$sTagDescription</div>
|
||||
HTML;
|
||||
$sTooltipHtmlEnabled = 'true';
|
||||
}
|
||||
$sTooltipContent = utils::EscapeHtml($sTooltipContent);
|
||||
$sTooltipContent = utils::HtmlEntities($sTooltipContent);
|
||||
|
||||
$sHtml .= '<a'.$sLink.' class="attribute-set-item attribute-set-item-'.$sTagCode.'" data-code="'.$sTagCode.'" data-label="'.$sLabelForHtml.'" data-description="'.$sDescriptionForHtml.'" data-tooltip-content="'.$sTooltipContent.'" data-tooltip-html-enabled="'.$sTooltipHtmlEnabled.'">'.$sLabelForHtml.'</a>';
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHtml .= '<span class="attribute-set-item">'.$oTag.'</span>';
|
||||
$sHtml .= '<span class="attribute-set-item">'.utils::EscapeHtml($oTag).'</span>';
|
||||
}
|
||||
}
|
||||
$sHtml .= '</span>';
|
||||
|
||||
@@ -1077,7 +1077,7 @@ class CMDBChangeOpSetAttributeLinksTune extends CMDBChangeOpSetAttributeLinks
|
||||
{
|
||||
$oField = new FieldExpression('objclass', $oSearch->GetClassAlias());
|
||||
$sListExpr = '('.implode(', ', CMDBSource::Quote($aLinkClasses)).')';
|
||||
$sOQLCondition = $oField->Render()." IN $sListExpr";
|
||||
$sOQLCondition = $oField->RenderExpression()." IN $sListExpr";
|
||||
$oNewCondition = Expression::FromOQL($sOQLCondition);
|
||||
$oSearch->AddConditionExpression($oNewCondition);
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ abstract class CMDBObject extends DBObject
|
||||
*
|
||||
* @param string $sId ID of the user doing the change, null if not done by a user (eg. background task)
|
||||
*
|
||||
* @since 3.0.0
|
||||
* @since 3.0.0 N°2847 following the addition of CMDBChange.user_id
|
||||
*/
|
||||
public static function SetTrackUserId($sId)
|
||||
{
|
||||
|
||||
@@ -350,6 +350,12 @@ class CMDBSource
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @uses \CMDBSource::QueryToCol() so needs a connection opened !
|
||||
*/
|
||||
public static function GetDBVersion()
|
||||
{
|
||||
$aVersions = self::QueryToCol('SELECT Version() as version', 'version');
|
||||
@@ -367,8 +373,10 @@ class CMDBSource
|
||||
/**
|
||||
* Get the DB vendor between MySQL and its main forks
|
||||
* @return string
|
||||
*
|
||||
* @uses \CMDBSource::GetServerVariable() so needs a connection opened !
|
||||
*/
|
||||
static public function GetDBVendor()
|
||||
public static function GetDBVendor()
|
||||
{
|
||||
$sDBVendor = static::ENUM_DB_VENDOR_MYSQL;
|
||||
|
||||
@@ -672,13 +680,13 @@ class CMDBSource
|
||||
private static function StartTransaction()
|
||||
{
|
||||
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
|
||||
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
|
||||
|
||||
$bHasExistingTransactions = self::IsInsideTransaction();
|
||||
if (!$bHasExistingTransactions) {
|
||||
IssueLog::Trace("START TRANSACTION $sCaller", LogChannels::CMDB_SOURCE);
|
||||
IssueLog::Trace("START TRANSACTION was sent to the DB", LogChannels::CMDB_SOURCE, ['stacktrace' => $aStackTrace]);
|
||||
self::DBQuery('START TRANSACTION');
|
||||
} else {
|
||||
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") START TRANSACTION $sCaller", LogChannels::CMDB_SOURCE);
|
||||
IssueLog::Trace("START TRANSACTION ignored as a transaction is already opened", LogChannels::CMDB_SOURCE, ['stacktrace' => $aStackTrace]);
|
||||
}
|
||||
|
||||
self::AddTransactionLevel();
|
||||
|
||||
@@ -22,7 +22,15 @@
|
||||
|
||||
define('ITOP_APPLICATION', 'iTop');
|
||||
define('ITOP_APPLICATION_SHORT', 'iTop');
|
||||
define('ITOP_VERSION', '3.0.0-dev');
|
||||
|
||||
/**
|
||||
* Constant containing the application version
|
||||
* Warning: this might be different from iTop core version!
|
||||
*
|
||||
* @see ITOP_CORE_VERSION to get iTop core version
|
||||
*/
|
||||
define('ITOP_VERSION', '3.0.1-dev');
|
||||
|
||||
define('ITOP_VERSION_NAME', 'Fullmoon');
|
||||
define('ITOP_REVISION', 'svn');
|
||||
define('ITOP_BUILD_DATE', '$WCNOW$');
|
||||
@@ -488,6 +496,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
],
|
||||
'cron_task_max_execution_time' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Background tasks will use this value (integer) multiplicated by its periodicity (in seconds) as max duration per cron execution. 0 is unlimited time',
|
||||
'default' => 0,
|
||||
'value' => 0,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'cron_sleep' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Duration (seconds) before cron.php checks again if something must be done',
|
||||
@@ -608,6 +624,13 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
/**
|
||||
* The timezone is automatically set using this parameter in \utils::InitTimeZone
|
||||
* This method is called almost everywhere, cause it's called in \MetaModel::LoadConfig and exec.php... but you might
|
||||
* need to get it yourself !
|
||||
*
|
||||
* @used-by utils::InitTimeZone()
|
||||
*/
|
||||
'timezone' => [
|
||||
'type' => 'string',
|
||||
'description' => 'Timezone (reference: http://php.net/manual/en/timezones.php). If empty, it will be left unchanged and MUST be explicitly configured in PHP',
|
||||
@@ -851,13 +874,23 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'impact_analysis_lazy_loading' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'In the impact analysis view: display the analysis or filter before display',
|
||||
'default' => false,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'url_validation_pattern' => [
|
||||
'type' => 'string',
|
||||
'description' => 'Regular expression to validate/detect the format of an URL (URL attributes and Wiki formatting for Text attributes)',
|
||||
'default' => '(https?|ftp)\://([a-zA-Z0-9+!*(),;?&=\$_.-]+(\:[a-zA-Z0-9+!*(),;?&=\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\:[0-9]{2,5})?(/([a-zA-Z0-9%+\$_-]\.?)+)*/?(\?[a-zA-Z+&\$_.-][a-zA-Z0-9;:[\]@&%=+/\$_.-]*)?(#[a-zA-Z_.-][a-zA-Z0-9+\$_.-]*)?',
|
||||
// SHEME.......... USER....................... PASSWORD.......................... HOST/IP........... PORT.......... PATH........................ GET............................................ ANCHOR............................
|
||||
'default' => /** @lang RegExp */
|
||||
'(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+\$_.-]*)?',
|
||||
// SCHEME....... USER....................... PASSWORD.......................... HOST/IP........... PORT.......... PATH......................... GET............................................ ANCHOR..........................
|
||||
// Example: http://User:passWord@127.0.0.1:8888/patH/Page.php?arrayArgument[2]=something:blah20#myAnchor
|
||||
// Origin of this regexp: http://www.php.net/manual/fr/function.preg-match.php#93824
|
||||
// RegExp source: http://www.php.net/manual/fr/function.preg-match.php#93824
|
||||
// Update with N°4515
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
@@ -1125,6 +1158,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'svg_sanitizer' => [
|
||||
'type' => 'string',
|
||||
'description' => 'The class to use for SVG sanitization : allow to provide a custom made sanitizer',
|
||||
'default' => 'SVGDOMSanitizer',
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'inline_image_max_display_width' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'The maximum width (in pixels) when displaying images inside an HTML formatted attribute. Images will be displayed using this this maximum width.',
|
||||
@@ -1167,7 +1208,7 @@ class Config
|
||||
],
|
||||
'compatibility.include_deprecated_js_files' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Include the deprecated JS files to ease usage of not migrated extensions',
|
||||
'description' => 'Include the deprecated JS files (in iTop previous version) to ease usage of not migrated extensions',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
@@ -1183,7 +1224,7 @@ class Config
|
||||
],
|
||||
'compatibility.include_deprecated_css_files' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Include the deprecated CSS files to ease usage of not migrated extensions',
|
||||
'description' => 'Include the deprecated CSS files (in iTop previous version) to ease usage of not migrated extensions',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
@@ -1592,6 +1633,16 @@ class Config
|
||||
return $this->m_aSettings[$sPropCode]['value'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*
|
||||
* @since 3.0.1 N°4515
|
||||
*/
|
||||
public function GetDefault(string $sPropCode)
|
||||
{
|
||||
return $this->m_aSettings[$sPropCode]['default'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the $sPropCode parameter has a custom value or the default one.
|
||||
*
|
||||
|
||||
@@ -220,16 +220,15 @@ class CSVBulkExport extends TabularBulkExport
|
||||
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="csv_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$oRadioCustom = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput), "csv_date_format_radio", "custom", "csv_date_time_format_custom", "radio");
|
||||
$oRadioCustom->SetDescription(Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip'));
|
||||
$oRadioCustom->GetInput()->SetIsChecked($sDateTimeFormat !== (string)AttributeDateTime::GetFormat());
|
||||
$oRadioCustom->SetBeforeInput(false);
|
||||
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
$oFieldSetDate->AddSubBlock($oRadioCustom);
|
||||
|
||||
$sJSTooltip = json_encode('<div class="date_format_tooltip">'.Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip').'</div>');
|
||||
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#csv_custom_date_time_format').tooltip({content: function() { return $sJSTooltip; } });
|
||||
$('#form_part_csv_options').on('preview_updated', function() { FormatDatesInPreview('csv', 'csv'); });
|
||||
$('#csv_date_time_format_default').on('click', function() { FormatDatesInPreview('csv', 'csv'); });
|
||||
$('#csv_date_time_format_custom').on('click', function() { FormatDatesInPreview('csv', 'csv'); });
|
||||
|
||||
@@ -2934,6 +2934,9 @@ abstract class DBObject implements iDisplay
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
|
||||
// - TriggerOnObjectMention
|
||||
$this->ActivateOnMentionTriggers(true);
|
||||
|
||||
return $this->m_iKey;
|
||||
}
|
||||
@@ -3197,50 +3200,7 @@ abstract class DBObject implements iDisplay
|
||||
// Activate any existing trigger
|
||||
$sClass = get_class($this);
|
||||
// - TriggerOnObjectMention
|
||||
// 1 - Check if any caselog updated
|
||||
$aUpdatedLogAttCodes = array();
|
||||
foreach($aChanges as $sAttCode => $value)
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if($oAttDef instanceof AttributeCaseLog)
|
||||
{
|
||||
$aUpdatedLogAttCodes[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
// 2 - Find mentioned objects
|
||||
$aMentionedObjects = array();
|
||||
foreach ($aUpdatedLogAttCodes as $sAttCode) {
|
||||
/** @var \ormCaseLog $oUpdatedCaseLog */
|
||||
$oUpdatedCaseLog = $this->Get($sAttCode);
|
||||
$aMentionedObjects = array_merge_recursive($aMentionedObjects, utils::GetMentionedObjectsFromText($oUpdatedCaseLog->GetModifiedEntry()));
|
||||
}
|
||||
// 3 - Trigger for those objects
|
||||
// TODO: This should be refactored and moved into the caselogs loop, otherwise, we won't be able to know which case log triggered the action.
|
||||
foreach ($aMentionedObjects as $sMentionedClass => $aMentionedIds) {
|
||||
foreach ($aMentionedIds as $sMentionedId) {
|
||||
/** @var \DBObject $oMentionedObject */
|
||||
$oMentionedObject = MetaModel::GetObject($sMentionedClass, $sMentionedId);
|
||||
$aTriggerArgs = $this->ToArgs('this') + array('mentioned->object()' => $oMentionedObject);
|
||||
|
||||
$aParams = array('class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectMention AS t WHERE t.target_class IN (:class_list)"), array(), $aParams);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
/** @var \TriggerOnObjectMention $oTrigger */
|
||||
try {
|
||||
// Ensure to handle only mentioned object in the trigger's scope
|
||||
if ($oTrigger->IsMentionedObjectInScope($oMentionedObject) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oTrigger->DoActivate($aTriggerArgs);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->ActivateOnMentionTriggers(false);
|
||||
|
||||
$bHasANewExternalKeyValue = false;
|
||||
$aHierarchicalKeys = array();
|
||||
@@ -3453,6 +3413,74 @@ abstract class DBObject implements iDisplay
|
||||
return $this->m_iKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate TriggerOnObjectMention triggers for the current object
|
||||
*
|
||||
* @param bool $bNewlyCreatedObject
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
* @since 3.0.1 N°4741
|
||||
*/
|
||||
private function ActivateOnMentionTriggers(bool $bNewlyCreatedObject): void
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
$aChanges = $bNewlyCreatedObject ? $this->m_aOrigValues : $this->ListChanges();
|
||||
|
||||
// 1 - Check if any caselog updated
|
||||
$aUpdatedLogAttCodes = [];
|
||||
foreach ($aChanges as $sAttCode => $value) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef instanceof AttributeCaseLog) {
|
||||
// Skip empty log on creation
|
||||
if ($bNewlyCreatedObject && $value->GetModifiedEntry() === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$aUpdatedLogAttCodes[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
|
||||
// 2 - Find mentioned objects
|
||||
$aMentionedObjects = [];
|
||||
foreach ($aUpdatedLogAttCodes as $sAttCode) {
|
||||
/** @var \ormCaseLog $oUpdatedCaseLog */
|
||||
$oUpdatedCaseLog = $this->Get($sAttCode);
|
||||
$aMentionedObjects = array_merge_recursive($aMentionedObjects, utils::GetMentionedObjectsFromText($oUpdatedCaseLog->GetModifiedEntry()));
|
||||
}
|
||||
|
||||
// 3 - Trigger for those objects
|
||||
// TODO: This should be refactored and moved into the caselogs loop, otherwise, we won't be able to know which case log triggered the action.
|
||||
foreach ($aMentionedObjects as $sMentionedClass => $aMentionedIds) {
|
||||
foreach ($aMentionedIds as $sMentionedId) {
|
||||
/** @var \DBObject $oMentionedObject */
|
||||
$oMentionedObject = MetaModel::GetObject($sMentionedClass, $sMentionedId);
|
||||
$aTriggerArgs = $this->ToArgs('this') + ['mentioned->object()' => $oMentionedObject];
|
||||
|
||||
$aParams = ['class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL)];
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectMention AS t WHERE t.target_class IN (:class_list)"), [], $aParams);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
/** @var \TriggerOnObjectMention $oTrigger */
|
||||
try {
|
||||
// Ensure to handle only mentioned object in the trigger's scope
|
||||
if ($oTrigger->IsMentionedObjectInScope($oMentionedObject) === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oTrigger->DoActivate($aTriggerArgs);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Save updated fields previous values for {@see DBObject::DBUpdate()} callbacks
|
||||
@@ -5378,7 +5406,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
$oLnk->Set($sRoleAttCode, $sRoleValue);
|
||||
}
|
||||
$oLinkSet->AddObject($oLnk);
|
||||
$oLinkSet->AddItem($oLnk);
|
||||
$this->Set($sTargetListAttCode, $oLinkSet);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -416,6 +416,10 @@ class DBObjectSearch extends DBSearch
|
||||
* @param string $sFilterCode
|
||||
* @param mixed $value
|
||||
* @param string $sOpCode operator to use : 'IN', 'NOT IN', 'Contains',' Begins with', 'Finishes with', ...
|
||||
* If no operator is specified then :
|
||||
* * for id field we will use "="
|
||||
* * for other fields we will call the corresponding {@link AttributeDefinition::GetSmartConditionExpression} method impl
|
||||
* to generate the expression
|
||||
* @param bool $bParseSearchString
|
||||
*
|
||||
* @throws \CoreException
|
||||
@@ -465,14 +469,14 @@ class DBObjectSearch extends DBSearch
|
||||
if (!is_array($value)) $value = array($value);
|
||||
if (count($value) === 0) throw new Exception('AddCondition '.$sOpCode.': Value cannot be an empty array.');
|
||||
$sListExpr = '('.implode(', ', CMDBSource::Quote($value)).')';
|
||||
$sOQLCondition = $oField->Render()." IN $sListExpr";
|
||||
$sOQLCondition = $oField->RenderExpression()." IN $sListExpr";
|
||||
break;
|
||||
|
||||
case 'NOTIN':
|
||||
if (!is_array($value)) $value = array($value);
|
||||
if (count($value) === 0) throw new Exception('AddCondition '.$sOpCode.': Value cannot be an empty array.');
|
||||
$sListExpr = '('.implode(', ', CMDBSource::Quote($value)).')';
|
||||
$sOQLCondition = $oField->Render()." NOT IN $sListExpr";
|
||||
$sOQLCondition = $oField->RenderExpression()." NOT IN $sListExpr";
|
||||
break;
|
||||
|
||||
case 'Contains':
|
||||
@@ -1232,7 +1236,7 @@ class DBObjectSearch extends DBSearch
|
||||
elseif (MetaModel::IsParentClass($oRightFilter->GetFirstJoinedClass(), $oLeftFilter->GetClass()))
|
||||
{
|
||||
// Specialize $oRightFilter
|
||||
$oRightFilter->ChangeClass($oLeftFilter->GetClass());
|
||||
$oRightFilter->ChangeClass($oLeftFilter->GetFirstJoinedClass());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1368,7 +1372,7 @@ class DBObjectSearch extends DBSearch
|
||||
public function GetQueryParams($bExcludeMagicParams = true)
|
||||
{
|
||||
$aParams = array();
|
||||
$this->m_oSearchCondition->Render($aParams, true);
|
||||
$this->m_oSearchCondition->RenderExpression(false, $aParams, true);
|
||||
|
||||
if ($bExcludeMagicParams)
|
||||
{
|
||||
@@ -1457,7 +1461,7 @@ class DBObjectSearch extends DBSearch
|
||||
|
||||
$sRes .= ' ' . $this->GetFirstJoinedClass() . ' AS `' . $this->GetFirstJoinedClassAlias() . '`';
|
||||
$sRes .= $this->ToOQL_Joins();
|
||||
$sRes .= " WHERE ".$this->m_oSearchCondition->Render($aParams, $bRetrofitParams);
|
||||
$sRes .= " WHERE ".$this->m_oSearchCondition->RenderExpression(false, $aParams, $bRetrofitParams);
|
||||
|
||||
if ($bWithAllowAllFlag && $this->m_bAllowAllData)
|
||||
{
|
||||
|
||||
@@ -416,7 +416,11 @@ class DBUnionSearch extends DBSearch
|
||||
$aSearches = array();
|
||||
foreach ($this->aSearches as $oSearch)
|
||||
{
|
||||
$aSearches[] = $oSearch->Filter($sClassAlias, $oFilter);
|
||||
if (!$oSearch->IsAllDataAllowed()) {
|
||||
$aSearches[] = $oSearch->Filter($sClassAlias, $oFilter);
|
||||
} else {
|
||||
$aSearches[] = $oSearch;
|
||||
}
|
||||
}
|
||||
return new DBUnionSearch($aSearches);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ class Dict
|
||||
protected static $m_aLanguages = array(); // array( code => array( 'description' => '...', 'localized_description' => '...') ...)
|
||||
protected static $m_aData = array();
|
||||
protected static $m_sApplicationPrefix = null;
|
||||
/** @var \ApcService $m_oApcService */
|
||||
protected static $m_oApcService = null;
|
||||
|
||||
/**
|
||||
* @param $sLanguageCode
|
||||
@@ -116,15 +118,17 @@ class Dict
|
||||
{
|
||||
// Attempt to find the string in the user language
|
||||
//
|
||||
self::InitLangIfNeeded(self::GetUserLanguage());
|
||||
$sLangCode = self::GetUserLanguage();
|
||||
self::InitLangIfNeeded($sLangCode);
|
||||
|
||||
if (!array_key_exists(self::GetUserLanguage(), self::$m_aData))
|
||||
if (!array_key_exists($sLangCode, self::$m_aData))
|
||||
{
|
||||
IssueLog::Warning("Cannot find $sLangCode in dictionnaries. default labels displayed");
|
||||
// It may happen, when something happens before the dictionaries get loaded
|
||||
return $sStringCode;
|
||||
}
|
||||
$aCurrentDictionary = self::$m_aData[self::GetUserLanguage()];
|
||||
if (array_key_exists($sStringCode, $aCurrentDictionary))
|
||||
$aCurrentDictionary = self::$m_aData[$sLangCode];
|
||||
if (is_array($aCurrentDictionary) && array_key_exists($sStringCode, $aCurrentDictionary))
|
||||
{
|
||||
return $aCurrentDictionary[$sStringCode];
|
||||
}
|
||||
@@ -135,7 +139,7 @@ class Dict
|
||||
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
|
||||
|
||||
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
|
||||
if (array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
{
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
}
|
||||
@@ -144,7 +148,7 @@ class Dict
|
||||
self::InitLangIfNeeded('EN US');
|
||||
|
||||
$aDefaultDictionary = self::$m_aData['EN US'];
|
||||
if (array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
{
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
}
|
||||
@@ -203,7 +207,26 @@ class Dict
|
||||
{
|
||||
self::$m_aLanguages = $aLanguagesList;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @since 2.7.6 N°4125
|
||||
* @return \ApcService
|
||||
*/
|
||||
public static function GetApcService() {
|
||||
if (self::$m_oApcService === null){
|
||||
self::$m_oApcService = new ApcService();
|
||||
}
|
||||
return self::$m_oApcService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.7.6 N°4125
|
||||
* @param \ApcService $m_oApcService
|
||||
*/
|
||||
public static function SetApcService($oApcService) {
|
||||
self::$m_oApcService = $oApcService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a language from the language dictionary, if not already loaded
|
||||
* @param string $sLangCode Language code
|
||||
@@ -212,20 +235,23 @@ class Dict
|
||||
public static function InitLangIfNeeded($sLangCode)
|
||||
{
|
||||
if (array_key_exists($sLangCode, self::$m_aData)) return true;
|
||||
|
||||
|
||||
$bResult = false;
|
||||
|
||||
if (function_exists('apc_fetch') && (self::$m_sApplicationPrefix !== null))
|
||||
|
||||
if (self::GetApcService()->function_exists('apc_fetch')
|
||||
&& (self::$m_sApplicationPrefix !== null))
|
||||
{
|
||||
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
|
||||
//
|
||||
self::$m_aData[$sLangCode] = apc_fetch(self::$m_sApplicationPrefix.'-dict-'.$sLangCode);
|
||||
if (self::$m_aData[$sLangCode] === false)
|
||||
{
|
||||
self::$m_aData[$sLangCode] = self::GetApcService()->apc_fetch(self::$m_sApplicationPrefix.'-dict-'.$sLangCode);
|
||||
if (self::$m_aData[$sLangCode] === false) {
|
||||
unset(self::$m_aData[$sLangCode]);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else if (! is_array(self::$m_aData[$sLangCode])) {
|
||||
// N°4125: we dont fix dictionnary corrupted cache (on iTop side).
|
||||
// but we log an error in a dedicated channel to let itop administrator be aware of a potential APCu issue to fix.
|
||||
IssueLog::Error("APCu corrupted data (with $sLangCode dictionnary). APCu configuration and running version should be troubleshooted...", LogChannels::APC);
|
||||
$bResult = true;
|
||||
} else {
|
||||
$bResult = true;
|
||||
}
|
||||
}
|
||||
@@ -234,9 +260,10 @@ class Dict
|
||||
$sDictFile = APPROOT.'env-'.utils::GetCurrentEnvironment().'/dictionaries/'.str_replace(' ', '-', strtolower($sLangCode)).'.dict.php';
|
||||
require_once($sDictFile);
|
||||
|
||||
if (function_exists('apc_store') && (self::$m_sApplicationPrefix !== null))
|
||||
if (self::GetApcService()->function_exists('apc_store')
|
||||
&& (self::$m_sApplicationPrefix !== null))
|
||||
{
|
||||
apc_store(self::$m_sApplicationPrefix.'-dict-'.$sLangCode, self::$m_aData[$sLangCode]);
|
||||
self::GetApcService()->apc_store(self::$m_sApplicationPrefix.'-dict-'.$sLangCode, self::$m_aData[$sLangCode]);
|
||||
}
|
||||
$bResult = true;
|
||||
}
|
||||
@@ -275,13 +302,12 @@ class Dict
|
||||
*
|
||||
* @param $sSourceCode
|
||||
* @param $sDestCode
|
||||
* @since 3.0.1 Not clone sSourceCode entry if sDestCode entry already exist
|
||||
*/
|
||||
public static function CloneString($sSourceCode, $sDestCode)
|
||||
{
|
||||
foreach(self::$m_aLanguages as $sLanguageCode => $foo)
|
||||
{
|
||||
if (isset(self::$m_aData[$sLanguageCode][$sSourceCode]))
|
||||
{
|
||||
foreach(self::$m_aLanguages as $sLanguageCode => $foo) {
|
||||
if (isset(self::$m_aData[$sLanguageCode][$sSourceCode]) && !isset(self::$m_aData[$sLanguageCode][$sDestCode] )) {
|
||||
self::$m_aData[$sLanguageCode][$sDestCode] = self::$m_aData[$sLanguageCode][$sSourceCode];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class DisplayableNode extends GraphNode
|
||||
{
|
||||
public $x;
|
||||
public $y;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new node inside a graph
|
||||
* @param SimpleGraph $oGraph
|
||||
@@ -51,27 +51,27 @@ class DisplayableNode extends GraphNode
|
||||
{
|
||||
return $this->GetProperty('icon_url', '');
|
||||
}
|
||||
|
||||
|
||||
public function GetLabel()
|
||||
{
|
||||
return $this->GetProperty('label', $this->sId);
|
||||
}
|
||||
|
||||
|
||||
public function GetWidth()
|
||||
{
|
||||
return max(32, 5*strlen($this->GetProperty('label'))); // approximation of the text's bounding box
|
||||
}
|
||||
|
||||
|
||||
public function GetHeight()
|
||||
{
|
||||
return 32;
|
||||
}
|
||||
|
||||
|
||||
public function Distance2(DisplayableNode $oNode)
|
||||
{
|
||||
$dx = $this->x - $oNode->x;
|
||||
$dy = $this->y - $oNode->y;
|
||||
|
||||
|
||||
$d2 = $dx*$dx + $dy*$dy - $this->GetHeight()*$this->GetHeight();
|
||||
if ($d2 < 40)
|
||||
{
|
||||
@@ -79,12 +79,12 @@ class DisplayableNode extends GraphNode
|
||||
}
|
||||
return $d2;
|
||||
}
|
||||
|
||||
|
||||
public function Distance(DisplayableNode $oNode)
|
||||
{
|
||||
return sqrt($this->Distance2($oNode));
|
||||
}
|
||||
|
||||
|
||||
public function GetForRaphael($aContextDefs)
|
||||
{
|
||||
$aNode = array();
|
||||
@@ -100,7 +100,7 @@ class DisplayableNode extends GraphNode
|
||||
$aNode['label'] = $this->GetLabel();
|
||||
$aNode['id'] = $this->GetId();
|
||||
$fOpacity = ($this->GetProperty('is_reached') ? 1 : 0.4);
|
||||
$aNode['icon_attr'] = array('opacity' => $fOpacity);
|
||||
$aNode['icon_attr'] = array('opacity' => $fOpacity);
|
||||
$aNode['text_attr'] = array('opacity' => $fOpacity);
|
||||
$aNode['tooltip'] = $this->GetTooltip($aContextDefs);
|
||||
$aNode['context_icons'] = array();
|
||||
@@ -114,7 +114,7 @@ class DisplayableNode extends GraphNode
|
||||
}
|
||||
return $aNode;
|
||||
}
|
||||
|
||||
|
||||
public function RenderAsPDF(iTopPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
|
||||
{
|
||||
$Alpha = 1.0;
|
||||
@@ -170,7 +170,7 @@ class DisplayableNode extends GraphNode
|
||||
$oPdf->SetTextColor(0, 0, 0);
|
||||
$oPdf->Text($this->x*$fScale - $width/2, ($this->y + 18)*$fScale, $this->GetProperty('label'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a "whitened" version of the icon (retaining the transparency) to be used a background for masking the underlying lines
|
||||
* @param string $sIconFile The path to the file containing the icon
|
||||
@@ -179,35 +179,35 @@ class DisplayableNode extends GraphNode
|
||||
protected function CreateWhiteIcon(DisplayableGraph $oGraph, $sIconFile)
|
||||
{
|
||||
$aInfo = getimagesize($sIconFile);
|
||||
|
||||
|
||||
$im = null;
|
||||
switch($aInfo['mime'])
|
||||
{
|
||||
case 'image/png':
|
||||
if (function_exists('imagecreatefrompng'))
|
||||
{
|
||||
$im = imagecreatefrompng($sIconFile);
|
||||
}
|
||||
break;
|
||||
|
||||
if (function_exists('imagecreatefrompng'))
|
||||
{
|
||||
$im = imagecreatefrompng($sIconFile);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'image/gif':
|
||||
if (function_exists('imagecreatefromgif'))
|
||||
{
|
||||
$im = imagecreatefromgif($sIconFile);
|
||||
}
|
||||
break;
|
||||
|
||||
if (function_exists('imagecreatefromgif'))
|
||||
{
|
||||
$im = imagecreatefromgif($sIconFile);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'image/jpeg':
|
||||
case 'image/jpg':
|
||||
if (function_exists('imagecreatefromjpeg'))
|
||||
{
|
||||
$im = imagecreatefromjpeg($sIconFile);
|
||||
}
|
||||
break;
|
||||
|
||||
if (function_exists('imagecreatefromjpeg'))
|
||||
{
|
||||
$im = imagecreatefromjpeg($sIconFile);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
if($im && imagefilter($im, IMG_FILTER_COLORIZE, 255, 255, 255))
|
||||
{
|
||||
@@ -222,17 +222,17 @@ class DisplayableNode extends GraphNode
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function GetObjectCount()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
public function GetObjectClass()
|
||||
{
|
||||
return is_object($this->GetProperty('object', null)) ? get_class($this->GetProperty('object', null)) : null;
|
||||
}
|
||||
|
||||
|
||||
protected function AddToStats($oNode, &$aNodesPerClass)
|
||||
{
|
||||
$sClass = $oNode->GetObjectClass();
|
||||
@@ -256,9 +256,9 @@ class DisplayableNode extends GraphNode
|
||||
{
|
||||
$aNodesPerClass[$sClass][$sKey]['nodes'][$oNode->GetId()] = $oNode;
|
||||
$aNodesPerClass[$sClass][$sKey]['count'] += $oNode->GetObjectCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the list of neighbour nodes, in the given direction: 'up' or 'down'
|
||||
* @param bool $bDirectionDown
|
||||
@@ -279,11 +279,11 @@ class DisplayableNode extends GraphNode
|
||||
foreach($this->GetIncomingEdges() as $oEdge)
|
||||
{
|
||||
$aNextNodes[] = $oEdge->GetSourceNode();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $aNextNodes;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replaces the next neighbour node (in the given direction: 'up' or 'down') by the supplied group node
|
||||
* preserving the connectivity of the graph
|
||||
@@ -351,7 +351,7 @@ class DisplayableNode extends GraphNode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($oGraph->GetNode($oNextNode->GetId()))
|
||||
{
|
||||
$oGraph->_RemoveNode($oNextNode);
|
||||
@@ -367,9 +367,9 @@ class DisplayableNode extends GraphNode
|
||||
{
|
||||
$oNewNode->AddObject($oNextNode->GetProperty('object'));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Group together (as a special kind of nodes) all the similar neighbours of the current node
|
||||
* @param DisplayableGraph $oGraph
|
||||
@@ -381,7 +381,7 @@ class DisplayableNode extends GraphNode
|
||||
{
|
||||
if ($this->GetProperty('grouped') === true) return;
|
||||
$this->SetProperty('grouped', true);
|
||||
|
||||
|
||||
$aNodesPerClass = array();
|
||||
foreach($this->GetNextNodes($bDirectionDown) as $oNode)
|
||||
{
|
||||
@@ -412,7 +412,7 @@ class DisplayableNode extends GraphNode
|
||||
$oNewNode->SetProperty('is_reached', ($sStatus == 'reached'));
|
||||
$oNewNode->SetProperty('count', $aGroupProps['count']);
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
if ($bDirectionDown)
|
||||
@@ -427,8 +427,8 @@ class DisplayableNode extends GraphNode
|
||||
catch(Exception $e)
|
||||
{
|
||||
// Ignore this redundant egde
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
foreach($aGroupProps['nodes'] as $oNextNode)
|
||||
{
|
||||
$this->ReplaceNextNodeBy($oGraph, $oNextNode, $oNewNode, $bDirectionDown);
|
||||
@@ -445,7 +445,7 @@ class DisplayableNode extends GraphNode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function GetTooltip($aContextDefs)
|
||||
{
|
||||
$sHtml = '';
|
||||
@@ -474,9 +474,9 @@ class DisplayableNode extends GraphNode
|
||||
$sHtml .= '<tr><td>'.$oAttDef->GetLabel().': </td><td>'.$oCurrObj->GetAsHtml($sAttCode).'</td></tr>';
|
||||
}
|
||||
$sHtml .= '</tbody></table>';
|
||||
return $sHtml;
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the description of the node in "dot" language
|
||||
* Used to generate the positions in the graph, but we'd better use fake label
|
||||
@@ -508,7 +508,7 @@ class DisplayableRedundancyNode extends DisplayableNode
|
||||
{
|
||||
return 24;
|
||||
}
|
||||
|
||||
|
||||
public function GetForRaphael($aContextDefs)
|
||||
{
|
||||
$aNode = array();
|
||||
@@ -519,7 +519,7 @@ class DisplayableRedundancyNode extends DisplayableNode
|
||||
$aNode['x'] = $this->x;
|
||||
$aNode['y']= $this->y;
|
||||
$aNode['label'] = $this->GetLabel();
|
||||
$aNode['id'] = $this->GetId();
|
||||
$aNode['id'] = $this->GetId();
|
||||
$fDiscOpacity = ($this->GetProperty('is_reached') ? 1 : 0.2);
|
||||
$sColor = ($this->GetProperty('is_reached_count') > $this->GetProperty('threshold')) ? '#c33' : '#999';
|
||||
$aNode['disc_attr'] = array('stroke-width' => 2, 'stroke' => '#000', 'fill' => $sColor, 'opacity' => $fDiscOpacity);
|
||||
@@ -550,35 +550,35 @@ class DisplayableRedundancyNode extends DisplayableNode
|
||||
$height = $oPdf->GetStringHeight(1000, $sLabel);
|
||||
$xPos = (float)$this->x*$fScale - $width/2;
|
||||
$yPos = (float)$this->y*$fScale - $height/2;
|
||||
|
||||
|
||||
$oPdf->SetXY(($this->x - 16)*$fScale, ($this->y - 16)*$fScale);
|
||||
|
||||
|
||||
$oPdf->Cell(32*$fScale, 32*$fScale, $sLabel, 0, 0, 'C', 0, '', 0, false, 'T', 'C');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see DisplayableNode::GroupSimilarNeighbours()
|
||||
*/
|
||||
public function GroupSimilarNeighbours(DisplayableGraph $oGraph, $iThresholdCount, $bDirectionUp = false, $bDirectionDown = true)
|
||||
{
|
||||
parent::GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
|
||||
|
||||
|
||||
if ($bDirectionUp)
|
||||
{
|
||||
$aNodesPerClass = array();
|
||||
foreach($this->GetIncomingEdges() as $oEdge)
|
||||
{
|
||||
$oNode = $oEdge->GetSourceNode();
|
||||
|
||||
|
||||
if (($oNode->GetObjectClass() !== null) && (!$oNode->GetProperty('is_reached')))
|
||||
{
|
||||
{
|
||||
$this->AddToStats($oNode, $aNodesPerClass);
|
||||
}
|
||||
else
|
||||
{
|
||||
//$oNode->GroupSimilarNeighbours($oGraph, $iThresholdCount, $bDirectionUp, $bDirectionDown);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach($aNodesPerClass as $sClass => $aDefs)
|
||||
{
|
||||
foreach($aDefs as $sStatus => $aGroupProps)
|
||||
@@ -591,8 +591,8 @@ class DisplayableRedundancyNode extends DisplayableNode
|
||||
$oNewNode->SetProperty('is_reached', ($sStatus == 'is_reached'));
|
||||
$oNewNode->SetProperty('class', $sClass);
|
||||
$oNewNode->SetProperty('count', count($aGroupProps['nodes']));
|
||||
|
||||
|
||||
|
||||
|
||||
$sNewId = $this->GetId().'::'.$sClass.'/'.(($sStatus == 'reached') ? '_reached': '');
|
||||
$oNewNode = $oGraph->GetNode($sNewId);
|
||||
if ($oNewNode == null)
|
||||
@@ -604,7 +604,7 @@ class DisplayableRedundancyNode extends DisplayableNode
|
||||
$oNewNode->SetProperty('is_reached', ($sStatus == 'reached'));
|
||||
$oNewNode->SetProperty('count', $aGroupProps['count']);
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
$oOutgoingEdge = new DisplayableEdge($oGraph, '-'.$this->GetId().'-'.$oNewNode->GetId().'/'.$sStatus, $oNewNode, $this);
|
||||
@@ -613,7 +613,7 @@ class DisplayableRedundancyNode extends DisplayableNode
|
||||
{
|
||||
// Ignore this redundant egde
|
||||
}
|
||||
|
||||
|
||||
foreach($aGroupProps['nodes'] as $oNextNode)
|
||||
{
|
||||
$this->ReplaceNextNodeBy($oGraph, $oNextNode, $oNewNode, !$bDirectionUp);
|
||||
@@ -631,7 +631,7 @@ class DisplayableRedundancyNode extends DisplayableNode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function GetTooltip($aContextDefs)
|
||||
{
|
||||
$sHtml = '';
|
||||
@@ -640,9 +640,9 @@ class DisplayableRedundancyNode extends DisplayableNode
|
||||
$sHtml .= "<tr><td>".Dict::Format('UI:RelationTooltip:ImpactedItems_N_of_M' , $this->GetProperty('is_reached_count'), $this->GetProperty('min_up') + $this->GetProperty('threshold'))."</td></tr>";
|
||||
$sHtml .= "<tr><td>".Dict::Format('UI:RelationTooltip:CriticalThreshold_N_of_M' , $this->GetProperty('threshold'), $this->GetProperty('min_up') + $this->GetProperty('threshold'))."</td></tr>";
|
||||
$sHtml .= '</tbody></table>';
|
||||
return $sHtml;
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function GetObjectCount()
|
||||
{
|
||||
@@ -666,7 +666,7 @@ class DisplayableEdge extends GraphEdge
|
||||
}
|
||||
$xStart = $oSourceNode->x * $fScale;
|
||||
$yStart = $oSourceNode->y * $fScale;
|
||||
|
||||
|
||||
$oSinkNode = $this->GetSinkNode();
|
||||
if (($oSinkNode->x == null) || ($oSinkNode->y == null))
|
||||
{
|
||||
@@ -674,9 +674,9 @@ class DisplayableEdge extends GraphEdge
|
||||
}
|
||||
$xEnd = $oSinkNode->x * $fScale;
|
||||
$yEnd = $oSinkNode->y * $fScale;
|
||||
|
||||
|
||||
$bReached = ($this->GetSourceNode()->GetProperty('is_reached') && $this->GetSinkNode()->GetProperty('is_reached'));
|
||||
|
||||
|
||||
$oPdf->setAlpha(1);
|
||||
if ($bReached)
|
||||
{
|
||||
@@ -688,8 +688,8 @@ class DisplayableEdge extends GraphEdge
|
||||
}
|
||||
$oPdf->SetLineStyle(array('width' => 2*$fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => $aColor));
|
||||
$oPdf->Line($xStart, $yStart, $xEnd, $yEnd);
|
||||
|
||||
|
||||
|
||||
|
||||
$vx = $xEnd - $xStart;
|
||||
$vy = $yEnd - $yStart;
|
||||
$l = sqrt($vx*$vx + $vy*$vy);
|
||||
@@ -699,24 +699,24 @@ class DisplayableEdge extends GraphEdge
|
||||
$uy = $vx;
|
||||
$lPos = max($l/2, $l - 40*$fScale);
|
||||
$iArrowSize = 5*$fScale;
|
||||
|
||||
|
||||
$x = $xStart + $lPos * $vx;
|
||||
$y = $yStart + $lPos * $vy;
|
||||
$oPdf->Line($x, $y, $x + $iArrowSize * ($ux-$vx), $y + $iArrowSize * ($uy-$vy));
|
||||
$oPdf->Line($x, $y, $x - $iArrowSize * ($ux+$vx), $y - $iArrowSize * ($uy+$vy));
|
||||
$oPdf->Line($x, $y, $x - $iArrowSize * ($ux+$vx), $y - $iArrowSize * ($uy+$vy));
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayableGroupNode extends DisplayableNode
|
||||
{
|
||||
protected $aObjects;
|
||||
|
||||
|
||||
public function __construct(SimpleGraph $oGraph, $sId, $x = 0, $y = 0)
|
||||
{
|
||||
parent::__construct($oGraph, $sId, $x, $y);
|
||||
$this->aObjects = array();
|
||||
}
|
||||
|
||||
|
||||
public function AddObject(DBObject $oObj = null)
|
||||
{
|
||||
if (is_object($oObj))
|
||||
@@ -729,12 +729,12 @@ class DisplayableGroupNode extends DisplayableNode
|
||||
$this->aObjects[$oObj->GetKey()] = $oObj;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function GetObjects()
|
||||
{
|
||||
return $this->aObjects;
|
||||
}
|
||||
|
||||
|
||||
public function GetWidth()
|
||||
{
|
||||
return 50;
|
||||
@@ -760,7 +760,7 @@ class DisplayableGroupNode extends DisplayableNode
|
||||
$aNode['tooltip'] = $this->GetTooltip($aContextDefs);
|
||||
return $aNode;
|
||||
}
|
||||
|
||||
|
||||
public function RenderAsPDF(iTopPDF $oPdf, DisplayableGraph $oGraph, $fScale, $aContextDefs)
|
||||
{
|
||||
$bReached = $this->GetProperty('is_reached');
|
||||
@@ -790,7 +790,7 @@ class DisplayableGroupNode extends DisplayableNode
|
||||
$oPdf->SetTextColor(0, 0, 0);
|
||||
$oPdf->Text($this->x * $fScale - $width / 2, ($this->y + 25) * $fScale, $this->GetProperty('label'));
|
||||
}
|
||||
|
||||
|
||||
public function GetTooltip($aContextDefs)
|
||||
{
|
||||
$iGroupIdx = $this->GetProperty('group_index');
|
||||
@@ -823,7 +823,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
protected $aTempImages;
|
||||
protected $aSourceObjects;
|
||||
protected $aSinkObjects;
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
@@ -831,14 +831,14 @@ class DisplayableGraph extends SimpleGraph
|
||||
$this->aSourceObjects = array();
|
||||
$this->aSinkObjects = array();
|
||||
}
|
||||
|
||||
|
||||
public function GetTempImageName()
|
||||
{
|
||||
$sNewTempName = tempnam(APPROOT.'data', 'img-');
|
||||
$this->aTempImages[] = $sNewTempName;
|
||||
return $sNewTempName;
|
||||
}
|
||||
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
foreach($this->aTempImages as $sTempFile)
|
||||
@@ -918,7 +918,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
$oSinkNode = $oNewGraph->GetNode($oEdge->GetSinkNode()->GetId());
|
||||
$oNewEdge = new DisplayableEdge($oNewGraph, $oEdge->GetId(), $oSourceNode, $oSinkNode);
|
||||
}
|
||||
|
||||
|
||||
// Remove duplicate edges between two nodes
|
||||
$oEdgesIter = new RelationTypeIterator($oNewGraph, 'Edge');
|
||||
$aEdgeKeys = array();
|
||||
@@ -946,7 +946,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$oNodesIter = new RelationTypeIterator($oNewGraph, 'Node');
|
||||
foreach($oNodesIter as $oNode)
|
||||
{
|
||||
@@ -981,7 +981,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Remove duplicate edges between two nodes
|
||||
$oEdgesIter = new RelationTypeIterator($oNewGraph, 'Edge');
|
||||
$aEdgeKeys = array();
|
||||
@@ -1010,10 +1010,10 @@ class DisplayableGraph extends SimpleGraph
|
||||
}
|
||||
}
|
||||
set_time_limit(intval($iPreviousTimeLimit));
|
||||
|
||||
|
||||
return $oNewGraph;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the positions by rendering using Graphviz in xdot format
|
||||
* and parsing the output.
|
||||
@@ -1026,7 +1026,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
{
|
||||
throw new Exception($sDot);
|
||||
}
|
||||
|
||||
|
||||
$aChunks = explode(";", $sDot);
|
||||
foreach($aChunks as $sChunk)
|
||||
{
|
||||
@@ -1035,7 +1035,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
$sId = $aMatches[1];
|
||||
$xPos = $aMatches[2];
|
||||
$yPos = $aMatches[3];
|
||||
|
||||
|
||||
$oNode = $this->GetNode($sId);
|
||||
if ($oNode !== null)
|
||||
{
|
||||
@@ -1049,7 +1049,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function GetBoundingBox()
|
||||
{
|
||||
$xMin = null;
|
||||
@@ -1074,10 +1074,10 @@ class DisplayableGraph extends SimpleGraph
|
||||
$yMax = max($yMax, $oNode->y + $oNode->GetHeight() / 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return array('xmin' => $xMin, 'xmax' => $xMax, 'ymin' => $yMin, 'ymax' => $yMax);
|
||||
}
|
||||
|
||||
|
||||
function Translate($dx, $dy)
|
||||
{
|
||||
$oIterator = new RelationTypeIterator($this, 'Node');
|
||||
@@ -1085,9 +1085,9 @@ class DisplayableGraph extends SimpleGraph
|
||||
{
|
||||
$oNode->x += $dx;
|
||||
$oNode->y += $dy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function UpdatePositions($aPositions)
|
||||
{
|
||||
foreach($aPositions as $sNodeId => $aPos)
|
||||
@@ -1107,7 +1107,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
function GetAsJSON($sContextKey)
|
||||
{
|
||||
$aContextDefs = static::GetContextDefinitions($sContextKey, false);
|
||||
|
||||
|
||||
$aData = array('nodes' => array(), 'edges' => array(), 'groups' => array(), 'lists' => array());
|
||||
$iGroupIdx = 0;
|
||||
$oIterator = new RelationTypeIterator($this, 'Node');
|
||||
@@ -1131,7 +1131,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
$aData['groups'][$iGroupIdx] = array('class' => $sClass, 'keys' => $aKeys);
|
||||
$oNode->SetProperty('group_index', $iGroupIdx);
|
||||
$iGroupIdx++;
|
||||
|
||||
|
||||
if ($oNode->GetProperty('is_reached'))
|
||||
{
|
||||
// Also add the objects from this group into the 'list' tab
|
||||
@@ -1139,11 +1139,11 @@ class DisplayableGraph extends SimpleGraph
|
||||
{
|
||||
$aData['lists'][$sClass] = $aKeys;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
$aData['lists'][$sClass] = array_merge($aData['lists'][$sClass], $aKeys);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
if (($oNode instanceof DisplayableNode) && $oNode->GetProperty('is_reached') && is_object($oNode->GetProperty('object')))
|
||||
@@ -1157,9 +1157,9 @@ class DisplayableGraph extends SimpleGraph
|
||||
}
|
||||
$aData['nodes'][] = $oNode->GetForRaphael($aContextDefs);
|
||||
}
|
||||
|
||||
|
||||
uksort($aData['lists'], array(get_class($this), 'SortOnClassLabel')); // sort on the localized names of the classes to provide a consistent and stable order
|
||||
|
||||
|
||||
$oIterator = new RelationTypeIterator($this, 'Edge');
|
||||
foreach($oIterator as $sId => $oEdge)
|
||||
{
|
||||
@@ -1171,7 +1171,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
$aEdge['attr'] = array('opacity' => $fOpacity, 'stroke' => '#000');
|
||||
$aData['edges'][] = $aEdge;
|
||||
}
|
||||
|
||||
|
||||
return json_encode($aData);
|
||||
}
|
||||
|
||||
@@ -1200,12 +1200,12 @@ class DisplayableGraph extends SimpleGraph
|
||||
{
|
||||
$aContextDefs = static::GetContextDefinitions($sContextKey, false); // No need to develop the parameters
|
||||
$oPdf = $oPage->get_tcpdf();
|
||||
|
||||
|
||||
$aBB = $this->GetBoundingBox();
|
||||
$this->Translate(-$aBB['xmin'], -$aBB['ymin']);
|
||||
|
||||
|
||||
$aMargins = $oPdf->getMargins();
|
||||
|
||||
|
||||
if ($xMin == -1)
|
||||
{
|
||||
$xMin = $aMargins['left'];
|
||||
@@ -1222,7 +1222,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
{
|
||||
$yMax = $oPdf->getPageHeight() - $aMargins['bottom'];
|
||||
}
|
||||
|
||||
|
||||
$fBreakMargin = $oPdf->getBreakMargin();
|
||||
$oPdf->SetAutoPageBreak(false);
|
||||
$aRemainingArea = $this->RenderKey($oPdf, $sComments, $xMin, $yMin, $xMax, $yMax, $aContextDefs);
|
||||
@@ -1230,19 +1230,19 @@ class DisplayableGraph extends SimpleGraph
|
||||
$xMax = $aRemainingArea['xmax'];
|
||||
$yMin = $aRemainingArea['ymin'];
|
||||
$yMax = $aRemainingArea['ymax'];
|
||||
|
||||
|
||||
//$oPdf->Rect($xMin, $yMin, $xMax - $xMin, $yMax - $yMin, 'D', array(), array(225, 50, 50));
|
||||
|
||||
|
||||
$fPageW = $xMax - $xMin;
|
||||
$fPageH = $yMax - $yMin;
|
||||
|
||||
$w = $aBB['xmax'] - $aBB['xmin'];
|
||||
|
||||
$w = $aBB['xmax'] - $aBB['xmin'];
|
||||
$h = $aBB['ymax'] - $aBB['ymin'] + 10; // Extra space for the labels which may appear "below" the icons
|
||||
|
||||
|
||||
$fScale = min($fPageW / $w, $fPageH / $h);
|
||||
$dx = ($fPageW - $fScale * $w) / 2;
|
||||
$dy = ($fPageH - $fScale * $h) / 2;
|
||||
|
||||
|
||||
$this->Translate(($xMin + $dx)/$fScale, ($yMin + $dy)/$fScale);
|
||||
|
||||
$oIterator = new RelationTypeIterator($this, 'Edge');
|
||||
@@ -1264,7 +1264,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
$oPdf->SetAlpha(1);
|
||||
$oPdf->SetTextColor(0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders (in PDF) the key (legend) of the graphics vertically to the left of the specified zone (xmin,ymin, xmax,ymax),
|
||||
* and the comment (if any) at the bottom of the page. Returns the position of remaining area.
|
||||
@@ -1332,7 +1332,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
$yPos += $fIconSize + 2 * $fPadding;
|
||||
}
|
||||
$oPdf->Rect($xMin, $yMin, $fMaxWidth + $fIconSize + 3*$fPadding, $yMax - $yMin, 'D');
|
||||
|
||||
|
||||
if ($sComments != '')
|
||||
{
|
||||
// Draw the comment text (surrounded by a rectangle)
|
||||
@@ -1347,10 +1347,10 @@ class DisplayableGraph extends SimpleGraph
|
||||
$oPdf->Rect($xPos, $yPos, $w + 2*$fPadding, $h + 2*$fPadding, 'D');
|
||||
$yMax = $yPos - $fPadding;
|
||||
}
|
||||
|
||||
|
||||
return array('xmin' => $xMin + $fMaxWidth + $fIconSize + 4*$fPadding, 'xmax' => $xMax, 'ymin' => $yMin, 'ymax' => $yMax);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the context definitions from the parameters / configuration. The format of the "key" string is:
|
||||
* <module>/relation_context/<class>/<relation>/<direction>
|
||||
@@ -1372,7 +1372,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
else
|
||||
{
|
||||
$sLeafClass = $aLevels[2];
|
||||
|
||||
|
||||
if (!MetaModel::IsValidClass($sLeafClass))
|
||||
{
|
||||
IssueLog::Warning("GetContextDefinitions: invalid 'sLeafClass' = '$sLeafClass'. A valid class name is expected in 3rd position inside '$sContextKey' !");
|
||||
@@ -1387,7 +1387,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
$aContextDefs = array_merge($aContextDefs, $aRelationContext[$sClass][$aLevels[3]][$aLevels[4]]['items']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Check if the queries are valid
|
||||
foreach($aContextDefs as $sKey => $sDefs)
|
||||
{
|
||||
@@ -1421,172 +1421,108 @@ class DisplayableGraph extends SimpleGraph
|
||||
* @param int $iObjKey
|
||||
* @param string $sContextKey
|
||||
* @param array $aContextParams
|
||||
* @param bool $bLazyLoading since 2.7.7 3.0.1
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
*/
|
||||
function Display(WebPage $oP, $aResults, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects, $sObjClass, $iObjKey, $sContextKey, $aContextParams = array())
|
||||
{
|
||||
$aContextDefs = static::GetContextDefinitions($sContextKey, true, $aContextParams);
|
||||
$aExcludedByClass = array();
|
||||
foreach($aExcludedObjects as $oObj)
|
||||
{
|
||||
if (!array_key_exists(get_class($oObj), $aExcludedByClass))
|
||||
{
|
||||
$aExcludedByClass[get_class($oObj)] = array();
|
||||
}
|
||||
$aExcludedByClass[get_class($oObj)][] = $oObj->GetKey();
|
||||
}
|
||||
$sSftShort = Dict::S('UI:ElementsDisplayed');
|
||||
$sSearchToggle = Dict::S('UI:Search:Toggle');
|
||||
$oP->add("<div class=\"not-printable\">\n");
|
||||
$oUiSearchBlock = new Panel($sSftShort, [],Panel::ENUM_COLOR_SCHEME_CYAN, 'ds_flash');
|
||||
$oUiSearchBlock->SetCSSClasses(["ibo-search-form-panel", "display_block"]);
|
||||
$oUiSearchBlock->SetIsCollapsible(true);
|
||||
$oUiHtmlBlock = new Combodo\iTop\Application\UI\Base\Component\Html\Html(
|
||||
<<<EOF
|
||||
<div id="ds_flash" class="search_box ibo-display-graph--search-box">
|
||||
<div id="dh_flash_criterion_outer" class="sf_criterion_area"><div class="sf_criterion_row">
|
||||
EOF
|
||||
);
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$("#dh_flash > .sf_title").on('click', function() {
|
||||
$("#dh_flash").toggleClass('closed');
|
||||
});
|
||||
$('#ReloadMovieBtn').button().button('disable');
|
||||
EOF
|
||||
);
|
||||
$aSortedElements = array();
|
||||
foreach($aResults as $sClassIdx => $aObjects)
|
||||
{
|
||||
foreach($aObjects as $oCurrObj)
|
||||
{
|
||||
$sSubClass = get_class($oCurrObj);
|
||||
$aSortedElements[$sSubClass] = MetaModel::GetName($sSubClass);
|
||||
}
|
||||
}
|
||||
|
||||
asort($aSortedElements);
|
||||
$idx = 0;
|
||||
foreach($aSortedElements as $sSubClass => $sClassName)
|
||||
{
|
||||
$oUiHtmlBlock->AddHtml("<div><input type=\"checkbox\" id=\"exclude_$idx\" name=\"excluded[]\" value=\"$sSubClass\" checked onChange=\"$('#ReloadMovieBtn').button('enable')\"><label for=\"exclude_$idx\">");
|
||||
$oUiMedallionBlock= new MedallionIcon(MetaModel::GetClassIcon($sSubClass, false));
|
||||
$oUiMedallionBlock->SetDescription($sClassName);
|
||||
$oUiHtmlBlock->AddHtml(BlockRenderer::RenderBlockTemplates($oUiMedallionBlock));
|
||||
$oUiHtmlBlock->AddHtml("</label></div>");
|
||||
$idx++;
|
||||
}
|
||||
$oUiHtmlBlock->AddHtml("</div>");
|
||||
$oUiHtmlBlock->AddHtml("<button type=\"button\" id=\"ReloadMovieBtn\" class=\"ibo-button ibo-is-neutral ibo-is-regular\" onClick=\"DoReload()\">".Dict::S('UI:Button:Refresh')."</button></div></form>");
|
||||
$oUiHtmlBlock->AddHtml("</div>\n");
|
||||
$oUiHtmlBlock->AddHtml("</div>\n"); // class="not-printable"
|
||||
function Display(WebPage $oP, $aResults, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects, $sObjClass, $iObjKey, $sContextKey, $aContextParams = array(), bool $bLazyLoading = false)
|
||||
{
|
||||
list($aExcludedByClass, $aAdditionalContexts) = $this->DisplayFiltering($sContextKey, $aContextParams, $aExcludedObjects, $oP, $aResults, $bLazyLoading);
|
||||
|
||||
$oUiSearchBlock->AddSubBlock($oUiHtmlBlock);
|
||||
$oP->AddUiBlock($oUiSearchBlock);
|
||||
$aAdditionalContexts = array();
|
||||
foreach($aContextDefs as $sKey => $aDefinition)
|
||||
{
|
||||
$aAdditionalContexts[] = array('key' => $sKey, 'label' => Dict::S($aDefinition['dict']), 'oql' => $aDefinition['oql'], 'default' => (array_key_exists('default', $aDefinition) && ($aDefinition['default'] == 'yes')));
|
||||
}
|
||||
|
||||
$sDirection = utils::ReadParam('d', 'horizontal');
|
||||
$iGroupingThreshold = utils::ReadParam('g', 5);
|
||||
|
||||
WebResourcesHelper::EnableSimpleGraphInWebPage($oP);
|
||||
try
|
||||
{
|
||||
try {
|
||||
$this->InitFromGraphviz();
|
||||
$sExportAsPdfURL = '';
|
||||
$sExportAsPdfURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_pdf&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
|
||||
$oAppcontext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
$sDrillDownURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class=%1$s&id=%2$s&'.$sContext;
|
||||
$sExportAsDocumentURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_attachment&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
|
||||
$sLoadFromURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_json&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
|
||||
$sAttachmentExportTitle = '';
|
||||
if (($sObjClass != null) && ($iObjKey != null))
|
||||
{
|
||||
if (($sObjClass != null) && ($iObjKey != null)) {
|
||||
$oTargetObj = MetaModel::GetObject($sObjClass, $iObjKey, false);
|
||||
if ($oTargetObj)
|
||||
{
|
||||
if ($oTargetObj) {
|
||||
$sAttachmentExportTitle = Dict::Format('UI:Relation:AttachmentExportOptions_Name', $oTargetObj->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$sId = 'graph';
|
||||
$sStyle = '';
|
||||
if ($oP->IsPrintableVersion())
|
||||
{
|
||||
if ($oP->IsPrintableVersion()) {
|
||||
// Optimize for printing on A4/Letter vertically
|
||||
$sStyle = 'margin-left:auto; margin-right:auto;';
|
||||
$oP->add_ready_script("$('.simple-graph').width(18/2.54*96).resizable({ stop: function() { $(window).trigger('resized'); }});"); // Default width about 18 cm, since most browsers assume 96 dpi
|
||||
}
|
||||
$oP->add('<div id="'.$sId.'" class="simple-graph" style="'.$sStyle.'"></div>');
|
||||
$aParams = array(
|
||||
'source_url' => $sLoadFromURL,
|
||||
'sources' => ($this->bDirectionDown ? $this->aSourceObjects : $this->aSinkObjects),
|
||||
'excluded' => $aExcludedByClass,
|
||||
'grouping_threshold' => $iGroupingThreshold,
|
||||
'export_as_pdf' => array('url' => $sExportAsPdfURL, 'label' => Dict::S('UI:Relation:ExportAsPDF')),
|
||||
'source_url' => $sLoadFromURL,
|
||||
'sources' => ($this->bDirectionDown ? $this->aSourceObjects : $this->aSinkObjects),
|
||||
'excluded' => $aExcludedByClass,
|
||||
'grouping_threshold' => $iGroupingThreshold,
|
||||
'export_as_pdf' => array('url' => $sExportAsPdfURL, 'label' => Dict::S('UI:Relation:ExportAsPDF')),
|
||||
'export_as_attachment' => array('url' => $sExportAsDocumentURL, 'label' => Dict::S('UI:Relation:ExportAsAttachment'), 'obj_class' => $sObjClass, 'obj_key' => $iObjKey),
|
||||
'drill_down' => array('url' => $sDrillDownURL, 'label' => Dict::S('UI:Relation:DrillDown')),
|
||||
'labels' => array(
|
||||
'export_pdf_title' => Dict::S('UI:Relation:PDFExportOptions'),
|
||||
'drill_down' => array('url' => $sDrillDownURL, 'label' => Dict::S('UI:Relation:DrillDown')),
|
||||
'labels' => array(
|
||||
'export_pdf_title' => Dict::S('UI:Relation:PDFExportOptions'),
|
||||
'export_as_attachment_title' => $sAttachmentExportTitle,
|
||||
'export' => Dict::S('UI:Button:Export'),
|
||||
'cancel' => Dict::S('UI:Button:Cancel'),
|
||||
'title' => Dict::S('UI:RelationOption:Title'),
|
||||
'untitled' => Dict::S('UI:RelationOption:Untitled'),
|
||||
'include_list' => Dict::S('UI:RelationOption:IncludeList'),
|
||||
'comments' => Dict::S('UI:RelationOption:Comments'),
|
||||
'grouping_threshold' => Dict::S('UI:RelationOption:GroupingThreshold'),
|
||||
'refresh' => Dict::S('UI:Button:Refresh'),
|
||||
'check_all' => Dict::S('UI:SearchValue:CheckAll'),
|
||||
'uncheck_all' => Dict::S('UI:SearchValue:UncheckAll'),
|
||||
'none_selected' => Dict::S('UI:Relation:NoneSelected'),
|
||||
'nb_selected' => Dict::S('UI:SearchValue:NbSelected'),
|
||||
'additional_context_info' => Dict::S('UI:Relation:AdditionalContextInfo'),
|
||||
'zoom' => Dict::S('UI:Relation:Zoom'),
|
||||
'loading' => Dict::S('UI:Loading'),
|
||||
'export' => Dict::S('UI:Button:Export'),
|
||||
'cancel' => Dict::S('UI:Button:Cancel'),
|
||||
'title' => Dict::S('UI:RelationOption:Title'),
|
||||
'untitled' => Dict::S('UI:RelationOption:Untitled'),
|
||||
'include_list' => Dict::S('UI:RelationOption:IncludeList'),
|
||||
'comments' => Dict::S('UI:RelationOption:Comments'),
|
||||
'grouping_threshold' => Dict::S('UI:RelationOption:GroupingThreshold'),
|
||||
'refresh' => Dict::S('UI:Button:Refresh'),
|
||||
'check_all' => Dict::S('UI:SearchValue:CheckAll'),
|
||||
'uncheck_all' => Dict::S('UI:SearchValue:UncheckAll'),
|
||||
'none_selected' => Dict::S('UI:Relation:NoneSelected'),
|
||||
'nb_selected' => Dict::S('UI:SearchValue:NbSelected'),
|
||||
'additional_context_info' => Dict::S('UI:Relation:AdditionalContextInfo'),
|
||||
'zoom' => Dict::S('UI:Relation:Zoom'),
|
||||
'loading' => Dict::S('UI:Loading'),
|
||||
),
|
||||
'page_format' => array(
|
||||
'label' => Dict::S('UI:Relation:PDFExportPageFormat'),
|
||||
'page_format' => array(
|
||||
'label' => Dict::S('UI:Relation:PDFExportPageFormat'),
|
||||
'values' => array(
|
||||
'A3' => Dict::S('UI:PageFormat_A3'),
|
||||
'A4' => Dict::S('UI:PageFormat_A4'),
|
||||
'A3' => Dict::S('UI:PageFormat_A3'),
|
||||
'A4' => Dict::S('UI:PageFormat_A4'),
|
||||
'Letter' => Dict::S('UI:PageFormat_Letter'),
|
||||
),
|
||||
),
|
||||
'page_orientation' => array(
|
||||
'label' => Dict::S('UI:Relation:PDFExportPageOrientation'),
|
||||
'page_orientation' => array(
|
||||
'label' => Dict::S('UI:Relation:PDFExportPageOrientation'),
|
||||
'values' => array(
|
||||
'P' => Dict::S('UI:PageOrientation_Portrait'),
|
||||
'L' => Dict::S('UI:PageOrientation_Landscape'),
|
||||
),
|
||||
),
|
||||
'additional_contexts' => $aAdditionalContexts,
|
||||
'context_key' => $sContextKey,
|
||||
'additional_contexts' => $aAdditionalContexts,
|
||||
'context_key' => $sContextKey,
|
||||
);
|
||||
if (!extension_loaded('gd'))
|
||||
{
|
||||
if (!extension_loaded('gd')) {
|
||||
// PDF export requires GD
|
||||
unset($aParams['export_as_pdf']);
|
||||
}
|
||||
if (!extension_loaded('gd') || is_null($sObjClass) || is_null($iObjKey))
|
||||
{
|
||||
if (!extension_loaded('gd') || is_null($sObjClass) || is_null($iObjKey)) {
|
||||
// Export as Attachment requires GD (for building the PDF) AND a valid objclass/objkey couple
|
||||
unset($aParams['export_as_attachment']);
|
||||
}
|
||||
$oP->add_ready_script("$('#$sId').simple_graph(".json_encode($aParams).");");
|
||||
if ($oP->IsPrintableVersion() || !$bLazyLoading) {
|
||||
$oP->add_ready_script(" $('#$sId').simple_graph(".json_encode($aParams).");");
|
||||
} else {
|
||||
$oP->add_script("function Load(){var aExcluded = []; $('input[name^=excluded]').each( function() {if (!$(this).prop('checked')) { aExcluded.push($(this).val()); }} ); var params= $.extend(".json_encode($aParams).", {excluded_classes: aExcluded}); $('#$sId').simple_graph(params);}");
|
||||
$oP->add_ready_script("$('#impacted_objects_lists').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');$('#impacted_groups').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');");
|
||||
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
$oP->add('<div>'.$e->getMessage().'</div>');
|
||||
}
|
||||
$oP->add_script(
|
||||
<<<EOF
|
||||
<<<EOF
|
||||
|
||||
function DoReload()
|
||||
{
|
||||
@@ -1611,5 +1547,96 @@ EOF
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $sContextKey
|
||||
* @param array $aContextParams
|
||||
* @param array $aExcludedObjects
|
||||
* @param \WebPage $oP
|
||||
* @param array $aResults
|
||||
* @param bool $bLazyLoading
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \ReflectionException
|
||||
* @throws \Twig\Error\LoaderError
|
||||
* @throws \Twig\Error\RuntimeError
|
||||
* @throws \Twig\Error\SyntaxError
|
||||
*/
|
||||
public function DisplayFiltering(string $sContextKey, array $aContextParams, array $aExcludedObjects, WebPage $oP, array $aResults, bool $bLazyLoading = false): array
|
||||
{
|
||||
$aContextDefs = static::GetContextDefinitions($sContextKey, true, $aContextParams);
|
||||
$aExcludedByClass = array();
|
||||
foreach ($aExcludedObjects as $oObj) {
|
||||
if (!array_key_exists(get_class($oObj), $aExcludedByClass)) {
|
||||
$aExcludedByClass[get_class($oObj)] = array();
|
||||
}
|
||||
$aExcludedByClass[get_class($oObj)][] = $oObj->GetKey();
|
||||
}
|
||||
$sSftShort = Dict::S('UI:ElementsDisplayed');
|
||||
$oP->add("<div class=\"not-printable\">\n");
|
||||
$oUiSearchBlock = new Panel($sSftShort, [], Panel::ENUM_COLOR_SCHEME_CYAN, 'dh_flash');
|
||||
$oUiSearchBlock->SetCSSClasses(["ibo-search-form-panel", "display_block"]);
|
||||
$oUiSearchBlock->SetIsCollapsible(true);
|
||||
$oUiHtmlBlock = new Combodo\iTop\Application\UI\Base\Component\Html\Html(
|
||||
<<<EOF
|
||||
|
||||
<div id="ds_flash" class="search_box ibo-display-graph--search-box">
|
||||
<div id="dh_flash_criterion_outer" class="sf_criterion_area"><div class="sf_criterion_row">
|
||||
EOF
|
||||
);
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$("#dh_flash > .sf_title").on("click", function() {
|
||||
$("#dh_flash").toggleClass("closed");
|
||||
});
|
||||
$("#ReloadMovieBtn").button().button("disable");
|
||||
EOF
|
||||
);
|
||||
if ($bLazyLoading) {
|
||||
$oP->add_ready_script("$('#ReloadMovieBtn').button('enable');");
|
||||
} else {
|
||||
$oP->add_ready_script("$('#dh_flash').addClass('closed');");
|
||||
}
|
||||
$aSortedElements = array();
|
||||
foreach ($aResults as $sClassIdx => $aObjects) {
|
||||
foreach ($aObjects as $oCurrObj) {
|
||||
$sSubClass = get_class($oCurrObj);
|
||||
$aSortedElements[$sSubClass] = MetaModel::GetName($sSubClass);
|
||||
}
|
||||
}
|
||||
|
||||
asort($aSortedElements);
|
||||
$idx = 0;
|
||||
foreach ($aSortedElements as $sSubClass => $sClassName) {
|
||||
$oUiHtmlBlock->AddHtml("<div><input type=\"checkbox\" id=\"exclude_$idx\" name=\"excluded[]\" value=\"$sSubClass\" checked onChange=\"$('#ReloadMovieBtn').button('enable')\"><label for=\"exclude_$idx\">");
|
||||
$oUiMedallionBlock = new MedallionIcon(MetaModel::GetClassIcon($sSubClass, false));
|
||||
$oUiMedallionBlock->SetDescription($sClassName);
|
||||
$oUiHtmlBlock->AddHtml(BlockRenderer::RenderBlockTemplates($oUiMedallionBlock));
|
||||
$oUiHtmlBlock->AddHtml("</label></div>");
|
||||
$idx++;
|
||||
}
|
||||
$oUiHtmlBlock->AddHtml("</div>");
|
||||
if ($bLazyLoading) {
|
||||
$sOnCLick = "Load(); $('#ReloadMovieBtn').attr('onclick','DoReload()');$('#ReloadMovieBtn').html('".Dict::S('UI:Button:Refresh')."');";
|
||||
$oUiHtmlBlock->AddHtml("<button type=\"button\" id=\"ReloadMovieBtn\" class=\"ibo-button ibo-is-neutral ibo-is-regular\" onClick=\"$sOnCLick\">".Dict::S('Relation:impacts/LoadData')."</button></div></form>");
|
||||
} else {
|
||||
$sOnCLick = "DoReload()";
|
||||
$oUiHtmlBlock->AddHtml("<button type=\"button\" id=\"ReloadMovieBtn\" class=\"ibo-button ibo-is-neutral ibo-is-regular\" onClick=\"$sOnCLick\">".Dict::S('UI:Button:Refresh')."</button></div></form>");
|
||||
}
|
||||
$oUiHtmlBlock->AddHtml("</div>\n");
|
||||
$oUiHtmlBlock->AddHtml("</div>\n"); // class="not-printable"
|
||||
|
||||
$oUiSearchBlock->AddSubBlock($oUiHtmlBlock);
|
||||
$oP->AddUiBlock($oUiSearchBlock);
|
||||
|
||||
$aAdditionalContexts = array();
|
||||
foreach ($aContextDefs as $sKey => $aDefinition) {
|
||||
$aAdditionalContexts[] = array('key' => $sKey, 'label' => Dict::S($aDefinition['dict']), 'oql' => $aDefinition['oql'], 'default' => (array_key_exists('default', $aDefinition) && ($aDefinition['default'] == 'yes')));
|
||||
}
|
||||
|
||||
return array($aExcludedByClass, $aAdditionalContexts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -325,20 +325,33 @@ class EMail
|
||||
// Note: Swift will add the angle brackets for you
|
||||
// so let's remove the angle brackets if present, for historical reasons
|
||||
$sId = str_replace(array('<', '>'), '', $sId);
|
||||
|
||||
|
||||
$oMsgId = $this->m_oMessage->getHeaders()->get('Message-ID');
|
||||
$oMsgId->SetId($sId);
|
||||
}
|
||||
|
||||
|
||||
public function SetReferences($sReferences)
|
||||
{
|
||||
$this->AddToHeader('References', $sReferences);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "In-Reply-To" header to allow emails to group as a conversation in modern mail clients (GMail, Outlook 2016+, ...)
|
||||
*
|
||||
* @link https://en.wikipedia.org/wiki/Email#Header_fields
|
||||
*
|
||||
* @param string $sMessageId
|
||||
*
|
||||
* @since 3.0.1 N°4849
|
||||
*/
|
||||
public function SetInReplyTo(string $sMessageId)
|
||||
{
|
||||
$this->AddToHeader('In-Reply-To', $sMessageId);
|
||||
}
|
||||
|
||||
public function SetBody($sBody, $sMimeType = 'text/html', $sCustomStyles = null)
|
||||
{
|
||||
if (($sMimeType === 'text/html') && ($sCustomStyles !== null))
|
||||
{
|
||||
if (($sMimeType === 'text/html') && ($sCustomStyles !== null)) {
|
||||
$oDomDocument = CssInliner::fromHtml($sBody)->inlineCss($sCustomStyles)->getDomDocument();
|
||||
HtmlPruner::fromDomDocument($oDomDocument)->removeElementsWithDisplayNone();
|
||||
$sBody = CssToAttributeConverter::fromDomDocument($oDomDocument)->convertCssToVisualAttributes()->render(); // Adds html/body tags if not already present
|
||||
|
||||
@@ -111,16 +111,15 @@ class ExcelBulkExport extends TabularBulkExport
|
||||
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="excel_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$oRadioCustom = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput), "excel_date_format_radio", "custom", "excel_date_time_format_custom", "radio");
|
||||
$oRadioCustom->SetDescription(Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip'));
|
||||
$oRadioCustom->GetInput()->SetIsChecked($sDateTimeFormat !== (string)AttributeDateTime::GetFormat());
|
||||
$oRadioCustom->SetBeforeInput(false);
|
||||
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
$oFieldSetDate->AddSubBlock($oRadioCustom);
|
||||
|
||||
|
||||
$sJSTooltip = json_encode('<div class="date_format_tooltip">'.Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip').'</div>');
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#excel_custom_date_time_format').tooltip({content: function() { return $sJSTooltip; } });
|
||||
$('#form_part_xlsx_options').on('preview_updated', function() { FormatDatesInPreview('excel', 'xlsx'); });
|
||||
$('#excel_date_time_format_default').on('click', function() { FormatDatesInPreview('excel', 'xlsx'); });
|
||||
$('#excel_date_time_format_custom').on('click', function() { FormatDatesInPreview('excel', 'xlsx'); });
|
||||
|
||||
@@ -97,64 +97,163 @@ class HTMLNullSanitizer extends HTMLSanitizer
|
||||
{
|
||||
return $sHTML;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* A standard-compliant HTMLSanitizer based on the HTMLPurifier library by Edward Z. Yang
|
||||
* Complete but quite slow
|
||||
* http://htmlpurifier.org
|
||||
* Common implementation for sanitizer using DOM parsing
|
||||
*/
|
||||
/*
|
||||
class HTMLPurifierSanitizer extends HTMLSanitizer
|
||||
abstract class DOMSanitizer extends HTMLSanitizer
|
||||
{
|
||||
protected static $oPurifier = null;
|
||||
/** @var DOMDocument */
|
||||
protected $oDoc;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (self::$oPurifier == null)
|
||||
{
|
||||
$sLibPath = APPROOT.'lib/htmlpurifier/HTMLPurifier.auto.php';
|
||||
if (!file_exists($sLibPath))
|
||||
{
|
||||
throw new Exception("Missing library '$sLibPath', cannot use HTMLPurifierSanitizer.");
|
||||
}
|
||||
require_once($sLibPath);
|
||||
abstract public function GetTagsWhiteList();
|
||||
|
||||
$oPurifierConfig = HTMLPurifier_Config::createDefault();
|
||||
$oPurifierConfig->set('Core.Encoding', 'UTF-8'); // defaults to 'UTF-8'
|
||||
$oPurifierConfig->set('HTML.Doctype', 'XHTML 1.0 Strict'); // defaults to 'XHTML 1.0 Transitional'
|
||||
$oPurifierConfig->set('URI.AllowedSchemes', array (
|
||||
'http' => true,
|
||||
'https' => true,
|
||||
'data' => true, // This one is not present by default
|
||||
));
|
||||
$sPurifierCache = APPROOT.'data/HTMLPurifier';
|
||||
if (!is_dir($sPurifierCache))
|
||||
{
|
||||
mkdir($sPurifierCache);
|
||||
}
|
||||
if (!is_dir($sPurifierCache))
|
||||
{
|
||||
throw new Exception("Could not create the cache directory '$sPurifierCache'");
|
||||
}
|
||||
$oPurifierConfig->set('Cache.SerializerPath', $sPurifierCache); // no trailing slash
|
||||
self::$oPurifier = new HTMLPurifier($oPurifierConfig);
|
||||
}
|
||||
}
|
||||
abstract public function GetTagsBlackList();
|
||||
|
||||
abstract public function GetAttrsWhiteList();
|
||||
|
||||
abstract public function GetAttrsBlackList();
|
||||
|
||||
abstract public function GetStylesWhiteList();
|
||||
|
||||
public function DoSanitize($sHTML)
|
||||
{
|
||||
$sCleanHtml = self::$oPurifier->purify($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->LoadDoc($sHTML);
|
||||
|
||||
$this->CleanNode($this->oDoc);
|
||||
|
||||
$sCleanHtml = $this->PrintDoc();
|
||||
|
||||
return $sCleanHtml;
|
||||
}
|
||||
|
||||
abstract public function LoadDoc($sHTML);
|
||||
|
||||
/**
|
||||
* @return string cleaned source
|
||||
* @uses \DOMSanitizer::oDoc
|
||||
*/
|
||||
abstract public function PrintDoc();
|
||||
|
||||
protected function CleanNode(DOMNode $oElement)
|
||||
{
|
||||
$aAttrToRemove = array();
|
||||
// Gather the attributes to remove
|
||||
if ($oElement->hasAttributes()) {
|
||||
foreach ($oElement->attributes as $oAttr) {
|
||||
$sAttr = strtolower($oAttr->name);
|
||||
if ((false === empty($this->GetAttrsBlackList()))
|
||||
&& (in_array($sAttr, $this->GetAttrsBlackList(), true))) {
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
} else if ((false === empty($this->GetTagsWhiteList()))
|
||||
&& (false === in_array($sAttr, $this->GetTagsWhiteList()[strtolower($oElement->tagName)]))) {
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
} else if (!$this->IsValidAttributeContent($sAttr, $oAttr->value)) {
|
||||
// Invalid content
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
} else if ($sAttr == 'style') {
|
||||
// Special processing for style tags
|
||||
$sCleanStyle = $this->CleanStyle($oAttr->value);
|
||||
if ($sCleanStyle == '') {
|
||||
// Invalid content
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
} else {
|
||||
$oElement->setAttribute($oAttr->name, $sCleanStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now remove them
|
||||
foreach($aAttrToRemove as $sName)
|
||||
{
|
||||
$oElement->removeAttribute($sName);
|
||||
}
|
||||
}
|
||||
|
||||
if ($oElement->hasChildNodes())
|
||||
{
|
||||
$aChildElementsToRemove = array();
|
||||
// Gather the child noes to remove
|
||||
foreach($oElement->childNodes as $oNode) {
|
||||
if ($oNode instanceof DOMElement) {
|
||||
$sNodeTagName = strtolower($oNode->tagName);
|
||||
}
|
||||
if (($oNode instanceof DOMElement)
|
||||
&& (false === empty($this->GetTagsBlackList()))
|
||||
&& (in_array($sNodeTagName, $this->GetTagsBlackList(), true))) {
|
||||
$aChildElementsToRemove[] = $oNode;
|
||||
} else if (($oNode instanceof DOMElement)
|
||||
&& (false === empty($this->GetTagsWhiteList()))
|
||||
&& (false === array_key_exists($sNodeTagName, $this->GetTagsWhiteList()))) {
|
||||
$aChildElementsToRemove[] = $oNode;
|
||||
} else if ($oNode instanceof DOMComment) {
|
||||
$aChildElementsToRemove[] = $oNode;
|
||||
} else {
|
||||
// Recurse
|
||||
$this->CleanNode($oNode);
|
||||
if (($oNode instanceof DOMElement) && (strtolower($oNode->tagName) == 'img')) {
|
||||
InlineImage::ProcessImageTag($oNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now remove them
|
||||
foreach($aChildElementsToRemove as $oDomElement)
|
||||
{
|
||||
$oElement->removeChild($oDomElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function IsValidAttributeContent($sAttributeName, $sValue)
|
||||
{
|
||||
if ((false === empty($this->GetAttrsBlackList()))
|
||||
&& (in_array($sAttributeName, $this->GetAttrsBlackList(), true))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (array_key_exists($sAttributeName, $this->GetAttrsWhiteList())) {
|
||||
return preg_match($this->GetAttrsWhiteList()[$sAttributeName], $sValue);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function CleanStyle($sStyle)
|
||||
{
|
||||
if (empty($this->GetStylesWhiteList())) {
|
||||
return $sStyle;
|
||||
}
|
||||
|
||||
$aAllowedStyles = array();
|
||||
$aItems = explode(';', $sStyle);
|
||||
{
|
||||
foreach ($aItems as $sItem) {
|
||||
$aElements = explode(':', trim($sItem));
|
||||
if (in_array(trim(strtolower($aElements[0])), $this->GetStylesWhiteList())) {
|
||||
$aAllowedStyles[] = trim($sItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return implode(';', $aAllowedStyles);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
|
||||
|
||||
class HTMLDOMSanitizer extends DOMSanitizer
|
||||
{
|
||||
protected $oDoc;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
* @see https://www.itophub.io/wiki/page?id=2_6_0%3Aadmin%3Arich_text_limitations
|
||||
@@ -239,6 +338,31 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
'white-space',
|
||||
);
|
||||
|
||||
public function GetTagsWhiteList()
|
||||
{
|
||||
return static::$aTagsWhiteList;
|
||||
}
|
||||
|
||||
public function GetTagsBlackList()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function GetAttrsWhiteList()
|
||||
{
|
||||
return static::$aAttrsWhiteList;
|
||||
}
|
||||
|
||||
public function GetAttrsBlackList()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function GetStylesWhiteList()
|
||||
{
|
||||
return static::$aStylesWhiteList;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
@@ -264,139 +388,152 @@ class HTMLDOMSanitizer extends HTMLSanitizer
|
||||
}
|
||||
}
|
||||
|
||||
public function DoSanitize($sHTML)
|
||||
public function LoadDoc($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);
|
||||
// Replace badly encoded non breaking space
|
||||
$sHTML = preg_replace('~\xc2\xa0~', ' ', $sHTML);
|
||||
|
||||
@$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified
|
||||
$this->oDoc->preserveWhitespace = true;
|
||||
}
|
||||
|
||||
$this->CleanNode($this->oDoc);
|
||||
|
||||
public function PrintDoc()
|
||||
{
|
||||
$oXPath = new DOMXPath($this->oDoc);
|
||||
$sXPath = "//body";
|
||||
$oNodesList = $oXPath->query($sXPath);
|
||||
|
||||
if ($oNodesList->length == 0)
|
||||
{
|
||||
if ($oNodesList->length == 0) {
|
||||
// No body, save the whole document
|
||||
$sCleanHtml = $this->oDoc->saveHTML();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Export only the content of the body tag
|
||||
$sCleanHtml = $this->oDoc->saveHTML($oNodesList->item(0));
|
||||
// remove the body tag itself
|
||||
$sCleanHtml = str_replace( array('<body>', '</body>'), '', $sCleanHtml);
|
||||
$sCleanHtml = str_replace(array('<body>', '</body>'), '', $sCleanHtml);
|
||||
}
|
||||
|
||||
return $sCleanHtml;
|
||||
}
|
||||
}
|
||||
|
||||
protected function CleanNode(DOMNode $oElement)
|
||||
|
||||
|
||||
/**
|
||||
* @since 2.6.5 2.7.6 3.0.0 N°4360
|
||||
*/
|
||||
class SVGDOMSanitizer extends DOMSanitizer
|
||||
{
|
||||
public function GetTagsWhiteList()
|
||||
{
|
||||
$aAttrToRemove = array();
|
||||
// Gather the attributes to remove
|
||||
if ($oElement->hasAttributes())
|
||||
{
|
||||
foreach($oElement->attributes as $oAttr)
|
||||
{
|
||||
$sAttr = strtolower($oAttr->name);
|
||||
if (!in_array($sAttr, self::$aTagsWhiteList[strtolower($oElement->tagName)]))
|
||||
{
|
||||
// Forbidden (or unknown) attribute
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
}
|
||||
else if (!$this->IsValidAttributeContent($sAttr, $oAttr->value))
|
||||
{
|
||||
// Invalid content
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
}
|
||||
else if ($sAttr == 'style')
|
||||
{
|
||||
// Special processing for style tags
|
||||
$sCleanStyle = $this->CleanStyle($oAttr->value);
|
||||
if ($sCleanStyle == '')
|
||||
{
|
||||
// Invalid content
|
||||
$aAttrToRemove[] = $oAttr->name;
|
||||
}
|
||||
else
|
||||
{
|
||||
$oElement->setAttribute($oAttr->name, $sCleanStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now remove them
|
||||
foreach($aAttrToRemove as $sName)
|
||||
{
|
||||
$oElement->removeAttribute($sName);
|
||||
}
|
||||
}
|
||||
|
||||
if ($oElement->hasChildNodes())
|
||||
{
|
||||
$aChildElementsToRemove = array();
|
||||
// Gather the child noes to remove
|
||||
foreach($oElement->childNodes as $oNode)
|
||||
{
|
||||
if (($oNode instanceof DOMElement) && (!array_key_exists(strtolower($oNode->tagName), self::$aTagsWhiteList)))
|
||||
{
|
||||
$aChildElementsToRemove[] = $oNode;
|
||||
}
|
||||
else if ($oNode instanceof DOMComment)
|
||||
{
|
||||
$aChildElementsToRemove[] = $oNode;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Recurse
|
||||
$this->CleanNode($oNode);
|
||||
if (($oNode instanceof DOMElement) && (strtolower($oNode->tagName) == 'img'))
|
||||
{
|
||||
InlineImage::ProcessImageTag($oNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now remove them
|
||||
foreach($aChildElementsToRemove as $oDomElement)
|
||||
{
|
||||
$oElement->removeChild($oDomElement);
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function CleanStyle($sStyle)
|
||||
/**
|
||||
* @return string[]
|
||||
* @link https://developer.mozilla.org/en-US/docs/Web/SVG/Element/script
|
||||
*/
|
||||
public function GetTagsBlackList()
|
||||
{
|
||||
$aAllowedStyles = array();
|
||||
$aItems = explode(';', $sStyle);
|
||||
{
|
||||
foreach($aItems as $sItem)
|
||||
{
|
||||
$aElements = explode(':', trim($sItem));
|
||||
if (in_array(trim(strtolower($aElements[0])), static::$aStylesWhiteList))
|
||||
{
|
||||
$aAllowedStyles[] = trim($sItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
return implode(';', $aAllowedStyles);
|
||||
return [
|
||||
'script',
|
||||
];
|
||||
}
|
||||
|
||||
protected function IsValidAttributeContent($sAttributeName, $sValue)
|
||||
public function GetAttrsWhiteList()
|
||||
{
|
||||
if (array_key_exists($sAttributeName, self::$aAttrsWhiteList))
|
||||
{
|
||||
return preg_match(self::$aAttrsWhiteList[$sAttributeName], $sValue);
|
||||
}
|
||||
return true;
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @link https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Events#document_event_attributes
|
||||
*/
|
||||
public function GetAttrsBlackList()
|
||||
{
|
||||
return [
|
||||
'onbegin',
|
||||
'onbegin',
|
||||
'onrepeat',
|
||||
'onabort',
|
||||
'onerror',
|
||||
'onerror',
|
||||
'onscroll',
|
||||
'onunload',
|
||||
'oncopy',
|
||||
'oncut',
|
||||
'onpaste',
|
||||
'oncancel',
|
||||
'oncanplay',
|
||||
'oncanplaythrough',
|
||||
'onchange',
|
||||
'onclick',
|
||||
'onclose',
|
||||
'oncuechange',
|
||||
'ondblclick',
|
||||
'ondrag',
|
||||
'ondragend',
|
||||
'ondragenter',
|
||||
'ondragleave',
|
||||
'ondragover',
|
||||
'ondragstart',
|
||||
'ondrop',
|
||||
'ondurationchange',
|
||||
'onemptied',
|
||||
'onended',
|
||||
'onerror',
|
||||
'onfocus',
|
||||
'oninput',
|
||||
'oninvalid',
|
||||
'onkeydown',
|
||||
'onkeypress',
|
||||
'onkeyup',
|
||||
'onload',
|
||||
'onloadeddata',
|
||||
'onloadedmetadata',
|
||||
'onloadstart',
|
||||
'onmousedown',
|
||||
'onmouseenter',
|
||||
'onmouseleave',
|
||||
'onmousemove',
|
||||
'onmouseout',
|
||||
'onmouseover',
|
||||
'onmouseup',
|
||||
'onmousewheel',
|
||||
'onpause',
|
||||
'onplay',
|
||||
'onplaying',
|
||||
'onprogress',
|
||||
'onratechange',
|
||||
'onreset',
|
||||
'onresize',
|
||||
'onscroll',
|
||||
'onseeked',
|
||||
'onseeking',
|
||||
'onselect',
|
||||
'onshow',
|
||||
'onstalled',
|
||||
'onsubmit',
|
||||
'onsuspend',
|
||||
'ontimeupdate',
|
||||
'ontoggle',
|
||||
'onvolumechange',
|
||||
'onwaiting',
|
||||
'onactivate',
|
||||
'onfocusin',
|
||||
'onfocusout',
|
||||
];
|
||||
}
|
||||
|
||||
public function GetStylesWhiteList()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function LoadDoc($sHTML)
|
||||
{
|
||||
@$this->oDoc->loadXml($sHTML, LIBXML_NOBLANKS);
|
||||
}
|
||||
|
||||
public function PrintDoc()
|
||||
{
|
||||
return $this->oDoc->saveXML();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,13 +362,8 @@ class InlineImage extends DBObject
|
||||
{
|
||||
$sJS =
|
||||
<<<JS
|
||||
$('img[data-img-id]').each(function() {
|
||||
if ($(this).width() > {$iMaxWidth})
|
||||
{
|
||||
$(this).css({'max-width': '{$iMaxWidth}px', width: '', height: '', 'max-height': ''});
|
||||
}
|
||||
$(this).addClass('inline-image').attr('href', $(this).attr('src'));
|
||||
}).magnificPopup({type: 'image', closeOnContentClick: true });
|
||||
CombodoInlineImage.SetMaxWidth('{$iMaxWidth}');
|
||||
CombodoInlineImage.FixImagesWidth();
|
||||
JS
|
||||
;
|
||||
}
|
||||
|
||||
@@ -542,13 +542,32 @@ class FileLog
|
||||
*/
|
||||
class LogChannels
|
||||
{
|
||||
public const CLI = 'CLI';
|
||||
public const APC = 'apc';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.1 N°4849
|
||||
*/
|
||||
public const NOTIFICATIONS = 'notifications';
|
||||
|
||||
public const CLI = 'CLI';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @since 2.7.7 N°4558 use this new channel when logging DB transactions
|
||||
* @since 3.0.0 logs info in CMDBSource (see commit a117906f)
|
||||
*/
|
||||
public const CMDB_SOURCE = 'cmdbsource';
|
||||
|
||||
public const CONSOLE = 'console';
|
||||
public const DEADLOCK = 'DeadLock';
|
||||
public const INLINE_IMAGE = 'InlineImage';
|
||||
public const PORTAL = 'portal';
|
||||
public const CMDB_SOURCE = 'cmdbsource';
|
||||
|
||||
public const CORE = 'core';
|
||||
|
||||
public const DEADLOCK = 'DeadLock';
|
||||
|
||||
public const INLINE_IMAGE = 'InlineImage';
|
||||
|
||||
public const PORTAL = 'portal';
|
||||
}
|
||||
|
||||
|
||||
@@ -1136,7 +1155,12 @@ class DeprecatedCallsLog extends LogAPI
|
||||
*/
|
||||
public static function NotifyDeprecatedFile(?string $sAdditionalMessage = null): void
|
||||
{
|
||||
if (!static::IsLogLevelEnabled(self::LEVEL_WARNING, self::ENUM_CHANNEL_FILE)) {
|
||||
try {
|
||||
if (!static::IsLogLevelEnabled(self::LEVEL_WARNING, self::ENUM_CHANNEL_FILE)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (ConfigException $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -655,7 +655,7 @@ abstract class MetaModel
|
||||
* @param string $sRuleId
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @since 2.6.1 N°1918 (sous les pavés, la plage) initialize in 'root_class' property the class that has the first
|
||||
* @since 2.6.1 N°1968 (sous les pavés, la plage) initialize in 'root_class' property the class that has the first
|
||||
* definition of the rule in the hierarchy
|
||||
*/
|
||||
private static function SetUniquenessRuleRootClass($sRootClass, $sRuleId)
|
||||
@@ -3001,7 +3001,32 @@ abstract class MetaModel
|
||||
|
||||
// Build the list of available extensions
|
||||
//
|
||||
$aInterfaces = array('iApplicationUIExtension', 'iPreferencesExtension', 'iApplicationObjectExtension', 'iLoginFSMExtension', 'iLoginUIExtension', 'iLogoutExtension', 'iQueryModifier', 'iOnClassInitialization', 'iPopupMenuExtension', 'iPageUIExtension', 'iPageUIBlockExtension', 'iPortalUIExtension', 'ModuleHandlerApiInterface', 'iNewsroomProvider', 'iModuleExtension');
|
||||
$aInterfaces = [
|
||||
'iApplicationUIExtension',
|
||||
'iPreferencesExtension',
|
||||
'iApplicationObjectExtension',
|
||||
'iLoginFSMExtension',
|
||||
'iLoginUIExtension',
|
||||
'iLogoutExtension',
|
||||
'iQueryModifier',
|
||||
'iOnClassInitialization',
|
||||
'iPopupMenuExtension',
|
||||
'iPageUIExtension',
|
||||
'iPageUIBlockExtension',
|
||||
'iBackofficeLinkedScriptsExtension',
|
||||
'iBackofficeEarlyScriptExtension',
|
||||
'iBackofficeScriptExtension',
|
||||
'iBackofficeInitScriptExtension',
|
||||
'iBackofficeReadyScriptExtension',
|
||||
'iBackofficeLinkedStylesheetsExtension',
|
||||
'iBackofficeStyleExtension',
|
||||
'iBackofficeDictEntriesExtension',
|
||||
'iBackofficeDictEntriesPrefixesExtension',
|
||||
'iPortalUIExtension',
|
||||
'ModuleHandlerApiInterface',
|
||||
'iNewsroomProvider',
|
||||
'iModuleExtension',
|
||||
];
|
||||
foreach($aInterfaces as $sInterface)
|
||||
{
|
||||
self::$m_aExtensionClassNames[$sInterface] = array();
|
||||
@@ -5970,7 +5995,8 @@ abstract class MetaModel
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated 2.7.0 N°2369 will be removed in 2.8
|
||||
* @deprecated 2.7.0 N°2369 Method will not be removed any time soon as we still need to drop view if the instance is migrating from an iTop 2.x to an iTop 3.0 or newer, even if they skip iTop 3.0.
|
||||
* @since 3.0.0 Does not recreate SQL views, only drops them. Method has not been renamed to avoid regressions
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
@@ -5984,7 +6010,7 @@ abstract class MetaModel
|
||||
|
||||
// Reporting views (must be created after any other table)
|
||||
//
|
||||
foreach(self::GetClasses('bizmodel') as $sClass)
|
||||
foreach(self::GetClasses() as $sClass)
|
||||
{
|
||||
$sView = self::DBGetView($sClass);
|
||||
if (CMDBSource::IsTable($sView))
|
||||
@@ -7463,9 +7489,11 @@ abstract class MetaModel
|
||||
* @param string $sInput
|
||||
* @param array $aParams
|
||||
*
|
||||
* @return mixed
|
||||
* @return string
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
static public function ApplyParams($sInput, $aParams)
|
||||
public static function ApplyParams($sInput, $aParams)
|
||||
{
|
||||
$aParams = static::AddMagicPlaceholders($aParams);
|
||||
|
||||
@@ -7475,7 +7503,7 @@ abstract class MetaModel
|
||||
|
||||
$aSearches = array();
|
||||
$aReplacements = array();
|
||||
foreach($aParams as $sSearch => $replace)
|
||||
foreach ($aParams as $sSearch => $replace)
|
||||
{
|
||||
// Some environment parameters are objects, we just need scalars
|
||||
if (is_object($replace))
|
||||
|
||||
@@ -33,6 +33,17 @@ define('CASELOG_SEPARATOR', "\n".'========== %1$s : %2$s (%3$d) ============'."\
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
class ormCaseLog {
|
||||
/**
|
||||
* @var string "plain text" format for the log
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_FORMAT_TEXT = 'text';
|
||||
/**
|
||||
* @var string "HTML" format for the log
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_FORMAT_HTML = 'html';
|
||||
|
||||
protected $m_sLog;
|
||||
protected $m_aIndex;
|
||||
protected $m_bModified;
|
||||
@@ -53,7 +64,7 @@ class ormCaseLog {
|
||||
{
|
||||
if ($bConvertToPlainText)
|
||||
{
|
||||
// Rebuild the log, but filtering any HTML markup for the all 'html' entries in the log
|
||||
// Rebuild the log, but filtering any HTML markup for the all {@see static::ENUM_FORMAT_HTML} entries in the log
|
||||
return $this->GetAsPlainText();
|
||||
}
|
||||
else
|
||||
@@ -136,14 +147,14 @@ class ormCaseLog {
|
||||
$sDate = '';
|
||||
}
|
||||
}
|
||||
$sFormat = array_key_exists('format', $this->m_aIndex[$index]) ? $this->m_aIndex[$index]['format'] : 'text';
|
||||
$sFormat = array_key_exists('format', $this->m_aIndex[$index]) ? $this->m_aIndex[$index]['format'] : static::ENUM_FORMAT_TEXT;
|
||||
switch($sFormat)
|
||||
{
|
||||
case 'text':
|
||||
case static::ENUM_FORMAT_TEXT:
|
||||
$sHtmlEntry = utils::TextToHtml($sTextEntry);
|
||||
break;
|
||||
|
||||
case 'html':
|
||||
case static::ENUM_FORMAT_HTML:
|
||||
$sHtmlEntry = $sTextEntry;
|
||||
$sTextEntry = utils::HtmlToText($sHtmlEntry);
|
||||
break;
|
||||
@@ -175,7 +186,8 @@ class ormCaseLog {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a "plain text" version of the log (equivalent to $this->m_sLog) where all the HTML markup from the 'html' entries have been removed
|
||||
* Returns a "plain text" version of the log (equivalent to $this->m_sLog) where all the HTML markup from the {@see static::ENUM_FORMAT_HTML} entries have been removed
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetAsPlainText()
|
||||
@@ -237,7 +249,7 @@ class ormCaseLog {
|
||||
$iPos += $aIndex[$index]['separator_length'];
|
||||
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
|
||||
$sCSSClass = 'caselog_entry_html';
|
||||
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
|
||||
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == static::ENUM_FORMAT_TEXT))
|
||||
{
|
||||
$sCSSClass = 'caselog_entry';
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
@@ -320,7 +332,7 @@ class ormCaseLog {
|
||||
$iPos += $aIndex[$index]['separator_length'];
|
||||
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
|
||||
$sCSSClass = 'case_log_simple_html_entry_html';
|
||||
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
|
||||
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == static::ENUM_FORMAT_TEXT))
|
||||
{
|
||||
$sCSSClass = 'case_log_simple_html_entry';
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
@@ -425,7 +437,7 @@ class ormCaseLog {
|
||||
}
|
||||
$iPos += $aIndex[$index]['separator_length'];
|
||||
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
|
||||
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == 'text'))
|
||||
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == static::ENUM_FORMAT_TEXT))
|
||||
{
|
||||
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
|
||||
if (!is_null($aTransfoHandler))
|
||||
@@ -506,6 +518,7 @@ class ormCaseLog {
|
||||
$oBlockRenderer = new BlockRenderer($oBlock);
|
||||
$sHtml = $oBlockRenderer->RenderHtml();
|
||||
$sScript = $oBlockRenderer->RenderJsInlineRecursively($oBlock,iUIBlock::ENUM_JS_TYPE_ON_READY);
|
||||
$aJsFiles = $oBlockRenderer->GetJsFiles();
|
||||
if ($sScript!=''){
|
||||
if ($oP == null) {
|
||||
$sScript = '<script>'.$sScript.'</script>';
|
||||
@@ -514,6 +527,18 @@ class ormCaseLog {
|
||||
$oP->add_ready_script($sScript);
|
||||
}
|
||||
}
|
||||
// Ugly hack as we use a block and strip its content above, we'll also need JS files it depends on
|
||||
if(count($aJsFiles) > 0){
|
||||
foreach ($aJsFiles as $sFileAbsUrl) {
|
||||
if ($oP === null) {
|
||||
$sScript = '<script src="'.$sFileAbsUrl.'"></></script>';
|
||||
$sHtml .= $sScript;
|
||||
} else {
|
||||
$oP->add_linked_script($sFileAbsUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
@@ -577,7 +602,7 @@ class ormCaseLog {
|
||||
'date' => time(),
|
||||
'text_length' => $iTextlength,
|
||||
'separator_length' => $iSepLength,
|
||||
'format' => 'html',
|
||||
'format' => static::ENUM_FORMAT_HTML,
|
||||
);
|
||||
$this->m_bModified = true;
|
||||
}
|
||||
@@ -631,11 +656,11 @@ class ormCaseLog {
|
||||
else
|
||||
{
|
||||
// The default is HTML
|
||||
$sFormat = 'html';
|
||||
$sFormat = static::ENUM_FORMAT_HTML;
|
||||
}
|
||||
|
||||
$sText = isset($oJson->message) ? $oJson->message : '';
|
||||
if ($sFormat == 'html')
|
||||
if ($sFormat == static::ENUM_FORMAT_HTML)
|
||||
{
|
||||
$sText = HTMLSanitizer::Sanitize($sText);
|
||||
}
|
||||
@@ -658,7 +683,7 @@ class ormCaseLog {
|
||||
$this->m_bModified = true;
|
||||
}
|
||||
|
||||
public function GetModifiedEntry($sFormat = 'text')
|
||||
public function GetModifiedEntry($sFormat = self::ENUM_FORMAT_TEXT)
|
||||
{
|
||||
$sModifiedEntry = '';
|
||||
if ($this->m_bModified)
|
||||
@@ -673,15 +698,15 @@ class ormCaseLog {
|
||||
* @param string The expected output format text|html
|
||||
* @return string
|
||||
*/
|
||||
public function GetLatestEntry($sFormat = 'text')
|
||||
public function GetLatestEntry($sFormat = self::ENUM_FORMAT_TEXT)
|
||||
{
|
||||
$sRes = '';
|
||||
$aLastEntry = end($this->m_aIndex);
|
||||
$sRaw = substr($this->m_sLog, $aLastEntry['separator_length'], $aLastEntry['text_length']);
|
||||
switch($sFormat)
|
||||
{
|
||||
case 'text':
|
||||
if ($aLastEntry['format'] == 'text')
|
||||
case static::ENUM_FORMAT_TEXT:
|
||||
if ($aLastEntry['format'] == static::ENUM_FORMAT_TEXT)
|
||||
{
|
||||
$sRes = $sRaw;
|
||||
}
|
||||
@@ -691,8 +716,8 @@ class ormCaseLog {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'html':
|
||||
if ($aLastEntry['format'] == 'text')
|
||||
case static::ENUM_FORMAT_HTML:
|
||||
if ($aLastEntry['format'] == static::ENUM_FORMAT_TEXT)
|
||||
{
|
||||
$sRes = utils::TextToHtml($sRaw);
|
||||
}
|
||||
|
||||
@@ -719,70 +719,68 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
{
|
||||
unset($this->aRemoved[$sLinkKey]);
|
||||
}
|
||||
$bIsDuplicate = true;
|
||||
break;
|
||||
$bIsDuplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($bIsDuplicate)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ($bIsDuplicate) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!array_key_exists($oLink->GetKey(), $aExistingLinks))
|
||||
{
|
||||
} else {
|
||||
if (!array_key_exists($oLink->GetKey(), $aExistingLinks)) {
|
||||
$oLink->DBClone();
|
||||
}
|
||||
}
|
||||
$oLink->DBWrite();
|
||||
|
||||
$this->aPreserved[$oLink->GetKey()] = $oLink;
|
||||
$this->aOriginalObjects[$oLink->GetKey()] = $oLink;
|
||||
}
|
||||
foreach ($this->aRemoved as $iLinkId)
|
||||
{
|
||||
if (array_key_exists($iLinkId, $aExistingLinks))
|
||||
{
|
||||
$this->aAdded = [];
|
||||
|
||||
foreach ($this->aRemoved as $iLinkId) {
|
||||
if (array_key_exists($iLinkId, $aExistingLinks)) {
|
||||
$oLink = $aExistingLinks[$iLinkId];
|
||||
if ($oAttDef->IsIndirect())
|
||||
{
|
||||
if ($oAttDef->IsIndirect()) {
|
||||
$oLink->DBDelete();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$oExtKeyToRemote = MetaModel::GetAttributeDef($this->sClass, $sExtKeyToMe);
|
||||
if ($oExtKeyToRemote->IsNullAllowed())
|
||||
{
|
||||
if ($oLink->Get($sExtKeyToMe) == $oHostObject->GetKey())
|
||||
{
|
||||
if ($oExtKeyToRemote->IsNullAllowed()) {
|
||||
if ($oLink->Get($sExtKeyToMe) == $oHostObject->GetKey()) {
|
||||
// Detach the link object from this
|
||||
$oLink->Set($sExtKeyToMe, 0);
|
||||
$oLink->DBUpdate();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$oLink->DBDelete();
|
||||
}
|
||||
}
|
||||
unset($this->aPreserved[$oLink->GetKey()], $this->aOriginalObjects[$oLink->GetKey()]);
|
||||
}
|
||||
}
|
||||
$this->aRemoved = [];
|
||||
|
||||
// Note: process modifications at the end: if a link to remove has also been listed as modified, then it will be gracefully ignored
|
||||
foreach ($this->aModified as $iLinkId => $oLink)
|
||||
{
|
||||
if (array_key_exists($oLink->GetKey(), $aExistingLinks))
|
||||
{
|
||||
foreach ($this->aModified as $iLinkId => $oLink) {
|
||||
if (array_key_exists($oLink->GetKey(), $aExistingLinks)) {
|
||||
$oLink->DBUpdate();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$oLink->DBClone();
|
||||
}
|
||||
$this->aPreserved[$oLink->GetKey()] = $oLink;
|
||||
$this->aOriginalObjects[$oLink->GetKey()] = $oLink;
|
||||
}
|
||||
$this->aModified = [];
|
||||
|
||||
// End of the critical section
|
||||
//
|
||||
$oMtx->Unlock();
|
||||
|
||||
// we updated the instance (original/preserved/added/modified/removed arrays) all along the way
|
||||
$this->bHasDelta = false;
|
||||
$this->oOriginalSet->GetFilter()->SetInternalParams(['id', $oHostObject->GetKey()]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -138,7 +138,7 @@ final class ormTagSet extends ormSet
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of tags indexed by code
|
||||
* @return array index: code, value: corresponding {@see \TagSetFieldData}
|
||||
*/
|
||||
public function GetTags()
|
||||
{
|
||||
|
||||
@@ -31,13 +31,13 @@ class iTopOwnershipToken extends DBObject
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
'category' => 'application',
|
||||
'key_type' => 'autoincrement',
|
||||
'name_attcode' => array('obj_class', 'obj_key'),
|
||||
'state_attcode' => '',
|
||||
'reconc_keys' => array(''),
|
||||
'db_table' => 'priv_ownership_token',
|
||||
'db_key_field' => 'id',
|
||||
'category' => '',
|
||||
'key_type' => 'autoincrement',
|
||||
'name_attcode' => array('obj_class', 'obj_key'),
|
||||
'state_attcode' => '',
|
||||
'reconc_keys' => array(''),
|
||||
'db_table' => 'priv_ownership_token',
|
||||
'db_key_field' => 'id',
|
||||
'db_finalclass_field' => '',
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
|
||||
@@ -95,15 +95,14 @@ class PDFBulkExport extends HTMLBulkExport
|
||||
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="pdf_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$oRadioCustom = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput), "pdf_date_format_radio", "custom", "pdf_date_time_format_custom", "radio");
|
||||
$oRadioCustom->SetDescription(Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip'));
|
||||
$oRadioCustom->GetInput()->SetIsChecked($sDateTimeFormat !== (string)AttributeDateTime::GetFormat());
|
||||
$oRadioCustom->SetBeforeInput(false);
|
||||
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
$oFieldSetDate->AddSubBlock($oRadioCustom);
|
||||
|
||||
$sJSTooltip = json_encode('<div id="date_format_tooltip">'.Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip').'</div>');
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#pdf_custom_date_time_format').tooltip({content: function() { return $sJSTooltip; } });
|
||||
$('#form_part_pdf_options').on('preview_updated', function() { FormatDatesInPreview('pdf', 'html'); });
|
||||
$('#pdf_date_time_format_default').on('click', function() { FormatDatesInPreview('pdf', 'html'); });
|
||||
$('#pdf_date_time_format_custom').on('click', function() { FormatDatesInPreview('pdf', 'html'); });
|
||||
|
||||
@@ -84,15 +84,14 @@ class SpreadsheetBulkExport extends TabularBulkExport
|
||||
|
||||
$sFormatInput = '<input type="text" size="15" name="date_format" id="spreadsheet_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
|
||||
$oRadioCustom = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput), "spreadsheet_date_format_radio", "custom", "spreadsheet_date_time_format_custom", "radio");
|
||||
$oRadioCustom->SetDescription(Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip'));
|
||||
$oRadioCustom->GetInput()->SetIsChecked($sDateTimeFormat !== (string)AttributeDateTime::GetFormat());
|
||||
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
$oRadioCustom->SetBeforeInput(false);
|
||||
$oFieldSetDate->AddSubBlock($oRadioCustom);
|
||||
|
||||
$sJSTooltip = json_encode('<div class="date_format_tooltip">'.Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip').'</div>');
|
||||
$oP->add_ready_script(
|
||||
<<<EOF
|
||||
$('#spreadsheet_custom_date_time_format').tooltip({content: function() { return $sJSTooltip; } });
|
||||
$('#form_part_spreadsheet_options').on('preview_updated', function() { FormatDatesInPreview('spreadsheet', 'spreadsheet'); });
|
||||
$('#spreadsheet_date_time_format_default').on('click', function() { FormatDatesInPreview('spreadsheet', 'spreadsheet'); });
|
||||
$('#spreadsheet_date_time_format_custom').on('click', function() { FormatDatesInPreview('spreadsheet', 'spreadsheet'); });
|
||||
@@ -354,7 +353,7 @@ EOF
|
||||
}
|
||||
else if ($oAttDef instanceof AttributeTagSet)
|
||||
{
|
||||
$sField = $oObj->GetAsCSV($sAttCode, $this->bLocalizeOutput, '');
|
||||
$sField = utils::HtmlEntities($oObj->GetAsCSV($sAttCode, $this->bLocalizeOutput, ''));
|
||||
$sData .= "<td x:str>$sField</td>";
|
||||
}
|
||||
else
|
||||
|
||||
@@ -494,7 +494,17 @@ class SQLObjectQuery extends SQLQuery
|
||||
}
|
||||
}
|
||||
|
||||
private function PrepareSingleTable(SQLObjectQuery $oRootQuery, &$aFrom, $sCallerAlias = '', $aJoinData)
|
||||
/**
|
||||
* @param \SQLObjectQuery $oRootQuery
|
||||
* @param $aFrom
|
||||
* @param $sCallerAlias
|
||||
* @param $aJoinData
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $sCallerAlias for PHP 8.0 compat (Private method with only 2 calls in the class, both providing the optional parameter)
|
||||
*/
|
||||
private function PrepareSingleTable(SQLObjectQuery $oRootQuery, &$aFrom, $sCallerAlias, $aJoinData)
|
||||
{
|
||||
$aTranslationTable[$this->m_sTable]['*'] = $this->m_sTableAlias;
|
||||
$sJoinCond = '';
|
||||
@@ -613,6 +623,7 @@ class SQLObjectQuery extends SQLQuery
|
||||
$aTempFrom = array(); // temporary subset of 'from' specs, to be grouped in the final query
|
||||
foreach ($this->m_aJoinSelects as $aJoinData)
|
||||
{
|
||||
/** @var \SQLObjectQuery $oRightSelect */
|
||||
$oRightSelect = $aJoinData["select"];
|
||||
|
||||
$oRightSelect->PrepareSingleTable($oRootQuery, $aTempFrom, $this->m_sTableAlias, $aJoinData);
|
||||
|
||||
@@ -254,6 +254,7 @@ abstract class TriggerOnObject extends Trigger
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL($sFilter);
|
||||
$oSearch->AddCondition('id', $iObjectId, '=');
|
||||
$oSearch->AllowAllData();
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$bRet = ($oSet->Count() > 0);
|
||||
}
|
||||
|
||||
@@ -22,4 +22,8 @@ fieldset {
|
||||
|
||||
legend {
|
||||
@extend .ibo-fieldset-legend;
|
||||
}
|
||||
|
||||
textarea {
|
||||
@extend .ibo-input-text;
|
||||
}
|
||||
@@ -42,4 +42,24 @@
|
||||
// N°2847 - Recolor svg illustrations with iTop's primary color
|
||||
.ibo-svg-illustration--container > svg *[fill="#6c63ff"]{
|
||||
fill: $ibo-svg-illustration--fill;
|
||||
}
|
||||
|
||||
// N°4481 - Restore HTML tables style identical between edition and visualization
|
||||
// This is a hack to compensate missing variables in the bulma lib, PR has been made here: https://github.com/jgthms/bulma/pull/3455
|
||||
// The following can't be reset to it's original value (from the browser stylesheet), so we have to hardcode it even though it might change in future browser versions...
|
||||
.ibo-is-html-content table {
|
||||
&:not(:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
tbody {
|
||||
tr {
|
||||
&:last-child {
|
||||
td,
|
||||
th {
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,9 @@
|
||||
overflow-x: auto;
|
||||
|
||||
th {
|
||||
position: relative;
|
||||
padding: 5px;
|
||||
padding-right: $ibo-spacing-600;
|
||||
border-width: 1px 1px 0;
|
||||
border-style: groove groove none;
|
||||
background: $ibo-color-white-200;
|
||||
@@ -30,7 +32,15 @@
|
||||
.ibo-preview-header {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.ibo-table-preview--remove-column {
|
||||
position: absolute;
|
||||
top: $ibo-spacing-300;
|
||||
right: $ibo-spacing-300;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
#form_part_interactive_fields_xlsx, #form_part_interactive_fields_csv, #form_part_interactive_fields_pdf {
|
||||
margin-top: $ibo-panel--spacing-top;
|
||||
margin-top: $ibo-spacing-600;
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ $ibo-scrollbar--scrollbar-thumb-background-color: $ibo-color-grey-300 !default;
|
||||
$ibo-scrollbar--scrollbar-thumb-border: none !default;
|
||||
$ibo-scrollbar--scrollbar-thumb-border-radius: $ibo-border-radius-500 !default;
|
||||
|
||||
$ibo-hyperlink-color: $ibo-color-primary-500 !default;
|
||||
$ibo-hyperlink-color--on-hover: $ibo-color-primary-600 !default;
|
||||
$ibo-hyperlink-color--on-active: $ibo-color-primary-700 !default;
|
||||
$ibo-hyperlink-color: $ibo-color-primary-700 !default;
|
||||
$ibo-hyperlink-color--on-hover: $ibo-color-primary-800 !default;
|
||||
$ibo-hyperlink-color--on-active: $ibo-color-primary-900 !default;
|
||||
|
||||
$ibo-svg-illustration--fill: $ibo-color-primary-500 !default;
|
||||
|
||||
|
||||
@@ -3,17 +3,23 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "dashlet-within-dashboard";
|
||||
@import "alert/all";
|
||||
@import "button/all";
|
||||
@import "collapsible-section/all";
|
||||
@import "datatable/all";
|
||||
@import "display-block/all";
|
||||
@import "field/all";
|
||||
@import "fieldset/all";
|
||||
@import "form/all";
|
||||
@import "input/all";
|
||||
@import "panel/all";
|
||||
@import "pill/all";
|
||||
@import "dashlet/all";
|
||||
@import "add-to-dashboard";
|
||||
@import "caselog-entry-form-within-activity-panel";
|
||||
@import "panel-with-datatable";
|
||||
@import "panel-with-tab-container";
|
||||
@import "panel-within-main-content";
|
||||
@import "panel-within-modal";
|
||||
@import "tab-container-within-panel";
|
||||
@import "object-details-with-tab-container";
|
||||
@import "medallion-with-blocklist";
|
||||
@import "input-within-datatable";
|
||||
@import "field-badge-within-datatable";
|
||||
@import "jquery-blockui-within-dialog";
|
||||
@import "jquery-blockui-within-datatable";
|
||||
@import "collapsible-section-within-caselog-list";
|
||||
@import "jquery-blockui-within-datatable";
|
||||
@@ -3,8 +3,8 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-field-badge-within-datatable--ibo-field-badge--margin: 0 !default;
|
||||
$ibo-field-badge-within-datatable--ibo-field-badge--padding: 0 !default;
|
||||
$ibo-field-badge-within-datatable--ibo-field-badge--margin: $ibo-spacing-0 !default;
|
||||
$ibo-field-badge-within-datatable--ibo-field-badge--padding: $ibo-spacing-0 !default;
|
||||
$ibo-field-badge-within-datatable--ibo-field-badge--text-color: unset !default;
|
||||
$ibo-field-badge-within-datatable--ibo-field-badge--background-color: unset !default;
|
||||
$ibo-field-badge-within-datatable--ibo-field-badge-dot--size: 10px !default;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-alert--spacing-top--with-same-block: $ibo-spacing-200 !default;
|
||||
$ibo-alert--spacing-top--with-other-blocks: $ibo-spacing-500 !default;
|
||||
|
||||
/* Spacing between alert blocks */
|
||||
.ibo-alert + .ibo-alert {
|
||||
margin-top: $ibo-alert--spacing-top--with-same-block;
|
||||
}
|
||||
/* Spacing between an alert block and something else */
|
||||
.ibo-alert + .ibo-block:not(.ibo-alert) {
|
||||
margin-top: $ibo-alert--spacing-top--with-other-blocks;
|
||||
}
|
||||
6
css/backoffice/blocks-integrations/alert/_all.scss
Normal file
6
css/backoffice/blocks-integrations/alert/_all.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "alert-with-blocks";
|
||||
7
css/backoffice/blocks-integrations/button/_all.scss
Normal file
7
css/backoffice/blocks-integrations/button/_all.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "button-with-button";
|
||||
@import "button-with-button-group";
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-button--spacing-left--with-button-group: $ibo-button--spacing-left--with-same-block !default;
|
||||
|
||||
/* Reset siblings spacing */
|
||||
.ibo-button-group + .ibo-button-group,
|
||||
.ibo-button + .ibo-button-group,
|
||||
.ibo-button-group + .ibo-button{
|
||||
margin-left: $ibo-button--spacing-left--with-button-group;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-button--spacing-left--with-same-block: $ibo-spacing-200 !default;
|
||||
|
||||
.ibo-button + .ibo-button {
|
||||
margin-left: $ibo-button--spacing-left--with-same-block;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "collapsible-section-with-blocks";
|
||||
@import "collapsible-section-within-caselog-list";
|
||||
@import "collapsible-section-within-alert";
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-collapsible-section--spacing-top--with-same-block: $ibo-spacing-400 !default;
|
||||
$ibo-collapsible-section--spacing-top--with-other-blocks: $ibo-spacing-500 !default;
|
||||
|
||||
/* Spacing between collapsible-section blocks */
|
||||
.ibo-collapsible-section + .ibo-collapsible-section {
|
||||
margin-top: $ibo-collapsible-section--spacing-top--with-same-block;
|
||||
}
|
||||
|
||||
/* Spacing between an alert block and something else */
|
||||
.ibo-collapsible-section + .ibo-block:not(.ibo-collapsible-section) {
|
||||
margin-top: $ibo-collapsible-section--spacing-top--with-other-blocks;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2022 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/* SCSS variables */
|
||||
$ibo-collapsible-section-within-alert--body--padding: $ibo-spacing-300 !default;
|
||||
$ibo-collapsible-section-within-alert--body--text-color: $ibo-color-grey-900 !default;
|
||||
|
||||
.ibo-alert--body {
|
||||
.ibo-collapsible-section {
|
||||
.ibo-collapsible-section--header .ibo-collapsible-section--title,
|
||||
.ibo-collapsible-section--body {
|
||||
@extend %ibo-font-size-150;
|
||||
}
|
||||
|
||||
.ibo-collapsible-section--body {
|
||||
color: $ibo-collapsible-section-within-alert--body--text-color;
|
||||
padding: $ibo-collapsible-section-within-alert--body--padding;
|
||||
}
|
||||
}
|
||||
> * + .ibo-collapsible-section {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/* SCSS variables */
|
||||
$ibo-caselog-entry-in-collapsible-section--body--background-color: transparentize($ibo-color-grey-100,0.5) !default;
|
||||
$ibo-caselog-entry-in-collapsible-section--body--padding: 8px !default;
|
||||
$ibo-caselog-entry-in-collapsible-section--body--padding: $ibo-spacing-300 !default;
|
||||
$ibo-caselog-entry-in-collapsible-section--body--color: $ibo-color-grey-900 !default;
|
||||
|
||||
/* - caselog display in ormcaselog */
|
||||
6
css/backoffice/blocks-integrations/dashlet/_all.scss
Normal file
6
css/backoffice/blocks-integrations/dashlet/_all.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "dashlet-within-dashboard";
|
||||
@@ -3,8 +3,8 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-dashlet-within-dashboard--dashlet-header-static--margin-top--is-first-dashlet: 0 !default;
|
||||
$ibo-dashlet-within-dashboard--dashlet-header-static--margin-top--is-not-first-dashlet: 12px !default;
|
||||
$ibo-dashlet-within-dashboard--dashlet-header-static--margin-top--is-first-dashlet: $ibo-spacing-0 !default;
|
||||
$ibo-dashlet-within-dashboard--dashlet-header-static--margin-top--is-not-first-dashlet: $ibo-spacing-400 !default;
|
||||
|
||||
.ibo-dashboard--grid-row{
|
||||
// Margin on top to have a better visual separation like with fieldsets
|
||||
7
css/backoffice/blocks-integrations/datatable/_all.scss
Normal file
7
css/backoffice/blocks-integrations/datatable/_all.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "datatable-with-blocks";
|
||||
@import "datatable-within-panel";
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-datatable--spacing-top--with-other-blocks: $ibo-spacing-200 !default;
|
||||
|
||||
.ibo-datatable + .ibo-block{
|
||||
margin-top: $ibo-datatable--spacing-top--with-other-blocks;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "display-block-with-blocks";
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-display-block--spacing-top--with-same-block: $ibo-spacing-600 !default;
|
||||
$ibo-display-block--spacing-top--with-other-block: $ibo-spacing-500 !default;
|
||||
|
||||
|
||||
.display_block + .display_block {
|
||||
margin-top: $ibo-display-block--spacing-top--with-same-block;
|
||||
}
|
||||
|
||||
.display_block + .ibo-block:not(.display_block) {
|
||||
margin-top: $ibo-display-block--spacing-top--with-other-block;
|
||||
}
|
||||
7
css/backoffice/blocks-integrations/field/_all.scss
Normal file
7
css/backoffice/blocks-integrations/field/_all.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "field-with-field";
|
||||
@import "field-with-fieldset";
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
$ibo-field--spacing-top--with-same-block: $ibo-spacing-500 !default;
|
||||
|
||||
.ibo-field + .ibo-field {
|
||||
margin-top: $ibo-field--spacing-top--with-same-block;
|
||||
}
|
||||
|
||||
.form_field + .form_field {
|
||||
margin-top: $ibo-field--spacing-top--with-same-block;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-field--spacing-top--with-fieldset: $ibo-spacing-700 !default;
|
||||
|
||||
.ibo-fieldset + .ibo-field {
|
||||
margin-top: $ibo-field--spacing-top--with-fieldset;
|
||||
}
|
||||
8
css/backoffice/blocks-integrations/fieldset/_all.scss
Normal file
8
css/backoffice/blocks-integrations/fieldset/_all.scss
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "fieldset-with-field";
|
||||
@import "fieldset-with-fieldset";
|
||||
@import "fieldset-with-multicolumn";
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-fieldset--spacing-top--with-field: $ibo-spacing-700 !default;
|
||||
|
||||
.ibo-field + .ibo-fieldset:not(.ibo-column) {
|
||||
margin-top: $ibo-fieldset--spacing-top--with-field;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-fieldset--spacing-top--with-fieldset: $ibo-spacing-800 !default;
|
||||
|
||||
.ibo-fieldset + .ibo-fieldset:not(.ibo-column) {
|
||||
margin-top: $ibo-fieldset--spacing-top--with-fieldset;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-fieldset--spacing-top--with-multicolumn: $ibo-spacing-800 !default;
|
||||
|
||||
|
||||
.ibo-multi-column + .ibo-fieldset {
|
||||
margin-top: $ibo-fieldset--spacing-top--with-multicolumn;
|
||||
}
|
||||
6
css/backoffice/blocks-integrations/form/_all.scss
Normal file
6
css/backoffice/blocks-integrations/form/_all.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "form-with-form";
|
||||
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
$ibo-form--sibling-spacing: $ibo-spacing-600 !default;
|
||||
|
||||
.ibo-form + .ibo-form {
|
||||
margin-top: $ibo-form--sibling-spacing;
|
||||
}
|
||||
7
css/backoffice/blocks-integrations/input/_all.scss
Normal file
7
css/backoffice/blocks-integrations/input/_all.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "input-with-label";
|
||||
@import "input-within-datatable";
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-input--spacing-left--with-label: $ibo-spacing-300 !default;
|
||||
|
||||
|
||||
/* Input reset */
|
||||
/* - Standard spacing between label and input */
|
||||
select + label, label + select, label > select,
|
||||
input + label, label + input, label > input {
|
||||
margin-left: $ibo-input--spacing-left--with-label;
|
||||
}
|
||||
|
||||
.ibo-input-with-label--label {
|
||||
&.ibo-has-description {
|
||||
&::after {
|
||||
content: $ibo-field--label--description--content;
|
||||
padding-left: $ibo-field--label--description--padding-left;
|
||||
vertical-align: top;
|
||||
|
||||
cursor: pointer;
|
||||
color: $ibo-field--label--description--color;
|
||||
@extend %ibo-font-ral-bol-50;
|
||||
}
|
||||
}
|
||||
}
|
||||
8
css/backoffice/blocks-integrations/panel/_all.scss
Normal file
8
css/backoffice/blocks-integrations/panel/_all.scss
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "panel-with-blocks";
|
||||
@import "panel-within-main-content";
|
||||
@import "panel-within-modal";
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
$ibo-panel--spacing-top--with-same-block: $ibo-spacing-600 !default;
|
||||
$ibo-panel--spacing-top--with-other-block: $ibo-spacing-500 !default;
|
||||
|
||||
|
||||
.ibo-panel + .ibo-panel {
|
||||
margin-top: $ibo-panel--spacing-top--with-same-block;
|
||||
}
|
||||
|
||||
.ibo-panel + .ibo-block:not(.ibo-panel) {
|
||||
margin-top: $ibo-panel--spacing-top--with-other-block;
|
||||
}
|
||||
6
css/backoffice/blocks-integrations/pill/_all.scss
Normal file
6
css/backoffice/blocks-integrations/pill/_all.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
@import "pill-with-pill";
|
||||
14
css/backoffice/blocks-integrations/pill/_pill-with-pill.scss
Normal file
14
css/backoffice/blocks-integrations/pill/_pill-with-pill.scss
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-pill--spacing-left--with-same-block: $ibo-spacing-500 !default;
|
||||
|
||||
|
||||
/* For pills, margin is set on the right to keep them aligned horizontally when they wrap */
|
||||
/* The drawback is that we can't set the margin only when next to another .ibo-pill as there is no such CSS selector */
|
||||
/* Also :last-child is used instead of :last-of-type as pill can be either an <a> or a <span> */
|
||||
.ibo-pill:not(:last-child) {
|
||||
margin-right: $ibo-pill--spacing-left--with-same-block;
|
||||
}
|
||||
@@ -4,8 +4,6 @@
|
||||
*/
|
||||
|
||||
/* SCSS variables */
|
||||
$ibo-alert--spacing-top--with-same-block: 6px !default;
|
||||
$ibo-alert--spacing-top--with-other-blocks: 16px !default;
|
||||
$ibo-alert--padding-y: 18px !default;
|
||||
$ibo-alert--padding-x: 20px !default;
|
||||
$ibo-alert--min-height: 30px !default;
|
||||
@@ -14,10 +12,10 @@ $ibo-alert--border-radius: $ibo-border-radius-300 !default;
|
||||
$ibo-alert--title--highlight--width: 4px !default;
|
||||
$ibo-alert--title--highlight--height: 100% !default;
|
||||
|
||||
$ibo-alert--body--margin-top: 4px !default;
|
||||
$ibo-alert--body--margin-top: $ibo-spacing-200 !default;
|
||||
|
||||
$ibo-alert-minimized--padding-y: 5px !default;
|
||||
$ibo-alert-minimized--title--padding-bottom: 0px !default;
|
||||
$ibo-alert-minimized--title--padding-bottom: $ibo-spacing-0 !default;
|
||||
|
||||
$ibo-alert--action-button--top: 5px !default;
|
||||
$ibo-alert--maximize-minimize-button--right: 30px !default;
|
||||
@@ -49,7 +47,10 @@ $ibo-alert-colors: (
|
||||
.ibo-alert.ibo-is-#{$sColor} {
|
||||
background-color: $bg-color;
|
||||
color: $text-color;
|
||||
|
||||
a {
|
||||
color: $text-color;
|
||||
@extend %ibo-font-ral-bol-150;
|
||||
}
|
||||
&::before {
|
||||
background-color: $highlight-color;
|
||||
}
|
||||
@@ -113,15 +114,6 @@ $ibo-alert-colors: (
|
||||
margin-top: $ibo-alert--body--margin-top;
|
||||
}
|
||||
|
||||
/* Spacing between alert blocks */
|
||||
.ibo-alert + .ibo-alert {
|
||||
margin-top: $ibo-alert--spacing-top--with-same-block;
|
||||
}
|
||||
/* Spacing between an alert block and something else */
|
||||
.ibo-alert + :not(.ibo-alert) {
|
||||
margin-top: $ibo-alert--spacing-top--with-other-blocks;
|
||||
}
|
||||
|
||||
.ibo-alert--action-button{
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -3,29 +3,29 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-breadcrumbs--margin-right: 32px !default;
|
||||
$ibo-breadcrumbs--margin-right: $ibo-spacing-700 !default;
|
||||
|
||||
$ibo-breadcrumbs--item--text-color: $ibo-color-grey-800 !default;
|
||||
|
||||
$ibo-breadcrumbs--item-icon--margin-x: 8px !default;
|
||||
$ibo-breadcrumbs--item-icon--margin-x: $ibo-spacing-300 !default;
|
||||
$ibo-breadcrumbs--item-icon--max-width: 16px !default;
|
||||
$ibo-breadcrumbs--item-icon--text-color: $ibo-color-grey-600 !default;
|
||||
|
||||
$ibo-breadcrumbs--item-label--max-width: 100px !default;
|
||||
|
||||
$ibo-breadcrumbs--item-separator--margin-x: 12px !default;
|
||||
$ibo-breadcrumbs--item-separator--margin-x: $ibo-spacing-400 !default;
|
||||
$ibo-breadcrumbs--item-separator--text-color: $ibo-color-grey-500 !default;
|
||||
|
||||
$ibo-breadcrumbs--previous-items-list-toggler--text-color: $ibo-color-grey-700 !default;
|
||||
$ibo-breadcrumbs--previous-items-list-toggler--margin-right: 2 * $ibo-breadcrumbs--item-separator--margin-x !default;
|
||||
|
||||
$ibo-breadcrumbs--previous-items-list--top: 37px !default;
|
||||
$ibo-breadcrumbs--previous-items-list--padding-y: 8px !default;
|
||||
$ibo-breadcrumbs--previous-items-list--padding-y: $ibo-spacing-300 !default;
|
||||
$ibo-breadcrumbs--previous-items-list--background-color: $ibo-color-white-100 !default;
|
||||
|
||||
$ibo-breadcrumbs--previous-item--text-color: $ibo-breadcrumbs--item--text-color !default;
|
||||
$ibo-breadcrumbs--previous-item--padding-x: 12px !default;
|
||||
$ibo-breadcrumbs--previous-item--padding-y: 12px !default;
|
||||
$ibo-breadcrumbs--previous-item--padding-x: $ibo-spacing-400 !default;
|
||||
$ibo-breadcrumbs--previous-item--padding-y: $ibo-spacing-400 !default;
|
||||
$ibo-breadcrumbs--previous-item-label--max-width: 200px !default;
|
||||
|
||||
.ibo-breadcrumbs{
|
||||
|
||||
@@ -63,11 +63,4 @@ $ibo-button-group--elements-separator--border-left: 1px solid transparent !defau
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset siblings spacing */
|
||||
.ibo-button-group + .ibo-button-group,
|
||||
.ibo-button + .ibo-button-group,
|
||||
.ibo-button-group + .ibo-button{
|
||||
margin-left: $ibo-button--sibling-spacing;
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-button--sibling-spacing: 5px !default;
|
||||
$ibo-button--padding-y: 6px !default;
|
||||
$ibo-button--padding-x: 9px !default;
|
||||
$ibo-button--border: 0 !default;
|
||||
@@ -11,10 +10,10 @@ $ibo-button--border-radius: $ibo-border-radius-400 !default;
|
||||
$ibo-button--box-shadow-bottom: 0px 2px 0px !default;
|
||||
$ibo-button--box-shadow-top: inset 0px 2px 0px !default;
|
||||
|
||||
$ibo-button--label--margin-left: 4px !default;
|
||||
$ibo-button--label--margin-left: $ibo-spacing-200 !default;
|
||||
|
||||
$ibo-button--vertical-align--margin-bottom: 4px !default;
|
||||
$ibo-button--vertical-align--margin-top: 4px !default;
|
||||
$ibo-button--vertical-align--margin-bottom: $ibo-spacing-200 !default;
|
||||
$ibo-button--vertical-align--margin-top: $ibo-spacing-200 !default;
|
||||
|
||||
/**
|
||||
* - Text color
|
||||
@@ -54,22 +53,22 @@ $ibo-button-colors: (
|
||||
/* Primary action does not have the colors from the primary brand color, at least not for now */
|
||||
'primary': (
|
||||
'': (
|
||||
$ibo-color-cyan-700,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-cyan-900,
|
||||
$ibo-color-cyan-800,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-cyan-950,
|
||||
$ibo-color-cyan-900,
|
||||
),
|
||||
':hover': (
|
||||
$ibo-color-cyan-800,
|
||||
$ibo-color-cyan-900,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-cyan-900,
|
||||
$ibo-color-cyan-800,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-cyan-950,
|
||||
$ibo-color-cyan-900,
|
||||
),
|
||||
':active': (
|
||||
$ibo-color-cyan-800,
|
||||
$ibo-color-cyan-900,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-top $ibo-color-cyan-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-cyan-800,
|
||||
$ibo-color-cyan-800,
|
||||
$ibo-button--box-shadow-top $ibo-color-cyan-950 #{','} $ibo-button--box-shadow-bottom $ibo-color-cyan-900,
|
||||
$ibo-color-cyan-900,
|
||||
),
|
||||
':disabled': (
|
||||
$ibo-color-grey-300,
|
||||
@@ -106,22 +105,22 @@ $ibo-button-colors: (
|
||||
),
|
||||
'danger': (
|
||||
'': (
|
||||
$ibo-color-danger-600,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-danger-800,
|
||||
$ibo-color-danger-700,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-danger-900,
|
||||
$ibo-color-danger-800,
|
||||
),
|
||||
':hover': (
|
||||
$ibo-color-danger-700,
|
||||
$ibo-color-danger-800,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-danger-800,
|
||||
$ibo-color-danger-700,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-danger-900,
|
||||
$ibo-color-danger-800,
|
||||
),
|
||||
':active': (
|
||||
$ibo-color-danger-700,
|
||||
$ibo-color-danger-800,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-top $ibo-color-danger-800 #{','} $ibo-button--box-shadow-bottom $ibo-color-danger-700,
|
||||
$ibo-color-danger-700,
|
||||
$ibo-button--box-shadow-top $ibo-color-danger-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-danger-800,
|
||||
$ibo-color-danger-800,
|
||||
),
|
||||
':disabled': (
|
||||
$ibo-color-grey-300,
|
||||
@@ -132,22 +131,22 @@ $ibo-button-colors: (
|
||||
),
|
||||
'success': (
|
||||
'': (
|
||||
$ibo-color-success-700,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-success-900,
|
||||
$ibo-color-success-800,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-success-950,
|
||||
$ibo-color-success-900,
|
||||
),
|
||||
':hover': (
|
||||
$ibo-color-success-800,
|
||||
$ibo-color-success-900,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-success-900,
|
||||
$ibo-color-success-800,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-success-950,
|
||||
$ibo-color-success-900,
|
||||
),
|
||||
':active': (
|
||||
$ibo-color-success-800,
|
||||
$ibo-color-success-900,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-top $ibo-color-success-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-success-800,
|
||||
$ibo-color-success-800,
|
||||
$ibo-button--box-shadow-top $ibo-color-success-950 #{','} $ibo-button--box-shadow-bottom $ibo-color-success-900,
|
||||
$ibo-color-success-900,
|
||||
),
|
||||
':disabled': (
|
||||
$ibo-color-grey-300,
|
||||
@@ -159,22 +158,22 @@ $ibo-button-colors: (
|
||||
/* Colors */
|
||||
'red': (
|
||||
'': (
|
||||
$ibo-color-red-600,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-red-800,
|
||||
$ibo-color-red-700,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-red-900,
|
||||
$ibo-color-red-800,
|
||||
),
|
||||
':hover': (
|
||||
$ibo-color-red-700,
|
||||
$ibo-color-red-800,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-red-800,
|
||||
$ibo-color-red-700,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-red-900,
|
||||
$ibo-color-red-800,
|
||||
),
|
||||
':active': (
|
||||
$ibo-color-red-700,
|
||||
$ibo-color-red-800,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-top $ibo-color-red-800 #{','} $ibo-button--box-shadow-bottom $ibo-color-red-700,
|
||||
$ibo-color-red-700,
|
||||
$ibo-button--box-shadow-top $ibo-color-red-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-red-800,
|
||||
$ibo-color-red-800,
|
||||
),
|
||||
':disabled': (
|
||||
$ibo-color-grey-300,
|
||||
@@ -185,22 +184,22 @@ $ibo-button-colors: (
|
||||
),
|
||||
'green': (
|
||||
'': (
|
||||
$ibo-color-green-700,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-green-900,
|
||||
$ibo-color-green-800,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-green-950,
|
||||
$ibo-color-green-900,
|
||||
),
|
||||
':hover': (
|
||||
$ibo-color-green-800,
|
||||
$ibo-color-green-900,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-green-900,
|
||||
$ibo-color-green-800,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-green-950,
|
||||
$ibo-color-green-900,
|
||||
),
|
||||
':active': (
|
||||
$ibo-color-green-800,
|
||||
$ibo-color-green-900,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-top $ibo-color-green-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-green-800,
|
||||
$ibo-color-green-800,
|
||||
$ibo-button--box-shadow-top $ibo-color-green-950 #{','} $ibo-button--box-shadow-bottom $ibo-color-green-900,
|
||||
$ibo-color-green-900,
|
||||
),
|
||||
':disabled': (
|
||||
$ibo-color-grey-300,
|
||||
@@ -211,22 +210,22 @@ $ibo-button-colors: (
|
||||
),
|
||||
'cyan': (
|
||||
'': (
|
||||
$ibo-color-cyan-500,
|
||||
$ibo-color-cyan-800,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-cyan-900,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-cyan-950,
|
||||
$ibo-color-cyan-900,
|
||||
),
|
||||
':hover': (
|
||||
$ibo-color-cyan-700,
|
||||
$ibo-color-cyan-900,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-cyan-900,
|
||||
$ibo-button--box-shadow-bottom $ibo-color-cyan-950,
|
||||
$ibo-color-cyan-900,
|
||||
),
|
||||
':active': (
|
||||
$ibo-color-cyan-700,
|
||||
$ibo-color-cyan-900,
|
||||
$ibo-color-white-100,
|
||||
$ibo-button--box-shadow-top $ibo-color-cyan-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-cyan-700,
|
||||
$ibo-color-cyan-700,
|
||||
$ibo-button--box-shadow-top $ibo-color-cyan-950 #{','} $ibo-button--box-shadow-bottom $ibo-color-cyan-900,
|
||||
$ibo-color-cyan-900,
|
||||
),
|
||||
':disabled': (
|
||||
$ibo-color-grey-300,
|
||||
@@ -268,9 +267,9 @@ $ibo-button-colors: (
|
||||
'primary': (
|
||||
'': (
|
||||
transparent,
|
||||
$ibo-color-cyan-800,
|
||||
$ibo-color-cyan-900,
|
||||
$ibo-button--box-shadow-bottom transparent,
|
||||
$ibo-color-cyan-700,
|
||||
$ibo-color-cyan-800,
|
||||
),
|
||||
':hover': (
|
||||
$ibo-color-white-100,
|
||||
@@ -475,14 +474,27 @@ $ibo-button-colors: (
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap; /* To force sub elements to be on 1 line */
|
||||
@extend %ibo-font-ral-sembol-100;
|
||||
|
||||
& + .ibo-button {
|
||||
margin-left: $ibo-button--sibling-spacing;
|
||||
}
|
||||
|
||||
|
||||
&.ibo-action-button {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.ibo-button--loading-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.ibo-is-loading {
|
||||
.ibo-button--icon{
|
||||
display: none;
|
||||
}
|
||||
.ibo-button--loading-icon {
|
||||
display: inline-block;
|
||||
&+ .ibo-button--label{
|
||||
margin-left: $ibo-button--label--margin-left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-button--label {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user