mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 23:44:11 +01:00
Compare commits
405 Commits
3
...
3.0.0-beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa9ab1ace5 | ||
|
|
bfe22b4ec0 | ||
|
|
f84bc9fbbc | ||
|
|
0dc03de7b2 | ||
|
|
af338de17f | ||
|
|
0cffd567c2 | ||
|
|
9d006c279b | ||
|
|
f410bff309 | ||
|
|
0e9ff71c0a | ||
|
|
a599a5c5b7 | ||
|
|
84dae0fb37 | ||
|
|
65e3ef32ed | ||
|
|
0396914068 | ||
|
|
0e55a30e5a | ||
|
|
c0fbf0c735 | ||
|
|
e59d472cec | ||
|
|
27e6840442 | ||
|
|
a6aa183e26 | ||
|
|
b5074c4cee | ||
|
|
fbb54c6d6d | ||
|
|
95066fbc49 | ||
|
|
e2958bd43f | ||
|
|
382135fde8 | ||
|
|
4d80235d89 | ||
|
|
8b9589744b | ||
|
|
250d95c3eb | ||
|
|
0997189969 | ||
|
|
410948fb4e | ||
|
|
9f75fae33f | ||
|
|
b1aebc6c7a | ||
|
|
85526b1983 | ||
|
|
2a1e0b7429 | ||
|
|
3a73bd3b24 | ||
|
|
d7ba0aac82 | ||
|
|
8259a79cd2 | ||
|
|
de71d490c1 | ||
|
|
60b015919d | ||
|
|
2ca11aa2b7 | ||
|
|
275daf3b0d | ||
|
|
671d112452 | ||
|
|
a23ea9a01f | ||
|
|
949b213f9d | ||
|
|
a940adc4ba | ||
|
|
5d994edd62 | ||
|
|
2a9ce75db0 | ||
|
|
f8fcfc4d7d | ||
|
|
663a9ab224 | ||
|
|
f604821bdf | ||
|
|
ac83c0334c | ||
|
|
457165b2fc | ||
|
|
b1ca1f2630 | ||
|
|
bce1bd8192 | ||
|
|
4d123e1450 | ||
|
|
58e315d7f6 | ||
|
|
1281d475e4 | ||
|
|
32ac999ff5 | ||
|
|
1059befa39 | ||
|
|
5db8bd06ba | ||
|
|
0f5130611d | ||
|
|
a1271da74a | ||
|
|
0d40235791 | ||
|
|
d1e594e225 | ||
|
|
bcc2d7140e | ||
|
|
ee5847ec82 | ||
|
|
108bc2fa1d | ||
|
|
efb7f84ec4 | ||
|
|
224031e0d6 | ||
|
|
e905838733 | ||
|
|
dd63f2b817 | ||
|
|
8f84c3b84b | ||
|
|
bc4f384948 | ||
|
|
00c58bb245 | ||
|
|
11f6c88ac7 | ||
|
|
77cf879f4f | ||
|
|
13f6f6ebe6 | ||
|
|
c413ac989e | ||
|
|
7a770b32ee | ||
|
|
9d8adae5c7 | ||
|
|
b0531597f0 | ||
|
|
0bdf43b085 | ||
|
|
27da8470b7 | ||
|
|
c0be48ceee | ||
|
|
6c561029e9 | ||
|
|
c417a454d6 | ||
|
|
d8e2a1cc7c | ||
|
|
51e24a4e35 | ||
|
|
a82f172ca4 | ||
|
|
8ef43452f8 | ||
|
|
18a4867b4d | ||
|
|
ff05a1d35e | ||
|
|
5a4c9393a4 | ||
|
|
4b03b42494 | ||
|
|
215dcfb465 | ||
|
|
b965138d57 | ||
|
|
485262ec0a | ||
|
|
dd8a51ea1b | ||
|
|
d85729803d | ||
|
|
3132d665e0 | ||
|
|
be03f3c6de | ||
|
|
3a876d5c75 | ||
|
|
147916062b | ||
|
|
bcfdf76b71 | ||
|
|
679542eaaa | ||
|
|
4d5ff22b60 | ||
|
|
d54c5b5f55 | ||
|
|
0de6f98add | ||
|
|
5bf6035bcf | ||
|
|
a076792e77 | ||
|
|
73132f981e | ||
|
|
80e4a46a20 | ||
|
|
95d9b845b6 | ||
|
|
609d9c7a60 | ||
|
|
3d338aff33 | ||
|
|
8a46900df9 | ||
|
|
8efbfd13d0 | ||
|
|
19264a7d64 | ||
|
|
742e3a2a20 | ||
|
|
117bf3046d | ||
|
|
b072e1bb3d | ||
|
|
d405fc0675 | ||
|
|
77527987ee | ||
|
|
2e6b773ba4 | ||
|
|
dd6a9fe16a | ||
|
|
d9ccc620c9 | ||
|
|
b245275c7d | ||
|
|
bf22c8b67a | ||
|
|
1b58d9b801 | ||
|
|
2d2a6857de | ||
|
|
373641e01d | ||
|
|
d11eceac62 | ||
|
|
3965806fa0 | ||
|
|
74eabafe31 | ||
|
|
2625d2da80 | ||
|
|
1b9eb2a6ad | ||
|
|
94c37b2e14 | ||
|
|
71d2d61c4e | ||
|
|
020dc8b819 | ||
|
|
c11ad30134 | ||
|
|
02d32a556d | ||
|
|
71fcc6f026 | ||
|
|
dd614f5931 | ||
|
|
499d429a0e | ||
|
|
7168860a0b | ||
|
|
a395c1760a | ||
|
|
4033773849 | ||
|
|
9fd6ba414d | ||
|
|
2aee978790 | ||
|
|
39efe1237e | ||
|
|
684c88e0b8 | ||
|
|
e7c7ca76ac | ||
|
|
a6e5969201 | ||
|
|
1e99ad436f | ||
|
|
8580eb6d2b | ||
|
|
c6b8526dc0 | ||
|
|
03c7d63588 | ||
|
|
32e714f4a6 | ||
|
|
b8ef4885e5 | ||
|
|
e669cfcea1 | ||
|
|
82f0cd5f3d | ||
|
|
b6661fde86 | ||
|
|
e46480032e | ||
|
|
aed1e7dfdc | ||
|
|
b32fd7e018 | ||
|
|
3ae8575c1d | ||
|
|
cc4b307bc9 | ||
|
|
6d004f83dd | ||
|
|
8f68ca3f67 | ||
|
|
84741c19f0 | ||
|
|
96126d235f | ||
|
|
4de40e289f | ||
|
|
86f649affc | ||
|
|
4f5c987d8b | ||
|
|
9ccd26a541 | ||
|
|
e7b5953feb | ||
|
|
e441e5e78a | ||
|
|
6d57945bd1 | ||
|
|
0cb0f52b12 | ||
|
|
6be9a87c15 | ||
|
|
43daa2ef08 | ||
|
|
8f9a69fa60 | ||
|
|
1d94e12da8 | ||
|
|
0a04fe5917 | ||
|
|
b03389068e | ||
|
|
c9171ef30e | ||
|
|
3853d38d69 | ||
|
|
caa2a05bf4 | ||
|
|
fc39d8aca9 | ||
|
|
022887258f | ||
|
|
cf12578289 | ||
|
|
3105a7ef77 | ||
|
|
f5ee19370c | ||
|
|
923134d67d | ||
|
|
3c2c2d7a51 | ||
|
|
3ee4a14c56 | ||
|
|
dd284a6c1d | ||
|
|
c240a8991d | ||
|
|
0948c46dc7 | ||
|
|
d9755fe59d | ||
|
|
7f82e9262b | ||
|
|
44952d1ea0 | ||
|
|
7f15eed9a8 | ||
|
|
14930fbab2 | ||
|
|
2cbca93d77 | ||
|
|
c2f5cafaf3 | ||
|
|
81822efa0f | ||
|
|
104cf9479c | ||
|
|
a36632def6 | ||
|
|
6a1b1d7740 | ||
|
|
f544d53c36 | ||
|
|
3188204d05 | ||
|
|
a7294d48a6 | ||
|
|
42be0c20cb | ||
|
|
1c6ffab0e3 | ||
|
|
095d61b6f9 | ||
|
|
923a025f1c | ||
|
|
a4b6f4e37c | ||
|
|
78b2824c13 | ||
|
|
f0c73451a2 | ||
|
|
62f5eb5ae9 | ||
|
|
7d9416cc81 | ||
|
|
a82a2df6aa | ||
|
|
f1e4120364 | ||
|
|
3471d9d693 | ||
|
|
6d7bcb8a7c | ||
|
|
db6e813cba | ||
|
|
d74e3e6b42 | ||
|
|
e9648ad75e | ||
|
|
e53da47647 | ||
|
|
1128490d47 | ||
|
|
186ef1689e | ||
|
|
0487cb8701 | ||
|
|
27b930c31c | ||
|
|
a8ef1c7899 | ||
|
|
644f0bc94c | ||
|
|
f3b0f79a59 | ||
|
|
eaae79a5f0 | ||
|
|
bc10baed3e | ||
|
|
b740cb2afd | ||
|
|
c0ec7e02f8 | ||
|
|
5977c5dd9e | ||
|
|
1c2dcc7b9a | ||
|
|
6ad3c40c42 | ||
|
|
f49c8ce188 | ||
|
|
acf828b72e | ||
|
|
cb61d85572 | ||
|
|
626316e8d1 | ||
|
|
ab30bae46a | ||
|
|
bd2c0d1121 | ||
|
|
64764228a9 | ||
|
|
463593d6cf | ||
|
|
85aac7bf52 | ||
|
|
dc36b4648d | ||
|
|
25145a68af | ||
|
|
dab522caca | ||
|
|
88634bcd92 | ||
|
|
6ab6930986 | ||
|
|
1f3b467083 | ||
|
|
34647cc402 | ||
|
|
2a9e16bd2c | ||
|
|
ed1fdfa830 | ||
|
|
1124a584cd | ||
|
|
d2c014f718 | ||
|
|
e488ec8c38 | ||
|
|
18b2e54581 | ||
|
|
3c89313795 | ||
|
|
11278d7ba5 | ||
|
|
a6eeb3a50a | ||
|
|
1f1028bbc0 | ||
|
|
08f973f8c0 | ||
|
|
ad22113881 | ||
|
|
e5498e3292 | ||
|
|
16c9599739 | ||
|
|
a7040a911d | ||
|
|
8227796c87 | ||
|
|
bac92716f3 | ||
|
|
993dbed7e7 | ||
|
|
390dc1c945 | ||
|
|
09b12bd06e | ||
|
|
3a06f2eaa9 | ||
|
|
a8e1db8b3c | ||
|
|
2cc4448a15 | ||
|
|
d0d254ad59 | ||
|
|
8f62b63309 | ||
|
|
227c90b47f | ||
|
|
6fc3033100 | ||
|
|
cfab20519e | ||
|
|
cbc5bb70d0 | ||
|
|
d5d3a7af47 | ||
|
|
03fcc94b1f | ||
|
|
4a28aa316f | ||
|
|
88a615528f | ||
|
|
b3e1b7124a | ||
|
|
48d0fc6ddb | ||
|
|
8bf9987174 | ||
|
|
0f4c325aab | ||
|
|
93a1f027cb | ||
|
|
3e0971597b | ||
|
|
fce3934977 | ||
|
|
d8feda89a5 | ||
|
|
7aecdd0dc7 | ||
|
|
a478294211 | ||
|
|
9de1f1800d | ||
|
|
94c8dbac18 | ||
|
|
efc745f574 | ||
|
|
3e45dcdbb1 | ||
|
|
07257cc2d2 | ||
|
|
ae6e08d430 | ||
|
|
473a49ab6b | ||
|
|
0c58d67f41 | ||
|
|
d2c4a6e2e9 | ||
|
|
d1ccd5c8eb | ||
|
|
370a07f596 | ||
|
|
9ae056e663 | ||
|
|
ce596877a4 | ||
|
|
3d9540e895 | ||
|
|
27d8869665 | ||
|
|
3b3fa7b1f8 | ||
|
|
135b5e8adb | ||
|
|
52dbf23245 | ||
|
|
6b899d3f77 | ||
|
|
87ba67225a | ||
|
|
2ad3b3c27e | ||
|
|
46dc7709ab | ||
|
|
92a640e41a | ||
|
|
842df7646b | ||
|
|
01b38d2ed6 | ||
|
|
ddb5ffc11a | ||
|
|
2a62e28c66 | ||
|
|
973133ce0b | ||
|
|
be0a728f42 | ||
|
|
1bb7d9947c | ||
|
|
65d94bd914 | ||
|
|
d951d0def0 | ||
|
|
beadd9cd03 | ||
|
|
fac97324a3 | ||
|
|
f33919c312 | ||
|
|
74d8eda713 | ||
|
|
6790fdb9e8 | ||
|
|
d545123ca7 | ||
|
|
59678ef8c6 | ||
|
|
a6d20ab648 | ||
|
|
e5559a1899 | ||
|
|
f8757c6d5d | ||
|
|
7f4ef12c04 | ||
|
|
2a90557782 | ||
|
|
7b3595357a | ||
|
|
f6d9d0f08b | ||
|
|
f3cf154969 | ||
|
|
923a4048d3 | ||
|
|
0816d27456 | ||
|
|
d07199db7c | ||
|
|
903afff687 | ||
|
|
c0f5906dce | ||
|
|
fa3140cc13 | ||
|
|
22aaba3c65 | ||
|
|
5878b18b02 | ||
|
|
530ec111ef | ||
|
|
430049d7c4 | ||
|
|
e45fd92022 | ||
|
|
605165fc5a | ||
|
|
644944c6fd | ||
|
|
8313d61521 | ||
|
|
81d30ca9f4 | ||
|
|
6078ccc144 | ||
|
|
7b9d89e485 | ||
|
|
48bc8b2341 | ||
|
|
8f03ac4aa3 | ||
|
|
97f5f0b50d | ||
|
|
05594ccdd6 | ||
|
|
e2f301827a | ||
|
|
067cbe2afa | ||
|
|
a99f4c0666 | ||
|
|
d594f28ed9 | ||
|
|
deed948f54 | ||
|
|
25e88b889d | ||
|
|
d6695d3ad8 | ||
|
|
0bc9aed3fe | ||
|
|
98ec4bba53 | ||
|
|
e527324468 | ||
|
|
8ccada40d1 | ||
|
|
80d974f2b4 | ||
|
|
521c493fd1 | ||
|
|
b68ee1136e | ||
|
|
0f43b0a6de | ||
|
|
58d1a1d311 | ||
|
|
5b3ab2965f | ||
|
|
f97f55bcca | ||
|
|
3262ad7029 | ||
|
|
a5bb6a0c55 | ||
|
|
f0d6b41cee | ||
|
|
5c01d222c9 | ||
|
|
7e65541d3c | ||
|
|
83b8fbb6b5 | ||
|
|
5b9e5f8de9 | ||
|
|
ddceeafc52 | ||
|
|
56087d9686 | ||
|
|
5aef6f209b | ||
|
|
1b6d69037e | ||
|
|
3941f323c0 | ||
|
|
b19f9d4875 | ||
|
|
057bb1a296 | ||
|
|
192cb2df73 | ||
|
|
47c797daba | ||
|
|
33dcb35344 | ||
|
|
68ab84716b |
@@ -77,24 +77,20 @@ Then, **for a method** of an eligible class:
|
||||
|
||||
:notebook: as spaces are used to mark code, the templates (`.doc/phpdoc-templates/combodo-wiki/*`) have very few indentation, thus they are awful to read (sorry).
|
||||
|
||||
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
cd .doc
|
||||
composer require phpdocumentor/phpdocumentor:~2 --dev
|
||||
```
|
||||
|
||||
## Generation
|
||||
`./bin/build-doc-object-manipulation` and `./bin/build-doc-extensions` contains examples of doc. generation, beware: they have to be called from the .doc directory:
|
||||
```shell
|
||||
cd /path/to/itop/.doc
|
||||
./bin/build-doc-object-manipulation
|
||||
```
|
||||
|
||||
the resulting documentation is written into `data/phpdocumentor/output`
|
||||
|
||||
1. Switch to this directory : `cd /path/to/itop/.doc`
|
||||
2. `composer install`
|
||||
3. `./bin/build-doc-object-manipulation`
|
||||
3. `./bin/build-doc-extensions`
|
||||
4. Get the generated files from `/path/to/itop/data/phpdocumentor/output`
|
||||
|
||||
## Dokuwiki requirements
|
||||
* the template uses the [wrap plugin](https://www.dokuwiki.org/plugin:wrap).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh -x
|
||||
|
||||
rm -rf /tmp/phpdoc-twig-cache/ && rm -rf data/phpdocumentor/output/extensions/ && rm -rf data/phpdocumentor/temp/extensions/ && .doc/vendor/bin/phpdoc -c .doc/phpdoc-extensions.dist.xml -vvv
|
||||
rm -rf /tmp/phpdoc-twig-cache/ && rm -rf data/phpdocumentor/output/extensions/ && rm -rf data/phpdocumentor/temp/extensions/ && ./vendor/bin/phpdoc -c ./phpdoc-extensions.dist.xml -vvv
|
||||
|
||||
# now wee need to lowercase every generated file because dokuwiki can't handle uppercase
|
||||
cd data/phpdocumentor/output/extensions/ && for i in $( ls | grep [A-Z] ); do mv -i $i `echo $i | tr 'A-Z' 'a-z'`; done
|
||||
cd ../data/phpdocumentor/output/extensions/ && for i in $(ls | grep [A-Z]); do mv -i $i $(echo $i | tr 'A-Z' 'a-z'); done
|
||||
|
||||
@@ -90,7 +90,7 @@ ij_xml_attribute_wrap = normal
|
||||
ij_xml_block_comment_at_first_column = true
|
||||
ij_xml_keep_blank_lines = 2
|
||||
ij_xml_keep_indents_on_empty_lines = false
|
||||
ij_xml_keep_line_breaks = false
|
||||
ij_xml_keep_line_breaks = true
|
||||
ij_xml_keep_line_breaks_in_text = true
|
||||
ij_xml_keep_whitespaces = false
|
||||
ij_xml_keep_whitespaces_around_cdata = preserve
|
||||
@@ -110,6 +110,7 @@ ij_shell_keep_column_alignment_padding = false
|
||||
ij_shell_minify_program = false
|
||||
ij_shell_redirect_followed_by_space = false
|
||||
ij_shell_switch_cases_indented = false
|
||||
ij_shell_use_unix_line_separator = true
|
||||
|
||||
[{*.cjs,*.js}]
|
||||
indent_style = tab
|
||||
@@ -514,7 +515,7 @@ ij_json_wrap_long_lines = false
|
||||
indent_style = tab
|
||||
ij_smart_tabs = true
|
||||
ij_visual_guides = none
|
||||
ij_html_add_new_line_before_tags = body, div, p, form, h1, h2, h3
|
||||
ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3
|
||||
ij_html_align_attributes = true
|
||||
ij_html_align_text = false
|
||||
ij_html_attribute_wrap = normal
|
||||
@@ -541,7 +542,21 @@ ij_html_space_inside_empty_tag = false
|
||||
ij_html_text_wrap = normal
|
||||
ij_html_uniform_ident = false
|
||||
|
||||
[{*.yaml, *.yml}]
|
||||
[{*.markdown,*.md}]
|
||||
ij_visual_guides = none
|
||||
ij_markdown_force_one_space_after_blockquote_symbol = true
|
||||
ij_markdown_force_one_space_after_header_symbol = true
|
||||
ij_markdown_force_one_space_after_list_bullet = true
|
||||
ij_markdown_force_one_space_between_words = true
|
||||
ij_markdown_keep_indents_on_empty_lines = false
|
||||
ij_markdown_max_lines_around_block_elements = 1
|
||||
ij_markdown_max_lines_around_header = 1
|
||||
ij_markdown_max_lines_between_paragraphs = 1
|
||||
ij_markdown_min_lines_around_block_elements = 1
|
||||
ij_markdown_min_lines_around_header = 1
|
||||
ij_markdown_min_lines_between_paragraphs = 1
|
||||
|
||||
[{*.yaml,*.yml}]
|
||||
indent_size = 2
|
||||
ij_visual_guides = none
|
||||
ij_yaml_align_values_properties = do_not_align
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -32,6 +32,8 @@ test/vendor/*
|
||||
!/data/.htaccess
|
||||
!/data/index.php
|
||||
!/data/web.config
|
||||
!/data/exclude.txt
|
||||
!/data/.compilation-symlinks
|
||||
|
||||
# iTop extensions
|
||||
/extensions/**
|
||||
|
||||
77
.make/release/changelog.php
Normal file
77
.make/release/changelog.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* Usage :
|
||||
* `php changelog.php 2.7.4`
|
||||
*
|
||||
* As argument is passed the git ref (tag name or sha1) we want to use as reference
|
||||
*
|
||||
* Outputs :
|
||||
*
|
||||
* 1. List of bugs as CSV :
|
||||
* bug ref;link
|
||||
* Example :
|
||||
* <code>
|
||||
* Bug_ref;Bug_URL;sha1
|
||||
* 1234;https://support.combodo.com/pages/UI.php?operation=details&class=Bug&id=1234;949b213f9|b1ca1f263|a1271da74
|
||||
* </code>
|
||||
*
|
||||
* 2. List of commits sha1/message without bug ref
|
||||
* Example :
|
||||
* <code>
|
||||
* sha1;subject
|
||||
* a6aa183e2;:bookmark: Prepare 2.7.5
|
||||
* </code>
|
||||
*/
|
||||
|
||||
|
||||
if (count($argv) === 1) {
|
||||
echo '⚠ You must pass the base tag/sha1 as parameter';
|
||||
exit(1);
|
||||
}
|
||||
$sBaseReference = $argv[1];
|
||||
|
||||
|
||||
//--- Get log
|
||||
$sGitLogCommand = 'git log --decorate --pretty="%h;%s" --date-order --no-merges '.$sBaseReference.'..HEAD';
|
||||
$sGitLogRaw = shell_exec($sGitLogCommand);
|
||||
|
||||
|
||||
//--- Analyze log
|
||||
$aGitLogLines = preg_split('/\n/', trim($sGitLogRaw));;
|
||||
$aLogLinesWithBugRef = [];
|
||||
$aLogLineNoBug = [];
|
||||
foreach ($aGitLogLines as $sLogLine) {
|
||||
$sBugRef = preg_match('/[nN]°(\d{3,4})/', $sLogLine, $aLineBugRef);
|
||||
if (($sBugRef === false) || empty($aLineBugRef)) {
|
||||
$aLogLineNoBug[] = $sLogLine;
|
||||
continue;
|
||||
}
|
||||
|
||||
$iBugId = $aLineBugRef[1];
|
||||
$sSha = substr($sLogLine, 0, 9);
|
||||
|
||||
if (array_key_exists($iBugId, $aLogLinesWithBugRef)) {
|
||||
$aBugShaRefs = $aLogLinesWithBugRef[$iBugId];
|
||||
$aBugShaRefs[] = $sSha;
|
||||
$aLogLinesWithBugRef[$iBugId] = $aBugShaRefs;
|
||||
} else {
|
||||
$aLogLinesWithBugRef[$iBugId] = [$sSha];
|
||||
}
|
||||
}
|
||||
$aBugsList = array_keys($aLogLinesWithBugRef);
|
||||
sort($aBugsList, SORT_NUMERIC);
|
||||
|
||||
|
||||
//-- Output results
|
||||
echo "# Bugs included\n";
|
||||
echo "Bug_ref;Bug_URL;sha1\n";
|
||||
foreach ($aBugsList as $sBugRef) {
|
||||
$sShaRefs = implode('|', $aLogLinesWithBugRef[$sBugRef]);
|
||||
echo "{$sBugRef};https://support.combodo.com/pages/UI.php?operation=details&class=Bug&id={$sBugRef};$sShaRefs\n";
|
||||
}
|
||||
echo "\n";
|
||||
echo "# Logs line without bug referenced\n";
|
||||
echo "sha1;subject\n";
|
||||
foreach ($aLogLineNoBug as $sLogLine) {
|
||||
echo "$sLogLine\n";
|
||||
}
|
||||
@@ -125,8 +125,7 @@ Our tests are located in the `test/` directory, containing a PHPUnit config file
|
||||
* ⬆️ `:arrow_up:` when upgrading dependencies
|
||||
* ⬇️ `:arrow_down:` when downgrading dependencies
|
||||
* ♻️ `:recycle:` code refactoring
|
||||
* 💄 `:lipstick:` Updating the UI and style files.
|
||||
|
||||
* 💄 `:lipstick:` Updating the UI and style files.
|
||||
|
||||
## 👥 Pull request
|
||||
|
||||
@@ -137,3 +136,21 @@ When your code is working, please:
|
||||
* create a pull request.
|
||||
|
||||
Detailed procedure to work on fork and create PR is available [in GitHub help pages](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
|
||||
|
||||
You might check the ["Allow edits from maintainers" PR checkbox][allow_edits_checkbox] to ease review.
|
||||
|
||||
[allow_edits_checkbox]: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork#enabling-repository-maintainer-permissions-on-existing-pull-requests
|
||||
|
||||
### 🙏 We are thankful
|
||||
|
||||
We are thankful for all your contributions to the iTop universe! As a thank you gift, we will send stickers to every iTop (& extensions) contributors!
|
||||
|
||||
Stickers' design might change from one year to another. For the first year we wanted to try a "craft beer label" look, see examples below:
|
||||
|
||||
* Bug hunter: Fix a bug
|
||||
* Translator: Add/update translations
|
||||
* White hat: Find and/or fix a vulnerability
|
||||
* Contributor: Contribute by finding a bug, making an extension or any other way
|
||||
* Partner: For Combodo's official partners
|
||||
|
||||

|
||||
|
||||
@@ -221,7 +221,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
(
|
||||
"category" => "addon/userrights,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "userid",
|
||||
"name_attcode" => array("userlogin", "profile"),
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_urp_userprofile",
|
||||
@@ -246,11 +246,6 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('userid', 'profileid')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
public function GetName()
|
||||
{
|
||||
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Profile', $this->Get('userlogin'), $this->Get('profile'));
|
||||
}
|
||||
|
||||
public function CheckToDelete(&$oDeletionPlan)
|
||||
{
|
||||
if (MetaModel::GetConfig()->Get('demo_mode')) {
|
||||
@@ -362,7 +357,7 @@ class URP_UserOrg extends UserRightsBaseClassGUI
|
||||
(
|
||||
"category" => "addon/userrights,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "userid",
|
||||
"name_attcode" => array("userlogin", "allowed_org_name"),
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_urp_userorg",
|
||||
@@ -387,12 +382,6 @@ class URP_UserOrg extends UserRightsBaseClassGUI
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('userid', 'allowed_org_id')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
public function GetName()
|
||||
{
|
||||
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Org', $this->Get('userlogin'), $this->Get('allowed_org_name'));
|
||||
}
|
||||
|
||||
|
||||
protected function OnInsert()
|
||||
{
|
||||
$this->CheckIfOrgIsAllowed();
|
||||
|
||||
@@ -327,7 +327,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "userid",
|
||||
"name_attcode" => array("userlogin", "profile"),
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_urp_userprofile",
|
||||
@@ -351,11 +351,6 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
MetaModel::Init_SetZListItems('standard_search', array('userid', 'profileid')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('userid', 'profileid')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
public function GetName()
|
||||
{
|
||||
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Profile', $this->Get('userlogin'), $this->Get('profile'));
|
||||
}
|
||||
}
|
||||
|
||||
class URP_UserOrg extends UserRightsBaseClassGUI
|
||||
@@ -366,7 +361,7 @@ class URP_UserOrg extends UserRightsBaseClassGUI
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "userid",
|
||||
"name_attcode" => array("userlogin", "allowed_org_name"),
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_urp_userorg",
|
||||
@@ -390,11 +385,6 @@ class URP_UserOrg extends UserRightsBaseClassGUI
|
||||
MetaModel::Init_SetZListItems('standard_search', array('userid', 'allowed_org_id')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('userid', 'allowed_org_id')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
public function GetName()
|
||||
{
|
||||
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Org', $this->Get('userlogin'), $this->Get('allowed_org_name'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -270,7 +270,7 @@ class URP_UserProfile extends UserRightsBaseClass
|
||||
(
|
||||
"category" => "addon/userrights",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "userid",
|
||||
"name_attcode" => array("userlogin", "profile"),
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"db_table" => "priv_urp_userprofile",
|
||||
@@ -294,11 +294,6 @@ class URP_UserProfile extends UserRightsBaseClass
|
||||
MetaModel::Init_SetZListItems('standard_search', array('userid', 'profileid')); // Criteria of the std search form
|
||||
MetaModel::Init_SetZListItems('advanced_search', array('userid', 'profileid')); // Criteria of the advanced search form
|
||||
}
|
||||
|
||||
public function GetName()
|
||||
{
|
||||
return Dict::Format('UI:UserManagement:LinkBetween_User_And_Profile', $this->Get('userlogin'), $this->Get('profile'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
60
application/DBSearchHelper.php
Normal file
60
application/DBSearchHelper.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class DBSearchHelper
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
class DBSearchHelper
|
||||
{
|
||||
/**
|
||||
* Add context filter to DBUnionSearch
|
||||
*
|
||||
* @param \DBSearch|null $oSearch
|
||||
*
|
||||
* @throws \Exception
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function AddContextFilter(?DBSearch $oSearch): void
|
||||
{
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sClass = $oSearch->GetClass();
|
||||
foreach ($oAppContext->GetNames() as $key) {
|
||||
// Find the value of the object corresponding to each 'context' parameter
|
||||
$aCallSpec = [$sClass, 'MapContextParam'];
|
||||
$sAttCode = '';
|
||||
if (is_callable($aCallSpec)) {
|
||||
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
|
||||
}
|
||||
|
||||
if (MetaModel::IsValidAttCode($sClass, $sAttCode)) {
|
||||
// Add Hierarchical condition if hierarchical key
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if (isset($oAttDef) && ($oAttDef->IsExternalKey())) {
|
||||
$iDefaultValue = intval($oAppContext->GetCurrentValue($key));
|
||||
if ($iDefaultValue != 0) {
|
||||
try {
|
||||
/** @var AttributeExternalKey $oAttDef */
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sTargetClass);
|
||||
if ($sHierarchicalKeyCode !== false) {
|
||||
$oFilter = new DBObjectSearch($sTargetClass);
|
||||
$oFilter->AddCondition('id', $iDefaultValue);
|
||||
$oHKFilter = new DBObjectSearch($sTargetClass);
|
||||
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
|
||||
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// If filtering fails just ignore it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/AjaxPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/AjaxPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/AjaxPage.php, now loadable using autoloader');
|
||||
|
||||
/**
|
||||
* Class ajax_page
|
||||
*
|
||||
* @deprecated will be removed in 3.1.0 - moved to AjaxPage
|
||||
*/
|
||||
class ajax_page extends AjaxPage
|
||||
|
||||
@@ -73,15 +73,14 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public abstract function ListSupportedLoginModes();
|
||||
abstract public function ListSupportedLoginModes();
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function LoginAction($sLoginState, &$iErrorCode)
|
||||
{
|
||||
switch ($sLoginState)
|
||||
{
|
||||
switch ($sLoginState) {
|
||||
case LoginWebPage::LOGIN_STATE_START:
|
||||
return $this->OnStart($iErrorCode);
|
||||
|
||||
@@ -777,6 +776,10 @@ abstract class ApplicationPopupMenuItem
|
||||
/** @ignore */
|
||||
protected $sLabel;
|
||||
/** @ignore */
|
||||
protected $sTooltip;
|
||||
/** @ignore */
|
||||
protected $sIconClass;
|
||||
/** @ignore */
|
||||
protected $aCssClasses;
|
||||
|
||||
/**
|
||||
@@ -791,6 +794,8 @@ abstract class ApplicationPopupMenuItem
|
||||
{
|
||||
$this->sUID = $sUID;
|
||||
$this->sLabel = $sLabel;
|
||||
$this->sTooltip = '';
|
||||
$this->sIconClass = '';
|
||||
$this->aCssClasses = array();
|
||||
}
|
||||
|
||||
@@ -845,6 +850,47 @@ abstract class ApplicationPopupMenuItem
|
||||
$this->aCssClasses[] = $sCssClass;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param $sTooltip
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function SetTooltip($sTooltip)
|
||||
{
|
||||
$this->sTooltip = $sTooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetTooltip()
|
||||
{
|
||||
return $this->sTooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sIconClass
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function SetIconClass($sIconClass)
|
||||
{
|
||||
$this->sIconClass = $sIconClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public function GetIconClass()
|
||||
{
|
||||
return $this->sIconClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the components to create a popup menu item in HTML
|
||||
*
|
||||
@@ -892,7 +938,13 @@ class URLPopupMenuItem extends ApplicationPopupMenuItem
|
||||
/** @ignore */
|
||||
public function GetMenuItem()
|
||||
{
|
||||
return array('label' => $this->GetLabel(), 'url' => $this->GetUrl(), 'target' => $this-> GetTarget(), 'css_classes' => $this->aCssClasses);
|
||||
return array('label' => $this->GetLabel(),
|
||||
'url' => $this->GetUrl(),
|
||||
'target' => $this-> GetTarget(),
|
||||
'css_classes' => $this->aCssClasses,
|
||||
'icon_class' => $this->sIconClass,
|
||||
'tooltip' => $this->sTooltip
|
||||
);
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
@@ -952,6 +1004,8 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
|
||||
'onclick' => $this->GetJsCode().'; return false;',
|
||||
'url' => $this->GetUrl(),
|
||||
'css_classes' => $this->GetCssClasses(),
|
||||
'icon_class' => $this->sIconClass,
|
||||
'tooltip' => $this->sTooltip
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1133,6 +1187,8 @@ abstract class AbstractPageUIExtension implements iPageUIExtension
|
||||
*/
|
||||
public function GetNorthPaneHtml(iTopWebPage $oPage)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use iPageUIBlockExtension instead');
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -1141,6 +1197,8 @@ abstract class AbstractPageUIExtension implements iPageUIExtension
|
||||
*/
|
||||
public function GetSouthPaneHtml(iTopWebPage $oPage)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use iPageUIBlockExtension instead');
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -1149,6 +1207,8 @@ abstract class AbstractPageUIExtension implements iPageUIExtension
|
||||
*/
|
||||
public function GetBannerHtml(iTopWebPage $oPage)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use iPageUIBlockExtension instead');
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CaptureWebPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CaptureWebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/CaptureWebPage.php, now loadable using autoloader');
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CLIPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CLIPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/CLIPage.php, now loadable using autoloader');
|
||||
@@ -10,9 +10,11 @@ use Combodo\iTop\Application\UI\Base\Component\Button\Button;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableSettings;
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\StaticTable\StaticTable;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Field\Field;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Field\FieldUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\FieldSet\FieldSet;
|
||||
use Combodo\iTop\Application\UI\Base\Component\FieldSet\FieldSetUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Form\Form;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Form\FormUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
||||
@@ -28,6 +30,7 @@ use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\MultiColumn;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\Object\ObjectFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\TabContainer\Tab\AjaxTab;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
|
||||
use Combodo\iTop\Renderer\BlockRenderer;
|
||||
use Combodo\iTop\Renderer\Console\ConsoleFormRenderer;
|
||||
|
||||
@@ -107,6 +110,13 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
*/
|
||||
public const DEFAULT_OBJECT_MODE = self::ENUM_OBJECT_MODE_VIEW;
|
||||
|
||||
/**
|
||||
* @var string Prefix for tags in the subtitle, allows to identify them more easily
|
||||
* @used-by static::DisplayBareHeader
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const HEADER_BLOCKS_SUBTITLE_TAG_PREFIX = 'tag-';
|
||||
|
||||
protected $m_iFormId; // The ID of the form used to edit the object (when in edition mode !)
|
||||
protected static $iGlobalFormId = 1;
|
||||
protected $aFieldsMap;
|
||||
@@ -313,7 +323,8 @@ EOF
|
||||
$oSingletonFilter = new DBObjectSearch(get_class($this));
|
||||
$oSingletonFilter->AddCondition('id', $this->GetKey(), '=');
|
||||
$oBlock = new MenuBlock($oSingletonFilter, 'details', false);
|
||||
$aHeaderBlocks['toolbar'][] = $oBlock->GetRenderContent($oPage);
|
||||
$oActionMenuBlock = $oBlock->GetRenderContent($oPage);
|
||||
$aHeaderBlocks['toolbar'][$oActionMenuBlock->GetId()] = $oActionMenuBlock;
|
||||
}
|
||||
|
||||
$aTags = array();
|
||||
@@ -419,8 +430,9 @@ EOF
|
||||
}
|
||||
|
||||
foreach ($aTags as $sIconId => $aIconData) {
|
||||
$aHeaderBlocks['subtitle'][] = new Html(<<<HTML
|
||||
<span id="{$sIconId}" class="ibo-object-details--tag {$aIconData['css_classes']}" data-tooltip-content="{$aIconData['title']}" data-tooltip-html-enabled="true"><span class="ibo-object-details--tag-icon"><span class="{$aIconData['decoration_classes']}"></span></span>{$aIconData['label']}</span>
|
||||
$sTagTooltipContent = utils::EscapeHtml($aIconData['title']);
|
||||
$aHeaderBlocks['subtitle'][static::HEADER_BLOCKS_SUBTITLE_TAG_PREFIX.$sIconId] = new Html(<<<HTML
|
||||
<span id="{$sIconId}" class="ibo-object-details--tag {$aIconData['css_classes']}" data-tooltip-content="{$sTagTooltipContent}" data-tooltip-html-enabled="true"><span class="ibo-object-details--tag-icon"><span class="{$aIconData['decoration_classes']}"></span></span>{$aIconData['label']}</span>
|
||||
HTML
|
||||
);
|
||||
}
|
||||
@@ -444,6 +456,7 @@ HTML
|
||||
*/
|
||||
public function DisplayBareHistory(WebPage $oPage, $bEditMode = false, $iLimitCount = 0, $iLimitStart = 0)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
// history block (with as a tab)
|
||||
$oHistoryFilter = new DBObjectSearch('CMDBChangeOp');
|
||||
$oHistoryFilter->AddCondition('objkey', $this->GetKey(), '=');
|
||||
@@ -500,11 +513,15 @@ HTML
|
||||
*/
|
||||
public function DisplayDashboard($oPage, $sAttCode)
|
||||
{
|
||||
// Retrieve parameters
|
||||
/** @var bool $bIsContainerInEdition True if the container of the dashboard is currently in edition; meaning that the dashboard could not be up-to-date with data changed in the container (eg. when editing an object and adding linkedset items) */
|
||||
$bIsContainerInEdition = (utils::ReadParam('host_container_in_edition', 'false') === 'true');
|
||||
|
||||
$sClass = get_class($this);
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
|
||||
if (!$oAttDef instanceof AttributeDashboard)
|
||||
{
|
||||
// Consistency checks
|
||||
if (!$oAttDef instanceof AttributeDashboard) {
|
||||
throw new CoreException(Dict::S('UI:Error:InvalidDashboard'));
|
||||
}
|
||||
|
||||
@@ -516,6 +533,10 @@ HTML
|
||||
|
||||
$bCanEdit = UserRights::IsAdministrator() || $oAttDef->IsUserEditable();
|
||||
$sDivId = $oDashboard->GetId();
|
||||
|
||||
if ($bIsContainerInEdition) {
|
||||
$oPage->AddUiBlock(AlertUIBlockFactory::MakeForInformation(Dict::S('UI:Dashboard:NotUpToDateUntilContainerSaved')));
|
||||
}
|
||||
$oPage->add('<div id="'.$sDivId.'" class="ibo-dashboard" data-role="ibo-dashboard">');
|
||||
$aExtraParams = array(
|
||||
'query_params' => $this->ToArgsForQuery(),
|
||||
@@ -551,17 +572,12 @@ HTML
|
||||
$aList = array_keys(MetaModel::ListAttributeDefs(get_class($this)));
|
||||
}
|
||||
$sClass = get_class($this);
|
||||
foreach($aList as $sAttCode)
|
||||
{
|
||||
foreach($aList as $sAttCode) {
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
if ($oAttDef instanceof AttributeDashboard)
|
||||
{
|
||||
if ($bEditMode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ($oAttDef instanceof AttributeDashboard) {
|
||||
$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(),
|
||||
utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=dashboard&class='.get_class($this).'&id='.$this->GetKey().'&attcode='.$oAttDef->GetCode().$sHostContainerInEditionUrlParam,
|
||||
true,
|
||||
'Class:'.$sClass.'/Attribute:'.$sAttCode,
|
||||
AjaxTab::ENUM_TAB_PLACEHOLDER_DASHBOARD);
|
||||
@@ -729,29 +745,25 @@ HTML
|
||||
$oBlock = new DisplayBlock($oLinkSet->GetFilter(), 'list', false);
|
||||
$oBlock->Display($oPage, 'rel_'.$sAttCode, $aParams);
|
||||
}
|
||||
if (array_key_exists($sAttCode, $aRedundancySettings))
|
||||
{
|
||||
foreach($aRedundancySettings[$sAttCode] as $oRedundancyAttDef)
|
||||
{
|
||||
if (array_key_exists($sAttCode, $aRedundancySettings)) {
|
||||
foreach ($aRedundancySettings[$sAttCode] as $oRedundancyAttDef) {
|
||||
$sRedundancyAttCode = $oRedundancyAttDef->GetCode();
|
||||
$sValue = $this->Get($sRedundancyAttCode);
|
||||
$iRedundancyFlags = $this->GetFormAttributeFlags($sRedundancyAttCode);
|
||||
$bRedundancyReadOnly = ($iRedundancyFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE));
|
||||
|
||||
$oPage->add('<fieldset>');
|
||||
$oPage->add('<legend>'.$oRedundancyAttDef->GetLabel().'</legend>');
|
||||
if ($bEditMode && (!$bRedundancyReadOnly))
|
||||
{
|
||||
$oFieldSet = FieldSetUIBlockFactory::MakeStandard($oRedundancyAttDef->GetLabel());
|
||||
$oFieldSet->AddCSSClass('mt-5');
|
||||
$oPage->AddSubBlock($oFieldSet);
|
||||
|
||||
if ($bEditMode && (!$bRedundancyReadOnly)) {
|
||||
$sInputId = $this->m_iFormId.'_'.$sRedundancyAttCode;
|
||||
$oPage->add("<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass,
|
||||
$oFieldSet->AddSubBlock(new Html("<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass,
|
||||
$sRedundancyAttCode, $oRedundancyAttDef, $sValue, '', $sInputId, '', $iFlags,
|
||||
$aArgs).'</span>');
|
||||
$aArgs).'</span>'));
|
||||
} else {
|
||||
$oFieldSet->AddSubBlock(new Html($oRedundancyAttDef->GetDisplayForm($sValue, $oPage, false, $this->m_iFormId)));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->add($oRedundancyAttDef->GetDisplayForm($sValue, $oPage, false, $this->m_iFormId));
|
||||
}
|
||||
$oPage->add('</fieldset>');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1155,12 +1167,17 @@ HTML
|
||||
*/
|
||||
public static function GetDisplaySet(WebPage $oPage, DBObjectSet $oSet, $aExtraParams = array())
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use GetDisplaySetBlock');
|
||||
$oPage->AddUiBlock(static::GetDisplaySetBlock($oPage, $oSet, $aExtraParams));
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public static function GetDisplaySetBlock(WebPage $oPage, DBObjectSet $oSet, $aExtraParams = array())
|
||||
{
|
||||
if ($oPage->IsPrintableVersion() || $oPage->is_pdf()) {
|
||||
return self::GetDisplaySetForPrinting($oPage, $oSet, $aExtraParams);
|
||||
}
|
||||
if (empty($aExtraParams['currentId'])) {
|
||||
$iListId = utils::GetUniqueId(); // Works only if not in an Ajax page !!
|
||||
} else {
|
||||
@@ -1169,6 +1186,146 @@ HTML
|
||||
|
||||
return DataTableUIBlockFactory::MakeForResult($oPage, $iListId, $oSet, $aExtraParams);
|
||||
}
|
||||
|
||||
public static function GetDataTableFromDBObjectSet(DBObjectSet $oSet, $aParams = array())
|
||||
{
|
||||
$aFields = null;
|
||||
if (isset($aParams['fields']) && (strlen($aParams['fields']) > 0)) {
|
||||
$aFields = explode(',', $aParams['fields']);
|
||||
}
|
||||
|
||||
$bFieldsAdvanced = false;
|
||||
if (isset($aParams['fields_advanced'])) {
|
||||
$bFieldsAdvanced = (bool)$aParams['fields_advanced'];
|
||||
}
|
||||
|
||||
$bLocalize = true;
|
||||
if (isset($aParams['localize_values'])) {
|
||||
$bLocalize = (bool)$aParams['localize_values'];
|
||||
}
|
||||
|
||||
$aList = array();
|
||||
|
||||
$aClasses = $oSet->GetFilter()->GetSelectedClasses();
|
||||
$aAuthorizedClasses = array();
|
||||
foreach ($aClasses as $sAlias => $sClassName) {
|
||||
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO) {
|
||||
$aAuthorizedClasses[$sAlias] = $sClassName;
|
||||
}
|
||||
}
|
||||
$aHeader = array();
|
||||
foreach ($aAuthorizedClasses as $sAlias => $sClassName) {
|
||||
$aList[$sAlias] = array();
|
||||
|
||||
foreach (MetaModel::GetZListItems($sClassName, 'list') as $sAttCode) {
|
||||
$oAttDef = Metamodel::GetAttributeDef($sClassName, $sAttCode);
|
||||
if (is_null($aFields) || (count($aFields) == 0)) {
|
||||
// Standard list of attributes (no link sets)
|
||||
if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField())) {
|
||||
$sAttCodeEx = $oAttDef->IsExternalField() ? $oAttDef->GetKeyAttCode().'->'.$oAttDef->GetExtAttCode() : $sAttCode;
|
||||
|
||||
$aList[$sAlias][$sAttCodeEx] = $oAttDef;
|
||||
|
||||
if ($bFieldsAdvanced && $oAttDef->IsExternalKey(EXTKEY_RELATIVE)) {
|
||||
$sRemoteClass = $oAttDef->GetTargetClass();
|
||||
foreach (MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode) {
|
||||
$aList[$sAlias][$sAttCode.'->'.$sRemoteAttCode] = MetaModel::GetAttributeDef($sRemoteClass,
|
||||
$sRemoteAttCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// User defined list of attributes
|
||||
if (in_array($sAttCode, $aFields) || in_array($sAlias.'.'.$sAttCode, $aFields)) {
|
||||
$aList[$sAlias][$sAttCode] = $oAttDef;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Replace external key by the corresponding friendly name (if not already in the list)
|
||||
foreach ($aList[$sAlias] as $sAttCode => $oAttDef) {
|
||||
if ($oAttDef->IsExternalKey()) {
|
||||
unset($aList[$sAlias][$sAttCode]);
|
||||
$sFriendlyNameAttCode = $sAttCode.'_friendlyname';
|
||||
if (!array_key_exists($sFriendlyNameAttCode,
|
||||
$aList[$sAlias]) && MetaModel::IsValidAttCode($sClassName, $sFriendlyNameAttCode)) {
|
||||
$oFriendlyNameAtt = MetaModel::GetAttributeDef($sClassName, $sFriendlyNameAttCode);
|
||||
$aList[$sAlias][$sFriendlyNameAttCode] = $oFriendlyNameAtt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($aList[$sAlias] as $sAttCodeEx => $oAttDef) {
|
||||
$sColLabel = $bLocalize ? MetaModel::GetLabel($sClassName, $sAttCodeEx) : $sAttCodeEx;
|
||||
|
||||
$oFinalAttDef = $oAttDef->GetFinalAttDef();
|
||||
if (get_class($oFinalAttDef) == 'AttributeDateTime') {
|
||||
$aHeader[$oAttDef->GetCode().'/D'] = ['label' => $sColLabel.' ('.Dict::S('UI:SplitDateTime-Date').')'];
|
||||
$aHeader[$oAttDef->GetCode().'/T'] = ['label' => $sColLabel.' ('.Dict::S('UI:SplitDateTime-Time').')'];
|
||||
} else {
|
||||
$aHeader[$oAttDef->GetCode()] = ['label' => $sColLabel];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$oSet->Seek(0);
|
||||
$aRows = [];
|
||||
while ($aObjects = $oSet->FetchAssoc()) {
|
||||
$aRow = [];
|
||||
foreach ($aAuthorizedClasses as $sAlias => $sClassName) {
|
||||
$oObj = $aObjects[$sAlias];
|
||||
foreach ($aList[$sAlias] as $sAttCodeEx => $oAttDef) {
|
||||
if (is_null($oObj)) {
|
||||
$aRow[$oAttDef->GetCode()] = '';
|
||||
} else {
|
||||
$oFinalAttDef = $oAttDef->GetFinalAttDef();
|
||||
if (get_class($oFinalAttDef) == 'AttributeDateTime') {
|
||||
$sDate = $oObj->Get($sAttCodeEx);
|
||||
if ($sDate === null) {
|
||||
$aRow[$oAttDef->GetCode().'/D'] = '';
|
||||
$aRow[$oAttDef->GetCode().'/T'] = '';
|
||||
} else {
|
||||
$iDate = AttributeDateTime::GetAsUnixSeconds($sDate);
|
||||
$aRow[$oAttDef->GetCode().'/D'] = date('Y-m-d', $iDate); // Format kept as-is for 100% backward compatibility of the exports
|
||||
$aRow[$oAttDef->GetCode().'/T'] = date('H:i:s', $iDate); // Format kept as-is for 100% backward compatibility of the exports
|
||||
}
|
||||
} else {
|
||||
if ($oAttDef instanceof AttributeCaseLog) {
|
||||
$rawValue = $oObj->Get($sAttCodeEx);
|
||||
$outputValue = str_replace("\n", "<br/>", htmlentities($rawValue->__toString(), ENT_QUOTES, 'UTF-8'));
|
||||
// Trick for Excel: treat the content as text even if it begins with an equal sign
|
||||
$aRow[$oAttDef->GetCode()] = $outputValue;
|
||||
} else {
|
||||
$rawValue = $oObj->Get($sAttCodeEx);
|
||||
// Due to custom formatting rules, empty friendlynames may be rendered as non-empty strings
|
||||
// let's fix this and make sure we render an empty string if the key == 0
|
||||
if ($oAttDef instanceof AttributeExternalField && $oAttDef->IsFriendlyName()) {
|
||||
$sKeyAttCode = $oAttDef->GetKeyAttCode();
|
||||
if ($oObj->Get($sKeyAttCode) == 0) {
|
||||
$rawValue = '';
|
||||
}
|
||||
}
|
||||
if ($bLocalize) {
|
||||
$outputValue = htmlentities($oFinalAttDef->GetEditValue($rawValue), ENT_QUOTES, 'UTF-8');
|
||||
} else {
|
||||
$outputValue = htmlentities($rawValue, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
$aRow[$oAttDef->GetCode()] = $outputValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$aRows[] = $aRow;
|
||||
}
|
||||
$oTable = new StaticTable();
|
||||
$oTable->SetColumns($aHeader);
|
||||
$oTable->SetData($aRows);
|
||||
|
||||
return $oTable;
|
||||
//DataTableUIBlockFactory::MakeForStaticData('', $aHeader, $aRows);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param \CMDBObjectSet $oSet
|
||||
@@ -1191,11 +1348,10 @@ HTML
|
||||
*/
|
||||
public static function GetDisplayExtendedSet(WebPage $oPage, CMDBObjectSet $oSet, $aExtraParams = array())
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
if (empty($aExtraParams['currentId'])) {
|
||||
$iListId = utils::GetUniqueId(); // Works only if not in an Ajax page !!
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$iListId = $aExtraParams['currentId'];
|
||||
}
|
||||
$aList = array();
|
||||
@@ -1846,11 +2002,14 @@ HTML
|
||||
$aEventsList[] = 'validate';
|
||||
$aEventsList[] = 'keyup';
|
||||
$aEventsList[] = 'change';
|
||||
$sPlaceholderValue = 'placeholder="'.htmlentities(AttributeDate::GetFormat()->ToPlaceholder(),
|
||||
ENT_QUOTES, 'UTF-8').'"';
|
||||
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_date ibo-input-wrapper ibo-input-date-wrapper\" data-validation=\"untouched\"><input title=\"$sHelpText\" class=\"date-pick ibo-input ibo-input-date\" type=\"text\" $sPlaceholderValue name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" value=\"".htmlentities($sDisplayValue,
|
||||
ENT_QUOTES, 'UTF-8')."\" id=\"$iId\"/></div>{$sValidationSpan}{$sReloadSpan}";
|
||||
$sPlaceholderValue = 'placeholder="'.utils::EscapeHtml(AttributeDate::GetFormat()->ToPlaceholder()).'"';
|
||||
$sDisplayValueForHtml = utils::EscapeHtml($sDisplayValue);
|
||||
$sHTMLValue = <<<HTML
|
||||
<div class="field_input_zone field_input_date ibo-input-wrapper ibo-input-date-wrapper" data-validation="untouched">
|
||||
<input title="$sHelpText" class="date-pick ibo-input ibo-input-date" type="text" {$sPlaceholderValue} name="attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}" value="{$sDisplayValueForHtml}" id="{$iId}" autocomplete="off" />
|
||||
</div>{$sValidationSpan}{$sReloadSpan}
|
||||
HTML;
|
||||
break;
|
||||
|
||||
case 'DateTime':
|
||||
@@ -1859,10 +2018,13 @@ HTML
|
||||
$aEventsList[] = 'keyup';
|
||||
$aEventsList[] = 'change';
|
||||
|
||||
$sPlaceholderValue = 'placeholder="'.htmlentities(AttributeDateTime::GetFormat()->ToPlaceholder(),
|
||||
ENT_QUOTES, 'UTF-8').'"';
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_datetime ibo-input-wrapper ibo-input-datetime-wrapper\" data-validation=\"untouched\"><input title=\"$sHelpText\" class=\"datetime-pick ibo-input ibo-input-datetime\" type=\"text\" size=\"19\" $sPlaceholderValue name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" value=\"".htmlentities($sDisplayValue,
|
||||
ENT_QUOTES, 'UTF-8')."\" id=\"$iId\"/></div>{$sValidationSpan}{$sReloadSpan}";
|
||||
$sPlaceholderValue = 'placeholder="'.utils::EscapeHtml(AttributeDateTime::GetFormat()->ToPlaceholder()).'"';
|
||||
$sDisplayValueForHtml = utils::EscapeHtml($sDisplayValue);
|
||||
$sHTMLValue = <<<HTML
|
||||
<div class="field_input_zone field_input_datetime ibo-input-wrapper ibo-input-datetime-wrapper" data-validation="untouched">
|
||||
<input title="{$sHelpText}" class="datetime-pick ibo-input ibo-input-datetime" type="text" size="19" {$sPlaceholderValue} name="attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}" value="{$sDisplayValueForHtml}" id="{$iId}" autoomplete="off" />
|
||||
</div>{$sValidationSpan}{$sReloadSpan}
|
||||
HTML;
|
||||
break;
|
||||
|
||||
case 'Duration':
|
||||
@@ -2019,6 +2181,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
|
||||
case 'CaseLog':
|
||||
$sInputType = self::ENUM_INPUT_TYPE_HTML_EDITOR;
|
||||
$aStyles = array();
|
||||
@@ -2122,9 +2285,11 @@ EOF
|
||||
$sFileName = $oDocument->GetFileName();
|
||||
}
|
||||
$sFileNameForHtml = utils::EscapeHtml($sFileName);
|
||||
$bHasFile = !empty($sFileName);
|
||||
|
||||
$iMaxFileSize = utils::ConvertToBytes(ini_get('upload_max_filesize'));
|
||||
$sRemoveBtnLabelForHtml = utils::EscapeHtml(Dict::S('UI:Button:RemoveDocument'));
|
||||
$sExtraCSSClassesForRemoveButton = $bHasFile ? '' : 'ibo-is-hidden';
|
||||
|
||||
$sHTMLValue = <<<HTML
|
||||
<div class="field_input_zone field_input_document">
|
||||
@@ -2132,7 +2297,7 @@ EOF
|
||||
<input type="hidden" id="do_remove_{$iId}" name="attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}[remove]" value="0"/>
|
||||
<input name="attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}[filename]" type="hidden" id="{$iId}" value="{$sFileNameForHtml}"/>
|
||||
<span id="name_{$iInputId}" >{$sFileNameForHtml}</span>  
|
||||
<button id="remove_attr_{$iId}" class="ibo-button ibo-is-alternative ibo-is-danger ibo-is-hidden" data-role="ibo-button" type="button" data-tooltip-content="{$sRemoveBtnLabelForHtml}" onClick="$('#file_{$iId}').val(''); UpdateFileName('{$iId}', '');">
|
||||
<button id="remove_attr_{$iId}" class="ibo-button ibo-is-alternative ibo-is-danger {$sExtraCSSClassesForRemoveButton}" data-role="ibo-button" type="button" data-tooltip-content="{$sRemoveBtnLabelForHtml}" onClick="$('#file_{$iId}').val(''); UpdateFileName('{$iId}', '');">
|
||||
<span class="fas fa-trash"></span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -2228,31 +2393,17 @@ HTML;
|
||||
break;
|
||||
|
||||
case 'RedundancySetting':
|
||||
$sHTMLValue = '<table>';
|
||||
$sHTMLValue .= '<tr>';
|
||||
$sHTMLValue .= '<td>';
|
||||
$sHTMLValue .= '<div id="'.$iId.'">';
|
||||
$sHTMLValue .= $oAttDef->GetDisplayForm($value, $oPage, true);
|
||||
$sHTMLValue .= '</div>';
|
||||
$sHTMLValue .= '</td>';
|
||||
$sHTMLValue .= '<td>'.$sValidationSpan.$sReloadSpan.'</td>';
|
||||
$sHTMLValue .= '</tr>';
|
||||
$sHTMLValue .= '</table>';
|
||||
$sHTMLValue .= '<div>'.$sValidationSpan.$sReloadSpan.'</div>';
|
||||
$oPage->add_ready_script("$('#$iId :input').on('keyup change validate', function(evt, sFormId) { return ValidateRedundancySettings('$iId',sFormId); } );"); // Custom validation function
|
||||
break;
|
||||
|
||||
case 'CustomFields':
|
||||
$sHTMLValue = '<table>';
|
||||
$sHTMLValue .= '<tr>';
|
||||
$sHTMLValue .= '<td>';
|
||||
$sHTMLValue .= '<div id="'.$iId.'_console_form">';
|
||||
$sHTMLValue .= '<div id="'.$iId.'_field_set">';
|
||||
$sHTMLValue .= '</div>';
|
||||
$sHTMLValue .= '</div>';
|
||||
$sHTMLValue .= '</td>';
|
||||
$sHTMLValue .= '<td>'.$sReloadSpan.'</td>'; // No validation span for this one: it does handle its own validation!
|
||||
$sHTMLValue .= '</tr>';
|
||||
$sHTMLValue .= '</table>';
|
||||
$sHTMLValue .= '<div>'.$sReloadSpan.'</div>'; // No validation span for this one: it does handle its own validation!
|
||||
$sHTMLValue .= "<input name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" type=\"hidden\" id=\"$iId\" value=\"\"/>\n";
|
||||
|
||||
$oForm = $value->GetForm($sFormPrefix);
|
||||
@@ -2301,9 +2452,9 @@ JS
|
||||
case 'Set':
|
||||
case 'TagSet':
|
||||
$sInputType = self::ENUM_INPUT_TYPE_TAGSET;
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'/js/selectize.min.js');
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/selectize.min.js');
|
||||
$oPage->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/selectize.default.css');
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'/js/jquery.itop-set-widget.js');
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.itop-set-widget.js');
|
||||
|
||||
$oPage->add_dict_entry('Core:AttributeSet:placeholder');
|
||||
|
||||
@@ -2637,8 +2788,7 @@ JS
|
||||
foreach($aInitialStates as $sStateCode => $sStateData)
|
||||
{
|
||||
$sSelected = '';
|
||||
if ($sStateCode == $this->GetState())
|
||||
{
|
||||
if ($sStateCode == $this->GetState()) {
|
||||
$sSelected = ' selected';
|
||||
}
|
||||
$sStatesSelection .= '<option value="'.$sStateCode.'" '.$sSelected.'>'.MetaModel::GetStateLabel($sClass,
|
||||
@@ -2646,7 +2796,7 @@ JS
|
||||
}
|
||||
$sStatesSelection .= '</select>';
|
||||
$sStatesSelection .= '<input type="hidden" id="obj_state_orig" name="obj_state_orig" value="'.$this->GetState().'"/>';
|
||||
$oPage->add_ready_script(<<<JAVASCRIPT
|
||||
$oPage->add_ready_script(<<<JS
|
||||
$('.state_select_{$this->m_iFormId}').change( function() {
|
||||
if ($('#obj_state_orig').val() != $(this).val()) {
|
||||
$('.state_select_{$this->m_iFormId}').val($(this).val());
|
||||
@@ -2654,8 +2804,8 @@ $('.state_select_{$this->m_iFormId}').change( function() {
|
||||
$('#form_{$this->m_iFormId}').submit();
|
||||
}
|
||||
});
|
||||
JAVASCRIPT
|
||||
);
|
||||
JS
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2698,6 +2848,18 @@ EOF
|
||||
$oPage->p($sStatesSelection);
|
||||
|
||||
$aFieldsMap = $this->DisplayBareProperties($oPage, true, $sPrefix, $aExtraParams);
|
||||
//if we are in bulk modify : Special case to display the case log, if any...
|
||||
// WARNING: if you modify the loop below, also check the corresponding code in UpdateObject and DisplayModifyForm
|
||||
if (isset($aExtraParams['nbBulkObj'])) {
|
||||
foreach (MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode => $oAttDef) {
|
||||
if ($oAttDef instanceof AttributeCaseLog) {
|
||||
$sComment = (isset($aExtraParams['fieldsComments'][$sAttCode])) ? $aExtraParams['fieldsComments'][$sAttCode] : '';
|
||||
$this->DisplayCaseLogForBulkModify($oPage, $sAttCode, $sComment, $sPrefix);
|
||||
$aFieldsMap[$sAttCode] = $this->m_iFormId.'_'.$sAttCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_array($aFieldsMap)) {
|
||||
$aFieldsMap = array();
|
||||
}
|
||||
@@ -3211,7 +3373,8 @@ EOF
|
||||
// - Fullscreen toggler for large fields
|
||||
$sFullscreenTogglerTooltip = Dict::S('UI:ToggleFullScreen');
|
||||
$sFullscreenTogglerHTML = (false === in_array($oAttDef->GetEditClass(), static::GetAttEditClassesToRenderAsLargeField())) ? '' : <<<HTML
|
||||
<a href="#" class="ibo-field--fullscreen-toggler" data-role="ibo-field--fullscreen-toggler"
|
||||
<a href="#" class="ibo-field--fullscreen-toggler" data-role="ibo-field--fullscreen-toggler"
|
||||
aria-label="{$sFullscreenTogglerTooltip}"
|
||||
data-tooltip-content="{$sFullscreenTogglerTooltip}" data-fullscreen-toggler-target="$(this).closest('[data-role=\'ibo-field\']')"><span class="fas fa-fw fa-expand-arrows-alt"></span></a>
|
||||
HTML;
|
||||
|
||||
@@ -3220,13 +3383,21 @@ HTML;
|
||||
// Then prepare the value
|
||||
// - The field is visible in the current state of the object
|
||||
if ($oAttDef->GetEditClass() == 'Document') {
|
||||
/** @var \ormDocument $oDocument */
|
||||
$oDocument = $this->Get($sAttCode);
|
||||
if (!$oDocument->IsEmpty()) {
|
||||
$sDisplayValue = $this->GetAsHTML($sAttCode);
|
||||
$sDisplayValue .= "<br/>".Dict::Format('UI:OpenDocumentInNewWindow_',
|
||||
$oDocument->GetDisplayLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
|
||||
$sDisplayValue .= "<br/>".Dict::Format('UI:DownloadDocument_',
|
||||
$oDocument->GetDownloadLink(get_class($this), $this->GetKey(), $sAttCode)).", \n";
|
||||
$sFieldAsHtml = $this->GetAsHTML($sAttCode);
|
||||
|
||||
$sDisplayLabel = Dict::S('UI:OpenDocumentInNewWindow_');
|
||||
$sDisplayUrl = $oDocument->GetDisplayURL(get_class($this), $this->GetKey(), $sAttCode);
|
||||
|
||||
$sDownloadLabel = Dict::Format('UI:DownloadDocument_');
|
||||
$sDownloadUrl = $oDocument->GetDownloadURL(get_class($this), $this->GetKey(), $sAttCode);
|
||||
|
||||
$sDisplayValue = <<<HTML
|
||||
{$sFieldAsHtml}<br>
|
||||
<a href="{$sDisplayUrl}" target="_blank">{$sDisplayLabel}</a> / <a href="{$sDownloadUrl}">{$sDownloadLabel}</a>
|
||||
HTML;
|
||||
} else {
|
||||
$sDisplayValue = '';
|
||||
}
|
||||
@@ -4263,23 +4434,19 @@ HTML;
|
||||
*/
|
||||
public function DisplayCaseLog(WebPage $oPage, $sAttCode, $sComment = '', $sPrefix = '', $bEditMode = false)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
$oPage->SetCurrentTab('UI:PropertiesTab');
|
||||
$sClass = get_class($this);
|
||||
|
||||
if ($this->IsNew())
|
||||
{
|
||||
if ($this->IsNew()) {
|
||||
$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$iFlags = $this->GetAttributeFlags($sAttCode);
|
||||
}
|
||||
|
||||
if ($iFlags & OPT_ATT_HIDDEN)
|
||||
{
|
||||
if ($iFlags & OPT_ATT_HIDDEN) {
|
||||
// The case log is hidden do nothing
|
||||
}
|
||||
else
|
||||
} else
|
||||
{
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$sAttDefClass = get_class($oAttDef);
|
||||
@@ -4353,6 +4520,69 @@ HTML
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Special display where the case log uses the whole "screen" at the bottom of the "Properties" tab
|
||||
*
|
||||
* @param \WebPage $oPage
|
||||
* @param string $sAttCode
|
||||
* @param string $sComment
|
||||
* @param string $sPrefix
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \DictExceptionMissingString
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function DisplayCaseLogForBulkModify(WebPage $oPage, $sAttCode, $sComment = '', $sPrefix = '')
|
||||
{
|
||||
$sClass = get_class($this);
|
||||
|
||||
$iFlags = $this->GetAttributeFlags($sAttCode);
|
||||
|
||||
if ($iFlags & (OPT_ATT_HIDDEN | OPT_ATT_READONLY | OPT_ATT_SLAVE)) {
|
||||
// The case log can not be updated
|
||||
} else {
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$sAttDefClass = get_class($oAttDef);
|
||||
$sAttLabel = $oAttDef->GetLabel();
|
||||
$sAttMetaDataLabel = utils::HtmlEntities($sAttLabel);
|
||||
$sAttMetaDataFlagMandatory = (($iFlags & OPT_ATT_MANDATORY) === OPT_ATT_MANDATORY) ? 'true' : 'false';
|
||||
$sAttMetaDataFlagMustChange = (($iFlags & OPT_ATT_MUSTCHANGE) === OPT_ATT_MUSTCHANGE) ? 'true' : 'false';
|
||||
$sAttMetaDataFlagMustPrompt = (($iFlags & OPT_ATT_MUSTPROMPT) === OPT_ATT_MUSTPROMPT) ? 'true' : 'false';
|
||||
|
||||
$sInputId = $this->m_iFormId.'_'.$sAttCode;
|
||||
|
||||
$sValue = $this->Get($sAttCode);
|
||||
$sDisplayValue = $this->GetEditValue($sAttCode);
|
||||
$aArgs = array('this' => $this, 'formPrefix' => $sPrefix);
|
||||
|
||||
$aFieldsMap[$sAttCode] = $sInputId;
|
||||
|
||||
$oFieldset = FieldSetUIBlockFactory::MakeStandard($sAttLabel);
|
||||
$oPage->AddSubBlock($oFieldset);
|
||||
|
||||
$oDivField = FieldUIBlockFactory::MakeLarge("");
|
||||
// UIContentBlockUIBlockFactory::MakeStandard(null,["field_container field_large"]);
|
||||
$oDivField->AddDataAttribute("attribute-type", $sAttDefClass);
|
||||
$oDivField->AddDataAttribute("attribute-label", $sAttMetaDataLabel);
|
||||
$oDivField->AddDataAttribute("attribute-flag-hidden", false);
|
||||
$oDivField->AddDataAttribute("attribute-flag-read-only", false);
|
||||
$oDivField->AddDataAttribute("attribute-flag-mandatory", $sAttMetaDataFlagMandatory);
|
||||
$oDivField->AddDataAttribute("attribute-flag-must-change", $sAttMetaDataFlagMustChange);
|
||||
$oDivField->AddDataAttribute("attribute-flag-must-prompt", $sAttMetaDataFlagMustPrompt);
|
||||
$oDivField->AddDataAttribute("attribute-flag-slave", false);
|
||||
$oFieldset->AddSubBlock($oDivField);
|
||||
|
||||
$sCommentAsHtml = ($sComment != '') ? '<span>'.$sComment.'</span><br/>' : '';
|
||||
$sFieldAsHtml = self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs);
|
||||
$sHTMLValue = $sCommentAsHtml.$sFieldAsHtml;
|
||||
$oDivField->AddSubBlock(new Html($sHTMLValue));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sCurrentState
|
||||
* @param $sStimulus
|
||||
@@ -4365,9 +4595,9 @@ HTML
|
||||
*/
|
||||
public function GetExpectedAttributes($sCurrentState, $sStimulus, $bOnlyNewOnes)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('Since iTop 2.4, use DBObject::GetTransitionAttributes() instead');
|
||||
$aTransitions = $this->EnumTransitions();
|
||||
if (!isset($aTransitions[$sStimulus]))
|
||||
{
|
||||
if (!isset($aTransitions[$sStimulus])) {
|
||||
// Invalid stimulus
|
||||
throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sStimulus,
|
||||
$this->GetName(), $this->GetStateLabel()));
|
||||
@@ -4665,10 +4895,6 @@ EOF
|
||||
{
|
||||
/** @var string[] $aHeaders */
|
||||
$aHeaders = array(
|
||||
'form::select' => array(
|
||||
'label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList:not(:disabled)', this.checked);\"></input>",
|
||||
'description' => Dict::S('UI:SelectAllToggle+'),
|
||||
),
|
||||
'object' => array('label' => MetaModel::GetName($sClass), 'description' => Dict::S('UI:ModifiedObject')),
|
||||
'status' => array(
|
||||
'label' => Dict::S('UI:BulkModifyStatus'),
|
||||
@@ -4712,11 +4938,8 @@ EOF
|
||||
} else {
|
||||
$sStatus = $bResult ? Dict::S('UI:BulkModifyStatusModified') : Dict::S('UI:BulkModifyStatusSkipped');
|
||||
}
|
||||
$sCSSClass = $bResult ? HILIGHT_CLASS_NONE : HILIGHT_CLASS_CRITICAL;
|
||||
$sChecked = $bResult ? 'checked' : '';
|
||||
$sDisabled = $bResult ? '' : 'disabled';
|
||||
$aRows[] = array(
|
||||
'form::select' => "<input type=\"checkbox\" class=\"selectList\" $sChecked $sDisabled\"></input>",
|
||||
'object' => $oObj->GetHyperlink(),
|
||||
'status' => $sStatus,
|
||||
'errors' => '<p>'.($bResult ? '' : implode('</p><p>', $aErrors)).'</p>',
|
||||
@@ -4727,8 +4950,8 @@ EOF
|
||||
}
|
||||
set_time_limit(intval($iPreviousTimeLimit));
|
||||
$oTable = DataTableUIBlockFactory::MakeForForm('BulkModify', $aHeaders, $aRows);
|
||||
$oTable->SetOptions(['select_mode' => 'custom']);
|
||||
|
||||
$oTable->AddOption("bFullscreen", true);
|
||||
|
||||
$oPanel = PanelUIBlockFactory::MakeForClass($sClass, '');
|
||||
$oPanel->AddCSSClass('ibo-datatable-panel');
|
||||
$oPanel->AddSubBlock($oTable);
|
||||
@@ -4913,32 +5136,28 @@ EOF
|
||||
'label' => 'Consequence',
|
||||
'description' => Dict::S('UI:Delete:Consequence+'),
|
||||
);
|
||||
$oP->table($aDisplayConfig, $aDisplayData);
|
||||
$oP->AddSubBlock(DataTableUIBlockFactory::MakeForForm(preg_replace('/[^a-zA-Z0-9_-]/', '', uniqid('form_', true)), $aDisplayConfig, $aDisplayData));
|
||||
}
|
||||
|
||||
if ($oDeletionPlan->FoundStopper())
|
||||
{
|
||||
if ($oDeletionPlan->FoundSecurityIssue())
|
||||
{
|
||||
if ($oDeletionPlan->FoundStopper()) {
|
||||
if ($oDeletionPlan->FoundSecurityIssue()) {
|
||||
$oP->p(Dict::S('UI:Delete:SorryDeletionNotAllowed'));
|
||||
}
|
||||
elseif ($oDeletionPlan->FoundManualOperation())
|
||||
{
|
||||
$oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations'));
|
||||
}
|
||||
else // $bFoundManualOp
|
||||
} elseif ($oDeletionPlan->FoundManualOperation()) {
|
||||
$oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations'));
|
||||
} else // $bFoundManualOp
|
||||
{
|
||||
$oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations'));
|
||||
}
|
||||
$oForm = FormUIBlockFactory::MakeStandard('');
|
||||
$oP->AddSubBlock($oForm);
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', utils::ReadParam('transaction_id', '', false, 'transaction_id')));
|
||||
$oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null);
|
||||
$oToolbarButtons->AddCSSClass('ibo-toolbar--button');
|
||||
$oForm->AddSubBlock($oToolbarButtons);
|
||||
$oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Back'))->SetOnClickJsCode("window.history.back();"));
|
||||
$oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForDestructiveAction(Dict::S('UI:Button:Delete'), null, null, true)->SetIsDisabled(true));
|
||||
$oAppContext = new ApplicationContext();
|
||||
$oP->add("<form method=\"post\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::ReadParam('transaction_id', '', false,
|
||||
'transaction_id')
|
||||
."\">\n");
|
||||
$oP->add("<input type=\"button\" onclick=\"window.history.back();\" value=\"".Dict::S('UI:Button:Back')."\">\n");
|
||||
$oP->add("<input DISABLED type=\"submit\" name=\"\" value=\"".Dict::S('UI:Button:Delete')."\">\n");
|
||||
$oP->add($oAppContext->GetForForm());
|
||||
$oP->add("</form>\n");
|
||||
$oForm->AddSubBlock($oAppContext->GetForFormBlock());
|
||||
}
|
||||
else {
|
||||
if (count($aObjects) == 1) {
|
||||
@@ -4956,28 +5175,31 @@ EOF
|
||||
$oFilter = new DBObjectSearch($sClass);
|
||||
$oFilter->AddCondition('id', $aKeys, 'IN');
|
||||
$oSet = new CMDBobjectSet($oFilter);
|
||||
$oP->add('<div id="0">');
|
||||
CMDBAbstractObject::DisplaySet($oP, $oSet, array('display_limit' => false, 'menu' => false));
|
||||
$oP->add("</div>\n");
|
||||
$oP->add("<form method=\"post\">\n");
|
||||
foreach ($aContextData as $sKey => $value)
|
||||
{
|
||||
$oP->add("<input type=\"hidden\" name=\"{$sKey}\" value=\"$value\">\n");
|
||||
$oDisplaySet = UIContentBlockUIBlockFactory::MakeStandard("0");
|
||||
$oP->AddSubBlock($oDisplaySet);
|
||||
$oDisplaySet->AddSubBlock(CMDBAbstractObject::GetDisplaySetBlock($oP, $oSet, array('display_limit' => false, 'menu' => false)));
|
||||
|
||||
$oForm = FormUIBlockFactory::MakeStandard('');
|
||||
$oP->AddSubBlock($oForm);
|
||||
foreach ($aContextData as $sKey => $value) {
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden($sKey, $value));
|
||||
}
|
||||
$oP->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"operation\" value=\"$sCustomOperation\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"filter\" value=\"".htmlentities($oFilter->Serialize(), ENT_QUOTES,
|
||||
'UTF-8')."\">\n");
|
||||
$oP->add("<input type=\"hidden\" name=\"class\" value=\"$sClass\">\n");
|
||||
foreach($aObjects as $oObj)
|
||||
{
|
||||
$oP->add("<input type=\"hidden\" name=\"selectObject[]\" value=\"".$oObj->GetKey()."\">\n");
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', utils::GetNewTransactionId()));
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', $sCustomOperation));
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('filter', $oFilter->Serialize()));
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $sClass));
|
||||
foreach ($aObjects as $oObj) {
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('selectObject[]', $oObj->GetKey()));
|
||||
}
|
||||
$oP->add("<input type=\"button\" onclick=\"window.history.back();\" value=\"".Dict::S('UI:Button:Back')."\">\n");
|
||||
$oP->add("<input type=\"submit\" name=\"\" value=\"".Dict::S('UI:Button:Delete')."\">\n");
|
||||
|
||||
$oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null);
|
||||
$oToolbarButtons->AddCSSClass('ibo-toolbar--button');
|
||||
$oForm->AddSubBlock($oToolbarButtons);
|
||||
$oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Back'))->SetOnClickJsCode("window.history.back();"));
|
||||
$oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForDestructiveAction(Dict::S('UI:Button:Delete'), null, null, true));
|
||||
$oAppContext = new ApplicationContext();
|
||||
$oP->add($oAppContext->GetForForm());
|
||||
$oP->add("</form>\n");
|
||||
$oForm->AddSubBlock($oAppContext->GetForFormBlock());
|
||||
|
||||
}
|
||||
}
|
||||
else // if ($bPreview)...
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CSVPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CSVPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/CSVPage.php, now loadable using autoloader');
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableSettings;
|
||||
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardLayout as DashboardLayoutUIBlock;
|
||||
|
||||
@@ -1147,7 +1148,8 @@ JS
|
||||
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_DASHBOARD_ACTIONS, $this, $aActions);
|
||||
|
||||
$oActionsMenu = $oPage->GetPopoverMenu($sPopoverMenuId, $aActions)
|
||||
->SetTogglerJSSelector("#$sMenuTogglerId");
|
||||
->SetTogglerJSSelector("#$sMenuTogglerId")
|
||||
->SetContainer(PopoverMenu::ENUM_CONTAINER_BODY);
|
||||
|
||||
$oToolbar->AddSubBlock($oActionButton)
|
||||
->AddSubBlock($oActionsMenu);
|
||||
|
||||
@@ -914,6 +914,7 @@ class DashletObjectList extends Dashlet
|
||||
'menu' => $sShowMenu,
|
||||
'table_id' => self::APPUSERPREFERENCES_PREFIX.$this->sId,
|
||||
'surround_with_panel' => false,
|
||||
'max_height' => '500px',
|
||||
);
|
||||
$sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occurring in the same DOM)
|
||||
$oBlock->DisplayIntoContentBlock($oPanel, $oPage, $sBlockId, array_merge($aExtraParams, $aParams));
|
||||
@@ -926,24 +927,36 @@ class DashletObjectList extends Dashlet
|
||||
*/
|
||||
public function RenderNoData($oPage, $bEditMode = false, $aExtraParams = array())
|
||||
{
|
||||
$oDashletContainer = new DashletContainer($this->sId, ['dashlet-content']);
|
||||
$sTitle = $this->aProperties['title'];
|
||||
$sQuery = $this->aProperties['query'];
|
||||
$bShowMenu = $this->aProperties['menu'];
|
||||
|
||||
$oPage->add('<div class="dashlet-content">');
|
||||
$sHtmlTitle = utils::HtmlEntities($this->oModelReflection->DictString($sTitle)); // done in the itop block
|
||||
$sHtmlTitle = utils::HtmlEntities($this->oModelReflection->DictString($sTitle));
|
||||
if ($sHtmlTitle != '') {
|
||||
$oPage->add('<h1>'.$sHtmlTitle.'</h1>');
|
||||
$sHtmlTitle = '<h1>'.$sHtmlTitle.'</h1>';
|
||||
}
|
||||
$oQuery = $this->oModelReflection->GetQuery($sQuery);
|
||||
$sClass = $oQuery->GetClass();
|
||||
$oPage->add('<div id="block_fake_'.$this->sId.'" class="display_block">');
|
||||
$oPage->p(Dict::S('UI:NoObjectToDisplay'));
|
||||
$sId = $this->sId;
|
||||
$sMessage = Dict::S('UI:NoObjectToDisplay');
|
||||
$sMenu = '';
|
||||
if ($bShowMenu) {
|
||||
$oPage->p('<a>'.Dict::Format('UI:ClickToCreateNew', $this->oModelReflection->GetName($sClass)).'</a>');
|
||||
$sMenu = '<p><a>'.Dict::Format('UI:ClickToCreateNew', $this->oModelReflection->GetName($sClass)).'</a></p>';
|
||||
}
|
||||
$oPage->add('</div>');
|
||||
$oPage->add('</div>');
|
||||
|
||||
$sHtml = <<<HTML
|
||||
<div class="dashlet-content">
|
||||
<h1>$sHtmlTitle</h1>
|
||||
<div id="block_fake_$sId" class="display_block">
|
||||
<p>$sMessage</p>
|
||||
$sMenu
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
$oDashletContainer->AddHtml($sHtml);
|
||||
|
||||
return $oDashletContainer;
|
||||
}
|
||||
|
||||
public function GetDBSearch($aExtraParams = array())
|
||||
@@ -2085,8 +2098,20 @@ class DashletHeaderDynamic extends Dashlet
|
||||
$sQuery = $this->aProperties['query'];
|
||||
$sGroupBy = $this->aProperties['group_by'];
|
||||
|
||||
$oQuery = $this->oModelReflection->GetQuery($sQuery);
|
||||
$sClass = $oQuery->GetClass();
|
||||
$aValueLabels = [];
|
||||
$aValues = [];
|
||||
try {
|
||||
$oQuery = $this->oModelReflection->GetQuery($sQuery);
|
||||
$sClass = $oQuery->GetClass();
|
||||
$aValues = $this->GetValues();
|
||||
foreach ($aValues as $sValue) {
|
||||
$aValueLabels[] = $this->oModelReflection->GetValueLabel($sClass, $sGroupBy, $sValue);
|
||||
}
|
||||
}
|
||||
catch (UnknownClassOqlException $e) {
|
||||
$aValueLabels[] = $e->GetUserFriendlyDescription();
|
||||
$aValues[] = 1;
|
||||
}
|
||||
|
||||
$oIconSelect = $this->oModelReflection->GetIconSelectionField('icon');
|
||||
$sIconPath = utils::HtmlEntities($oIconSelect->MakeFileUrl($sIcon));
|
||||
@@ -2099,20 +2124,18 @@ class DashletHeaderDynamic extends Dashlet
|
||||
$sBlockId = 'block_fake_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
|
||||
|
||||
$iTotal = 0;
|
||||
$aValues = $this->GetValues();
|
||||
|
||||
$sHtml .= '<div class="display_block" id="'.$sBlockId.'">';
|
||||
$sHtml .= '<div class="summary-details">';
|
||||
$sHtml .= '<table><tbody>';
|
||||
$sHtml .= '<tr>';
|
||||
foreach ($aValues as $sValue) {
|
||||
$sValueLabel = $this->oModelReflection->GetValueLabel($sClass, $sGroupBy, $sValue);
|
||||
foreach ($aValueLabels as $sValueLabel) {
|
||||
$sHtml .= ' <th>'.$sValueLabel.'</th>';
|
||||
}
|
||||
$sHtml .= '</tr>';
|
||||
$sHtml .= '<tr>';
|
||||
foreach ($aValues as $sValue) {
|
||||
$iCount = (int)rand(2, 100);
|
||||
$iCount = rand(2, 100);
|
||||
$iTotal += $iCount;
|
||||
$sHtml .= ' <td>'.$iCount.'</td>';
|
||||
}
|
||||
@@ -2127,7 +2150,6 @@ class DashletHeaderDynamic extends Dashlet
|
||||
$sHtml .= '<a class="summary">'.utils::HtmlEntities($sSubtitle).'</a>';
|
||||
$sHtml .= '</div>';
|
||||
|
||||
$sHtml .= '</div>';
|
||||
|
||||
$oDashletContainer->AddHtml($sHtml);
|
||||
|
||||
@@ -2309,18 +2331,19 @@ class DashletBadge extends Dashlet
|
||||
$oDashletContainer = new DashletContainer($this->sId, ['dashlet-content']);
|
||||
|
||||
$sClass = $this->aProperties['class'];
|
||||
$sIconUrl = $this->oModelReflection->GetClassIcon($sClass, false);
|
||||
$sIconUrl = utils::HtmlEntities($this->oModelReflection->GetClassIcon($sClass, false));
|
||||
$sClassLabel = $this->oModelReflection->GetName($sClass);
|
||||
$sId = $this->sId;
|
||||
$sClassCreate = Dict::Format('UI:ClickToCreateNew', $sClassLabel);
|
||||
|
||||
$sHtml = '';
|
||||
$sHtml .= '<div id="block_fake_'.$this->sId.'" class="display_block">';
|
||||
$sHtml .= '<p>';
|
||||
$sHtml .= ' <a class="actions"><img src="'.utils::HtmlEntities($sIconUrl).'" style="vertical-align:middle;float;left;margin-right:10px;border:0;">'.$sClassLabel.': 947</a>';
|
||||
$sHtml .= '</p>';
|
||||
$sHtml .= '<p>';
|
||||
$sHtml .= ' <a>'.Dict::Format('UI:ClickToCreateNew', $sClassLabel).'</a>';
|
||||
$sHtml .= '</p>';
|
||||
$sHtml .= '</div>';
|
||||
$sHtml = <<<HTML
|
||||
<div id="block_fake_$sId" class="display_block">
|
||||
<div class="ibo-dashlet-badge--body" data-role="ibo-dashlet-badge--body" title="$sClassLabel">
|
||||
<div class="ibo-dashlet-badge--icon-container"><img class="ibo-dashlet-badge--icon" src="$sIconUrl"></div>
|
||||
<div class="ibo-dashlet-badge--actions"><a class="ibo-dashlet-badge--action-list" href="#" data-role="ibo-dashlet-badge--action-list"><span class="ibo-dashlet-badge--action-list-count">4</span><span class="ibo-dashlet-badge--action-list-label">$sClassLabel</span></a><a class="ibo-dashlet-badge--action-create" href="#"><span class="ibo-dashlet-badge--action-create-icon fas fa-plus"></span><span class="ibo-dashlet-badge--action-create-label"> $sClassCreate </span></a></div>
|
||||
</div>
|
||||
</div>
|
||||
HTML;
|
||||
|
||||
$oDashletContainer->AddHtml($sHtml);
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ class DataTable
|
||||
*/
|
||||
public function __construct($iListId, $oSet, $aClassAliases, $sTableId = null)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use Combodo\iTop\Application\UI\Base\Component\DataTable\Datatable');
|
||||
$this->iListId = utils::GetSafeId($iListId); // Make a "safe" ID for jQuery
|
||||
$this->sDatatableContainerId = 'datatable_'.utils::GetSafeId($iListId);
|
||||
$this->oSet = $oSet;
|
||||
@@ -115,8 +116,7 @@ class DataTable
|
||||
// See if this column is a must to load
|
||||
$sClass = $this->aClassAliases[$sAlias];
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef->alwaysLoadInTables())
|
||||
{
|
||||
if ($oAttDef->AlwaysLoadInTables()) {
|
||||
$aColumnsToLoad[$sAlias][] = $sAttCode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory
|
||||
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Pill\PillFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
|
||||
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenuItem\PopoverMenuItemFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Toolbar\Separator\ToolbarSeparatorUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\iUIBlock;
|
||||
@@ -168,6 +169,8 @@ class DisplayBlock
|
||||
/** bool add toolkit menu */
|
||||
'selectionMode',
|
||||
/**positive or negative*/
|
||||
'max_height',
|
||||
/** string Max. height of the list, if not specified will occupy all the available height no matter the pagination */
|
||||
], DataTableUIBlockFactory::GetAllowedParams()),
|
||||
'list_search' => array_merge([
|
||||
'update_history',
|
||||
@@ -1138,7 +1141,7 @@ JS
|
||||
$aValues[$iRow] = $sValue;
|
||||
$sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue);
|
||||
$aLabels[$iRow] = $sHtmlValue;
|
||||
$aGroupBy[$iRow] = (int)$aRow[$sFctVar];
|
||||
$aGroupBy[$iRow] = (float)$aRow[$sFctVar];
|
||||
$iTotalCount += $aRow['_itop_count_'];
|
||||
}
|
||||
|
||||
@@ -1538,7 +1541,7 @@ JS
|
||||
$aValues[] = array(
|
||||
'label' => html_entity_decode(strip_tags($sHtmlValue), ENT_QUOTES, 'UTF-8'),
|
||||
'label_html' => $sHtmlValue,
|
||||
'value' => (int)$aRow[$sFctVar],
|
||||
'value' => (float)$aRow[$sFctVar],
|
||||
);
|
||||
|
||||
// Build the search for this subset
|
||||
@@ -1573,7 +1576,7 @@ JS
|
||||
$aColumns = array();
|
||||
$aNames = array();
|
||||
foreach ($aValues as $idx => $aValue) {
|
||||
$aColumns[] = array('series_'.$idx, (int)$aValue['value']);
|
||||
$aColumns[] = array('series_'.$idx, (float)$aValue['value']);
|
||||
$aNames['series_'.$idx] = $aValue['label'];
|
||||
}
|
||||
$oBlock = new BlockChartAjaxPie();
|
||||
@@ -1646,6 +1649,7 @@ class HistoryBlock extends DisplayBlock
|
||||
|
||||
public function __construct(DBSearch $oFilter, $sStyle = 'list', $bAsynchronous = false, $aParams = array(), $oSet = null)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
parent::__construct($oFilter, $sStyle, $bAsynchronous, $aParams, $oSet);
|
||||
$this->iLimitStart = 0;
|
||||
$this->iLimitCount = 0;
|
||||
@@ -1771,6 +1775,13 @@ EOF
|
||||
*/
|
||||
class MenuBlock extends DisplayBlock
|
||||
{
|
||||
/**
|
||||
* @var string Prefix to use for the ID of the actions toolbar
|
||||
* @used-by static::GetRenderContent
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ACTIONS_TOOLBAR_ID_PREFIX = 'ibo-actions-toolbar-';
|
||||
|
||||
/**
|
||||
* Renders the "Actions" popup menu for the given set of objects
|
||||
*
|
||||
@@ -1812,6 +1823,8 @@ class MenuBlock extends DisplayBlock
|
||||
$aRegularActions = [];
|
||||
/** @var array $aTransitionActions Only transitions */
|
||||
$aTransitionActions = [];
|
||||
/** @var array $aToolkitActions Any "legacy" toolkit menu item, which are now displayed in the same menu as the $aRegularActions, after them */
|
||||
$aToolkitActions = [];
|
||||
if ((!isset($aExtraParams['selection_mode']) || $aExtraParams['selection_mode'] == "") && $this->m_sStyle != 'listInObject') {
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
@@ -1981,13 +1994,9 @@ class MenuBlock extends DisplayBlock
|
||||
|
||||
$this->AddMenuSeparator($aRegularActions);
|
||||
|
||||
/** @var \iApplicationUIExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) {
|
||||
$oSet->Rewind();
|
||||
foreach ($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $sUrl) {
|
||||
$aRegularActions[$sLabel] = array('label' => $sLabel, 'url' => $sUrl) + $aActionParams;
|
||||
}
|
||||
}
|
||||
$this->GetEnumAllowedActions($oSet, function ($sLabel, $data) use (&$aRegularActions, $aActionParams) {
|
||||
$aRegularActions[$sLabel] = array('label' => $sLabel, 'url' => $data) + $aActionParams;
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -2089,24 +2098,20 @@ class MenuBlock extends DisplayBlock
|
||||
|
||||
$this->AddMenuSeparator($aRegularActions);
|
||||
|
||||
/** @var \iApplicationUIExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) {
|
||||
$oSet->Rewind();
|
||||
foreach ($oExtensionInstance->EnumAllowedActions($oSet) as $sLabel => $data) {
|
||||
if (is_array($data)) {
|
||||
// New plugins can provide javascript handlers via the 'onclick' property
|
||||
//TODO: enable extension of different menus by checking the 'target' property ??
|
||||
$aRegularActions[$sLabel] = [
|
||||
'label' => $sLabel,
|
||||
'url' => isset($data['url']) ? $data['url'] : '#',
|
||||
'onclick' => isset($data['onclick']) ? $data['onclick'] : '',
|
||||
];
|
||||
} else {
|
||||
// Backward compatibility with old plugins
|
||||
$aRegularActions[$sLabel] = ['label' => $sLabel, 'url' => $data] + $aActionParams;
|
||||
}
|
||||
$this->GetEnumAllowedActions($oSet, function ($sLabel, $data) use (&$aRegularActions, $aActionParams) {
|
||||
if (is_array($data)) {
|
||||
// New plugins can provide javascript handlers via the 'onclick' property
|
||||
//TODO: enable extension of different menus by checking the 'target' property ??
|
||||
$aRegularActions[$sLabel] = [
|
||||
'label' => $sLabel,
|
||||
'url' => isset($data['url']) ? $data['url'] : '#',
|
||||
'onclick' => isset($data['onclick']) ? $data['onclick'] : '',
|
||||
];
|
||||
} else {
|
||||
// Backward compatibility with old plugins
|
||||
$aRegularActions[$sLabel] = ['label' => $sLabel, 'url' => $data] + $aActionParams;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (empty($sRefreshAction) && $this->m_sStyle == 'list') {
|
||||
//for the detail page this var is defined way beyond this line
|
||||
@@ -2116,18 +2121,17 @@ class MenuBlock extends DisplayBlock
|
||||
//it's easier just display configure this list and MENU_OBJLIST_TOOLKIT
|
||||
}
|
||||
$param = null;
|
||||
$iMenuId = null;
|
||||
if (is_null($sId)) {
|
||||
$sId = uniqid();
|
||||
}
|
||||
|
||||
// New extensions based on iPopupMenuItem interface
|
||||
$oPopupMenuItemsBlock = new UIContentBlock();
|
||||
switch ($this->m_sStyle) {
|
||||
case 'list':
|
||||
case 'listInObject':
|
||||
$oSet->Rewind();
|
||||
$param = $oSet;
|
||||
$iMenuId = iPopupMenuExtension::MENU_OBJLIST_ACTIONS;
|
||||
$bToolkitMenu = true;
|
||||
if (isset($aExtraParams['toolkit_menu'])) {
|
||||
$bToolkitMenu = (bool)$aExtraParams['toolkit_menu'];
|
||||
@@ -2136,16 +2140,20 @@ class MenuBlock extends DisplayBlock
|
||||
$sLabel = Dict::S('UI:ConfigureThisList');
|
||||
$aRegularActions['iTop::ConfigureList'] = ['label' => $sLabel, 'url' => '#', 'onclick' => "$('#datatable_dlg_datatable_{$sId}').dialog('open'); return false;"];
|
||||
}
|
||||
utils::GetPopupMenuItemsBlock($oPopupMenuItemsBlock, iPopupMenuExtension::MENU_OBJLIST_ACTIONS, $param, $aRegularActions, $sId);
|
||||
utils::GetPopupMenuItemsBlock($oPopupMenuItemsBlock, iPopupMenuExtension::MENU_OBJLIST_TOOLKIT, $param, $aToolkitActions, $sId);
|
||||
break;
|
||||
|
||||
case 'details':
|
||||
$oSet->Rewind();
|
||||
$param = $oSet->Fetch();
|
||||
$iMenuId = iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS;
|
||||
utils::GetPopupMenuItemsBlock($oPopupMenuItemsBlock, iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS, $param, $aRegularActions, $sId);
|
||||
break;
|
||||
|
||||
}
|
||||
$oRenderBlock->AddSubBlock(utils::GetPopupMenuItemsBlock($iMenuId, $param, $aRegularActions, $sId));
|
||||
if ($oPopupMenuItemsBlock->HasSubBlocks()) {
|
||||
$oRenderBlock->AddSubBlock($oPopupMenuItemsBlock);
|
||||
}
|
||||
|
||||
// Extract favorite actions from their menus
|
||||
$aFavoriteRegularActions = [];
|
||||
@@ -2168,7 +2176,7 @@ class MenuBlock extends DisplayBlock
|
||||
}
|
||||
}
|
||||
|
||||
$oActionsToolbar = ToolbarUIBlockFactory::MakeForAction("ibo-actions-toolbar-{$sId}");
|
||||
$oActionsToolbar = ToolbarUIBlockFactory::MakeForAction(static::ACTIONS_TOOLBAR_ID_PREFIX.$sId);
|
||||
$oRenderBlock->AddSubBlock($oActionsToolbar);
|
||||
$sRegularActionsMenuTogglerId = "ibo-regular-actions-menu-toggler-{$sId}";
|
||||
$sRegularActionsPopoverMenuId = "ibo-regular-actions-popover-{$sId}";
|
||||
@@ -2276,7 +2284,7 @@ class MenuBlock extends DisplayBlock
|
||||
}
|
||||
|
||||
// - Others
|
||||
if (!empty($aRegularActions)) {
|
||||
if (!empty($aRegularActions) || !empty($aToolkitActions)) {
|
||||
if (count($aFavoriteRegularActions) > 0) {
|
||||
$sName = 'UI:Menu:OtherActions';
|
||||
} else {
|
||||
@@ -2291,15 +2299,75 @@ class MenuBlock extends DisplayBlock
|
||||
|
||||
$oActionsToolbar->AddSubBlock($oActionButton)
|
||||
->AddSubBlock($oRegularActionsMenu);
|
||||
|
||||
// Toolkit actions
|
||||
if (!empty($aToolkitActions)) {
|
||||
foreach ($aToolkitActions as $sActionId => $aActionData) {
|
||||
$oRegularActionsMenu->AddItem('toolkit-actions', PopoverMenuItemFactory::MakeFromApplicationPopupMenuItemData($sActionId, $aActionData));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $oRenderBlock;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If an extension doesn't return an array as expected :
|
||||
* - calls IssueLog:Warning
|
||||
* - if is dev env, then throw CoreUnexpectedValue exception
|
||||
*
|
||||
* @param \DBObjectSet $oSet
|
||||
* @param callable $callback EnumAllowedActions returns an array, we will call this anonymous function on each of its value
|
||||
* with two parameters : label (array index), data (array value)
|
||||
*
|
||||
* @throws \CoreUnexpectedValue
|
||||
*
|
||||
* @uses \MetaModel::EnumPlugins()
|
||||
* @uses \iApplicationUIExtension::EnumAllowedActions()
|
||||
* @uses \utils::IsDevelopmentEnvironment()
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
private function GetEnumAllowedActions(DBObjectSet $oSet, callable $callback): void
|
||||
{
|
||||
$aInvalidExtensions = [];
|
||||
|
||||
/** @var \iApplicationUIExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) {
|
||||
$oSet->Rewind();
|
||||
$aExtEnumAllowedActions = $oExtensionInstance->EnumAllowedActions($oSet);
|
||||
|
||||
if (!is_array($aExtEnumAllowedActions)) {
|
||||
$aInvalidExtensions[] = get_class($oExtensionInstance);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($aExtEnumAllowedActions as $sLabel => $data) {
|
||||
$callback($sLabel, $data);
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($aInvalidExtensions)) {
|
||||
$sMessage = 'Some extensions returned non array value for EnumAllowedActions() method impl';
|
||||
|
||||
IssueLog::Warning(
|
||||
$sMessage,
|
||||
null,
|
||||
['extensions' => $aInvalidExtensions]
|
||||
);
|
||||
|
||||
if (utils::IsDevelopmentEnvironment()) {
|
||||
throw new CoreUnexpectedValue($sMessage, $aInvalidExtensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a menu separator to the current list of actions
|
||||
*
|
||||
* @param array $aActions The current actions list
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function AddMenuSeparator(&$aActions)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/ErrorPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/ErrorPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/ErrorPage.php, now loadable using autoloader');
|
||||
@@ -28,14 +28,14 @@ class CoreCannotSaveObjectException extends CoreException
|
||||
*
|
||||
* @param array $aContextData containing at least those keys : issues, id, class
|
||||
*/
|
||||
public function __construct($aContextData)
|
||||
public function __construct($aContextData, $oPrevious = null)
|
||||
{
|
||||
$this->aIssues = $aContextData['issues'];
|
||||
$this->iObjectId = $aContextData['id'];
|
||||
$this->sObjectClass = $aContextData['class'];
|
||||
|
||||
$sIssues = implode(', ', $this->aIssues);
|
||||
parent::__construct($sIssues, $aContextData);
|
||||
parent::__construct($sIssues, $aContextData, '', $oPrevious);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,5 +30,10 @@ class MySQLException extends CoreException
|
||||
$aContext['mysql_error'] = CMDBSource::GetError();
|
||||
}
|
||||
parent::__construct($sIssue, $aContext);
|
||||
//if is connection error, don't log the default message with password in
|
||||
if (mysqli_connect_errno()) {
|
||||
error_log($this->message);
|
||||
error_reporting(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/iTopWebPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/iTopWebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/iTopWebPage.php, now loadable using autoloader');
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/iTopWizardWebPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/iTopWizardWebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/iTopWizardWebPage.php, now loadable using autoloader');
|
||||
@@ -1,20 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-2021 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
|
||||
@@ -342,15 +329,17 @@ class ApplicationMenu
|
||||
*/
|
||||
public static function DisplayMenu($oPage, $aExtraParams)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use static::GetMenuGroups() instead');
|
||||
self::LoadAdditionalMenus();
|
||||
// Sort the root menu based on the rank
|
||||
usort(self::$aRootMenus, array('ApplicationMenu', 'CompareOnRank'));
|
||||
$iAccordion = 0;
|
||||
$iActiveAccordion = $iAccordion;
|
||||
$iActiveMenu = self::GetMenuIndexById(self::GetActiveNodeId());
|
||||
foreach(self::$aRootMenus as $aMenu)
|
||||
{
|
||||
if (!self::CanDisplayMenu($aMenu)) { continue; }
|
||||
foreach (self::$aRootMenus as $aMenu) {
|
||||
if (!self::CanDisplayMenu($aMenu)) {
|
||||
continue;
|
||||
}
|
||||
$oMenuNode = self::GetMenuNode($aMenu['index']);
|
||||
$oPage->AddToMenu('<h3 id="'.utils::GetSafeId('AccordionMenu_'.$oMenuNode->GetMenuID()).'" class="navigation-menu-group" data-menu-id="'.$oMenuNode->GetMenuId().'">'.$oMenuNode->GetTitle().'</h3>');
|
||||
$oPage->AddToMenu('<div>');
|
||||
@@ -418,13 +407,12 @@ EOF
|
||||
*/
|
||||
protected static function DisplaySubMenu($oPage, $aMenus, $aExtraParams, $iActiveMenu = -1)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use static::GetSubMenuNodes() instead');
|
||||
// Sort the menu based on the rank
|
||||
$bActive = false;
|
||||
usort($aMenus, array('ApplicationMenu', 'CompareOnRank'));
|
||||
foreach($aMenus as $aMenu)
|
||||
{
|
||||
if (!self::CanDisplayMenu($aMenu))
|
||||
{
|
||||
foreach ($aMenus as $aMenu) {
|
||||
if (!self::CanDisplayMenu($aMenu)) {
|
||||
continue;
|
||||
}
|
||||
$index = $aMenu['index'];
|
||||
@@ -728,41 +716,7 @@ abstract class MenuNode
|
||||
// Count the entries up to 99
|
||||
$oSearch = DBSearch::FromOQL($sOQL);
|
||||
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sClass = $oSearch->GetClass();
|
||||
foreach ($oAppContext->GetNames() as $key) {
|
||||
// Find the value of the object corresponding to each 'context' parameter
|
||||
$aCallSpec = [$sClass, 'MapContextParam'];
|
||||
$sAttCode = '';
|
||||
if (is_callable($aCallSpec)) {
|
||||
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
|
||||
}
|
||||
|
||||
if (MetaModel::IsValidAttCode($sClass, $sAttCode)) {
|
||||
// Add Hierarchical condition if hierarchical key
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
if (isset($oAttDef) && ($oAttDef->IsExternalKey())) {
|
||||
$iDefaultValue = intval($oAppContext->GetCurrentValue($key));
|
||||
if ($iDefaultValue != 0) {
|
||||
try {
|
||||
/** @var AttributeExternalKey $oAttDef */
|
||||
$sTargetClass = $oAttDef->GetTargetClass();
|
||||
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sTargetClass);
|
||||
if ($sHierarchicalKeyCode !== false) {
|
||||
$oFilter = new DBObjectSearch($sTargetClass);
|
||||
$oFilter->AddCondition('id', $iDefaultValue);
|
||||
$oHKFilter = new DBObjectSearch($sTargetClass);
|
||||
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
|
||||
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// If filtering fails just ignore it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DBSearchHelper::AddContextFilter($oSearch);
|
||||
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
$iCount = $oSet->CountWithLimit(99);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/NiceWebPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/NiceWebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/NiceWebPage.php, now loadable using autoloader');
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/PDFPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/PDFPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/PDFPage.php, now loadable using autoloader');
|
||||
@@ -17,6 +17,12 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Field\Field;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Field\FieldUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Input\TextArea;
|
||||
|
||||
abstract class Query extends cmdbAbstractObject
|
||||
{
|
||||
/**
|
||||
@@ -120,36 +126,37 @@ class QueryOQL extends Query
|
||||
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
|
||||
{
|
||||
$aFieldsMap = parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
|
||||
|
||||
if (!$bEditMode)
|
||||
{
|
||||
$oPage->add_script("$('[name=\"attr_oql\"]').addClass('ibo-queryoql'); $('[data-attribute-code=\"oql\"]').addClass('ibo-queryoql');");
|
||||
|
||||
if (!$bEditMode) {
|
||||
$sFields = trim($this->Get('fields'));
|
||||
$bExportV1Recommended = ($sFields == '');
|
||||
if ($bExportV1Recommended)
|
||||
{
|
||||
if ($bExportV1Recommended) {
|
||||
$oFieldAttDef = MetaModel::GetAttributeDef('QueryOQL', 'fields');
|
||||
$oPage->add('<div class="message message_error" style="padding-left: 30px;"><div style="padding: 10px;">'.Dict::Format('UI:Query:UrlV1', $oFieldAttDef->GetLabel()).'</div></div>');
|
||||
$oAlert = AlertUIBlockFactory::MakeForFailure()
|
||||
->SetIsClosable(false)
|
||||
->SetIsCollapsible(false);
|
||||
$oAlert->AddCSSClass('mb-5');
|
||||
$oAlert->AddSubBlock(new Html(Dict::Format('UI:Query:UrlV1', '')));
|
||||
$oPage->AddSubBlock($oAlert);
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php?format=spreadsheet&login_mode=basic&date_format='.urlencode((string)AttributeDateTime::GetFormat()).'&query='.$this->GetKey();
|
||||
}
|
||||
$sOql = $this->Get('oql');
|
||||
$sMessage = null;
|
||||
try
|
||||
{
|
||||
try {
|
||||
$oSearch = DBObjectSearch::FromOQL($sOql);
|
||||
$aParameters = $oSearch->GetQueryParams();
|
||||
foreach($aParameters as $sParam => $val)
|
||||
{
|
||||
foreach ($aParameters as $sParam => $val) {
|
||||
$sUrl .= '&arg_'.$sParam.'=["'.$sParam.'"]';
|
||||
}
|
||||
|
||||
$oPage->p(Dict::S('UI:Query:UrlForExcel').':<br/><textarea cols="80" rows="3" READONLY>'.$sUrl.'</textarea>');
|
||||
$oTextArea = new TextArea("", $sUrl, null, 80, 3);
|
||||
$oFieldUrl = FieldUIBlockFactory::MakeFromObject(Dict::S('UI:Query:UrlForExcel'), $oTextArea, Field::ENUM_FIELD_LAYOUT_LARGE);
|
||||
$oPage->AddSubBlock($oFieldUrl);
|
||||
|
||||
if (count($aParameters) == 0)
|
||||
{
|
||||
if (count($aParameters) == 0) {
|
||||
$oBlock = new DisplayBlock($oSearch, 'list');
|
||||
$aExtraParams = array(
|
||||
//'menu' => $sShowMenu,
|
||||
@@ -159,10 +166,13 @@ class QueryOQL extends Query
|
||||
$oBlock->Display($oPage, $sBlockId, $aExtraParams);
|
||||
}
|
||||
}
|
||||
catch (OQLException $e)
|
||||
{
|
||||
$sMessage = '<div class="message message_error" style="padding-left: 30px;"><div style="padding: 10px;">'.Dict::Format('UI:RunQuery:Error', $e->getHtmlDesc()).'</div></div>';
|
||||
$oPage->p($sMessage);
|
||||
catch
|
||||
(OQLException $e) {
|
||||
$oAlert = AlertUIBlockFactory::MakeForFailure(Dict::Format('UI:RunQuery:Error'), $e->getHtmlDesc())
|
||||
->SetIsClosable(false)
|
||||
->SetIsCollapsible(false);
|
||||
$oAlert->AddCSSClass('mb-5');
|
||||
$oPage->AddSubBlock($oAlert);
|
||||
}
|
||||
}
|
||||
return $aFieldsMap;
|
||||
|
||||
@@ -45,7 +45,7 @@ register_shutdown_function(function()
|
||||
$sMessage = substr($sMessage, 0, $iStackTracePos);
|
||||
}
|
||||
}
|
||||
IssueLog::error($sMessage);
|
||||
IssueLog::error($sMessage, null, $err);
|
||||
if (strpos($err['message'], 'Allowed memory size of') !== false)
|
||||
{
|
||||
$sLimit = ini_get('memory_limit');
|
||||
|
||||
@@ -31,14 +31,15 @@ class DisplayTemplate
|
||||
|
||||
public function __construct($sTemplate)
|
||||
{
|
||||
$this->m_aTags = array (
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
$this->m_aTags = array(
|
||||
'itopblock',
|
||||
'itopcheck',
|
||||
'itoptabs',
|
||||
'itoptab',
|
||||
'itoptoggle',
|
||||
'itopstring',
|
||||
'sqlblock'
|
||||
'sqlblock',
|
||||
);
|
||||
$this->m_sTemplate = $sTemplate;
|
||||
}
|
||||
|
||||
@@ -46,11 +46,10 @@ class ThemeHandler
|
||||
'name' => 'fullmoon',
|
||||
'parameters' => [
|
||||
'variables' => [],
|
||||
'imports' => [
|
||||
],
|
||||
'imports' => [],
|
||||
'stylesheets' => [
|
||||
'main' => '../css/backoffice/main.scss',
|
||||
],
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
@@ -58,22 +57,27 @@ class ThemeHandler
|
||||
/**
|
||||
* Return the ID of the theme currently defined in the config. file
|
||||
*
|
||||
* @deprecated 3.0.0, will be removed in 3.1, see N°3898
|
||||
* @return string
|
||||
*/
|
||||
public static function GetCurrentThemeId()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (is_null(MetaModel::GetConfig()))
|
||||
{
|
||||
throw new CoreException('no config');
|
||||
}
|
||||
$sThemeId = MetaModel::GetConfig()->Get('backoffice_default_theme');
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
static::GetCurrentUserThemeId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string ID of the theme currently defined in the config. file, which applies to all users by default. If non defined, fallback on the default one.
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetApplicationThemeId(): string
|
||||
{
|
||||
try {
|
||||
$sThemeId = utils::GetConfig()->Get('backoffice_default_theme');
|
||||
}
|
||||
catch(CoreException $oCompileException)
|
||||
{
|
||||
catch (CoreException $oCompileException) {
|
||||
// Fallback on our default theme in case the config. is not available yet
|
||||
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
|
||||
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
|
||||
$sThemeId = $aDefaultTheme['name'];
|
||||
}
|
||||
|
||||
@@ -81,49 +85,173 @@ class ThemeHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the absolute path of the compiled theme folder.
|
||||
*
|
||||
* @return string ID of the theme to use for the current user as per they preferences. If non defined, fallback on the app. theme ID.
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetCurrentUserThemeId(): string
|
||||
{
|
||||
$sThemeId = null;
|
||||
|
||||
try {
|
||||
if (true === utils::GetConfig()->Get('user_preferences.allow_backoffice_theme_override')) {
|
||||
$sThemeId = appUserPreferences::GetPref('backoffice_theme', null);
|
||||
}
|
||||
}
|
||||
catch (Exception $oException) {
|
||||
// Do nothing, already handled by $sThemeId null by default
|
||||
}
|
||||
|
||||
// Fallback on the app. theme
|
||||
if (is_null($sThemeId)) {
|
||||
$sThemeId = static::GetApplicationThemeId();
|
||||
}
|
||||
|
||||
return $sThemeId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sThemeId
|
||||
*
|
||||
* @return string
|
||||
* @return string Label of the theme which is either a dict entry ('theme:<THEME_ID>') or the ID if no localized dict. entry found.
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetCompiledThemeFolderAbsolutePath($sThemeId)
|
||||
public static function GetThemeLabel(string $sThemeId): string
|
||||
{
|
||||
return APPROOT.'env-'.utils::GetCurrentEnvironment().'/branding/themes/'.$sThemeId.'/';
|
||||
$sDictEntryCode = 'theme:'.$sThemeId;
|
||||
$sDictEntryValue = Dict::S('theme:'.$sThemeId);
|
||||
|
||||
return ($sDictEntryCode === $sDictEntryValue) ? $sThemeId : $sDictEntryValue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array Associative array of <THEME_ID> => <THEME_LABEL>, ordered by labels
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetAvailableThemes(): array
|
||||
{
|
||||
$aThemes = [];
|
||||
|
||||
foreach (glob(static::GetCompiledThemesFolderAbsolutePath().'/*') as $sPath) {
|
||||
if (is_dir($sPath)) {
|
||||
$sThemeId = basename($sPath);
|
||||
$sThemeLabel = static::GetThemeLabel($sThemeId);
|
||||
|
||||
$aThemes[$sThemeId] = $sThemeLabel;
|
||||
}
|
||||
}
|
||||
asort($aThemes);
|
||||
|
||||
return $aThemes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sThemeId
|
||||
*
|
||||
* @return bool True if $sThemeId is a valid theme that can be used.
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function IsValidTheme(string $sThemeId): bool
|
||||
{
|
||||
return array_key_exists($sThemeId, static::GetAvailableThemes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Absolute path to the folder containing all the compiled themes
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetCompiledThemesFolderAbsolutePath(): string
|
||||
{
|
||||
return APPROOT.'env-'.utils::GetCurrentEnvironment().'/branding/themes/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sThemeId
|
||||
*
|
||||
* @return string Absolute path to the folder containing the $sThemeId theme
|
||||
*/
|
||||
public static function GetCompiledThemeFolderAbsolutePath(string $sThemeId): string
|
||||
{
|
||||
return static::GetCompiledThemesFolderAbsolutePath().$sThemeId.'/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sThemeId
|
||||
*
|
||||
* @return string Absolute path of the compiled file for the $sThemeId theme (Note: It doesn't mean that the theme is actually compiled)
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetCompiledThemeFileAbsolutePath(string $sThemeId): string
|
||||
{
|
||||
return static::GetCompiledThemeFolderAbsolutePath($sThemeId).'main.css';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sThemeId
|
||||
*
|
||||
* @return string Absolute URL of the compiled file for the $sThemeId theme (Note: It doesn't mean that the theme is actually compiled)
|
||||
* @throws \Exception
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetCompiledThemeFileAbsoluteUrl(string $sThemeId): string
|
||||
{
|
||||
return utils::GetAbsoluteUrlModulesRoot().'branding/themes/'.$sThemeId.'/main.css';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the absolute URL for the current theme CSS file
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function GetCurrentThemeUrl()
|
||||
public static function GetCurrentThemeUrl(): string
|
||||
{
|
||||
try
|
||||
{
|
||||
try {
|
||||
// Try to compile theme defined in the configuration
|
||||
$sThemeId = static::GetCurrentThemeId();
|
||||
static::CompileTheme($sThemeId);
|
||||
// Note: In maintenance mode we should stick to the app theme (also we don't have access to many PHP classes, including the user preferences)
|
||||
$sThemeId = SetupUtils::IsInMaintenanceMode() ? static::GetApplicationThemeId() : static::GetCurrentUserThemeId();
|
||||
if (static::ShouldThemeSignatureCheckBeForced($sThemeId)) {
|
||||
static::CompileTheme($sThemeId);
|
||||
}
|
||||
}
|
||||
catch(CoreException $oCompileException)
|
||||
{
|
||||
catch (CoreException $oCompileException) {
|
||||
// Fallback on our default theme (should always be compilable) in case the previous theme doesn't exists
|
||||
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
|
||||
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
|
||||
$sThemeId = $aDefaultTheme['name'];
|
||||
$sDefaultThemeDirPath = static::GetCompiledThemeFolderAbsolutePath($sThemeId);
|
||||
|
||||
|
||||
// Create our theme dir if it doesn't exist (XML theme node removed, renamed etc..)
|
||||
if(!is_dir($sDefaultThemeDirPath))
|
||||
{
|
||||
if (!is_dir($sDefaultThemeDirPath)) {
|
||||
SetupUtils::builddir($sDefaultThemeDirPath);
|
||||
}
|
||||
|
||||
static::CompileTheme($sThemeId, false, "", $aDefaultTheme['parameters']);
|
||||
|
||||
if (static::ShouldThemeSignatureCheckBeForced($sThemeId)) {
|
||||
static::CompileTheme($sThemeId, false, "", $aDefaultTheme['parameters']);
|
||||
}
|
||||
}
|
||||
|
||||
// Return absolute url to theme compiled css
|
||||
return utils::GetAbsoluteUrlModulesRoot().'branding/themes/'.$sThemeId.'/main.css';
|
||||
return static::GetCompiledThemeFileAbsoluteUrl($sThemeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sThemeId
|
||||
*
|
||||
* @return bool True if the $sThemeId signature check -and possibly the compilation- should be forced (dev. environment, missing compiled file, ...)
|
||||
*/
|
||||
protected static function ShouldThemeSignatureCheckBeForced(string $sThemeId): bool
|
||||
{
|
||||
if (utils::IsDevelopmentEnvironment()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (false === file_exists(static::GetCompiledThemeFileAbsolutePath($sThemeId))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (true === utils::GetConfig()->Get('theme.force_signature_check_at_runtime')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,7 +263,7 @@ class ThemeHandler
|
||||
* @param boolean $bSetup
|
||||
* @param string $sSetupCompilationTimestamp : setup compilation timestamp in micro secunds
|
||||
* @param array|null $aThemeParameters Parameters (variables, imports, stylesheets) for the theme, if not passed, will be retrieved from compiled DM
|
||||
* @param array|null $aImportsPaths Paths where imports can be found. Must end with '/'
|
||||
* @param array|null $aImportsPaths Folder paths where imports can be found. Must end with '/'
|
||||
* @param string|null $sWorkingPath Path of the folder used during compilation. Must end with a '/'
|
||||
*
|
||||
* @throws \CoreException
|
||||
@@ -180,15 +308,21 @@ class ThemeHandler
|
||||
}
|
||||
}
|
||||
|
||||
$aThemeParametersWithVersion = self::CloneThemeParameterAndIncludeVersion($aThemeParameters, $sSetupCompilationTimestampInSecunds);
|
||||
$aThemeParametersWithVersion = self::CloneThemeParameterAndIncludeVersion($aThemeParameters, $sSetupCompilationTimestampInSecunds, $aImportsPaths);
|
||||
|
||||
clearstatcache();
|
||||
|
||||
// Loading files to import and stylesheet to compile, also getting most recent modification time on overall files
|
||||
$sTmpThemeScssContent = '';
|
||||
$oFindStylesheetObject = new FindStylesheetObject();
|
||||
if (isset($aThemeParameters['imports_utility'])) {
|
||||
foreach ($aThemeParameters['imports_utility'] as $sImport) {
|
||||
if (isset($aThemeParameters['variable_imports'])) {
|
||||
foreach ($aThemeParameters['variable_imports'] as $sImport) {
|
||||
static::FindStylesheetFile($sImport, $aImportsPaths, $oFindStylesheetObject);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($aThemeParameters['utility_imports'])) {
|
||||
foreach ($aThemeParameters['utility_imports'] as $sImport) {
|
||||
static::FindStylesheetFile($sImport, $aImportsPaths, $oFindStylesheetObject);
|
||||
}
|
||||
}
|
||||
@@ -262,11 +396,12 @@ CSS;
|
||||
static::$oCompileCSSService = new CompileCSSService();
|
||||
}
|
||||
//store it again to change $version with latest compiled time
|
||||
SetupLog::Info("Compiling theme $sThemeId...");
|
||||
$sTmpThemeCssContent = static::$oCompileCSSService->CompileCSSFromSASS($sTmpThemeScssContent, $aImportsPaths,
|
||||
$aThemeParametersWithVersion);
|
||||
SetupLog::Info("$sThemeId theme compilation done.");
|
||||
file_put_contents($sThemeFolderPath.'/theme-parameters.json', json_encode($aThemeParameters));
|
||||
file_put_contents($sThemeCssPath, $sSignatureComment.$sTmpThemeCssContent);
|
||||
SetupLog::Info("Theme $sThemeId file compiled.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -292,27 +427,29 @@ CSS;
|
||||
$aSignature = [
|
||||
'variables' => md5(json_encode($aThemeParameters['variables'])),
|
||||
'stylesheets' => [],
|
||||
'imports' => [],
|
||||
'images' => []
|
||||
'variable_imports' => [],
|
||||
'images' => [],
|
||||
'utility_imports' => []
|
||||
];
|
||||
|
||||
$oFindStylesheetObject = new FindStylesheetObject();
|
||||
|
||||
if (isset($aThemeParameters['imports_variable'])) {
|
||||
foreach ($aThemeParameters['imports_variable'] as $key => $sImport) {
|
||||
if (isset($aThemeParameters['variable_imports'])) {
|
||||
foreach ($aThemeParameters['variable_imports'] as $key => $sImport) {
|
||||
static::FindStylesheetFile($sImport, $aImportsPaths, $oFindStylesheetObject);
|
||||
$sFile = $oFindStylesheetObject->GetLastStylesheetFile();
|
||||
if (!empty($sFile)) {
|
||||
$aSignature['stylesheets'][$key] = md5_file($sFile);
|
||||
$aSignature['variable_imports'][$key] = md5_file($sFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($aThemeParameters['imports_utility'])) {
|
||||
foreach ($aThemeParameters['imports_utility'] as $key => $sImport) {
|
||||
|
||||
if (isset($aThemeParameters['utility_imports'])) {
|
||||
foreach ($aThemeParameters['utility_imports'] as $key => $sImport) {
|
||||
static::FindStylesheetFile($sImport, $aImportsPaths, $oFindStylesheetObject);
|
||||
$sFile = $oFindStylesheetObject->GetLastStylesheetFile();
|
||||
if (!empty($sFile)) {
|
||||
$aSignature['stylesheets'][$key] = md5_file($sFile);
|
||||
$aSignature['utility_imports'][$key] = md5_file($sFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -330,7 +467,7 @@ CSS;
|
||||
$aFiles = $oFindStylesheetObject->GetImportPaths();
|
||||
if (count($aFiles) !== 0) {
|
||||
foreach ($aFiles as $sFileURI => $sFilePath) {
|
||||
$aSignature['imports_utility'][$sFileURI] = md5_file($sFilePath);
|
||||
$aSignature['utility_imports'][$sFileURI] = md5_file($sFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -847,10 +984,11 @@ CSS;
|
||||
* Clone variable array and include $version with bSetupCompilationTimestamp value
|
||||
* @param $aThemeParameters
|
||||
* @param $bSetupCompilationTimestamp
|
||||
* @param $aImportsPaths
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function CloneThemeParameterAndIncludeVersion($aThemeParameters, $bSetupCompilationTimestamp)
|
||||
public static function CloneThemeParameterAndIncludeVersion($aThemeParameters, $bSetupCompilationTimestamp, $aImportsPaths)
|
||||
{
|
||||
$aThemeParametersVariable = [];
|
||||
if (array_key_exists('variables', $aThemeParameters))
|
||||
@@ -859,12 +997,13 @@ CSS;
|
||||
{
|
||||
$aThemeParametersVariable = array_merge([], $aThemeParameters['variables']);
|
||||
}
|
||||
}
|
||||
if (array_key_exists('imports_variable', $aThemeParameters))
|
||||
}
|
||||
|
||||
if (array_key_exists('variable_imports', $aThemeParameters))
|
||||
{
|
||||
if (is_array($aThemeParameters['imports_variable']))
|
||||
if (is_array($aThemeParameters['variable_imports']))
|
||||
{
|
||||
$aThemeParametersVariable = array_merge($aThemeParametersVariable, static::GetVariablesFromFile($aThemeParameters['imports_variable']));
|
||||
$aThemeParametersVariable = array_merge($aThemeParametersVariable, static::GetVariablesFromFile($aThemeParameters['variable_imports'], $aImportsPaths));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -874,20 +1013,30 @@ CSS;
|
||||
|
||||
/**
|
||||
* @param $aVariableFiles
|
||||
* @param $aImportsPaths
|
||||
*
|
||||
* @return array
|
||||
* @since 3.0.0 N°3593
|
||||
*/
|
||||
public static function GetVariablesFromFile($aVariableFiles){
|
||||
public static function GetVariablesFromFile($aVariableFiles, $aImportsPaths){
|
||||
$aVariablesResults = [];
|
||||
foreach ($aVariableFiles as $sVariableFile)
|
||||
{
|
||||
$sFileContent = file_get_contents(APPROOT.'env-'.utils::GetCurrentEnvironment().'/'.$sVariableFile);
|
||||
$aVariableMatches = [];
|
||||
|
||||
preg_match_all( '/\$(.*?):(.*?);/', $sFileContent,$aVariableMatches);
|
||||
$aVariableMatches = array_combine( $aVariableMatches[1], array_map( function($sVariableValue) { return ltrim($sVariableValue); }, $aVariableMatches[2] ) );
|
||||
$aVariablesResults = array_merge($aVariablesResults, $aVariableMatches);
|
||||
foreach($aImportsPaths as $sPath) {
|
||||
$sFilePath = $sPath.'/'.$sVariableFile;
|
||||
$sImportedFile = realpath($sFilePath);
|
||||
if ($sImportedFile !== false) {
|
||||
$sFileContent = file_get_contents($sImportedFile);
|
||||
$aVariableMatches = [];
|
||||
|
||||
preg_match_all('/\s*\$(.*?)\s*:\s*[\"\']{0,1}(.*?)[\"\']{0,1}\s*[;!]/', $sFileContent, $aVariableMatches);
|
||||
$aVariableMatches = array_combine($aVariableMatches[1], array_map(function ($sVariableValue) {
|
||||
return $sVariableValue;
|
||||
}, $aVariableMatches[2]));
|
||||
$aVariablesResults = array_merge($aVariablesResults, $aVariableMatches);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
array_map( function($sVariableValue) { return ltrim($sVariableValue); }, $aVariablesResults );
|
||||
return $aVariablesResults;
|
||||
|
||||
35
application/themehandlerservice.class.inc.php
Normal file
35
application/themehandlerservice.class.inc.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-2020 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class ThemeHandlerService : used to ease testing MFCompiler::CompileThemes class via mocks
|
||||
*
|
||||
* @author Olivier DAIN <olivier.dain@combodo.com>
|
||||
* @since 3.0.0 N°2982
|
||||
*/
|
||||
class ThemeHandlerService
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function CompileTheme($sThemeId, $bSetup = false, $sSetupCompilationTimestamp="", $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null){
|
||||
return ThemeHandler::CompileTheme($sThemeId, $bSetup, $sSetupCompilationTimestamp="", $aThemeParameters, $aImportsPaths, $sWorkingPath);
|
||||
}
|
||||
}
|
||||
@@ -160,7 +160,8 @@ class UIExtKeyWidget
|
||||
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
|
||||
$sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
|
||||
|
||||
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey ibo-input-wrapper ibo-input-select-wrapper--with-buttons\" data-attcode=\"".$this->sAttCode."\" data-validation=\"untouched\">";
|
||||
|
||||
|
||||
$sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL());
|
||||
if ($this->bSearchMode) {
|
||||
$sWizHelper = 'null';
|
||||
@@ -183,8 +184,12 @@ class UIExtKeyWidget
|
||||
$bDoSearch = !utils::IsHighCardinality($this->sTargetClass);
|
||||
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
|
||||
|
||||
$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\">";
|
||||
|
||||
// We just need to compare the number of entries with MaxComboLength, so no need to get the real count.
|
||||
if (!$oAllowedValues->CountExceeds($iMaxComboLength)) {
|
||||
if (!$bIsAutocomplete) {
|
||||
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
|
||||
$sHelpText = ''; //$this->oAttDef->GetHelpOnEdition();
|
||||
//$sHTMLValue .= "<div class=\"field_select_wrapper\">\n";
|
||||
@@ -295,12 +300,12 @@ EOF
|
||||
EOF
|
||||
);
|
||||
$sHTMLValue .= "<div class=\"ibo-input-select--action-buttons\">";
|
||||
$sHTMLValue .= " <div class=\"ibo-input-select--action-button ibo-input-select--action-button--clear ibo-is-hidden\" id=\"mini_clear_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Clear();\" data-tooltip-content='".Dict::S('UI:Button:Clear')."''><i class=\"fas fa-times\"></i></div>";
|
||||
$sHTMLValue .= " <div class=\"ibo-input-select--action-button ibo-input-select--action-button--clear ibo-is-hidden\" id=\"mini_clear_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Clear();\" data-tooltip-content='".Dict::S('UI:Button:Clear')."'><i class=\"fas fa-times\"></i></div>";
|
||||
}
|
||||
if ($bCreate && $bExtensions) {
|
||||
$sCallbackName = (MetaModel::IsAbstract($this->sTargetClass)) ? 'SelectObjectClass' : 'CreateObject';
|
||||
|
||||
$sHTMLValue .= "<div class=\"ibo-input-select--action-button ibo-input-select--action-button--create\" id=\"mini_add_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\" data-tooltip-content='".Dict::S('UI:Button:Create')."''><i class=\"fas fa-plus\"></i></div>";
|
||||
$sHTMLValue .= "<div class=\"ibo-input-select--action-button ibo-input-select--action-button--create\" id=\"mini_add_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\" data-tooltip-content='".Dict::S('UI:Button:Create')."'><i class=\"fas fa-plus\"></i></div>";
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
if ($('#ajax_{$this->iId}').length == 0)
|
||||
@@ -311,7 +316,7 @@ JS
|
||||
);
|
||||
}
|
||||
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false) {
|
||||
$sHTMLValue .= "<div class=\"ibo-input-select--action-button ibo-input-select--action-button--hierarchy\" id=\"mini_tree_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\" data-tooltip-content='".Dict::S('UI:Button:SearchInHierarchy')."''><i class=\"fas fa-sitemap\"></i></div>";
|
||||
$sHTMLValue .= "<div class=\"ibo-input-select--action-button ibo-input-select--action-button--hierarchy\" id=\"mini_tree_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\" data-tooltip-content='".Dict::S('UI:Button:SearchInHierarchy')."'><i class=\"fas fa-sitemap\"></i></div>";
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
if ($('#ac_tree_{$this->iId}').length == 0)
|
||||
@@ -322,7 +327,7 @@ JS
|
||||
);
|
||||
}
|
||||
if ($oAllowedValues->CountExceeds($iMaxComboLength)) {
|
||||
$sHTMLValue .= " <div class=\"ibo-input-select--action-button ibo-input-select--action-button--search\" id=\"mini_search_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Search();\" data-tooltip-content='".Dict::S('UI:Button:Search')."''><i class=\"fas fa-search\"></i></div>";
|
||||
$sHTMLValue .= " <div class=\"ibo-input-select--action-button ibo-input-select--action-button--search\" id=\"mini_search_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Search();\" data-tooltip-content='".Dict::S('UI:Button:Search')."'><i class=\"fas fa-search\"></i></div>";
|
||||
}
|
||||
$sHTMLValue .= "</div>";
|
||||
$sHTMLValue .= "</div>";
|
||||
@@ -673,11 +678,9 @@ JS
|
||||
$sEmptyList = Dict::S('UI:Message:EmptyList:UseSearchForm');
|
||||
$oPage->add(<<<HTML
|
||||
<form id="fr_{$this->iId}" OnSubmit="return oACWidget_{$this->iId}.DoOk();">
|
||||
<div id="dr_{$this->iId}" style="vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;">
|
||||
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
|
||||
<div id="dr_{$this->iId}">
|
||||
<div><p>{$sEmptyList}</p></div>
|
||||
</div>
|
||||
<input type="button" id="btn_cancel_{$this->iId}" value="{$sCancel}" onClick="$('#ac_dlg_{$this->iId}').dialog('close');">
|
||||
<input type="button" id="btn_ok_{$this->iId}_results" value="{$sOK}" onClick="oACWidget_{$this->iId}.DoOk();">
|
||||
<input type="hidden" id="count_{$this->iId}_results" value="0">
|
||||
</form>
|
||||
</div></div>
|
||||
@@ -686,7 +689,27 @@ HTML
|
||||
|
||||
$sDialogTitle = addslashes($sTitle);
|
||||
$oPage->add_ready_script(<<<JS
|
||||
$('#ac_dlg_{$this->iId}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, title: '$sDialogTitle', resizeStop: oACWidget_{$this->iId}.UpdateSizes, close: oACWidget_{$this->iId}.OnClose });
|
||||
$('#ac_dlg_{$this->iId}').dialog({
|
||||
width: $(window).width()*0.8,
|
||||
height: $(window).height()*0.8,
|
||||
autoOpen: false,
|
||||
modal: true,
|
||||
title: '$sDialogTitle',
|
||||
resizeStop: oACWidget_{$this->iId}.UpdateSizes,
|
||||
close: oACWidget_{$this->iId}.OnClose,
|
||||
buttons: [
|
||||
{ text: "$sCancel",
|
||||
class: "ibo-is-alternative ibo-is-neutral",
|
||||
click: function() {
|
||||
$(this).dialog('close');
|
||||
} },
|
||||
{ text: "$sOK",
|
||||
class: "ibo-is-regular ibo-is-primary",
|
||||
click: function() {
|
||||
oACWidget_{$this->iId}.DoOk();
|
||||
} },
|
||||
],
|
||||
});
|
||||
$('#fs_{$this->iId}').on('submit.uiAutocomplete', oACWidget_{$this->iId}.DoSearchObjects);
|
||||
$('#dc_{$this->iId}').resize(oACWidget_{$this->iId}.UpdateSizes);
|
||||
JS
|
||||
@@ -977,8 +1000,8 @@ JS
|
||||
$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("<input type=\"button\" id=\"btn_cancel_{$this->iId}\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_tree_{$this->iId}').dialog('close');\"> ");
|
||||
$oPage->add("<input type=\"button\" id=\"btn_ok_{$this->iId}\" value=\"".Dict::S('UI:Button:Ok')."\" onClick=\"oACWidget_{$this->iId}.DoHKOk();\">");
|
||||
$oPage->add("<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>');
|
||||
|
||||
|
||||
@@ -337,11 +337,10 @@ class UILinksWidgetDirect
|
||||
|
||||
$oPage->add(<<<HTML
|
||||
<form id="ObjectsAddForm_{$this->sInputid}">
|
||||
<div id="SearchResultsToAdd_{$this->sInputid}" style="vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;">
|
||||
<div id="SearchResultsToAdd_{$this->sInputid}">
|
||||
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
|
||||
</div>
|
||||
<input type="hidden" id="count_{$this->sInputid}" value="0"/>
|
||||
<button type="button" class="cancel">{$sCancel}</button> <button type="button" class="ok" disabled="disabled">{$sAdd}</button>
|
||||
</form>
|
||||
HTML
|
||||
);
|
||||
|
||||
@@ -64,16 +64,42 @@ class UISearchFormForeignKeys
|
||||
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
|
||||
</div>
|
||||
<input type="hidden" id="count_{$this->m_iInputId}" value="0"/>
|
||||
<input type="button" value="{$sCancel}" onClick="$('#dlg_{$this->m_iInputId}').dialog('close');">
|
||||
<input id="btn_ok_add_{$this->m_iInputId}" disabled="disabled" type="button" onclick="return oForeignKeysWidget{$this->m_iInputId}.DoAddObjects(this.id);" value="{$sAdd}">
|
||||
</form>
|
||||
HTML
|
||||
);
|
||||
|
||||
$oPage->add_ready_script("$('#dlg_{$this->m_iInputId}').dialog({ width: $(window).width()*0.8, height: $(window).height()*0.8, autoOpen: false, modal: true, resizeStop: oForeignKeysWidget{$this->m_iInputId}.UpdateSizes });");
|
||||
$oPage->add_ready_script("$('#dlg_{$this->m_iInputId}').dialog('option', {title:'$sTitle'});");
|
||||
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_iInputId} form').on('submit.uilinksWizard', oForeignKeysWidget{$this->m_iInputId}.SearchObjectsToAdd);");
|
||||
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_iInputId}').resize(oForeignKeysWidget{$this->m_iInputId}.UpdateSizes);");
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
$('#dlg_{$this->m_iInputId}').dialog({
|
||||
width: $(window).width()*0.8,
|
||||
height: $(window).height()*0.8,
|
||||
autoOpen: false,
|
||||
modal: true,
|
||||
resizeStop: oForeignKeysWidget{$this->m_iInputId}.UpdateSizes,
|
||||
buttons: [
|
||||
{
|
||||
text: Dict.S('UI:Button:Cancel'),
|
||||
class: "cancel ibo-is-alternative ibo-is-neutral",
|
||||
click: function() {
|
||||
$('#dlg_{$this->m_iInputId}').dialog('close');
|
||||
}
|
||||
},
|
||||
{
|
||||
text: Dict.S('UI:Button:Add'),
|
||||
id: 'btn_ok_{$this->m_iInputId}',
|
||||
class: "ok ibo-is-regular ibo-is-primary",
|
||||
click: function() {
|
||||
oForeignKeysWidget{$this->m_iInputId}.DoAddObjects(this.id);
|
||||
}
|
||||
},
|
||||
],
|
||||
|
||||
});
|
||||
$('#dlg_{$this->m_iInputId}').dialog('option', {title:'$sTitle'});
|
||||
$('#SearchFormToAdd_{$this->m_iInputId} form').on('submit.uilinksWizard', oForeignKeysWidget{$this->m_iInputId}.SearchObjectsToAdd);
|
||||
$('#SearchFormToAdd_{$this->m_iInputId}').resize(oForeignKeysWidget{$this->m_iInputId}.UpdateSizes);
|
||||
JS
|
||||
);
|
||||
}
|
||||
|
||||
public function GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter)
|
||||
|
||||
@@ -270,23 +270,20 @@ $sJSHandlerCode
|
||||
$aFields = array(); // reset
|
||||
foreach(MetaModel::ListAttributeDefs($this->m_sClass) as $sAttCode=>$oAttDef)
|
||||
{
|
||||
$iOptions = (isset($aStates[$this->m_sTargetState]['attribute_list'][$sAttCode])) ? $aStates[$this->m_sTargetState]['attribute_list'][$sAttCode] : 0;
|
||||
if ( ($sStateAttCode != $sAttCode) &&
|
||||
(!$oAttDef->IsExternalField()) &&
|
||||
(($iOptions & (OPT_ATT_HIDDEN | OPT_ATT_READONLY)) == 0) &&
|
||||
(!isset($aFieldsDone[$sAttCode])) )
|
||||
|
||||
{
|
||||
// 'State', external fields, read-only and hidden fields
|
||||
$iOptions = (isset($aStates[$this->m_sTargetState]['attribute_list'][$sAttCode])) ? $aStates[$this->m_sTargetState]['attribute_list'][$sAttCode] : 0;
|
||||
if (($sStateAttCode != $sAttCode) &&
|
||||
(!$oAttDef->IsExternalField()) &&
|
||||
($oAttDef->IsWritable()) &&
|
||||
(($iOptions & (OPT_ATT_HIDDEN | OPT_ATT_READONLY)) == 0) &&
|
||||
(!isset($aFieldsDone[$sAttCode]))) {
|
||||
// 'State', external fields, read-only (both because of the flags or the attribute type) and hidden fields
|
||||
// and fields that are already listed in the wizard
|
||||
// are removed from the 'optional' part of the wizard
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
$aPrerequisites = $oAttDef->GetPrerequisiteAttributes();
|
||||
$aFields[$sAttCode] = array();
|
||||
foreach($aPrerequisites as $sCode)
|
||||
{
|
||||
if (!isset($aFieldsDone[$sCode]))
|
||||
{
|
||||
foreach ($aPrerequisites as $sCode) {
|
||||
if (!isset($aFieldsDone[$sCode])) {
|
||||
// retain only the dependencies that were not covered
|
||||
// in the 'mandatory' part of the wizard
|
||||
$aFields[$sAttCode][$sCode] = '';
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\iUIBlock;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
|
||||
use ScssPhp\ScssPhp\Compiler;
|
||||
|
||||
@@ -90,12 +91,31 @@ class utils
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_VARIABLE_NAME = 'variable_name';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_RAW_DATA = 'raw_data';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
* @used-by static::GetMentionedObjectsFromText
|
||||
*/
|
||||
public const ENUM_TEXT_FORMAT_PLAIN = 'text';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
* @used-by static::GetMentionedObjectsFromText
|
||||
*/
|
||||
public const ENUM_TEXT_FORMAT_HTML = 'html';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
* @used-by static::GetMentionedObjectsFromText
|
||||
*/
|
||||
public const ENUM_TEXT_FORMAT_MARKDOWN = 'markdown';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
@@ -518,14 +538,12 @@ class utils
|
||||
*/
|
||||
public static function ReadMultipleSelection($oFullSetFilter)
|
||||
{
|
||||
$aSelectedObj = utils::ReadParam('selectObject', array());
|
||||
$aSelectedObj = utils::ReadParam('selectObject[]', array());
|
||||
$sSelectionMode = utils::ReadParam('selectionMode', '');
|
||||
if ($sSelectionMode != '')
|
||||
{
|
||||
if ($sSelectionMode != '') {
|
||||
// Paginated selection
|
||||
$aExceptions = utils::ReadParam('storedSelection', array());
|
||||
if ($sSelectionMode == 'positive')
|
||||
{
|
||||
if ($sSelectionMode == 'positive') {
|
||||
// Only the explicitely listed items are selected
|
||||
$aSelectedObj = $aExceptions;
|
||||
}
|
||||
@@ -638,48 +656,93 @@ class utils
|
||||
|
||||
public static function ReadFromFile($sFileName)
|
||||
{
|
||||
if (!file_exists($sFileName)) return false;
|
||||
if (!file_exists($sFileName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return file_get_contents($sFileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to convert a value expressed in a 'user friendly format'
|
||||
* as in php.ini, e.g. 256k, 2M, 1G etc. Into a number of bytes
|
||||
* @param mixed $value The value as read from php.ini
|
||||
* @return number
|
||||
* @param mixed $value The value as read from php.ini (eg 256k, 2M, 1G etc.)
|
||||
*
|
||||
* @return int conversion to number of bytes
|
||||
*
|
||||
* @since 2.7.5 3.0.0 convert to int numeric values
|
||||
*
|
||||
* @link https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes Shorthand bytes value reference in PHP.net FAQ
|
||||
*/
|
||||
public static function ConvertToBytes( $value )
|
||||
public static function ConvertToBytes($value)
|
||||
{
|
||||
$iReturn = $value;
|
||||
if ( !is_numeric( $value ) )
|
||||
{
|
||||
$iLength = strlen( $value );
|
||||
$iReturn = substr( $value, 0, $iLength - 1 );
|
||||
$sUnit = strtoupper( substr( $value, $iLength - 1 ) );
|
||||
switch ( $sUnit )
|
||||
{
|
||||
case 'G':
|
||||
$iReturn *= 1024;
|
||||
case 'M':
|
||||
$iReturn *= 1024;
|
||||
case 'K':
|
||||
$iReturn *= 1024;
|
||||
}
|
||||
}
|
||||
return $iReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the memory limit is at least what is required
|
||||
*
|
||||
* @param int $memoryLimit set limit in bytes
|
||||
* @param int $requiredLimit required limit in bytes
|
||||
* @return bool
|
||||
*/
|
||||
public static function IsMemoryLimitOk($memoryLimit, $requiredLimit)
|
||||
{
|
||||
return ($memoryLimit >= $requiredLimit) || ($memoryLimit == -1);
|
||||
}
|
||||
if (!is_numeric($value)) {
|
||||
$iLength = strlen($value);
|
||||
$iReturn = substr($value, 0, $iLength - 1);
|
||||
$sUnit = strtoupper(substr($value, $iLength - 1));
|
||||
switch ($sUnit) {
|
||||
case 'G':
|
||||
$iReturn *= 1024;
|
||||
case 'M':
|
||||
$iReturn *= 1024;
|
||||
case 'K':
|
||||
$iReturn *= 1024;
|
||||
}
|
||||
} else {
|
||||
$iReturn = (int)$value;
|
||||
}
|
||||
|
||||
return $iReturn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the memory limit is at least what is required
|
||||
*
|
||||
* @param int $iMemoryLimit set limit in bytes, use {@link utils::ConvertToBytes()} to convert current php.ini value
|
||||
* @param int $iRequiredLimit required limit in bytes
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function IsMemoryLimitOk($iMemoryLimit, $iRequiredLimit)
|
||||
{
|
||||
if ($iMemoryLimit === -1) {
|
||||
// -1 means : no limit (see https://www.php.net/manual/fr/ini.core.php#ini.memory-limit)
|
||||
return true;
|
||||
}
|
||||
|
||||
return ($iMemoryLimit >= $iRequiredLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set memory_limit to required value
|
||||
*
|
||||
* @param string $sRequiredLimit required limit, for example '512M'
|
||||
*
|
||||
* @return bool|null null if nothing was done, true if modifying memory_limit was successful, false otherwise
|
||||
*
|
||||
* @uses utils::ConvertToBytes()
|
||||
* @uses \ini_get('memory_limit')
|
||||
* @uses \ini_set()
|
||||
* @uses utils::ConvertToBytes()
|
||||
*
|
||||
* @since 2.7.5 N°3806
|
||||
*/
|
||||
public static function SetMinMemoryLimit($sRequiredLimit)
|
||||
{
|
||||
$iRequiredLimit = static::ConvertToBytes($sRequiredLimit);
|
||||
$sMemoryLimit = trim(ini_get('memory_limit'));
|
||||
if (empty($sMemoryLimit)) {
|
||||
// On some PHP installations, memory_limit does not exist as a PHP setting!
|
||||
// (encountered on a 5.2.0 under Windows)
|
||||
// In that case, ini_set will not work
|
||||
return false;
|
||||
}
|
||||
$iMemoryLimit = static::ConvertToBytes($sMemoryLimit);
|
||||
|
||||
if (static::IsMemoryLimitOk($iMemoryLimit, $iRequiredLimit)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ini_set('memory_limit', $iRequiredLimit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a value into a more friendly format (KB, MB, GB, TB) instead a juste a Bytes amount.
|
||||
@@ -808,7 +871,7 @@ class utils
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @since 2.7.0 N°2478 always call {@link MetaModel::GetConfig} first, cache is only set when loading from disk
|
||||
* @since 2.7.0 N°2478 this method will now always call {@link MetaModel::GetConfig} first, and cache in this class is only set when loading from disk
|
||||
*/
|
||||
public static function GetConfig()
|
||||
{
|
||||
@@ -1291,12 +1354,15 @@ class utils
|
||||
*/
|
||||
public static function GetPopupMenuItems($oPage, $iMenuId, $param, &$aActions, $sTableId = null, $sDataTableId = null)
|
||||
{
|
||||
$oPage->AddUiBlock(static::GetPopupMenuItemsBlock($iMenuId, $param, $aActions, $sDataTableId));
|
||||
$oBlock = new UIContentBlock();
|
||||
static::GetPopupMenuItemsBlock($oBlock, $iMenuId, $param, $aActions, $sDataTableId);
|
||||
$oPage->AddUiBlock($oBlock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge standard menu items with plugin provided menus items
|
||||
*
|
||||
* @param \Combodo\iTop\Application\UI\Base\iUIBlock $oContainerBlock The UIBlock containing the menu
|
||||
* @param int $iMenuId
|
||||
* @param \DBObjectSet $param
|
||||
* @param array $aActions
|
||||
@@ -1306,9 +1372,8 @@ class utils
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public static function GetPopupMenuItemsBlock($iMenuId, $param, &$aActions, $sDataTableId = null)
|
||||
public static function GetPopupMenuItemsBlock(iUIBlock &$oContainerBlock, $iMenuId, $param, &$aActions, $sDataTableId = null)
|
||||
{
|
||||
$oBlock = new UIContentBlock();
|
||||
// 1st - add standard built-in menu items
|
||||
//
|
||||
switch($iMenuId)
|
||||
@@ -1322,9 +1387,9 @@ class utils
|
||||
$sOQL = addslashes($param->GetFilter()->ToOQL(true));
|
||||
$sFilter = urlencode($param->GetFilter()->serialize());
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
|
||||
$oBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
|
||||
$aResult = array();
|
||||
if (strlen($sUrl) < SERVER_MAX_URL_LENGTH)
|
||||
@@ -1357,12 +1422,12 @@ class utils
|
||||
$oObj = $param;
|
||||
$sOQL = "SELECT ".get_class($oObj)." WHERE id=".$oObj->GetKey();
|
||||
$sUrl = ApplicationContext::MakeObjectUrl(get_class($oObj), $oObj->GetKey());
|
||||
$oBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
$oBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
|
||||
$aResult = array(
|
||||
new SeparatorPopupMenuItem(),
|
||||
@@ -1430,13 +1495,11 @@ class utils
|
||||
|
||||
foreach($oMenuItem->GetLinkedScripts() as $sLinkedScript)
|
||||
{
|
||||
$oBlock->AddJsFileRelPath($sLinkedScript);
|
||||
$oContainerBlock->AddJsFileRelPath($sLinkedScript);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $oBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2567,7 +2630,7 @@ class utils
|
||||
$aDefaultConf = array(
|
||||
'language'=> $sLanguage,
|
||||
'contentsLanguage' => $sLanguage,
|
||||
'extraPlugins' => 'disabler,codesnippet,mentions',
|
||||
'extraPlugins' => 'disabler,codesnippet,mentions,objectshortcut',
|
||||
);
|
||||
|
||||
// Mentions
|
||||
@@ -2684,15 +2747,13 @@ HTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return keyboard shortcuts config as an array
|
||||
*
|
||||
* @return array
|
||||
* @return array All keyboard shortcuts config as an array
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetKeyboardShortcutPref(): array
|
||||
public static function GetAllKeyboardShortcutsPrefs(): array
|
||||
{
|
||||
$aResultPref = [];
|
||||
$aShortcutPrefs = appUserPreferences::GetPref('keyboard_shortcuts', []);
|
||||
@@ -2703,13 +2764,48 @@ HTML;
|
||||
$sTriggeredElement = $cShortcutPlugin::GetShortcutTriggeredElementSelector();
|
||||
foreach ($cShortcutPlugin::GetShortcutKeys() as $aShortcutKey) {
|
||||
$sKey = isset($aShortcutPrefs[$aShortcutKey['id']]) ? $aShortcutPrefs[$aShortcutKey['id']] : $aShortcutKey['key'];
|
||||
$aResultPref[$aShortcutKey['id']] = ['key' => $sKey, 'label' => $aShortcutKey['label'], 'event' => $aShortcutKey['event'], 'triggered_element_selector' => $sTriggeredElement];
|
||||
|
||||
// Format key for display
|
||||
$aKeyParts = explode('+', $sKey);
|
||||
$aFormattedKeyParts = [];
|
||||
foreach ($aKeyParts as $sKeyPart) {
|
||||
$aFormattedKeyParts[] = ucfirst(trim($sKeyPart));
|
||||
}
|
||||
$sFormattedKey = implode(' + ', $aFormattedKeyParts);
|
||||
|
||||
$aResultPref[$aShortcutKey['id']] = [
|
||||
'key' => $sKey,
|
||||
'key_for_display' => $sFormattedKey,
|
||||
'label' => $aShortcutKey['label'],
|
||||
'event' => $aShortcutKey['event'],
|
||||
'triggered_element_selector' => $sTriggeredElement,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $aResultPref;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sShortcutId
|
||||
*
|
||||
* @return array The properties of the $sShortcutId shorcut
|
||||
* @throws \Exception
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MySQLException
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetKeyboardShortcutPref(string $sShortcutId): array
|
||||
{
|
||||
$aPrefs = static::GetAllKeyboardShortcutsPrefs();
|
||||
if (false === array_key_exists($sShortcutId, $aPrefs)) {
|
||||
throw new Exception('No shortcut identified as "'.$sShortcutId.'" is currently handled by the application.');
|
||||
}
|
||||
|
||||
return $aPrefs[$sShortcutId];
|
||||
}
|
||||
|
||||
//----------------------------------------------
|
||||
// Environment helpers
|
||||
//----------------------------------------------
|
||||
@@ -2721,6 +2817,12 @@ HTML;
|
||||
*/
|
||||
public static function IsDevelopmentEnvironment()
|
||||
{
|
||||
if (! defined('ITOP_REVISION')) {
|
||||
//defensive behaviour: by default we are not in dev environment
|
||||
//can happen even in production (unattended install for example) or with exotic use of iTop
|
||||
return false;
|
||||
}
|
||||
|
||||
return ITOP_REVISION === 'svn';
|
||||
}
|
||||
|
||||
@@ -2830,4 +2932,65 @@ HTML;
|
||||
|
||||
return $sAcronym;
|
||||
}
|
||||
|
||||
//----------------------------------------------
|
||||
// Text manipulation
|
||||
//----------------------------------------------
|
||||
|
||||
/**
|
||||
* Note: Only works for backoffice URLs for now
|
||||
*
|
||||
* @param string $sText Text containing the mentioned objects to be found
|
||||
* @param string $sFormat {@uses static::ENUM_TEXT_FORMAT_HTML, ...}
|
||||
*
|
||||
* @return array Array of object classes / IDs for the ones found in $sText
|
||||
*
|
||||
* [
|
||||
* 'ClassA' => ['ID1', 'ID2', ...],
|
||||
* 'ClassB' => ['ID3'],
|
||||
* ]
|
||||
*
|
||||
* @throws \Exception
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public static function GetMentionedObjectsFromText(string $sText, string $sFormat = self::ENUM_TEXT_FORMAT_HTML): array
|
||||
{
|
||||
// First transform text so it can be parsed
|
||||
switch ($sFormat) {
|
||||
case static::ENUM_TEXT_FORMAT_HTML:
|
||||
$sText = static::HtmlToText($sText);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Don't transform it
|
||||
break;
|
||||
}
|
||||
|
||||
// Then parse text to find objects
|
||||
$aMentionedObjects = array();
|
||||
$aMentionMatches = array();
|
||||
|
||||
// Note: As the sanitizer (or CKEditor autocomplete plugin? 🤔) removes data-* attributes from the hyperlink,
|
||||
// - we can't use the following (simpler) regexp that only checks data attributes on hyperlinks, which would have worked for hyperlinks pointing to any GUIs: '/<a\s*([^>]*)data-object-class="([^"]*)"\s*data-object-id="([^"]*)">/i'
|
||||
// - instead we use a regexp to match the following pattern '[Some object label](<APP_ROOT_URL>...&class=<OBJECT_CLASS>&id=<OBJECT_ID>...)' which only works for the backoffice
|
||||
// If we change the sanitizer, we might want to switch to the other regexp as it's universal and easier to read
|
||||
$sAppRootUrlForRegExp = addcslashes(utils::GetAbsoluteUrlAppRoot(), '/&');
|
||||
preg_match_all("/\[([^\]]*)\]\({$sAppRootUrlForRegExp}[^\)]*\&class=([^\)\&]*)\&id=([\d]*)[^\)]*\)/i", $sText, $aMentionMatches);
|
||||
|
||||
foreach ($aMentionMatches[0] as $iMatchIdx => $sCompleteMatch) {
|
||||
$sMatchedClass = $aMentionMatches[2][$iMatchIdx];
|
||||
$sMatchedId = $aMentionMatches[3][$iMatchIdx];
|
||||
|
||||
// Prepare array for matched class if not already present
|
||||
if (!array_key_exists($sMatchedClass, $aMentionedObjects)) {
|
||||
$aMentionedObjects[$sMatchedClass] = array();
|
||||
}
|
||||
// Add matched ID if not already there
|
||||
if (!in_array($sMatchedId, $aMentionedObjects[$sMatchedClass])) {
|
||||
$aMentionedObjects[$sMatchedClass][] = $sMatchedId;
|
||||
}
|
||||
}
|
||||
|
||||
return $aMentionedObjects;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/WebPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/WebPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/WebPage.php, now loadable using autoloader');
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/XMLPage.php
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/XMLPage.php, now loadable using autoloader
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
*/
|
||||
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/XMLPage.php, now loadable using autoloader');
|
||||
@@ -50,8 +50,7 @@ if (function_exists('microtime')) {
|
||||
$fItopStarted = 1000 * time();
|
||||
}
|
||||
|
||||
if (! isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false)
|
||||
{
|
||||
if (!isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false) {
|
||||
require_once APPROOT.'/lib/autoload.php';
|
||||
}
|
||||
|
||||
@@ -60,8 +59,7 @@ if (! isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false
|
||||
//
|
||||
|
||||
// Use 'maintenance' parameter to bypass maintenance mode
|
||||
if (!isset($bBypassMaintenance))
|
||||
{
|
||||
if (!isset($bBypassMaintenance)) {
|
||||
$bBypassMaintenance = isset($_REQUEST['maintenance']) ? boolval($_REQUEST['maintenance']) : false;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"ext-soap": "*",
|
||||
"combodo/tcpdf": "6.3.5",
|
||||
"nikic/php-parser": "^3.1",
|
||||
"pear/archive_tar": "1.4.10",
|
||||
"pear/archive_tar": "1.4.13",
|
||||
"pelago/emogrifier": "2.1.0",
|
||||
"scssphp/scssphp": "1.0.6",
|
||||
"swiftmailer/swiftmailer": "5.4.12",
|
||||
|
||||
59
composer.lock
generated
59
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "27af144ea2acf2c138f587052a4ceddc",
|
||||
"content-hash": "62e394b1ef30b4e716e3e3e519de11dd",
|
||||
"packages": [
|
||||
{
|
||||
"name": "combodo/tcpdf",
|
||||
@@ -168,16 +168,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pear/archive_tar",
|
||||
"version": "1.4.10",
|
||||
"version": "1.4.13",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pear/Archive_Tar.git",
|
||||
"reference": "bbb4f10f71a1da2715ec6d9a683f4f23c507a49b"
|
||||
"reference": "2b87b41178cc6d4ad3cba678a46a1cae49786011"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pear/Archive_Tar/zipball/bbb4f10f71a1da2715ec6d9a683f4f23c507a49b",
|
||||
"reference": "bbb4f10f71a1da2715ec6d9a683f4f23c507a49b",
|
||||
"url": "https://api.github.com/repos/pear/Archive_Tar/zipball/19bb8e95490d3e3ad92fcac95500ca80bdcc7495",
|
||||
"reference": "19bb8e95490d3e3ad92fcac95500ca80bdcc7495",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -230,7 +230,21 @@
|
||||
"archive",
|
||||
"tar"
|
||||
],
|
||||
"time": "2020-09-15T14:13:23+00:00"
|
||||
"support": {
|
||||
"issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Archive_Tar",
|
||||
"source": "https://github.com/pear/Archive_Tar"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/mrook",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.patreon.com/michielrook",
|
||||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2021-01-18T19:32:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pear/console_getopt",
|
||||
@@ -277,6 +291,10 @@
|
||||
}
|
||||
],
|
||||
"description": "More info available on: http://pear.php.net/package/Console_Getopt",
|
||||
"support": {
|
||||
"issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Console_Getopt",
|
||||
"source": "https://github.com/pear/Console_Getopt"
|
||||
},
|
||||
"time": "2019-11-20T18:27:48+00:00"
|
||||
},
|
||||
{
|
||||
@@ -321,27 +339,31 @@
|
||||
}
|
||||
],
|
||||
"description": "Minimal set of PEAR core files to be used as composer dependency",
|
||||
"support": {
|
||||
"issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=PEAR",
|
||||
"source": "https://github.com/pear/pear-core-minimal"
|
||||
},
|
||||
"time": "2019-11-19T19:00:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pear/pear_exception",
|
||||
"version": "v1.0.1",
|
||||
"version": "v1.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pear/PEAR_Exception.git",
|
||||
"reference": "dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7"
|
||||
"reference": "b14fbe2ddb0b9f94f5b24cf08783d599f776fff0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7",
|
||||
"reference": "dbb42a5a0e45f3adcf99babfb2a1ba77b8ac36a7",
|
||||
"url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/b14fbe2ddb0b9f94f5b24cf08783d599f776fff0",
|
||||
"reference": "b14fbe2ddb0b9f94f5b24cf08783d599f776fff0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=4.4.0"
|
||||
"php": ">=5.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*"
|
||||
"phpunit/phpunit": "<9"
|
||||
},
|
||||
"type": "class",
|
||||
"extra": {
|
||||
@@ -376,7 +398,11 @@
|
||||
"keywords": [
|
||||
"exception"
|
||||
],
|
||||
"time": "2019-12-10T10:24:42+00:00"
|
||||
"support": {
|
||||
"issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=PEAR_Exception",
|
||||
"source": "https://github.com/pear/PEAR_Exception"
|
||||
},
|
||||
"time": "2021-03-21T15:43:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pelago/emogrifier",
|
||||
@@ -2578,7 +2604,7 @@
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": ">=5.6.0",
|
||||
"php": ">=7.1.3",
|
||||
"ext-ctype": "*",
|
||||
"ext-dom": "*",
|
||||
"ext-gd": "*",
|
||||
@@ -2589,6 +2615,7 @@
|
||||
},
|
||||
"platform-dev": [],
|
||||
"platform-overrides": {
|
||||
"php": "5.6.0"
|
||||
}
|
||||
"php": "7.2.0"
|
||||
},
|
||||
"plugin-api-version": "2.0.0"
|
||||
}
|
||||
|
||||
@@ -1,25 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-2021 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\UI\Base\Component\FieldBadge\FieldBadgeUIBlockFactory;
|
||||
use Combodo\iTop\Form\Field\LabelField;
|
||||
use Combodo\iTop\Form\Field\TextAreaField;
|
||||
use Combodo\iTop\Form\Form;
|
||||
use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator;
|
||||
use Combodo\iTop\Form\Validator\Validator;
|
||||
use Combodo\iTop\Renderer\BlockRenderer;
|
||||
@@ -317,6 +305,8 @@ abstract class AttributeDefinition
|
||||
*/
|
||||
public function ListDBJoins()
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
|
||||
return "";
|
||||
// e.g: return array("Site", "infrid", "name");
|
||||
}
|
||||
@@ -842,9 +832,9 @@ abstract class AttributeDefinition
|
||||
*/
|
||||
public function MakeValue()
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
$sComputeFunc = $this->Get("compute_func");
|
||||
if (empty($sComputeFunc))
|
||||
{
|
||||
if (empty($sComputeFunc)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -6818,23 +6808,6 @@ class AttributeExternalKey extends AttributeDBFieldVoid
|
||||
return (int)$proposedValue;
|
||||
}
|
||||
|
||||
public function GetPrerequisiteAttributes($sClass = null)
|
||||
{
|
||||
$aAttributes = parent::GetPrerequisiteAttributes($sClass);
|
||||
$oExpression = DBSearch::FromOQL($this->GetValuesDef()->GetFilterExpression())->GetCriteria();
|
||||
foreach ($oExpression->GetParameters('this') as $sAttCode)
|
||||
{
|
||||
// Skip the id as it cannot change anyway
|
||||
if ($sAttCode =='id') continue;
|
||||
|
||||
if (!in_array($sAttCode, $aAttributes))
|
||||
{
|
||||
$aAttributes[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
return $aAttributes;
|
||||
}
|
||||
|
||||
public function GetMaximumComboLength()
|
||||
{
|
||||
return $this->GetOptional('max_combo_length', MetaModel::GetConfig()->Get('max_combo_length'));
|
||||
@@ -11804,13 +11777,12 @@ class AttributeFriendlyName extends AttributeDefinition
|
||||
// Code duplicated with AttributeObsolescenceFlag
|
||||
$aAttributes = $this->GetOptional("depends_on", array());
|
||||
$oExpression = $this->GetOQLExpression();
|
||||
foreach ($oExpression->ListRequiredFields() as $sClass => $sAttCode)
|
||||
{
|
||||
if (!in_array($sAttCode, $aAttributes))
|
||||
{
|
||||
foreach ($oExpression->ListRequiredFields() as $sAttCode) {
|
||||
if (!in_array($sAttCode, $aAttributes)) {
|
||||
$aAttributes[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
|
||||
return $aAttributes;
|
||||
}
|
||||
|
||||
@@ -12634,16 +12606,15 @@ class AttributeCustomFields extends AttributeDefinition
|
||||
*/
|
||||
public function GetForm(DBObject $oHostObject, $sFormPrefix = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
try {
|
||||
$oValue = $oHostObject->Get($this->GetCode());
|
||||
$oHandler = $this->GetHandler($oValue->GetValues());
|
||||
$sFormId = is_null($sFormPrefix) ? 'cf_'.$this->GetCode() : $sFormPrefix.'_cf_'.$this->GetCode();
|
||||
$oHandler->BuildForm($oHostObject, $sFormId);
|
||||
$oForm = $oHandler->GetForm();
|
||||
} catch (Exception $e)
|
||||
{
|
||||
$oForm = new \Combodo\iTop\Form\Form('');
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$oForm = new Form('');
|
||||
$oField = new LabelField('');
|
||||
$oField->SetLabel('Custom field error: '.$e->getMessage());
|
||||
$oForm->AddField($oField);
|
||||
|
||||
@@ -452,20 +452,23 @@ class BulkChange
|
||||
foreach ($this->m_aAttList as $sAttCode => $iCol)
|
||||
{
|
||||
// skip the private key, if any
|
||||
if ($sAttCode == 'id') continue;
|
||||
if (($sAttCode == 'id') || ($sAttCode == 'friendlyname')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
|
||||
|
||||
// skip reconciliation keys
|
||||
if (!$oAttDef->IsWritable() && in_array($sAttCode, $this->m_aReconcilKeys)){ continue; }
|
||||
if (!$oAttDef->IsWritable() && in_array($sAttCode, $this->m_aReconcilKeys)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$aReasons = array();
|
||||
$iFlags = ($oTargetObj->IsNew())
|
||||
? $oTargetObj->GetInitialStateAttributeFlags($sAttCode, $aReasons)
|
||||
: $oTargetObj->GetAttributeFlags($sAttCode, $aReasons);
|
||||
if ( (($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY) && ( $oTargetObj->Get($sAttCode) != $aRowData[$iCol]) )
|
||||
{
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Readonly', $sAttCode, $oTargetObj->Get($sAttCode), $aRowData[$iCol]);
|
||||
if ( (($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY) && ( $oTargetObj->Get($sAttCode) != $aRowData[$iCol]) ) {
|
||||
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Readonly', $sAttCode, $oTargetObj->Get($sAttCode), $aRowData[$iCol]);
|
||||
}
|
||||
else if ($oAttDef->IsLinkSet() && $oAttDef->IsIndirect())
|
||||
{
|
||||
@@ -1180,16 +1183,16 @@ class BulkChange
|
||||
$oPage->add('<p>'.$sCollapsedLabel.' <a class="truncated" onclick="OnTruncatedHistoryToggle(true);">'.$sLinkLabel.'</p>');
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
<<<EOF
|
||||
$('#$sAjaxDivId table.listResults').addClass('truncated');
|
||||
$('#$sAjaxDivId table.listResults tr:last td').addClass('truncated');
|
||||
EOF
|
||||
);
|
||||
|
||||
|
||||
|
||||
$sAppContext = $oAppContext->GetForLink();
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
<<<EOF
|
||||
function OnTruncatedHistoryToggle(bShowAll)
|
||||
{
|
||||
$('#csv_history_reload').html('<img src="../images/indicator.gif"/>');
|
||||
|
||||
@@ -376,10 +376,11 @@ abstract class BulkExport
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 3.0 replaced by GetFormPart
|
||||
* @deprecated 3.0.0 use GetFormPart instead
|
||||
*/
|
||||
public function DisplayFormPart(WebPage $oP, $sPartId)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use GetFormPart instead');
|
||||
$oP->AddSubBlock($this->GetFormPart($oP, $sPartId));
|
||||
}
|
||||
|
||||
|
||||
@@ -436,28 +436,31 @@ class CMDBChangeOpSetAttributeBlob extends CMDBChangeOpSetAttribute
|
||||
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
|
||||
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
|
||||
{
|
||||
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
|
||||
{
|
||||
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode'))) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// The attribute was renamed or removed from the object ?
|
||||
$sAttName = $this->Get('attcode');
|
||||
}
|
||||
/** @var \ormDocument $oPrevDoc */
|
||||
$oPrevDoc = $this->Get('prevdata');
|
||||
if ($oPrevDoc->IsEmpty())
|
||||
{
|
||||
if ($oPrevDoc->IsEmpty()) {
|
||||
$sPrevious = '';
|
||||
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sPrevious);
|
||||
}
|
||||
else
|
||||
{
|
||||
$sDocView = $oPrevDoc->GetAsHtml();
|
||||
$sDocView .= "<br/>".Dict::Format('UI:OpenDocumentInNewWindow_', $oPrevDoc->GetDisplayLink(get_class($this), $this->GetKey(), 'prevdata')).", \n";
|
||||
$sDocView .= Dict::Format('UI:DownloadDocument_', $oPrevDoc->GetDownloadLink(get_class($this), $this->GetKey(), 'prevdata'))."\n";
|
||||
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
|
||||
} else {
|
||||
$sFieldAsHtml = $oPrevDoc->GetAsHTML();
|
||||
|
||||
$sDisplayLabel = Dict::S('UI:OpenDocumentInNewWindow_');
|
||||
$sDisplayUrl = $oPrevDoc->GetDisplayURL(get_class($this), $this->GetKey(), 'prevdata');
|
||||
|
||||
$sDownloadLabel = Dict::Format('UI:DownloadDocument_');
|
||||
$sDownloadUrl = $oPrevDoc->GetDownloadURL(get_class($this), $this->GetKey(), 'prevdata');
|
||||
|
||||
$sDocView = <<<HTML
|
||||
{$sFieldAsHtml}
|
||||
<a href="{$sDisplayUrl}" target="_blank">{$sDisplayLabel}</a> / <a href="{$sDownloadUrl}">{$sDownloadLabel}</a>
|
||||
HTML;
|
||||
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sDocView);
|
||||
}
|
||||
}
|
||||
@@ -770,18 +773,15 @@ class CMDBChangeOpSetAttributeHTML extends CMDBChangeOpSetAttributeLongText
|
||||
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
|
||||
if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES)
|
||||
{
|
||||
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode')))
|
||||
{
|
||||
if (MetaModel::IsValidAttCode($this->Get('objclass'), $this->Get('attcode'))) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode'));
|
||||
$sAttName = $oAttDef->GetLabel();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// The attribute was renamed or removed from the object ?
|
||||
$sAttName = $this->Get('attcode');
|
||||
}
|
||||
$sTextView = '<div class="history_entry history_entry_truncated"><div class="history_html_content">'.$this->Get('prevdata').'</div></div>';
|
||||
|
||||
$sTextView = $this->Get('prevdata');
|
||||
|
||||
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
|
||||
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sTextView);
|
||||
}
|
||||
|
||||
@@ -566,8 +566,8 @@ abstract class CMDBObject extends DBObject
|
||||
*/
|
||||
protected function CheckUserRights($bSkipStrongSecurity, $iActionCode)
|
||||
{
|
||||
if (is_null($bSkipStrongSecurity))
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
if (is_null($bSkipStrongSecurity)) {
|
||||
// This is temporary
|
||||
// We have implemented this safety net right before releasing iTop 1.0
|
||||
// and we decided that it was too risky to activate it
|
||||
|
||||
@@ -68,6 +68,8 @@ class CMDBSource
|
||||
|
||||
/** @var mysqli $m_oMysqli */
|
||||
protected static $m_oMysqli;
|
||||
/** @var mysqli or mock used for test purpose, only used in query() method */
|
||||
protected static $oMySQLiForQuery;
|
||||
|
||||
/**
|
||||
* @var int number of level for nested transactions : 0 if no transaction was ever opened, +1 for each 'START TRANSACTION' sent
|
||||
@@ -134,6 +136,7 @@ class CMDBSource
|
||||
self::$m_sDBTlsCA = empty($sTlsCA) ? null : $sTlsCA;
|
||||
|
||||
self::$m_oMysqli = self::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, true);
|
||||
self::SetMySQLiForQuery(self::$m_oMysqli);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,8 +154,6 @@ class CMDBSource
|
||||
public static function GetMysqliInstance(
|
||||
$sDbHost, $sUser, $sPwd, $sSource = '', $bTlsEnabled = false, $sTlsCa = null, $bCheckTlsAfterConnection = false
|
||||
) {
|
||||
$oMysqli = null;
|
||||
|
||||
$sServer = null;
|
||||
$iPort = null;
|
||||
self::InitServerAndPort($sDbHost, $sServer, $iPort);
|
||||
@@ -182,7 +183,7 @@ class CMDBSource
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
throw new MySQLException('Could not connect to the DB server', array('host' => $sServer, 'user' => $sUser), $e);
|
||||
throw new MySQLException('Could not connect to the DB server', array('host' => $sServer, 'user' => $sUser),$e);
|
||||
}
|
||||
|
||||
if ($bTlsEnabled
|
||||
@@ -450,6 +451,24 @@ class CMDBSource
|
||||
return self::$m_oMysqli;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
private static function GetMySQLiForQuery()
|
||||
{
|
||||
return self::$oMySQLiForQuery;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used for test purpose (mysqli mock)
|
||||
* @param $oMySQLi
|
||||
*/
|
||||
private static function SetMySQLiForQuery($oMySQLi)
|
||||
{
|
||||
self::$oMySQLiForQuery = $oMySQLi;
|
||||
}
|
||||
|
||||
public static function GetErrNo()
|
||||
{
|
||||
if (self::$m_oMysqli->errno != 0)
|
||||
@@ -585,10 +604,15 @@ class CMDBSource
|
||||
*/
|
||||
private static function DBQuery($sSql)
|
||||
{
|
||||
$sShortSQL = substr(preg_replace("/\s+/", " ", substr($sSql, 0, 180)), 0, 150);
|
||||
if (substr_compare($sShortSQL, "SELECT", 0, strlen("SELECT")) !== 0) {
|
||||
IssueLog::Trace("$sShortSQL", 'cmdbsource');
|
||||
}
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
$oResult = self::GetMySQLiForQuery()->query($sSql);
|
||||
}
|
||||
catch (mysqli_sql_exception $e)
|
||||
{
|
||||
@@ -652,7 +676,7 @@ class CMDBSource
|
||||
);
|
||||
DeadLockLog::Info($sMessage, $iMySqlErrorNo, $aLogContext);
|
||||
|
||||
IssueLog::Error($sMessage, 'DeadLock', $e->getMessage());
|
||||
IssueLog::Error($sMessage, LogChannels::DEADLOCK, $e->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -668,10 +692,15 @@ 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", 'cmdbsource');
|
||||
self::DBQuery('START TRANSACTION');
|
||||
} else {
|
||||
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") START TRANSACTION $sCaller", 'cmdbsource');
|
||||
}
|
||||
|
||||
self::AddTransactionLevel();
|
||||
@@ -689,9 +718,12 @@ class CMDBSource
|
||||
*/
|
||||
private static function Commit()
|
||||
{
|
||||
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
|
||||
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
|
||||
if (!self::IsInsideTransaction())
|
||||
{
|
||||
// should not happen !
|
||||
IssueLog::Error("No Transaction COMMIT $sCaller", 'cmdbsource');
|
||||
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
|
||||
}
|
||||
|
||||
@@ -699,8 +731,10 @@ class CMDBSource
|
||||
|
||||
if (self::IsInsideTransaction())
|
||||
{
|
||||
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") COMMIT $sCaller", 'cmdbsource');
|
||||
return;
|
||||
}
|
||||
IssueLog::Trace("COMMIT $sCaller", 'cmdbsource');
|
||||
self::DBQuery('COMMIT');
|
||||
}
|
||||
|
||||
@@ -719,17 +753,22 @@ class CMDBSource
|
||||
*/
|
||||
private static function Rollback()
|
||||
{
|
||||
$aStackTrace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT , 3);
|
||||
$sCaller = 'From '.$aStackTrace[1]['file'].'('.$aStackTrace[1]['line'].'): '.$aStackTrace[2]['class'].'->'.$aStackTrace[2]['function'].'()';
|
||||
if (!self::IsInsideTransaction())
|
||||
{
|
||||
// should not happen !
|
||||
IssueLog::Error("No Transaction ROLLBACK $sCaller", 'cmdbsource');
|
||||
throw new MySQLNoTransactionException('Trying to commit transaction whereas none have been started !', null);
|
||||
}
|
||||
self::RemoveLastTransactionLevel();
|
||||
if (self::IsInsideTransaction())
|
||||
{
|
||||
IssueLog::Trace("Ignore nested (".self::$m_iTransactionLevel.") ROLLBACK $sCaller", 'cmdbsource');
|
||||
return;
|
||||
}
|
||||
|
||||
IssueLog::Trace("ROLLBACK $sCaller", 'cmdbsource');
|
||||
self::DBQuery('ROLLBACK');
|
||||
}
|
||||
|
||||
@@ -779,6 +818,18 @@ class CMDBSource
|
||||
self::$m_iTransactionLevel = 0;
|
||||
}
|
||||
|
||||
public static function IsDeadlockException(Exception $e)
|
||||
{
|
||||
while ($e instanceof Exception) {
|
||||
if (($e instanceof MySQLException) && ($e->getCode() == 1213)) {
|
||||
return true;
|
||||
}
|
||||
$e = $e->getPrevious();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static function GetInsertId()
|
||||
{
|
||||
$iRes = self::$m_oMysqli->insert_id;
|
||||
@@ -823,7 +874,7 @@ class CMDBSource
|
||||
$oKPI = new ExecutionKPI();
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
$oResult = self::GetMySQLiForQuery()->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
@@ -863,7 +914,7 @@ class CMDBSource
|
||||
$oKPI = new ExecutionKPI();
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
$oResult = self::GetMySQLiForQuery()->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
@@ -945,7 +996,7 @@ class CMDBSource
|
||||
{
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
$oResult = self::GetMySQLiForQuery()->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
@@ -1433,7 +1484,7 @@ class CMDBSource
|
||||
$sSql = "SELECT * FROM `$sTable`";
|
||||
try
|
||||
{
|
||||
$oResult = self::$m_oMysqli->query($sSql);
|
||||
$oResult = self::GetMySQLiForQuery()->query($sSql);
|
||||
}
|
||||
catch(mysqli_sql_exception $e)
|
||||
{
|
||||
|
||||
@@ -1229,6 +1229,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'activity_panel.prefilter_only_current_log' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether the "Logs" filter should only be set to the log from the current tab or to all of them.',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'activity_panel.show_author_name_below_entries' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether or not to show the author friendlyname next to the date on the last entry.',
|
||||
@@ -1279,6 +1287,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'user_preferences.allow_backoffice_theme_override' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Whether the user can choose which theme to use in the backoffice. If set to false, all users will have the theme defined in "backoffice_default_theme"',
|
||||
'default' => true,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'secure_rest_services' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'When set to true, only the users with the profile "REST Services User" are allowed to use the REST web services.',
|
||||
@@ -1383,6 +1399,22 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
],
|
||||
'theme.enable_precompilation' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If false, theme compilation will not use any precompiled file setup optimization.)',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'theme.force_signature_check_at_runtime' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true, checking that the current theme signature matches the compiled file -to recompile it if necessary- will be done for each page. This can slow the application, only use it if you are experiencing issues while customizing a theme.)',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
];
|
||||
|
||||
public function IsProperty($sPropCode)
|
||||
|
||||
@@ -3,4 +3,6 @@
|
||||
* This file is only here for compatibility issues. Will be removed in iTop 3.1.0 (N°3664)
|
||||
*
|
||||
* @deprecated 3.0.0 N°3663 Exception classes were moved to `/application/exceptions`, use autoloader instead of require !
|
||||
*/
|
||||
*/
|
||||
require_once '../approot.inc.php';
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('Classes were moved to /application/exceptions');
|
||||
|
||||
@@ -185,13 +185,13 @@ class CSVBulkExport extends TabularBulkExport
|
||||
$oFieldSetLocalization->AddSubBlock(new Html('</br>'));
|
||||
|
||||
$oSelect = SelectUIBlockFactory::MakeForSelectWithLabel("charset", Dict::S('UI:CSVImport:Encoding'));
|
||||
$oSelect->SetBeforeInput(true);
|
||||
$oSelect->SetIsLabelBefore(true);
|
||||
$oFieldSetLocalization->AddSubBlock($oSelect);
|
||||
|
||||
$aPossibleEncodings = utils::GetPossibleEncodings(MetaModel::GetConfig()->GetCSVImportCharsets());
|
||||
$sDefaultEncoding = MetaModel::GetConfig()->Get('csv_file_default_charset');
|
||||
foreach ($aPossibleEncodings as $sIconvCode => $sDisplayName) {
|
||||
$oSelect->GetInput()->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sIconvCode, $sDisplayName, ($sIconvCode == $sDefaultEncoding)));
|
||||
$oSelect->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sIconvCode, $sDisplayName, ($sIconvCode == $sDefaultEncoding)));
|
||||
}
|
||||
//markup
|
||||
$oFieldSetMarkup = FieldSetUIBlockFactory::MakeStandard(Dict::S('Core:BulkExport:TextFormat'));
|
||||
@@ -211,15 +211,15 @@ class CSVBulkExport extends TabularBulkExport
|
||||
|
||||
$sDefaultFormat = htmlentities((string)AttributeDateTime::GetFormat(), ENT_QUOTES, 'UTF-8');
|
||||
$sExample = htmlentities(date((string)AttributeDateTime::GetFormat()), ENT_QUOTES, 'UTF-8');
|
||||
$oRadioDefault = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatDefault_Example', $sDefaultFormat, $sExample), "csv_custom_date_time_format", "default", "csv_date_time_format_default", "radio");
|
||||
$oRadioDefault = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatDefault_Example', $sDefaultFormat, $sExample), "csv_date_format_radio", "default", "csv_date_time_format_default", "radio");
|
||||
$oRadioDefault->GetInput()->SetIsChecked(($sDateTimeFormat == (string)AttributeDateTime::GetFormat()));
|
||||
$oRadioDefault->SetBeforeInput(false);
|
||||
$oRadioDefault->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
$oFieldSetDate->AddSubBlock($oRadioDefault);
|
||||
$oFieldSetDate->AddSubBlock(new Html('</br>'));
|
||||
|
||||
$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), "csv_custom_date_time_format", "custom", "csv_date_time_format_custom", "radio");
|
||||
$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->GetInput()->SetIsChecked($sDateTimeFormat !== (string)AttributeDateTime::GetFormat());
|
||||
$oRadioCustom->SetBeforeInput(false);
|
||||
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-2021 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -803,7 +790,7 @@ abstract class DBObject implements iDisplay
|
||||
//
|
||||
/** @var \AttributeExternalField $oAttDef */
|
||||
$sExtKeyAttCode = $oAttDef->GetKeyAttCode();
|
||||
|
||||
|
||||
if (($iRemote = $this->Get($sExtKeyAttCode)) && ($iRemote > 0)) // Objects in memory have negative IDs
|
||||
{
|
||||
$oExtKeyAttDef = MetaModel::GetAttributeDef(get_class($this), $sExtKeyAttCode);
|
||||
@@ -1530,7 +1517,8 @@ abstract class DBObject implements iDisplay
|
||||
/**
|
||||
* Helper to get the friendly name in a safe manner for displaying inside a web page
|
||||
*
|
||||
* @api
|
||||
* @internal
|
||||
* @since 3.0.0 N°4106 This method is now internal. It will be set final in 3.1.0 (N°4107)
|
||||
*
|
||||
* @return string
|
||||
* @throws \CoreException
|
||||
@@ -1541,16 +1529,17 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get the friendly name
|
||||
*
|
||||
* This is not safe for displaying inside a web page since the " < > characters are not escaped.
|
||||
* In example, the name may contain some XSS script instructions.
|
||||
* Helper to get the friendly name
|
||||
*
|
||||
* This is not safe for displaying inside a web page since the " < > characters are not escaped.
|
||||
* In example, the name may contain some XSS script instructions.
|
||||
* Use this function only for internal computations or for an output to a non-HTML destination
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @internal
|
||||
* @return string
|
||||
* @throws \CoreException
|
||||
* @since 3.0.0 N°4106 This method is now internal. It will be set final in 3.1.0 (N°4107)
|
||||
*
|
||||
*/
|
||||
public function GetRawName()
|
||||
{
|
||||
@@ -2434,11 +2423,10 @@ abstract class DBObject implements iDisplay
|
||||
* Reset during {@see DBObject::DBUpdate()}
|
||||
* @throws Exception
|
||||
* @uses m_aCurrValues
|
||||
*/
|
||||
*/
|
||||
public function ListChanges()
|
||||
{
|
||||
if ($this->m_bIsInDB)
|
||||
{
|
||||
if ($this->m_bIsInDB) {
|
||||
return $this->ListChangedValues($this->m_aCurrValues);
|
||||
}
|
||||
|
||||
@@ -2784,50 +2772,75 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
|
||||
$iTransactionRetry = 1;
|
||||
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
|
||||
try
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('START TRANSACTION');
|
||||
}
|
||||
|
||||
// First query built upon on the root class, because the ID must be created first
|
||||
$this->m_iKey = $this->DBInsertSingleTable($sRootClass);
|
||||
|
||||
// Then do the leaf class, if different from the root class
|
||||
if ($sClass != $sRootClass)
|
||||
{
|
||||
$this->DBInsertSingleTable($sClass);
|
||||
}
|
||||
|
||||
// Then do the other classes
|
||||
foreach (MetaModel::EnumParentClasses($sClass) as $sParentClass)
|
||||
{
|
||||
if ($sParentClass == $sRootClass)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$this->DBInsertSingleTable($sParentClass);
|
||||
}
|
||||
|
||||
$this->OnObjectKeyReady();
|
||||
|
||||
$this->DBWriteLinks();
|
||||
$this->WriteExternalAttributes();
|
||||
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('COMMIT');
|
||||
}
|
||||
// TODO Deep clone this object before the transaction (to use it in case of rollback)
|
||||
// $iTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
||||
$iTransactionRetryCount = 1;
|
||||
$iTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
|
||||
$iTransactionRetry = $iTransactionRetryCount;
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
while ($iTransactionRetry > 0) {
|
||||
try {
|
||||
$iTransactionRetry--;
|
||||
if ($bIsTransactionEnabled) {
|
||||
CMDBSource::Query('START TRANSACTION');
|
||||
}
|
||||
|
||||
// First query built upon on the root class, because the ID must be created first
|
||||
$this->m_iKey = $this->DBInsertSingleTable($sRootClass);
|
||||
|
||||
// Then do the leaf class, if different from the root class
|
||||
if ($sClass != $sRootClass) {
|
||||
$this->DBInsertSingleTable($sClass);
|
||||
}
|
||||
|
||||
// Then do the other classes
|
||||
foreach (MetaModel::EnumParentClasses($sClass) as $sParentClass) {
|
||||
if ($sParentClass == $sRootClass) {
|
||||
continue;
|
||||
}
|
||||
$this->DBInsertSingleTable($sParentClass);
|
||||
}
|
||||
|
||||
$this->OnObjectKeyReady();
|
||||
|
||||
$this->DBWriteLinks();
|
||||
$this->WriteExternalAttributes();
|
||||
|
||||
// Write object creation history within the transaction
|
||||
$this->RecordObjCreation();
|
||||
|
||||
if ($bIsTransactionEnabled) {
|
||||
CMDBSource::Query('COMMIT');
|
||||
}
|
||||
break;
|
||||
}
|
||||
catch (Exception $e) {
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
if (!CMDBSource::IsInsideTransaction() && CMDBSource::IsDeadlockException($e))
|
||||
{
|
||||
// Deadlock found when trying to get lock; try restarting transaction (only in main transaction)
|
||||
if ($iTransactionRetry > 0)
|
||||
{
|
||||
// wait and retry
|
||||
IssueLog::Error("Insert TRANSACTION Retrying...");
|
||||
usleep(random_int(1, 5) * 1000 * $iTransactionRetryDelay * ($iTransactionRetryCount - $iTransactionRetry));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
IssueLog::Error("Insert Deadlock TRANSACTION prevention failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->m_bIsInDB = true;
|
||||
@@ -2860,8 +2873,6 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
|
||||
$this->RecordObjCreation();
|
||||
|
||||
return $this->m_iKey;
|
||||
}
|
||||
|
||||
@@ -3136,39 +3147,15 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
// 2 - Find mentioned objects
|
||||
$aMentionedObjects = array();
|
||||
foreach($aUpdatedLogAttCodes as $sAttCode)
|
||||
{
|
||||
foreach ($aUpdatedLogAttCodes as $sAttCode) {
|
||||
/** @var \ormCaseLog $oUpdatedCaseLog */
|
||||
$oUpdatedCaseLog = $this->Get($sAttCode);
|
||||
$aMentionMatches = array();
|
||||
// Note: As the sanitizer (or CKEditor autocomplete plugin? 🤔) removes data-* attributes from the hyperlink, we can't use the following (simpler) regexp: '/<a\s*([^>]*)data-object-class="([^"]*)"\s*data-object-id="([^"]*)">/i'
|
||||
// If we change the sanitizer, we might want to use this regexp as it's easier to read
|
||||
// Note 2: This is only working for backoffice URLs...
|
||||
$sAppRootUrlForRegExp = addcslashes(utils::GetAbsoluteUrlAppRoot(), '/&');
|
||||
preg_match_all("/\[([^\]]*)\]\({$sAppRootUrlForRegExp}[^\)]*\&class=([^\)\&]*)\&id=([\d]*)[^\)]*\)/i", $oUpdatedCaseLog->GetModifiedEntry(), $aMentionMatches);
|
||||
|
||||
foreach($aMentionMatches[0] as $iMatchIdx => $sCompleteMatch)
|
||||
{
|
||||
$sMatchedClass = $aMentionMatches[2][$iMatchIdx];
|
||||
$sMatchedId = $aMentionMatches[3][$iMatchIdx];
|
||||
|
||||
// Prepare array for matched class if not already present
|
||||
if(!array_key_exists($sMatchedClass, $aMentionedObjects))
|
||||
{
|
||||
$aMentionedObjects[$sMatchedClass] = array();
|
||||
}
|
||||
// Add matched ID if not already there
|
||||
if(!in_array($sMatchedId, $aMentionedObjects[$sMatchedClass]))
|
||||
{
|
||||
$aMentionedObjects[$sMatchedClass][] = $sMatchedId;
|
||||
}
|
||||
}
|
||||
$aMentionedObjects = array_merge_recursive($aMentionedObjects, utils::GetMentionedObjectsFromText($oUpdatedCaseLog->GetModifiedEntry()));
|
||||
}
|
||||
// 3 - Trigger for those objects
|
||||
foreach($aMentionedObjects as $sMentionedClass => $aMentionedIds)
|
||||
{
|
||||
foreach($aMentionedIds as $sMentionedId)
|
||||
{
|
||||
// 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);
|
||||
// Important: Here the "$this->object()$" placeholder is actually the mentioned object and not the current object. The current object can be used through the $source->object()$ placeholder.
|
||||
@@ -3180,8 +3167,13 @@ abstract class DBObject implements iDisplay
|
||||
array(), $aParams);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
/** @var \Trigger $oTrigger */
|
||||
$oTrigger->DoActivate($aTriggerArgs);
|
||||
/** @var \TriggerOnObjectMention $oTrigger */
|
||||
try {
|
||||
$oTrigger->DoActivate($aTriggerArgs);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3210,9 +3202,11 @@ abstract class DBObject implements iDisplay
|
||||
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
$iIsTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
||||
// TODO Deep clone this object before the transaction (to use it in case of rollback)
|
||||
// $iTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
||||
$iTransactionRetryCount = 1;
|
||||
$iIsTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
|
||||
$iTransactionRetry = $iIsTransactionRetryCount;
|
||||
$iTransactionRetry = $iTransactionRetryCount;
|
||||
}
|
||||
while ($iTransactionRetry > 0)
|
||||
{
|
||||
@@ -3228,7 +3222,7 @@ abstract class DBObject implements iDisplay
|
||||
// Update the left & right indexes for each hierarchical key
|
||||
foreach ($aHierarchicalKeys as $sAttCode => $oAttDef)
|
||||
{
|
||||
$sTable = $sTable = MetaModel::DBGetTable(get_class($this), $sAttCode);
|
||||
$sTable = MetaModel::DBGetTable(get_class($this), $sAttCode);
|
||||
$sSQL = "SELECT `".$oAttDef->GetSQLRight()."` AS `right`, `".$oAttDef->GetSQLLeft()."` AS `left` FROM `$sTable` WHERE id=".$this->GetKey();
|
||||
$aRes = CMDBSource::QueryToArray($sSQL);
|
||||
$iMyLeft = $aRes[0]['left'];
|
||||
@@ -3236,8 +3230,7 @@ abstract class DBObject implements iDisplay
|
||||
$iDelta = $iMyRight - $iMyLeft + 1;
|
||||
MetaModel::HKTemporaryCutBranch($iMyLeft, $iMyRight, $oAttDef, $sTable);
|
||||
|
||||
if ($aDBChanges[$sAttCode] == 0)
|
||||
{
|
||||
if ($aDBChanges[$sAttCode] == 0) {
|
||||
// No new parent, insert completely at the right of the tree
|
||||
$sSQL = "SELECT max(`".$oAttDef->GetSQLRight()."`) AS max FROM `$sTable`";
|
||||
$aRes = CMDBSource::QueryToArray($sSQL);
|
||||
@@ -3280,38 +3273,29 @@ abstract class DBObject implements iDisplay
|
||||
$this->DBWriteLinks();
|
||||
$this->WriteExternalAttributes();
|
||||
|
||||
// following lines are resetting changes (so after this {@see DBObject::ListChanges()} won't return changes anymore)
|
||||
// new values are already in the object (call {@see DBObject::Get()} to get them)
|
||||
// call {@see DBObject::ListPreviousValuesForUpdatedAttributes()} to get changed fields and previous values
|
||||
$this->m_bDirty = false;
|
||||
$this->m_aTouchedAtt = array();
|
||||
$this->m_aModifiedAtt = array();
|
||||
|
||||
if (count($aChanges) != 0)
|
||||
{
|
||||
if (count($aChanges) != 0) {
|
||||
$this->RecordAttChanges($aChanges, $aOriginalValues);
|
||||
}
|
||||
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
if ($bIsTransactionEnabled) {
|
||||
CMDBSource::Query('COMMIT');
|
||||
}
|
||||
break;
|
||||
}
|
||||
catch (MySQLException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
if ($e->getCode() == 1213)
|
||||
if (!CMDBSource::IsInsideTransaction() && CMDBSource::IsDeadlockException($e))
|
||||
{
|
||||
// Deadlock found when trying to get lock; try restarting transaction
|
||||
IssueLog::Error($e->getMessage());
|
||||
// Deadlock found when trying to get lock; try restarting transaction (only in main transaction)
|
||||
if ($iTransactionRetry > 0)
|
||||
{
|
||||
// wait and retry
|
||||
IssueLog::Error("Update TRANSACTION Retrying...");
|
||||
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iIsTransactionRetryCount - $iTransactionRetry));
|
||||
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iTransactionRetryCount - $iTransactionRetry));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -3325,10 +3309,11 @@ abstract class DBObject implements iDisplay
|
||||
'id' => $this->GetKey(),
|
||||
'class' => get_class($this),
|
||||
'issues' => $aErrors
|
||||
));
|
||||
), $e);
|
||||
}
|
||||
catch (CoreCannotSaveObjectException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
@@ -3337,6 +3322,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
@@ -3350,13 +3336,20 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
|
||||
// following lines are resetting changes (so after this {@see DBObject::ListChanges()} won't return changes anymore)
|
||||
// new values are already in the object (call {@see DBObject::Get()} to get them)
|
||||
// call {@see DBObject::ListPreviousValuesForUpdatedAttributes()} to get changed fields and previous values
|
||||
$this->m_bDirty = false;
|
||||
$this->m_aTouchedAtt = array();
|
||||
$this->m_aModifiedAtt = array();
|
||||
|
||||
try {
|
||||
// - TriggerOnObjectUpdate
|
||||
$aParams = array('class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectUpdate AS t WHERE t.target_class IN (:class_list)"),
|
||||
array(), $aParams);
|
||||
while ($oTrigger = $oSet->Fetch()) {
|
||||
/** @var \Trigger $oTrigger */
|
||||
/** @var \TriggerOnObjectUpdate $oTrigger */
|
||||
try {
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
@@ -3539,9 +3532,11 @@ abstract class DBObject implements iDisplay
|
||||
$bIsTransactionEnabled = MetaModel::GetConfig()->Get('db_core_transactions_enabled');
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
$iIsTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
||||
$iIsTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
|
||||
$iTransactionRetry = $iIsTransactionRetryCount;
|
||||
// TODO Deep clone this object before the transaction (to use it in case of rollback)
|
||||
// $iTransactionRetryCount = MetaModel::GetConfig()->Get('db_core_transactions_retry_count');
|
||||
$iTransactionRetryCount = 1;
|
||||
$iTransactionRetryDelay = MetaModel::GetConfig()->Get('db_core_transactions_retry_delay_ms');
|
||||
$iTransactionRetry = $iTransactionRetryCount;
|
||||
}
|
||||
while ($iTransactionRetry > 0)
|
||||
{
|
||||
@@ -3564,18 +3559,18 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
catch (MySQLException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($bIsTransactionEnabled)
|
||||
{
|
||||
CMDBSource::Query('ROLLBACK');
|
||||
if ($e->getCode() == 1213)
|
||||
if (!CMDBSource::IsInsideTransaction() && CMDBSource::IsDeadlockException($e))
|
||||
{
|
||||
// Deadlock found when trying to get lock; try restarting transaction
|
||||
IssueLog::Error($e->getMessage());
|
||||
if ($iTransactionRetry > 0)
|
||||
{
|
||||
// wait and retry
|
||||
IssueLog::Error("Delete TRANSACTION Retrying...");
|
||||
usleep(random_int(1, 5) * 1000 * $iIsTransactionRetryDelay * ($iIsTransactionRetryCount - $iTransactionRetry));
|
||||
usleep(random_int(1, 5) * 1000 * $iTransactionRetryDelay * ($iTransactionRetryCount - $iTransactionRetry));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -3728,16 +3723,22 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
/**
|
||||
* Apply a stimulus (workflow)
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sStimulusCode
|
||||
* @param bool $bDoNotWrite
|
||||
*
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @param string $sStimulusCode
|
||||
* @param bool $bDoNotWrite if true we won't save the object !
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
*
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
*
|
||||
* @uses \AttributeStopWatch::Start
|
||||
* @uses \AttributeStopWatch::Stop
|
||||
* @uses \DBObject::DBWrite
|
||||
* @uses \TriggerOnStateLeave::DoActivate
|
||||
* @uses \TriggerOnStateEnter::DoActivate
|
||||
*/
|
||||
public function ApplyStimulus($sStimulusCode, $bDoNotWrite = false)
|
||||
{
|
||||
@@ -3852,17 +3853,14 @@ abstract class DBObject implements iDisplay
|
||||
if (in_array($sNewState, $oAttDef->GetStates()))
|
||||
{
|
||||
$oSW->Start($this, $oAttDef);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$oSW->Stop($this, $oAttDef);
|
||||
}
|
||||
$this->Set($sAttCode, $oSW);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$bDoNotWrite)
|
||||
{
|
||||
if (!$bDoNotWrite) {
|
||||
$this->DBWrite();
|
||||
}
|
||||
|
||||
@@ -3870,30 +3868,26 @@ abstract class DBObject implements iDisplay
|
||||
$aParams = array(
|
||||
'class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL),
|
||||
'previous_state' => $sPreviousState,
|
||||
'new_state' => $sNewState);
|
||||
'new_state' => $sNewState,
|
||||
);
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateLeave AS t WHERE t.target_class IN (:class_list) AND t.state=:previous_state"), array(), $aParams);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
/** @var \Trigger $oTrigger */
|
||||
try
|
||||
{
|
||||
while ($oTrigger = $oSet->Fetch()) {
|
||||
/** @var \TriggerOnStateLeave $oTrigger */
|
||||
try {
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
catch (Exception $e) {
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
|
||||
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateEnter AS t WHERE t.target_class IN (:class_list) AND t.state=:new_state"), array(), $aParams);
|
||||
while ($oTrigger = $oSet->Fetch())
|
||||
{
|
||||
/** @var \Trigger $oTrigger */
|
||||
try{
|
||||
while ($oTrigger = $oSet->Fetch()) {
|
||||
/** @var \TriggerOnStateEnter $oTrigger */
|
||||
try {
|
||||
$oTrigger->DoActivate($this->ToArgs('this'));
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
catch (Exception $e) {
|
||||
utils::EnrichRaisedException($oTrigger, $e);
|
||||
}
|
||||
}
|
||||
@@ -4718,8 +4712,10 @@ abstract class DBObject implements iDisplay
|
||||
*/
|
||||
public function GetMasterReplica()
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
|
||||
$sOQL = "SELECT replica,datasource FROM SynchroReplica AS replica JOIN SynchroDataSource AS datasource ON replica.sync_source_id=datasource.id WHERE replica.dest_class = :dest_class AND replica.dest_id = :dest_id";
|
||||
$oReplicaSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array() /* order by*/, array('dest_class' => get_class($this), 'dest_id' => $this->GetKey()));
|
||||
|
||||
return $oReplicaSet;
|
||||
}
|
||||
|
||||
@@ -5552,27 +5548,31 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
$aFields = $oExpression->ListRequiredFields();
|
||||
$aArgs = array();
|
||||
foreach ($aFields as $sFieldDesc)
|
||||
{
|
||||
foreach ($aFields as $sFieldDesc) {
|
||||
$aFieldParts = explode('.', $sFieldDesc);
|
||||
if (count($aFieldParts) == 2)
|
||||
{
|
||||
if (count($aFieldParts) == 2) {
|
||||
$sClass = $aFieldParts[0];
|
||||
$sAttCode = $aFieldParts[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sClass = get_class($this);
|
||||
$sAttCode = $aFieldParts[0];
|
||||
}
|
||||
if (get_class($this) != $sClass) continue;
|
||||
if (!MetaModel::IsValidAttCode(get_class($this), $sAttCode)) continue;
|
||||
if (get_class($this) != $sClass) {
|
||||
continue;
|
||||
}
|
||||
if (!MetaModel::IsValidAttCode(get_class($this), $sAttCode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
|
||||
$aSQLValues = $oAttDef->GetSQLValues($this->m_aCurrValues[$sAttCode]);
|
||||
$value = reset($aSQLValues);
|
||||
if ($oAttDef->IsNull($value)) {
|
||||
return '';
|
||||
}
|
||||
$aArgs[$sFieldDesc] = $value;
|
||||
}
|
||||
|
||||
return $oExpression->Evaluate($aArgs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,62 +280,97 @@ abstract class DBSearch
|
||||
|
||||
abstract public function TranslateConditions($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return mixed
|
||||
*/
|
||||
/**
|
||||
* @internal
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function IsAny();
|
||||
|
||||
/**
|
||||
* @deprecated use ToOQL() instead
|
||||
* @internal
|
||||
* @return string
|
||||
*/
|
||||
public function Describe(){return 'deprecated - use ToOQL() instead';}
|
||||
/**
|
||||
* @deprecated use ToOQL() instead
|
||||
* @internal
|
||||
* @return string
|
||||
*/
|
||||
public function DescribeConditionPointTo($sExtKeyAttCode, $aPointingTo){return 'deprecated - use ToOQL() instead';}
|
||||
/**
|
||||
* @deprecated use ToOQL() instead
|
||||
* @internal
|
||||
* @return string
|
||||
*/
|
||||
public function DescribeConditionRefBy($sForeignClass, $sForeignExtKeyAttCode){return 'deprecated - use ToOQL() instead';}
|
||||
/**
|
||||
* @deprecated use ToOQL() instead
|
||||
* @internal
|
||||
* @return string
|
||||
*/
|
||||
public function DescribeConditionRelTo($aRelInfo){return 'deprecated - use ToOQL() instead';}
|
||||
/**
|
||||
* @deprecated use ToOQL() instead
|
||||
* @internal
|
||||
* @return string
|
||||
*/
|
||||
public function DescribeConditions(){return 'deprecated - use ToOQL() instead';}
|
||||
/**
|
||||
* @deprecated use ToOQL() instead
|
||||
* @internal
|
||||
* @return string
|
||||
*/
|
||||
public function __DescribeHTML(){return 'deprecated - use ToOQL() instead';}
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated use ToOQL() instead
|
||||
* @return string
|
||||
*/
|
||||
public function Describe()
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use ToOQL() instead');
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return mixed
|
||||
*/
|
||||
return 'deprecated - use ToOQL() instead';
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated use ToOQL() instead
|
||||
* @return string
|
||||
*/
|
||||
public function DescribeConditionPointTo($sExtKeyAttCode, $aPointingTo)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use ToOQL() instead');
|
||||
|
||||
return 'deprecated - use ToOQL() instead';
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated use ToOQL() instead
|
||||
* @return string
|
||||
*/
|
||||
public function DescribeConditionRefBy($sForeignClass, $sForeignExtKeyAttCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use ToOQL() instead');
|
||||
|
||||
return 'deprecated - use ToOQL() instead';
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated use ToOQL() instead
|
||||
* @return string
|
||||
*/
|
||||
public function DescribeConditionRelTo($aRelInfo)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use ToOQL() instead');
|
||||
|
||||
return 'deprecated - use ToOQL() instead';
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated use ToOQL() instead
|
||||
* @return string
|
||||
*/
|
||||
public function DescribeConditions()
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use ToOQL() instead');
|
||||
|
||||
return 'deprecated - use ToOQL() instead';
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated use ToOQL() instead
|
||||
* @return string
|
||||
*/
|
||||
public function __DescribeHTML()
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use ToOQL() instead');
|
||||
|
||||
return 'deprecated - use ToOQL() instead';
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function ResetCondition();
|
||||
|
||||
/**
|
||||
* add $oExpression as a OR
|
||||
*
|
||||
* @api
|
||||
* @see DBSearch::AddConditionExpression()
|
||||
*
|
||||
* @param Expression $oExpression
|
||||
/**
|
||||
* add $oExpression as a OR
|
||||
*
|
||||
* @api
|
||||
* @see DBSearch::AddConditionExpression()
|
||||
*
|
||||
* @param Expression $oExpression
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -1004,10 +1039,8 @@ abstract class DBSearch
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a SQL query from the current search
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* Generate a SQL query from the current search
|
||||
*
|
||||
* @param array $aOrderBy Array of '[<classalias>.]attcode' => bAscending
|
||||
* @param array $aArgs
|
||||
* @param null $aAttToLoad
|
||||
@@ -1015,12 +1048,16 @@ abstract class DBSearch
|
||||
* @param int $iLimitCount
|
||||
* @param int $iLimitStart
|
||||
* @param bool $bGetCount
|
||||
* @param bool $bBeautifulSQL
|
||||
*
|
||||
* @return string
|
||||
* @throws CoreException
|
||||
* @throws Exception
|
||||
* @throws MissingQueryArgument
|
||||
* @throws \ConfigException
|
||||
* @throws \CoreException
|
||||
* @throws \MissingQueryArgument
|
||||
* @internal
|
||||
*
|
||||
*/
|
||||
public function MakeSelectQuery($aOrderBy = array(), $aArgs = array(), $aAttToLoad = null, $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false)
|
||||
public function MakeSelectQuery($aOrderBy = array(), $aArgs = array(), $aAttToLoad = null, $aExtendedDataSpec = null, $iLimitCount = 0, $iLimitStart = 0, $bGetCount = false, $bBeautifulSQL = true)
|
||||
{
|
||||
// Check the order by specification, and prefix with the class alias
|
||||
// and make sure that the ordering columns are going to be selected
|
||||
@@ -1085,8 +1122,7 @@ abstract class DBSearch
|
||||
}
|
||||
try
|
||||
{
|
||||
// $bBeautifulSQL = self::$m_bTraceQueries || self::$m_bDebugQuery || self::$m_bIndentQueries;
|
||||
$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, true);
|
||||
$sRes = $oSQLQuery->RenderSelect($aOrderSpec, $aScalarArgs, $iLimitCount, $iLimitStart, $bGetCount, $bBeautifulSQL);
|
||||
if ($sClassAlias == '_itop_')
|
||||
{
|
||||
IssueLog::Info('SQL Query (_itop_): '.$sRes);
|
||||
|
||||
@@ -119,13 +119,12 @@ class DisplayableNode extends GraphNode
|
||||
$Alpha = 1.0;
|
||||
$oPdf->SetFillColor(200, 200, 200);
|
||||
$oPdf->setAlpha(1);
|
||||
|
||||
|
||||
$sIconUrl = $this->GetProperty('icon_url');
|
||||
$sIconPath = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-'.utils::GetCurrentEnvironment().'/', $sIconUrl);
|
||||
|
||||
if ($this->GetProperty('source'))
|
||||
{
|
||||
$oPdf->SetLineStyle(array('width' => 2*$fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => array(204, 51, 51)));
|
||||
|
||||
if ($this->GetProperty('source')) {
|
||||
$oPdf->SetLineStyle(array('width' => 2 * $fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => array(204, 51, 51)));
|
||||
$oPdf->Circle($this->x * $fScale, $this->y * $fScale, 16 * 1.25 * $fScale, 0, 360, 'D');
|
||||
}
|
||||
else if ($this->GetProperty('sink'))
|
||||
@@ -133,34 +132,30 @@ class DisplayableNode extends GraphNode
|
||||
$oPdf->SetLineStyle(array('width' => 2*$fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => array(51, 51, 204)));
|
||||
$oPdf->Circle($this->x * $fScale, $this->y * $fScale, 16 * 1.25 * $fScale, 0, 360, 'D');
|
||||
}
|
||||
|
||||
if (!$this->GetProperty('is_reached'))
|
||||
{
|
||||
|
||||
if (!$this->GetProperty('is_reached')) {
|
||||
$sTempImageName = $this->CreateWhiteIcon($oGraph, $sIconPath);
|
||||
if ($sTempImageName != null)
|
||||
{
|
||||
$oPdf->Image($sTempImageName, ($this->x - 16)*$fScale, ($this->y - 16)*$fScale, 32*$fScale, 32*$fScale, 'PNG');
|
||||
if ($sTempImageName != null) {
|
||||
$oPdf->AddImage($sTempImageName, ($this->x - 16) * $fScale, ($this->y - 16) * $fScale, 32 * $fScale, 32 * $fScale, 'PNG');
|
||||
}
|
||||
$Alpha = 0.4;
|
||||
$oPdf->setAlpha($Alpha);
|
||||
}
|
||||
|
||||
$oPdf->Image($sIconPath, ($this->x - 16)*$fScale, ($this->y - 16)*$fScale, 32*$fScale, 32*$fScale);
|
||||
|
||||
|
||||
$oPdf->AddImage($sIconPath, ($this->x - 16) * $fScale, ($this->y - 16) * $fScale, 32 * $fScale, 32 * $fScale);
|
||||
|
||||
$aContextRootCauses = $this->GetProperty('context_root_causes');
|
||||
if (!is_null($aContextRootCauses))
|
||||
{
|
||||
if (!is_null($aContextRootCauses)) {
|
||||
$idx = 0;
|
||||
foreach($aContextRootCauses as $key => $aObjects)
|
||||
{
|
||||
$sgn = 2*($idx %2) -1;
|
||||
$coef = floor((1+$idx)/2) * $sgn;
|
||||
$alpha = $coef*pi()/4 - pi()/2;
|
||||
$x = $this->x * $fScale + cos($alpha) * 16*1.25 * $fScale;
|
||||
$y = $this->y * $fScale + sin($alpha) * 16*1.25 * $fScale;
|
||||
foreach ($aContextRootCauses as $key => $aObjects) {
|
||||
$sgn = 2 * ($idx % 2) - 1;
|
||||
$coef = floor((1 + $idx) / 2) * $sgn;
|
||||
$alpha = $coef * pi() / 4 - pi() / 2;
|
||||
$x = $this->x * $fScale + cos($alpha) * 16 * 1.25 * $fScale;
|
||||
$y = $this->y * $fScale + sin($alpha) * 16 * 1.25 * $fScale;
|
||||
$l = 32 * $fScale / 3;
|
||||
$sIconPath = APPROOT.'env-'.utils::GetCurrentEnvironment().'/'.$aContextDefs[$key]['icon'];
|
||||
$oPdf->Image($sIconPath, $x - $l/2, $y - $l/2, $l, $l);
|
||||
$oPdf->AddImage($sIconPath, $x - $l / 2, $y - $l / 2, $l, $l);
|
||||
$idx++;
|
||||
}
|
||||
}
|
||||
@@ -779,8 +774,8 @@ class DisplayableGroupNode extends DisplayableNode
|
||||
{
|
||||
$aBorderColor = array(200, 200, 200);
|
||||
}
|
||||
$oPdf->SetLineStyle(array('width' => 2*$fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => $aBorderColor));
|
||||
|
||||
$oPdf->SetLineStyle(array('width' => 2 * $fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => $aBorderColor));
|
||||
|
||||
$sIconUrl = $this->GetProperty('icon_url');
|
||||
$sIconPath = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-'.utils::GetCurrentEnvironment().'/', $sIconUrl);
|
||||
$oPdf->SetAlpha(1);
|
||||
@@ -794,13 +789,13 @@ class DisplayableGroupNode extends DisplayableNode
|
||||
{
|
||||
$oPdf->SetAlpha(0.4);
|
||||
}
|
||||
$oPdf->Image($sIconPath, ($this->x - 17)*$fScale, ($this->y - 17)*$fScale, 16*$fScale, 16*$fScale);
|
||||
$oPdf->Image($sIconPath, ($this->x + 1)*$fScale, ($this->y - 17)*$fScale, 16*$fScale, 16*$fScale);
|
||||
$oPdf->Image($sIconPath, ($this->x -8)*$fScale, ($this->y +1)*$fScale, 16*$fScale, 16*$fScale);
|
||||
$oPdf->AddImage($sIconPath, ($this->x - 17) * $fScale, ($this->y - 17) * $fScale, 16 * $fScale, 16 * $fScale);
|
||||
$oPdf->AddImage($sIconPath, ($this->x + 1) * $fScale, ($this->y - 17) * $fScale, 16 * $fScale, 16 * $fScale);
|
||||
$oPdf->AddImage($sIconPath, ($this->x - 8) * $fScale, ($this->y + 1) * $fScale, 16 * $fScale, 16 * $fScale);
|
||||
$oPdf->SetFontParams('', 24 * $fScale, '', true);
|
||||
$width = $oPdf->GetStringWidth($this->GetProperty('label'));
|
||||
$oPdf->SetTextColor(0, 0, 0);
|
||||
$oPdf->Text($this->x*$fScale - $width/2, ($this->y + 25)*$fScale, $this->GetProperty('label'));
|
||||
$oPdf->Text($this->x * $fScale - $width / 2, ($this->y + 25) * $fScale, $this->GetProperty('label'));
|
||||
}
|
||||
|
||||
public function GetTooltip($aContextDefs)
|
||||
@@ -1185,7 +1180,7 @@ class DisplayableGraph extends SimpleGraph
|
||||
|
||||
return json_encode($aData);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sort class "codes" based on their localized name
|
||||
* @param string $sClass1
|
||||
@@ -1196,12 +1191,12 @@ class DisplayableGraph extends SimpleGraph
|
||||
{
|
||||
return strcasecmp(MetaModel::GetName($sClass1), MetaModel::GetName($sClass2));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renders the graph in a PDF document: centered in the current page
|
||||
* @param PDFPage $oPage The PDFPage representing the PDF document to draw into
|
||||
* @param string $sComments An optional comment to display next to the graph (HTML entities will be escaped, \n replaced by <br/>)
|
||||
* @param string $sContextKey The key to fetch the queries in the configuration. Example: itop-tickets/relation_context/UserRequest/impacts/down
|
||||
* @param string $sContextKey The key to fetch the queries in the configuration. Example: itop-tickets/relation_context/UserRequest/impacts/down
|
||||
* @param float $xMin Left coordinate of the bounding box to display the graph
|
||||
* @param float $xMax Right coordinate of the bounding box to display the graph
|
||||
* @param float $yMin Top coordinate of the bounding box to display the graph
|
||||
@@ -1329,20 +1324,18 @@ class DisplayableGraph extends SimpleGraph
|
||||
$yPos = $yMin + $fPadding;
|
||||
$oPdf->SetFillColor(225, 225, 225);
|
||||
$oPdf->Cell($fIconSize + $fPadding + $fMaxWidth, $fIconSize + $fPadding, Dict::S('UI:Relation:Key'), 0 /* border */, 1 /* ln */, 'C', true /* fill */);
|
||||
$yPos += $fIconSize + 2*$fPadding;
|
||||
foreach($aClasses as $sClass => $sLabel)
|
||||
{
|
||||
$yPos += $fIconSize + 2 * $fPadding;
|
||||
foreach ($aClasses as $sClass => $sLabel) {
|
||||
$oPdf->SetX($xMin + $fIconSize + $fPadding);
|
||||
$oPdf->Cell(0, $fIconSize + 2*$fPadding, $sLabel, 0 /* border */, 1 /* ln */);
|
||||
$oPdf->Image($aIcons[$sClass], $xMin+1, $yPos, $fIconSize, $fIconSize);
|
||||
$yPos += $fIconSize + 2*$fPadding;
|
||||
$oPdf->Cell(0, $fIconSize + 2 * $fPadding, $sLabel, 0 /* border */, 1 /* ln */);
|
||||
$oPdf->AddImage($aIcons[$sClass], $xMin + 1, $yPos, $fIconSize, $fIconSize);
|
||||
$yPos += $fIconSize + 2 * $fPadding;
|
||||
}
|
||||
foreach($aContexts as $key => $sLabel)
|
||||
{
|
||||
foreach ($aContexts as $key => $sLabel) {
|
||||
$oPdf->SetX($xMin + $fIconSize + $fPadding);
|
||||
$oPdf->Cell(0, $fIconSize + 2*$fPadding, $sLabel, 0 /* border */, 1 /* ln */);
|
||||
$oPdf->Image($aContextIcons[$key], $xMin+1+$fIconSize*0.125, $yPos+$fIconSize*0.125, $fIconSize*0.75, $fIconSize*0.75);
|
||||
$yPos += $fIconSize + 2*$fPadding;
|
||||
$oPdf->Cell(0, $fIconSize + 2 * $fPadding, $sLabel, 0 /* border */, 1 /* ln */);
|
||||
$oPdf->AddImage($aContextIcons[$key], $xMin + 1 + $fIconSize * 0.125, $yPos + $fIconSize * 0.125, $fIconSize * 0.75, $fIconSize * 0.75);
|
||||
$yPos += $fIconSize + 2 * $fPadding;
|
||||
}
|
||||
$oPdf->Rect($xMin, $yMin, $fMaxWidth + $fIconSize + 3*$fPadding, $yMax - $yMin, 'D');
|
||||
|
||||
|
||||
@@ -198,31 +198,29 @@ class InlineImage extends DBObject
|
||||
$sOQL = 'SELECT InlineImage WHERE temp_id = :temp_id';
|
||||
$oSearch = DBObjectSearch::FromOQL($sOQL);
|
||||
$oSet = new DBObjectSet($oSearch, array(), array('temp_id' => $sTempId));
|
||||
$aInlineImagesId = array();
|
||||
while($oInlineImage = $oSet->Fetch())
|
||||
{
|
||||
$aInlineImagesId[] = $oInlineImage->GetKey();
|
||||
$aInlineImagesId = array();
|
||||
while ($oInlineImage = $oSet->Fetch()) {
|
||||
$aInlineImagesId[] = $oInlineImage->GetKey();
|
||||
$oInlineImage->SetItem($oObject);
|
||||
$oInlineImage->Set('temp_id', '');
|
||||
$oInlineImage->DBUpdate();
|
||||
}
|
||||
IssueLog::Trace('FinalizeInlineImages (see $aInlineImagesId for the id list)', 'InlineImage', array(
|
||||
'$sObjectClass' => get_class($oObject),
|
||||
'$sTransactionId' => $iTransactionId,
|
||||
'$sTempId' => $sTempId,
|
||||
'$aInlineImagesId' => $aInlineImagesId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
IssueLog::Trace('FinalizeInlineImages (see $aInlineImagesId for the id list)', LogChannels::INLINE_IMAGE, array(
|
||||
'$sObjectClass' => get_class($oObject),
|
||||
'$sTransactionId' => $iTransactionId,
|
||||
'$sTempId' => $sTempId,
|
||||
'$aInlineImagesId' => $aInlineImagesId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
}
|
||||
else
|
||||
{
|
||||
IssueLog::Trace('FinalizeInlineImages "error" $iTransactionId is null', 'InlineImage', array(
|
||||
'$sObjectClass' => get_class($oObject),
|
||||
'$sTransactionId' => $iTransactionId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
else {
|
||||
IssueLog::Trace('FinalizeInlineImages "error" $iTransactionId is null', LogChannels::INLINE_IMAGE, array(
|
||||
'$sObjectClass' => get_class($oObject),
|
||||
'$sTransactionId' => $iTransactionId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,12 +251,12 @@ class InlineImage extends DBObject
|
||||
$aInlineImagesId[] = $oInlineImage->GetKey();
|
||||
$oInlineImage->DBDelete();
|
||||
}
|
||||
IssueLog::Trace('OnFormCancel', 'InlineImage', array(
|
||||
'$sTempId' => $sTempId,
|
||||
'$aInlineImagesId' => $aInlineImagesId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
IssueLog::Trace('OnFormCancel', LogChannels::INLINE_IMAGE, array(
|
||||
'$sTempId' => $sTempId,
|
||||
'$aInlineImagesId' => $aInlineImagesId,
|
||||
'$sUser' => UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -608,17 +606,17 @@ JS
|
||||
*/
|
||||
protected function AfterInsert()
|
||||
{
|
||||
IssueLog::Trace(__METHOD__, 'InlineImage', array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
IssueLog::Trace(__METHOD__, LogChannels::INLINE_IMAGE, array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
));
|
||||
|
||||
parent::AfterInsert();
|
||||
@@ -629,17 +627,17 @@ JS
|
||||
*/
|
||||
protected function AfterUpdate()
|
||||
{
|
||||
IssueLog::Trace(__METHOD__, 'InlineImage', array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
IssueLog::Trace(__METHOD__, LogChannels::INLINE_IMAGE, array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
));
|
||||
|
||||
parent::AfterUpdate();
|
||||
@@ -650,17 +648,17 @@ JS
|
||||
*/
|
||||
protected function AfterDelete()
|
||||
{
|
||||
IssueLog::Trace(__METHOD__, 'InlineImage', array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
IssueLog::Trace(__METHOD__, LogChannels::INLINE_IMAGE, array(
|
||||
'id' => $this->GetKey(),
|
||||
'expire' => $this->Get('expire'),
|
||||
'temp_id' => $this->Get('temp_id'),
|
||||
'item_class' => $this->Get('item_class'),
|
||||
'item_id' => $this->Get('item_id'),
|
||||
'item_org_id' => $this->Get('item_org_id'),
|
||||
'secret' => $this->Get('secret'),
|
||||
'user' => $sUser = UserRights::GetUser(),
|
||||
'HTTP_REFERER' => @$_SERVER['HTTP_REFERER'],
|
||||
'REQUEST_URI' => @$_SERVER['REQUEST_URI'],
|
||||
));
|
||||
|
||||
parent::AfterDelete();
|
||||
|
||||
@@ -399,10 +399,10 @@ class MonthlyRotatingLogFileNameBuilder extends RotatingLogFileNameBuilder
|
||||
*/
|
||||
protected function GetFileSuffix($oDate)
|
||||
{
|
||||
$sWeekYear = $oDate->format('o');
|
||||
$sWeekNumber = $oDate->format('m');
|
||||
$sMonthYear = $oDate->format('o');
|
||||
$sMonthNumber = $oDate->format('m');
|
||||
|
||||
return $sWeekYear.'-month'.$sWeekNumber;
|
||||
return $sMonthYear.'-month'.$sMonthNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -503,6 +503,7 @@ class FileLog
|
||||
protected function Write($sText, $sLevel = '', $sChannel = '', $aContext = array())
|
||||
{
|
||||
$sTextPrefix = empty($sLevel) ? '' : (str_pad($sLevel, 7).' | ');
|
||||
$sTextPrefix .= str_pad(UserRights::GetUserId(), 5)." | ";
|
||||
$sTextSuffix = empty($sChannel) ? '' : " | $sChannel";
|
||||
$sText = "{$sTextPrefix}{$sText}{$sTextSuffix}";
|
||||
$sLogFilePath = $this->oFileNameBuilder->GetLogFilePath();
|
||||
@@ -517,12 +518,9 @@ class FileLog
|
||||
{
|
||||
flock($hLogFile, LOCK_EX);
|
||||
$sDate = date('Y-m-d H:i:s');
|
||||
if (empty($aContext))
|
||||
{
|
||||
if (empty($aContext)) {
|
||||
fwrite($hLogFile, "$sDate | $sText\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sContext = var_export($aContext, true);
|
||||
fwrite($hLogFile, "$sDate | $sText\n$sContext\n");
|
||||
}
|
||||
@@ -533,32 +531,53 @@ class FileLog
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple enum like class to factorize channels values as constants
|
||||
* Channels are used especially as parameters in {@see \LogAPI} methods
|
||||
*
|
||||
* @since 2.7.5 3.0.0 N°4012
|
||||
*/
|
||||
class LogChannels
|
||||
{
|
||||
public const CLI = 'CLI';
|
||||
public const CONSOLE = 'console';
|
||||
public const DEADLOCK = 'DeadLock';
|
||||
public const INLINE_IMAGE = 'InlineImage';
|
||||
public const PORTAL = 'portal';
|
||||
}
|
||||
|
||||
|
||||
abstract class LogAPI
|
||||
{
|
||||
const CHANNEL_DEFAULT = '';
|
||||
public const CHANNEL_DEFAULT = '';
|
||||
|
||||
const LEVEL_ERROR = 'Error';
|
||||
const LEVEL_WARNING = 'Warning';
|
||||
const LEVEL_INFO = 'Info';
|
||||
const LEVEL_OK = 'Ok';
|
||||
const LEVEL_DEBUG = 'Debug';
|
||||
const LEVEL_TRACE = 'Trace';
|
||||
public const LEVEL_ERROR = 'Error';
|
||||
public const LEVEL_WARNING = 'Warning';
|
||||
public const LEVEL_INFO = 'Info';
|
||||
public const LEVEL_OK = 'Ok';
|
||||
public const LEVEL_DEBUG = 'Debug';
|
||||
public const LEVEL_TRACE = 'Trace';
|
||||
/**
|
||||
* @var string default log level, can be overrided
|
||||
* @var string default log level
|
||||
* @see GetMinLogLevel
|
||||
* @used-by GetLevelDefault
|
||||
* @since 2.7.1 N°2977
|
||||
*/
|
||||
const LEVEL_DEFAULT = self::LEVEL_OK;
|
||||
public const LEVEL_DEFAULT = self::LEVEL_OK;
|
||||
|
||||
protected static $aLevelsPriority = array(
|
||||
self::LEVEL_ERROR => 400,
|
||||
self::LEVEL_ERROR => 400,
|
||||
self::LEVEL_WARNING => 300,
|
||||
self::LEVEL_INFO => 200,
|
||||
self::LEVEL_OK => 200,
|
||||
self::LEVEL_DEBUG => 100,
|
||||
self::LEVEL_TRACE => 50,
|
||||
self::LEVEL_INFO => 200,
|
||||
self::LEVEL_OK => 200,
|
||||
self::LEVEL_DEBUG => 100,
|
||||
self::LEVEL_TRACE => 50,
|
||||
);
|
||||
|
||||
/**
|
||||
* @var \Config attribute allowing to mock config in the tests
|
||||
*/
|
||||
protected static $m_oMockMetaModelConfig = null;
|
||||
|
||||
public static function Enable($sTargetFile)
|
||||
@@ -567,7 +586,7 @@ abstract class LogAPI
|
||||
static::$m_oFileLog = new FileLog($sTargetFile);
|
||||
}
|
||||
|
||||
public static function MockStaticObjects($oFileLog, $oMetaModelConfig=null)
|
||||
public static function MockStaticObjects($oFileLog, $oMetaModelConfig = null)
|
||||
{
|
||||
static::$m_oFileLog = $oFileLog;
|
||||
static::$m_oMockMetaModelConfig = $oMetaModelConfig;
|
||||
@@ -603,85 +622,115 @@ abstract class LogAPI
|
||||
static::Log(self::LEVEL_TRACE, $sMessage, $sChannel, $aContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \ConfigException if log wrongly configured
|
||||
*/
|
||||
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array())
|
||||
{
|
||||
if (! static::$m_oFileLog)
|
||||
{
|
||||
if (!static::$m_oFileLog) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! isset(self::$aLevelsPriority[$sLevel]))
|
||||
{
|
||||
if (!isset(self::$aLevelsPriority[$sLevel])) {
|
||||
IssueLog::Error("invalid log level '{$sLevel}'");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_null($sChannel))
|
||||
{
|
||||
if (is_null($sChannel)) {
|
||||
$sChannel = static::CHANNEL_DEFAULT;
|
||||
}
|
||||
|
||||
$sMinLogLevel = self::GetMinLogLevel($sChannel);
|
||||
|
||||
if ($sMinLogLevel === false || $sMinLogLevel === 'false')
|
||||
{
|
||||
if (!static::IsLogLevelEnabled($sLevel, $sChannel)) {
|
||||
return;
|
||||
}
|
||||
if (is_string($sMinLogLevel))
|
||||
{
|
||||
if (! isset(self::$aLevelsPriority[$sMinLogLevel]))
|
||||
{
|
||||
throw new Exception("invalid configuration for log_level '{$sMinLogLevel}' is not within the list: ".implode(',', array_keys(self::$aLevelsPriority)));
|
||||
}
|
||||
elseif (self::$aLevelsPriority[$sLevel] < self::$aLevelsPriority[$sMinLogLevel])
|
||||
{
|
||||
//priority too low regarding the conf, do not log this
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static::$m_oFileLog->$sLevel($sMessage, $sChannel, $aContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sChannel
|
||||
*
|
||||
* @return string one of the LEVEL_* const value
|
||||
* @uses \LogAPI::LEVEL_DEFAULT
|
||||
* @throws \ConfigException if log wrongly configured
|
||||
* @uses GetMinLogLevel
|
||||
*/
|
||||
private static function GetMinLogLevel($sChannel)
|
||||
final public static function IsLogLevelEnabled(string $sLevel, string $sChannel): bool
|
||||
{
|
||||
$oConfig = (static::$m_oMockMetaModelConfig !== null) ? static::$m_oMockMetaModelConfig : \MetaModel::GetConfig();
|
||||
if (!$oConfig instanceof Config)
|
||||
{
|
||||
return static::LEVEL_DEFAULT;
|
||||
$sMinLogLevel = self::GetMinLogLevel($sChannel);
|
||||
|
||||
if ($sMinLogLevel === false || $sMinLogLevel === 'false') {
|
||||
return false;
|
||||
}
|
||||
if (!is_string($sMinLogLevel)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isset(self::$aLevelsPriority[$sMinLogLevel])) {
|
||||
throw new ConfigException("invalid configuration for log_level '{$sMinLogLevel}' is not within the list: ".implode(',', array_keys(self::$aLevelsPriority)));
|
||||
} elseif (self::$aLevelsPriority[$sLevel] < self::$aLevelsPriority[$sMinLogLevel]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sChannel
|
||||
*
|
||||
* @return string one of the LEVEL_* const value : the one configured it if exists, otherwise default log level for this channel
|
||||
* Config can be done globally : `'log_level_min' => LogAPI::LEVEL_TRACE,`
|
||||
* Or per channel : `'log_level_min' => ['InlineImage' => LogAPI::LEVEL_TRACE, 'UserRequest' => LogAPI::LEVEL_TRACE],`
|
||||
*
|
||||
* @uses \LogAPI::GetConfig()
|
||||
* @uses \LogAPI::GetLevelDefault
|
||||
*/
|
||||
protected static function GetMinLogLevel($sChannel)
|
||||
{
|
||||
$oConfig = static::GetConfig();
|
||||
if (!$oConfig instanceof Config) {
|
||||
return static::GetLevelDefault();
|
||||
}
|
||||
|
||||
$sLogLevelMin = $oConfig->Get('log_level_min');
|
||||
|
||||
if (empty($sLogLevelMin))
|
||||
{
|
||||
return static::LEVEL_DEFAULT;
|
||||
if (empty($sLogLevelMin)) {
|
||||
return static::GetLevelDefault();
|
||||
}
|
||||
|
||||
if (!is_array($sLogLevelMin))
|
||||
{
|
||||
if (!is_array($sLogLevelMin)) {
|
||||
return $sLogLevelMin;
|
||||
}
|
||||
|
||||
if (isset($sLogLevelMin[$sChannel]))
|
||||
{
|
||||
if (isset($sLogLevelMin[$sChannel])) {
|
||||
return $sLogLevelMin[$sChannel];
|
||||
}
|
||||
|
||||
if (isset($sLogLevelMin[static::CHANNEL_DEFAULT]))
|
||||
{
|
||||
if (isset($sLogLevelMin[static::CHANNEL_DEFAULT])) {
|
||||
return $sLogLevelMin[$sChannel];
|
||||
}
|
||||
|
||||
return static::LEVEL_DEFAULT;
|
||||
return static::GetLevelDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* @uses m_oMockMetaModelConfig if defined
|
||||
* @uses \MetaModel::GetConfig()
|
||||
*/
|
||||
protected static function GetConfig(): ?Config
|
||||
{
|
||||
return static::$m_oMockMetaModelConfig ?? \MetaModel::GetConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* A method to override if default log level needs to be computed. Otherwise simply override the {@see LEVEL_DEFAULT} constant
|
||||
*
|
||||
* @used-by GetMinLogLevel
|
||||
* @uses \LogAPI::LEVEL_DEFAULT
|
||||
*
|
||||
* @since 3.0.0 N°3731
|
||||
*/
|
||||
protected static function GetLevelDefault(): string
|
||||
{
|
||||
return static::LEVEL_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
class SetupLog extends LogAPI
|
||||
@@ -741,7 +790,7 @@ class DeadLockLog extends LogAPI
|
||||
return self::CHANNEL_WAIT_TIMEOUT;
|
||||
break;
|
||||
case 1213:
|
||||
return self::CHANNEL_DEADLOCK_FOUND;
|
||||
return self::CHANNEL_DEADLOCK_FOUND;
|
||||
break;
|
||||
default:
|
||||
return self::CHANNEL_DEFAULT;
|
||||
@@ -750,17 +799,136 @@ class DeadLockLog extends LogAPI
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $iMySQLErrNo will be converted to channel using {@link GetChannelFromMysqlErrorNo}
|
||||
* @param string $sLevel
|
||||
* @param string $sMessage
|
||||
* @param null $iMysqlErroNo
|
||||
* @param int $iMysqlErrorNumber will be converted to channel using {@link GetChannelFromMysqlErrorNo}
|
||||
* @param array $aContext
|
||||
*
|
||||
* @throws \Exception
|
||||
* @noinspection PhpParameterNameChangedDuringInheritanceInspection
|
||||
*
|
||||
* @since 2.7.1 method creation
|
||||
* @since 2.7.5 3.0.0 rename param names and fix phpdoc (thanks Hipska !)
|
||||
*/
|
||||
public static function Log($iMySQLErrNo, $sMessage, $iMysqlErroNo = null, $aContext = array())
|
||||
public static function Log($sLevel, $sMessage, $iMysqlErrorNumber = null, $aContext = array())
|
||||
{
|
||||
$sChannel = self::GetChannelFromMysqlErrorNo($iMysqlErroNo);
|
||||
parent::Log($iMySQLErrNo, $sMessage, $sChannel, $aContext);
|
||||
$sChannel = self::GetChannelFromMysqlErrorNo($iMysqlErrorNumber);
|
||||
parent::Log($sLevel, $sMessage, $sChannel, $aContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @since 3.0.0 N°3731
|
||||
*/
|
||||
class DeprecatedCallsLog extends LogAPI
|
||||
{
|
||||
public const ENUM_CHANNEL_PHP_METHOD = 'deprecated-php-method';
|
||||
public const ENUM_CHANNEL_FILE = 'deprecated-file';
|
||||
public const CHANNEL_DEFAULT = self::ENUM_CHANNEL_PHP_METHOD;
|
||||
|
||||
public const LEVEL_DEFAULT = self::LEVEL_ERROR;
|
||||
|
||||
/** @var \FileLog we want our own instance ! */
|
||||
protected static $m_oFileLog = null;
|
||||
|
||||
public static function Enable($sTargetFile = null): void
|
||||
{
|
||||
if (empty($sTargetFile)) {
|
||||
$sTargetFile = APPROOT.'log/deprecated-calls.log';
|
||||
}
|
||||
parent::Enable($sTargetFile);
|
||||
}
|
||||
|
||||
protected static function GetLevelDefault(): string
|
||||
{
|
||||
if (utils::IsDevelopmentEnvironment()) {
|
||||
return static::LEVEL_DEBUG;
|
||||
}
|
||||
|
||||
return static::LEVEL_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \ConfigException
|
||||
* @link https://www.php.net/debug_backtrace
|
||||
* @uses \debug_backtrace()
|
||||
*/
|
||||
public static function NotifyDeprecatedFile(?string $sAdditionalMessage = null): void
|
||||
{
|
||||
if (!static::IsLogLevelEnabled(self::LEVEL_WARNING, self::ENUM_CHANNEL_FILE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
|
||||
$sDeprecatedFile = $aStack[0]['file'];
|
||||
if (array_key_exists(1, $aStack)) {
|
||||
$sCallerFile = $aStack[1]['file'];
|
||||
$sCallerLine = $aStack[1]['line'];
|
||||
} else {
|
||||
$sCallerFile = 'N/A';
|
||||
$sCallerLine = 'N/A';
|
||||
}
|
||||
|
||||
$sMessage = "{$sCallerFile} L{$sCallerLine} including/requiring {$sDeprecatedFile}";
|
||||
|
||||
if (!is_null($sAdditionalMessage)) {
|
||||
$sMessage .= ' : '.$sAdditionalMessage;
|
||||
}
|
||||
|
||||
static::Warning($sMessage, static::ENUM_CHANNEL_FILE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $sAdditionalMessage
|
||||
*
|
||||
* @link https://www.php.net/debug_backtrace
|
||||
* @uses \debug_backtrace()
|
||||
*/
|
||||
public static function NotifyDeprecatedPhpMethod(?string $sAdditionalMessage = null): void
|
||||
{
|
||||
try {
|
||||
if (!static::IsLogLevelEnabled(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_METHOD)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (ConfigException $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
|
||||
|
||||
$sDeprecatedObject = $aStack[1]['class'];
|
||||
$sDeprecatedMethod = $aStack[1]['function'];
|
||||
$sCallerFile = $aStack[1]['file'];
|
||||
$sCallerLine = $aStack[1]['line'];
|
||||
$sMessage = "Call to {$sDeprecatedObject}::{$sDeprecatedMethod} in {$sCallerFile}#L{$sCallerLine}";
|
||||
|
||||
if (array_key_exists(2, $aStack)) {
|
||||
$sCallerObject = $aStack[2]['class'];
|
||||
$sCallerMethod = $aStack[2]['function'];
|
||||
$sMessage .= " ({$sCallerObject}::{$sCallerMethod})";
|
||||
}
|
||||
|
||||
if (!is_null($sAdditionalMessage)) {
|
||||
$sMessage .= ' : '.$sAdditionalMessage;
|
||||
}
|
||||
|
||||
static::Warning($sMessage, static::ENUM_CHANNEL_PHP_METHOD);
|
||||
}
|
||||
|
||||
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array()): void
|
||||
{
|
||||
if (utils::IsDevelopmentEnvironment()) {
|
||||
trigger_error($sMessage, E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
try {
|
||||
parent::Log($sLevel, $sMessage, $sChannel, $aContext);
|
||||
}
|
||||
catch (ConfigException $e) {
|
||||
// nothing much we can do... and we don't want to crash the caller !
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -769,6 +937,7 @@ class LogFileRotationProcess implements iScheduledProcess
|
||||
{
|
||||
/**
|
||||
* Cannot get this list from anywhere as log file name is provided by the caller using LogAPI::Enable
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
const LOGFILES_TO_ROTATE = array(
|
||||
|
||||
@@ -775,17 +775,73 @@ abstract class MetaModel
|
||||
}
|
||||
|
||||
return array($sFormat, $nameRawSpec);
|
||||
}
|
||||
elseif (empty($nameRawSpec))
|
||||
{
|
||||
} elseif (empty($nameRawSpec)) {
|
||||
return array($sClass, array());
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// string -> attcode
|
||||
return array('%1$s', array($nameRawSpec));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $sClass
|
||||
* @param bool $bWithAttributeDefinition
|
||||
*
|
||||
* @return array of attribute codes used by friendlyname
|
||||
* @throws \CoreException
|
||||
* @since 3.0.0
|
||||
*/
|
||||
final public static function GetNameAttributes(string $sClass, $bWithAttributeDefinition = false): array
|
||||
{
|
||||
self::_check_subclass($sClass);
|
||||
$rawNameAttCodes = self::$m_aClassParams[$sClass]["name_attcode"];
|
||||
$aNameAttCodes = [];
|
||||
if (!is_array($rawNameAttCodes)) {
|
||||
if (self::IsValidAttCode($sClass, $rawNameAttCodes)) {
|
||||
$aNameAttCodes[] = $rawNameAttCodes;
|
||||
}
|
||||
} else {
|
||||
$aNameAttCodes = $rawNameAttCodes;
|
||||
}
|
||||
|
||||
if ($bWithAttributeDefinition) {
|
||||
$aResults = [];
|
||||
foreach ($aNameAttCodes as $sAttCode) {
|
||||
$aResults[$sAttCode] = self::GetAttributeDef($sClass, $sAttCode);
|
||||
}
|
||||
|
||||
return $aResults;
|
||||
}
|
||||
|
||||
return $aNameAttCodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param false $bWithAttributeDefinition
|
||||
*
|
||||
* @return array of attributes to always reload in tables
|
||||
* @throws \CoreException
|
||||
* @since 3.0.0
|
||||
*/
|
||||
final public static function GetAttributesToAlwaysLoadInTables(string $sClass, $bWithAttributeDefinition = false): array
|
||||
{
|
||||
$aResults = [];
|
||||
foreach (self::GetAttributesList($sClass) as $sAttCode) {
|
||||
$oAttDef = self::GetAttributeDef($sClass, $sAttCode);
|
||||
if ($oAttDef->AlwaysLoadInTables()) {
|
||||
if ($bWithAttributeDefinition) {
|
||||
$aResults[$sAttCode] = $oAttDef;
|
||||
} else {
|
||||
$aResults[] = $sAttCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $aResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
*
|
||||
@@ -1060,6 +1116,7 @@ abstract class MetaModel
|
||||
*/
|
||||
final public static function GetFilterCodeOrigin($sClass, $sAttCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
self::_check_subclass($sClass);
|
||||
|
||||
return self::$m_aFilterOrigins[$sClass][$sAttCode];
|
||||
@@ -1419,7 +1476,10 @@ abstract class MetaModel
|
||||
*/
|
||||
final public static function GetFiltersList($sClass)
|
||||
{
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
self::_check_subclass($sClass);
|
||||
|
||||
return array_keys(self::$m_aFilterDefs[$sClass]);
|
||||
}
|
||||
|
||||
@@ -1526,6 +1586,8 @@ abstract class MetaModel
|
||||
*/
|
||||
final public static function IsValidFilterCode($sClass, $sFilterCode)
|
||||
{
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
if (!array_key_exists($sClass, self::$m_aFilterDefs)) {
|
||||
return false;
|
||||
}
|
||||
@@ -1837,7 +1899,10 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function GetClassFilterDefs($sClass)
|
||||
{
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
self::_check_subclass($sClass);
|
||||
|
||||
return self::$m_aFilterDefs[$sClass];
|
||||
}
|
||||
|
||||
@@ -1852,6 +1917,7 @@ abstract class MetaModel
|
||||
*/
|
||||
final public static function GetClassFilterDef($sClass, $sFilterCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
self::_check_subclass($sClass);
|
||||
if (!array_key_exists($sFilterCode, self::$m_aFilterDefs[$sClass])) {
|
||||
throw new CoreException("Unknown filter code '$sFilterCode' for class '$sClass'");
|
||||
@@ -1871,9 +1937,9 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function GetFilterLabel($sClass, $sFilterCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
$oFilter = self::GetClassFilterDef($sClass, $sFilterCode);
|
||||
if ($oFilter)
|
||||
{
|
||||
if ($oFilter) {
|
||||
return $oFilter->GetLabel();
|
||||
}
|
||||
|
||||
@@ -1890,11 +1956,12 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function GetFilterDescription($sClass, $sFilterCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
$oFilter = self::GetClassFilterDef($sClass, $sFilterCode);
|
||||
if ($oFilter)
|
||||
{
|
||||
if ($oFilter) {
|
||||
return $oFilter->GetDescription();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -1908,11 +1975,12 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function GetFilterOperators($sClass, $sFilterCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
$oFilter = self::GetClassFilterDef($sClass, $sFilterCode);
|
||||
if ($oFilter)
|
||||
{
|
||||
if ($oFilter) {
|
||||
return $oFilter->GetOperators();
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
@@ -1926,9 +1994,9 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function GetFilterLooseOperator($sClass, $sFilterCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
$oFilter = self::GetClassFilterDef($sClass, $sFilterCode);
|
||||
if ($oFilter)
|
||||
{
|
||||
if ($oFilter) {
|
||||
return $oFilter->GetLooseOperator();
|
||||
}
|
||||
|
||||
@@ -1946,9 +2014,9 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function GetFilterOpDescription($sClass, $sFilterCode, $sOpCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
$oFilter = self::GetClassFilterDef($sClass, $sFilterCode);
|
||||
if ($oFilter)
|
||||
{
|
||||
if ($oFilter) {
|
||||
return $oFilter->GetOpDescription($sOpCode);
|
||||
}
|
||||
|
||||
@@ -1963,6 +2031,8 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function GetFilterHTMLInput($sFilterCode)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
|
||||
return "<INPUT name=\"$sFilterCode\">";
|
||||
}
|
||||
|
||||
@@ -2137,24 +2207,21 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function EnumRelations($sClass = '')
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('Use EnumRelationsEx instead');
|
||||
$aResult = array_keys(self::$m_aRelationInfos);
|
||||
if (!empty($sClass))
|
||||
{
|
||||
if (!empty($sClass)) {
|
||||
// Return only the relations that have a meaning (i.e. for which at least one query is defined)
|
||||
// for the specified class
|
||||
$aClassRelations = array();
|
||||
foreach($aResult as $sRelCode)
|
||||
{
|
||||
foreach ($aResult as $sRelCode) {
|
||||
$aQueriesDown = self::EnumRelationQueries($sClass, $sRelCode);
|
||||
if (count($aQueriesDown) > 0)
|
||||
{
|
||||
if (count($aQueriesDown) > 0) {
|
||||
$aClassRelations[] = $sRelCode;
|
||||
}
|
||||
// Temporary patch: until the impact analysis GUI gets rewritten,
|
||||
// let's consider that "depends on" is equivalent to "impacts/up"
|
||||
// The current patch has been implemented in DBObject and MetaModel
|
||||
if ($sRelCode == 'impacts')
|
||||
{
|
||||
if ($sRelCode == 'impacts') {
|
||||
$aQueriesUp = self::EnumRelationQueries($sClass, 'impacts', false);
|
||||
if (count($aQueriesUp) > 0)
|
||||
{
|
||||
@@ -5512,7 +5579,7 @@ abstract class MetaModel
|
||||
$aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable` ADD $sKeyFieldDefinition";
|
||||
if (!$bTableToCreate)
|
||||
{
|
||||
$aAlterTableItems[$sTable][$sKeyField] = "ADD $sKeyFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sKeyField] = "ADD $sKeyFieldDefinition";
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -5525,7 +5592,7 @@ abstract class MetaModel
|
||||
$aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable`, DROP PRIMARY KEY, ADD PRIMARY key(`$sKeyField`)";
|
||||
if (!$bTableToCreate)
|
||||
{
|
||||
$aAlterTableItems[$sTable][$sKeyField] = "CHANGE `$sKeyField` $sKeyFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sKeyField] = "CHANGE `$sKeyField` $sKeyFieldDefinition";
|
||||
}
|
||||
}
|
||||
if (self::IsAutoIncrementKey($sClass) && !CMDBSource::IsAutoIncrement($sTable, $sKeyField))
|
||||
@@ -5534,7 +5601,7 @@ abstract class MetaModel
|
||||
$aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable` CHANGE `$sKeyField` $sKeyFieldDefinition";
|
||||
if (!$bTableToCreate)
|
||||
{
|
||||
$aAlterTableItems[$sTable][$sKeyField] = "CHANGE `$sKeyField` $sKeyFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sKeyField] = "CHANGE `$sKeyField` $sKeyFieldDefinition";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5597,7 +5664,7 @@ abstract class MetaModel
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAlterTableItems[$sTable][$sField] = "ADD $sFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sField] = "ADD $sFieldDefinition";
|
||||
$aAdditionalRequests = self::GetAdditionalRequestAfterAlter($sClass, $sTable, $sField);
|
||||
if (!empty($aAdditionalRequests))
|
||||
{
|
||||
@@ -5641,7 +5708,7 @@ abstract class MetaModel
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAlterTableItems[$sTable][] = "ADD $sIndexType `$sIndexName` ($sColumns)";
|
||||
$aAlterTableItems[$sTable]['index'][] = "ADD $sIndexType `$sIndexName` ($sColumns)";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5682,7 +5749,7 @@ abstract class MetaModel
|
||||
if (CMDBSource::HasIndex($sTable, $sField))
|
||||
{
|
||||
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` DROP INDEX `$sIndexName`";
|
||||
$aAlterTableItems[$sTable][] = "DROP INDEX `$sIndexName`";
|
||||
$aAlterTableItems[$sTable]['index'][] = "DROP INDEX `$sIndexName`";
|
||||
}
|
||||
$sSugFixAfterChange = "ALTER TABLE `$sTable` ADD $sIndexType `$sIndexName` ($sColumns)";
|
||||
$sAlterTableItemsAfterChange = "ADD $sIndexType `$sIndexName` ($sColumns)";
|
||||
@@ -5696,7 +5763,7 @@ abstract class MetaModel
|
||||
{
|
||||
$aErrors[$sClass][$sAttCode][] = "field '$sField' in table '$sTable' has a wrong type: found <code>$sActualFieldSpec</code> while expecting <code>$sDBFieldSpec</code>";
|
||||
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` CHANGE `$sField` $sFieldDefinition";
|
||||
$aAlterTableItems[$sTable][$sField] = "CHANGE `$sField` $sFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sField] = "CHANGE `$sField` $sFieldDefinition";
|
||||
}
|
||||
|
||||
// Create indexes (external keys only... so far)
|
||||
@@ -5711,7 +5778,7 @@ abstract class MetaModel
|
||||
}
|
||||
else
|
||||
{
|
||||
$aAlterTableItems[$sTable][] = $sAlterTableItemsAfterChange;
|
||||
$aAlterTableItems[$sTable]['index'][] = $sAlterTableItemsAfterChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5771,9 +5838,12 @@ abstract class MetaModel
|
||||
{
|
||||
$aAlterTableItems[$sTable] = array();
|
||||
}
|
||||
array_unshift($aAlterTableItems[$sTable], "DROP INDEX `$sIndexId`");
|
||||
if (isset($aAlterTableItems[$sTable]['index']))
|
||||
{
|
||||
array_unshift($aAlterTableItems[$sTable]['index'], "DROP INDEX `$sIndexId`");
|
||||
}
|
||||
}
|
||||
$aAlterTableItems[$sTable][] = "ADD INDEX `$sIndexId` ($sColumns)";
|
||||
$aAlterTableItems[$sTable]['index'][] = "ADD INDEX `$sIndexId` ($sColumns)";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5791,7 +5861,7 @@ abstract class MetaModel
|
||||
// without specifying the value of this unknown column
|
||||
$sFieldDefinition = "`$sField` ".CMDBSource::GetFieldType($sTable, $sField).' NULL';
|
||||
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` CHANGE `$sField` $sFieldDefinition";
|
||||
$aAlterTableItems[$sTable][$sField] = "CHANGE `$sField` $sFieldDefinition";
|
||||
$aAlterTableItems[$sTable]['field'][$sField] = "CHANGE `$sField` $sFieldDefinition";
|
||||
}
|
||||
$aSugFix[$sClass][$sAttCode][] = "-- Recommended action: ALTER TABLE `$sTable` DROP `$sField`";
|
||||
}
|
||||
@@ -5810,7 +5880,10 @@ abstract class MetaModel
|
||||
{
|
||||
$aAlterTableItems[$sTable] = array();
|
||||
}
|
||||
array_unshift($aAlterTableItems[$sTable], "DROP INDEX `$sIndexId`");
|
||||
if (isset($aAlterTableItems[$sTable]['index']))
|
||||
{
|
||||
array_unshift($aAlterTableItems[$sTable]['index'], "DROP INDEX `$sIndexId`");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5842,8 +5915,16 @@ abstract class MetaModel
|
||||
}
|
||||
foreach ($aAlterTableItems as $sTable => $aChangeList)
|
||||
{
|
||||
$sChangeList = implode(', ', $aChangeList);
|
||||
$aCondensedQueries[] = "ALTER TABLE `$sTable` $sChangeList";
|
||||
if (isset($aAlterTableItems[$sTable]['field']))
|
||||
{
|
||||
$sChangeList = implode(', ', $aChangeList['field']);
|
||||
$aCondensedQueries[] = "ALTER TABLE `$sTable` $sChangeList";
|
||||
}
|
||||
if (isset($aAlterTableItems[$sTable]['index']))
|
||||
{
|
||||
$sChangeList = implode(', ', $aChangeList['index']);
|
||||
$aCondensedQueries[] = "ALTER TABLE `$sTable` $sChangeList";
|
||||
}
|
||||
// Add request right after the ALTER TABLE
|
||||
if (isset($aPostTableAlteration[$sTable]))
|
||||
{
|
||||
@@ -5893,6 +5974,7 @@ abstract class MetaModel
|
||||
$aSugFix[$sClass]['*'][] = "DROP VIEW `$sView`";
|
||||
}
|
||||
}
|
||||
|
||||
return array($aErrors, $aSugFix);
|
||||
}
|
||||
|
||||
@@ -6438,8 +6520,7 @@ abstract class MetaModel
|
||||
// Set log ASAP
|
||||
if (self::$m_oConfig->GetLogGlobal())
|
||||
{
|
||||
if (self::$m_oConfig->GetLogIssue())
|
||||
{
|
||||
if (self::$m_oConfig->GetLogIssue()) {
|
||||
self::$m_bLogIssue = true;
|
||||
IssueLog::Enable(APPROOT.'log/error.log');
|
||||
}
|
||||
@@ -6448,6 +6529,7 @@ abstract class MetaModel
|
||||
|
||||
ToolsLog::Enable(APPROOT.'log/tools.log');
|
||||
DeadLockLog::Enable();
|
||||
DeprecatedCallsLog::Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -7118,6 +7200,8 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function GetNextKey($sClass)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use ItopCounter::incRootClass($sClass) instead');
|
||||
|
||||
return ItopCounter::IncClass($sClass);
|
||||
}
|
||||
|
||||
@@ -7141,9 +7225,9 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function BulkDelete(DBObjectSearch $oFilter)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
$sSQL = $oFilter->MakeDeleteQuery();
|
||||
if (!self::DBIsReadOnly())
|
||||
{
|
||||
if (!self::DBIsReadOnly()) {
|
||||
CMDBSource::Query($sSQL);
|
||||
}
|
||||
}
|
||||
@@ -7160,12 +7244,13 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function BulkUpdate(DBObjectSearch $oFilter, array $aValues)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('do not use : dead code, will be removed in the future');
|
||||
// $aValues is an array of $sAttCode => $value
|
||||
$sSQL = $oFilter->MakeUpdateQuery($aValues);
|
||||
if (!self::DBIsReadOnly())
|
||||
{
|
||||
if (!self::DBIsReadOnly()) {
|
||||
CMDBSource::Query($sSQL);
|
||||
}
|
||||
|
||||
return CMDBSource::AffectedRows();
|
||||
}
|
||||
|
||||
|
||||
@@ -242,6 +242,8 @@ class iTopMutex
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \MySQLException
|
||||
*
|
||||
* @since 2.7.5 3.0.0 N°3968 specify `wait_timeout` for the mutex dedicated connection
|
||||
*/
|
||||
public function InitMySQLSession()
|
||||
{
|
||||
@@ -254,10 +256,36 @@ class iTopMutex
|
||||
|
||||
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
|
||||
|
||||
if (!$this->hDBLink)
|
||||
{
|
||||
if (!$this->hDBLink) {
|
||||
throw new Exception("Could not connect to the DB server (host=$sServer, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
|
||||
}
|
||||
|
||||
// Make sure that the server variable `wait_timeout` is at least 86400 seconds for this connection,
|
||||
// since the lock will be released if/when the connection times out.
|
||||
// Source https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html :
|
||||
// > A lock obtained with GET_LOCK() is released explicitly by executing RELEASE_LOCK() or implicitly when your session terminates
|
||||
//
|
||||
// BEWARE: If you want to check the value of this variable, when run from an interactive console `SHOW VARIABLES LIKE 'wait_timeout'`
|
||||
// will actually returns the value of the variable `interactive_timeout` which may be quite different.
|
||||
$sSql = "SHOW VARIABLES LIKE 'wait_timeout'";
|
||||
$result = mysqli_query($this->hDBLink, $sSql);
|
||||
if (!$result) {
|
||||
throw new Exception("Failed to issue MySQL query '".$sSql."': ".mysqli_error($this->hDBLink).' (mysql errno: '.mysqli_errno($this->hDBLink).')');
|
||||
}
|
||||
if ($aRow = mysqli_fetch_array($result, MYSQLI_BOTH)) {
|
||||
$iTimeout = (int)$aRow[1];
|
||||
} else {
|
||||
mysqli_free_result($result);
|
||||
throw new Exception("No result for query '".$sSql."'");
|
||||
}
|
||||
mysqli_free_result($result);
|
||||
|
||||
if ($iTimeout < 86400) {
|
||||
$result = mysqli_query($this->hDBLink, 'SET SESSION wait_timeout=86400');
|
||||
if ($result === false) {
|
||||
throw new Exception("Failed to issue MySQL query '".$sSql."': ".mysqli_error($this->hDBLink).' (mysql errno: '.mysqli_errno($this->hDBLink).')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -117,6 +117,9 @@ abstract class Expression {
|
||||
*/
|
||||
public function Render(&$aArgs = null, $bRetrofitParams = false)
|
||||
{
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use RenderExpression');
|
||||
|
||||
return $this->RenderExpression(false, $aArgs, $bRetrofitParams);
|
||||
}
|
||||
|
||||
|
||||
@@ -597,7 +597,7 @@ static public $yy_action = array(
|
||||
** defined, then do no error processing.
|
||||
*/
|
||||
const YYNOCODE = 119;
|
||||
const YYSTACKDEPTH = 100;
|
||||
const YYSTACKDEPTH = 1000;
|
||||
const YYNSTATE = 175;
|
||||
const YYNRULE = 125;
|
||||
const YYERRORSYMBOL = 76;
|
||||
@@ -1175,6 +1175,10 @@ static public $yy_action = array(
|
||||
}
|
||||
/* Here code is inserted which will execute if the parser
|
||||
** stack ever overflows */
|
||||
#line 30 "..\oql-parser.y"
|
||||
|
||||
throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
|
||||
#line 1186 "..\oql-parser.php"
|
||||
return;
|
||||
}
|
||||
$yytos = new OQLParser_yyStackEntry;
|
||||
@@ -1474,116 +1478,116 @@ static public $yy_action = array(
|
||||
** function yy_r0($yymsp){ ... } // User supplied code
|
||||
** #line <lineno> <thisfile>
|
||||
*/
|
||||
#line 29 "..\oql-parser.y"
|
||||
#line 37 "..\oql-parser.y"
|
||||
function yy_r0(){ $this->my_result = $this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1483 "..\oql-parser.php"
|
||||
#line 33 "..\oql-parser.y"
|
||||
#line 1488 "..\oql-parser.php"
|
||||
#line 41 "..\oql-parser.y"
|
||||
function yy_r3(){
|
||||
$this->_retvalue = new OqlUnionQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1488 "..\oql-parser.php"
|
||||
#line 40 "..\oql-parser.y"
|
||||
#line 1493 "..\oql-parser.php"
|
||||
#line 48 "..\oql-parser.y"
|
||||
function yy_r5(){
|
||||
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, array($this->yystack[$this->yyidx + -2]->minor));
|
||||
}
|
||||
#line 1493 "..\oql-parser.php"
|
||||
#line 43 "..\oql-parser.y"
|
||||
#line 1498 "..\oql-parser.php"
|
||||
#line 51 "..\oql-parser.y"
|
||||
function yy_r6(){
|
||||
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, array($this->yystack[$this->yyidx + -2]->minor));
|
||||
}
|
||||
#line 1498 "..\oql-parser.php"
|
||||
#line 47 "..\oql-parser.y"
|
||||
#line 1503 "..\oql-parser.php"
|
||||
#line 55 "..\oql-parser.y"
|
||||
function yy_r7(){
|
||||
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + -4]->minor);
|
||||
}
|
||||
#line 1503 "..\oql-parser.php"
|
||||
#line 50 "..\oql-parser.y"
|
||||
#line 1508 "..\oql-parser.php"
|
||||
#line 58 "..\oql-parser.y"
|
||||
function yy_r8(){
|
||||
$this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + -6]->minor);
|
||||
}
|
||||
#line 1508 "..\oql-parser.php"
|
||||
#line 55 "..\oql-parser.y"
|
||||
#line 1513 "..\oql-parser.php"
|
||||
#line 63 "..\oql-parser.y"
|
||||
function yy_r9(){
|
||||
$this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1513 "..\oql-parser.php"
|
||||
#line 58 "..\oql-parser.y"
|
||||
#line 1518 "..\oql-parser.php"
|
||||
#line 66 "..\oql-parser.y"
|
||||
function yy_r10(){
|
||||
array_push($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
|
||||
$this->_retvalue = $this->yystack[$this->yyidx + -2]->minor;
|
||||
}
|
||||
#line 1519 "..\oql-parser.php"
|
||||
#line 63 "..\oql-parser.y"
|
||||
#line 1524 "..\oql-parser.php"
|
||||
#line 71 "..\oql-parser.y"
|
||||
function yy_r11(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1522 "..\oql-parser.php"
|
||||
#line 64 "..\oql-parser.y"
|
||||
#line 1527 "..\oql-parser.php"
|
||||
#line 72 "..\oql-parser.y"
|
||||
function yy_r12(){ $this->_retvalue = null; }
|
||||
#line 1525 "..\oql-parser.php"
|
||||
#line 66 "..\oql-parser.y"
|
||||
#line 1530 "..\oql-parser.php"
|
||||
#line 74 "..\oql-parser.y"
|
||||
function yy_r13(){
|
||||
// insert the join statement on top of the existing list
|
||||
array_unshift($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor);
|
||||
// and return the updated array
|
||||
$this->_retvalue = $this->yystack[$this->yyidx + 0]->minor;
|
||||
}
|
||||
#line 1533 "..\oql-parser.php"
|
||||
#line 72 "..\oql-parser.y"
|
||||
#line 1538 "..\oql-parser.php"
|
||||
#line 80 "..\oql-parser.y"
|
||||
function yy_r14(){
|
||||
$this->_retvalue = Array($this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1538 "..\oql-parser.php"
|
||||
#line 78 "..\oql-parser.y"
|
||||
#line 1543 "..\oql-parser.php"
|
||||
#line 86 "..\oql-parser.y"
|
||||
function yy_r16(){
|
||||
// create an array with one single item
|
||||
$this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1544 "..\oql-parser.php"
|
||||
#line 83 "..\oql-parser.y"
|
||||
#line 1549 "..\oql-parser.php"
|
||||
#line 91 "..\oql-parser.y"
|
||||
function yy_r17(){
|
||||
// create an array with one single item
|
||||
$this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
#line 1550 "..\oql-parser.php"
|
||||
#line 88 "..\oql-parser.y"
|
||||
function yy_r18(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, '=', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1553 "..\oql-parser.php"
|
||||
#line 89 "..\oql-parser.y"
|
||||
function yy_r19(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1556 "..\oql-parser.php"
|
||||
#line 90 "..\oql-parser.y"
|
||||
function yy_r20(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1559 "..\oql-parser.php"
|
||||
#line 91 "..\oql-parser.y"
|
||||
function yy_r21(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1562 "..\oql-parser.php"
|
||||
#line 92 "..\oql-parser.y"
|
||||
function yy_r22(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1565 "..\oql-parser.php"
|
||||
#line 93 "..\oql-parser.y"
|
||||
function yy_r23(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1568 "..\oql-parser.php"
|
||||
#line 94 "..\oql-parser.y"
|
||||
function yy_r24(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1571 "..\oql-parser.php"
|
||||
#line 95 "..\oql-parser.y"
|
||||
function yy_r25(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1574 "..\oql-parser.php"
|
||||
#line 1555 "..\oql-parser.php"
|
||||
#line 96 "..\oql-parser.y"
|
||||
function yy_r26(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1577 "..\oql-parser.php"
|
||||
function yy_r18(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, '=', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1558 "..\oql-parser.php"
|
||||
#line 97 "..\oql-parser.y"
|
||||
function yy_r19(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1561 "..\oql-parser.php"
|
||||
#line 98 "..\oql-parser.y"
|
||||
function yy_r27(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1580 "..\oql-parser.php"
|
||||
function yy_r20(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1564 "..\oql-parser.php"
|
||||
#line 99 "..\oql-parser.y"
|
||||
function yy_r21(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1567 "..\oql-parser.php"
|
||||
#line 100 "..\oql-parser.y"
|
||||
function yy_r22(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_BELOW_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1570 "..\oql-parser.php"
|
||||
#line 101 "..\oql-parser.y"
|
||||
function yy_r23(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1573 "..\oql-parser.php"
|
||||
#line 102 "..\oql-parser.y"
|
||||
function yy_r24(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1576 "..\oql-parser.php"
|
||||
#line 103 "..\oql-parser.y"
|
||||
function yy_r31(){ $this->_retvalue = new FunctionOqlExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); }
|
||||
#line 1583 "..\oql-parser.php"
|
||||
function yy_r25(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1579 "..\oql-parser.php"
|
||||
#line 104 "..\oql-parser.y"
|
||||
function yy_r32(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; }
|
||||
#line 1586 "..\oql-parser.php"
|
||||
#line 105 "..\oql-parser.y"
|
||||
function yy_r33(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1589 "..\oql-parser.php"
|
||||
function yy_r26(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, 'NOT_ABOVE_STRICT', $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1582 "..\oql-parser.php"
|
||||
#line 106 "..\oql-parser.y"
|
||||
function yy_r27(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1585 "..\oql-parser.php"
|
||||
#line 111 "..\oql-parser.y"
|
||||
function yy_r31(){ $this->_retvalue = new FunctionOqlExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); }
|
||||
#line 1588 "..\oql-parser.php"
|
||||
#line 112 "..\oql-parser.y"
|
||||
function yy_r32(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; }
|
||||
#line 1591 "..\oql-parser.php"
|
||||
#line 113 "..\oql-parser.y"
|
||||
function yy_r33(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1594 "..\oql-parser.php"
|
||||
#line 119 "..\oql-parser.y"
|
||||
function yy_r37(){
|
||||
if ($this->yystack[$this->yyidx + -1]->minor == 'MATCHES')
|
||||
{
|
||||
@@ -1594,44 +1598,44 @@ static public $yy_action = array(
|
||||
$this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor);
|
||||
}
|
||||
}
|
||||
#line 1601 "..\oql-parser.php"
|
||||
#line 128 "..\oql-parser.y"
|
||||
#line 1606 "..\oql-parser.php"
|
||||
#line 136 "..\oql-parser.y"
|
||||
function yy_r42(){
|
||||
$this->_retvalue = new ListOqlExpression($this->yystack[$this->yyidx + -1]->minor);
|
||||
}
|
||||
#line 1606 "..\oql-parser.php"
|
||||
#line 131 "..\oql-parser.y"
|
||||
#line 1611 "..\oql-parser.php"
|
||||
#line 139 "..\oql-parser.y"
|
||||
function yy_r43(){
|
||||
$this->_retvalue = new NestedQueryOqlExpression($this->yystack[$this->yyidx + -1]->minor);
|
||||
}
|
||||
#line 1611 "..\oql-parser.php"
|
||||
#line 146 "..\oql-parser.y"
|
||||
#line 1616 "..\oql-parser.php"
|
||||
#line 154 "..\oql-parser.y"
|
||||
function yy_r47(){
|
||||
$this->_retvalue = array();
|
||||
}
|
||||
#line 1616 "..\oql-parser.php"
|
||||
#line 157 "..\oql-parser.y"
|
||||
#line 1621 "..\oql-parser.php"
|
||||
#line 165 "..\oql-parser.y"
|
||||
function yy_r51(){ $this->_retvalue = new IntervalOqlExpression($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1619 "..\oql-parser.php"
|
||||
#line 170 "..\oql-parser.y"
|
||||
#line 1624 "..\oql-parser.php"
|
||||
#line 178 "..\oql-parser.y"
|
||||
function yy_r61(){ $this->_retvalue = new ScalarOqlExpression($this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1622 "..\oql-parser.php"
|
||||
#line 172 "..\oql-parser.y"
|
||||
#line 1627 "..\oql-parser.php"
|
||||
#line 180 "..\oql-parser.y"
|
||||
function yy_r63(){ $this->_retvalue = new ScalarOqlExpression(null); }
|
||||
#line 1625 "..\oql-parser.php"
|
||||
#line 174 "..\oql-parser.y"
|
||||
#line 1630 "..\oql-parser.php"
|
||||
#line 182 "..\oql-parser.y"
|
||||
function yy_r64(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1628 "..\oql-parser.php"
|
||||
#line 175 "..\oql-parser.y"
|
||||
#line 1633 "..\oql-parser.php"
|
||||
#line 183 "..\oql-parser.y"
|
||||
function yy_r65(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -2]->minor); }
|
||||
#line 1631 "..\oql-parser.php"
|
||||
#line 176 "..\oql-parser.y"
|
||||
#line 1636 "..\oql-parser.php"
|
||||
#line 184 "..\oql-parser.y"
|
||||
function yy_r66(){ $this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1634 "..\oql-parser.php"
|
||||
#line 179 "..\oql-parser.y"
|
||||
#line 1639 "..\oql-parser.php"
|
||||
#line 187 "..\oql-parser.y"
|
||||
function yy_r67(){ $this->_retvalue = new VariableOqlExpression(substr($this->yystack[$this->yyidx + 0]->minor, 1)); }
|
||||
#line 1637 "..\oql-parser.php"
|
||||
#line 181 "..\oql-parser.y"
|
||||
#line 1642 "..\oql-parser.php"
|
||||
#line 189 "..\oql-parser.y"
|
||||
function yy_r68(){
|
||||
if ($this->yystack[$this->yyidx + 0]->minor[0] == '`')
|
||||
{
|
||||
@@ -1643,22 +1647,22 @@ static public $yy_action = array(
|
||||
}
|
||||
$this->_retvalue = new OqlName($name, $this->m_iColPrev);
|
||||
}
|
||||
#line 1650 "..\oql-parser.php"
|
||||
#line 192 "..\oql-parser.y"
|
||||
#line 1655 "..\oql-parser.php"
|
||||
#line 200 "..\oql-parser.y"
|
||||
function yy_r69(){$this->_retvalue=(int)$this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1653 "..\oql-parser.php"
|
||||
#line 193 "..\oql-parser.y"
|
||||
#line 1658 "..\oql-parser.php"
|
||||
#line 201 "..\oql-parser.y"
|
||||
function yy_r70(){$this->_retvalue=(int)-$this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1656 "..\oql-parser.php"
|
||||
#line 194 "..\oql-parser.y"
|
||||
#line 1661 "..\oql-parser.php"
|
||||
#line 202 "..\oql-parser.y"
|
||||
function yy_r71(){$this->_retvalue=new OqlHexValue($this->yystack[$this->yyidx + 0]->minor); }
|
||||
#line 1659 "..\oql-parser.php"
|
||||
#line 195 "..\oql-parser.y"
|
||||
#line 1664 "..\oql-parser.php"
|
||||
#line 203 "..\oql-parser.y"
|
||||
function yy_r72(){$this->_retvalue=stripslashes(substr($this->yystack[$this->yyidx + 0]->minor, 1, strlen($this->yystack[$this->yyidx + 0]->minor) - 2)); }
|
||||
#line 1662 "..\oql-parser.php"
|
||||
#line 198 "..\oql-parser.y"
|
||||
#line 1667 "..\oql-parser.php"
|
||||
#line 206 "..\oql-parser.y"
|
||||
function yy_r73(){$this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; }
|
||||
#line 1665 "..\oql-parser.php"
|
||||
#line 1670 "..\oql-parser.php"
|
||||
|
||||
/**
|
||||
* placeholder for the left hand side in a reduce operation.
|
||||
@@ -1759,6 +1763,10 @@ static public $yy_action = array(
|
||||
}
|
||||
/* Here code is inserted which will be executed whenever the
|
||||
** parser fails */
|
||||
#line 33 "..\oql-parser.y"
|
||||
|
||||
throw new OQLParserParseFailureException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
|
||||
#line 1775 "..\oql-parser.php"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1772,8 +1780,8 @@ static public $yy_action = array(
|
||||
{
|
||||
#line 25 "..\oql-parser.y"
|
||||
|
||||
throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
#line 1781 "..\oql-parser.php"
|
||||
throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
#line 1791 "..\oql-parser.php"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1940,19 +1948,47 @@ throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCo
|
||||
} while ($yymajor != self::YYNOCODE && $this->yyidx >= 0);
|
||||
}
|
||||
}
|
||||
#line 263 "..\oql-parser.y"
|
||||
#line 271 "..\oql-parser.y"
|
||||
|
||||
|
||||
class OQLParserException extends OQLException
|
||||
{
|
||||
public function __construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue)
|
||||
{
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserSyntaxErrorException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol, $sTokenName, $sTokenValue)
|
||||
{
|
||||
$sIssue = "Unexpected token $sTokenName";
|
||||
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserStackOverFlowException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol)
|
||||
{
|
||||
$sIssue = "Stack overflow";
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserParseFailureException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol)
|
||||
{
|
||||
$sIssue = "Unexpected token $sTokenName";
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParser extends OQLParserRaw
|
||||
{
|
||||
// dirty, but working for us (no other mean to get the final result :-(
|
||||
@@ -2005,4 +2041,4 @@ class OQLParser extends OQLParserRaw
|
||||
}
|
||||
}
|
||||
|
||||
#line 2014 "..\oql-parser.php"
|
||||
#line 2052 "..\oql-parser.php"
|
||||
|
||||
@@ -23,7 +23,15 @@ later : solve the 2 remaining shift-reduce conflicts (JOIN)
|
||||
%name OQLParser_
|
||||
%declare_class {class OQLParserRaw}
|
||||
%syntax_error {
|
||||
throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
throw new OQLParserSyntaxErrorException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN);
|
||||
}
|
||||
/* Bug N°4052 Parser stack size too small for huge OQL requests */
|
||||
%stack_size 1000
|
||||
%stack_overflow {
|
||||
throw new OQLParserStackOverFlowException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
|
||||
}
|
||||
%parse_failure {
|
||||
throw new OQLParserParseFailureException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol);
|
||||
}
|
||||
|
||||
result ::= union(X). { $this->my_result = X; }
|
||||
@@ -263,15 +271,43 @@ func_name(A) ::= F_INET_NTOA(X). { A=X; }
|
||||
%code {
|
||||
|
||||
class OQLParserException extends OQLException
|
||||
{
|
||||
public function __construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue)
|
||||
{
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserSyntaxErrorException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol, $sTokenName, $sTokenValue)
|
||||
{
|
||||
$sIssue = "Unexpected token $sTokenName";
|
||||
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue);
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserStackOverFlowException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol)
|
||||
{
|
||||
$sIssue = "Stack overflow";
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParserParseFailureException extends OQLParserException
|
||||
{
|
||||
public function __construct($sInput, $iLine, $iCol)
|
||||
{
|
||||
$sIssue = "Unexpected token $sTokenName";
|
||||
|
||||
parent::__construct($sIssue, $sInput, $iLine, $iCol, '');
|
||||
}
|
||||
}
|
||||
|
||||
class OQLParser extends OQLParserRaw
|
||||
{
|
||||
// dirty, but working for us (no other mean to get the final result :-(
|
||||
|
||||
@@ -1 +1 @@
|
||||
2020-09-29
|
||||
2021-06-03
|
||||
@@ -55,7 +55,7 @@ class OQLClassTreeOptimizer
|
||||
$sJoinedClass = $oJoin->GetOOQLClassNode()->GetNodeClass();
|
||||
$sExtKeyAttCode = $oJoin->GetLeftField();
|
||||
$oExtKeyAttDef = MetaModel::GetAttributeDef($oCurrentClassNode->GetNodeClass(), $sExtKeyAttCode);
|
||||
if ($sJoinedClass == $oExtKeyAttDef->GetTargetClass()) {
|
||||
if (($oExtKeyAttDef instanceof AttributeExternalKey) && ($sJoinedClass == $oExtKeyAttDef->GetTargetClass())) {
|
||||
// The join is not used, remove from tree
|
||||
$oCurrentClassNode->RemoveJoin($sLeftKey, $index);
|
||||
}
|
||||
|
||||
@@ -118,7 +118,8 @@ class ormDocument
|
||||
else
|
||||
{
|
||||
$data = $this->GetData();
|
||||
$sResult = htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8').' [ '.$this->GetMimeType().', size: '.strlen($data).' byte(s) ]<br/>';
|
||||
$sSize = utils::BytesToFriendlyFormat(strlen($data));
|
||||
$sResult = htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8').' ('.$sSize.')<br/>';
|
||||
}
|
||||
return $sResult;
|
||||
}
|
||||
|
||||
@@ -153,7 +153,9 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
*/
|
||||
public function AddObject(DBObject $oObject, $sClassAlias = '')
|
||||
{
|
||||
$this->AddItem($oObject);
|
||||
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
|
||||
//DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use \ormLinkSet::AddItem() instead');
|
||||
$this->AddItem($oObject);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,16 +260,13 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
|
||||
*/
|
||||
public function ToArray($bWithId = true)
|
||||
{
|
||||
$aRet = array();
|
||||
foreach($this as $oItem)
|
||||
{
|
||||
if ($bWithId)
|
||||
{
|
||||
$aRet[$oItem->GetKey()] = $oItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
$aRet[] = $oItem;
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use foreach($this as $oItem){} instead');
|
||||
$aRet = array();
|
||||
foreach ($this as $oItem) {
|
||||
if ($bWithId) {
|
||||
$aRet[$oItem->GetKey()] = $oItem;
|
||||
} else {
|
||||
$aRet[] = $oItem;
|
||||
}
|
||||
}
|
||||
return $aRet;
|
||||
|
||||
@@ -55,27 +55,27 @@ class PDFBulkExport extends HTMLBulkExport
|
||||
|
||||
//page format
|
||||
$oSelectFormat = SelectUIBlockFactory::MakeForSelectWithLabel("page_size", Dict::S('Core:BulkExport:PDFPageSize'));
|
||||
$oSelectFormat->SetBeforeInput(false);
|
||||
$oSelectFormat->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
$oSelectFormat->SetIsLabelBefore(false);
|
||||
//$oSelectFormat->AddCSSClass('ibo-input-checkbox');
|
||||
$oFieldSetFormat->AddSubBlock($oSelectFormat);
|
||||
|
||||
$aPossibleFormat = ['A3', 'A4', 'Letter'];
|
||||
$sDefaultFormat = 'A4';
|
||||
foreach ($aPossibleFormat as $sVal) {
|
||||
$oSelectFormat->GetInput()->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sVal, htmlentities(Dict::S('Core:BulkExport:PageSize-'.$sVal), ENT_QUOTES, 'UTF-8'), ($sVal == $sDefaultFormat)));
|
||||
$oSelectFormat->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sVal, htmlentities(Dict::S('Core:BulkExport:PageSize-'.$sVal), ENT_QUOTES, 'UTF-8'), ($sVal == $sDefaultFormat)));
|
||||
}
|
||||
$oFieldSetFormat->AddSubBlock(new Html('</br>'));
|
||||
|
||||
$oSelectOrientation = SelectUIBlockFactory::MakeForSelectWithLabel("page_size",
|
||||
$oSelectOrientation = SelectUIBlockFactory::MakeForSelectWithLabel("page_orientation",
|
||||
Dict::S('Core:BulkExport:PDFPageOrientation'));
|
||||
$oSelectOrientation->SetBeforeInput(false);
|
||||
$oSelectOrientation->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
$oSelectOrientation->SetIsLabelBefore(false);
|
||||
//$oSelectOrientation->AddCSSClass('ibo-input-checkbox');
|
||||
$oFieldSetFormat->AddSubBlock($oSelectOrientation);
|
||||
|
||||
$aPossibleOrientation = ['P', 'L'];
|
||||
$sDefaultOrientation = 'L';
|
||||
foreach ($aPossibleOrientation as $sVal) {
|
||||
$oSelectOrientation->GetInput()->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sVal, htmlentities(Dict::S('Core:BulkExport:PageOrientation-'.$sVal), ENT_QUOTES, 'UTF-8'), ($sVal == $sDefaultOrientation)));
|
||||
$oSelectOrientation->AddSubBlock(SelectOptionUIBlockFactory::MakeForSelectOption($sVal, htmlentities(Dict::S('Core:BulkExport:PageOrientation-'.$sVal), ENT_QUOTES, 'UTF-8'), ($sVal == $sDefaultOrientation)));
|
||||
}
|
||||
|
||||
//date format
|
||||
@@ -86,15 +86,15 @@ class PDFBulkExport extends HTMLBulkExport
|
||||
|
||||
$sDefaultFormat = htmlentities((string)AttributeDateTime::GetFormat(), ENT_QUOTES, 'UTF-8');
|
||||
$sExample = htmlentities(date((string)AttributeDateTime::GetFormat()), ENT_QUOTES, 'UTF-8');
|
||||
$oRadioDefault = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatDefault_Example', $sDefaultFormat, $sExample), "pdf_custom_date_time_format", "default", "pdf_date_time_format_default", "radio");
|
||||
$oRadioDefault = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatDefault_Example', $sDefaultFormat, $sExample), "pdf_date_format_radio", "default", "pdf_date_time_format_default", "radio");
|
||||
$oRadioDefault->GetInput()->SetIsChecked(($sDateTimeFormat == (string)AttributeDateTime::GetFormat()));
|
||||
$oRadioDefault->SetBeforeInput(false);
|
||||
$oRadioDefault->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
$oFieldSetDate->AddSubBlock($oRadioDefault);
|
||||
$oFieldSetDate->AddSubBlock(new Html('</br>'));
|
||||
|
||||
$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), "pdf_custom_date_time_format", "custom", "pdf_date_time_format_custom", "radio");
|
||||
$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->GetInput()->SetIsChecked($sDateTimeFormat !== (string)AttributeDateTime::GetFormat());
|
||||
$oRadioCustom->SetBeforeInput(false);
|
||||
$oRadioCustom->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
@@ -184,9 +184,8 @@ EOF
|
||||
$sData = parent::GetFooter();
|
||||
|
||||
// We need a lot of time for the PDF conversion
|
||||
set_time_limit(60*10); // 10 minutes max ???
|
||||
|
||||
require_once(APPROOT.'application/pdfpage.class.inc.php');
|
||||
set_time_limit(60 * 10); // 10 minutes max ???
|
||||
|
||||
$oPage = new PDFPage(Dict::Format('Core:BulkExportOf_Class', MetaModel::GetName($this->oSearch->GetClass())), $this->aStatusInfo['page_size'], $this->aStatusInfo['page_orientation']);
|
||||
$oPDF = $oPage->get_tcpdf();
|
||||
$oPDF->SetFontSize(8);
|
||||
|
||||
@@ -31,29 +31,25 @@ class PluginManager
|
||||
|
||||
/**
|
||||
* @param string $sInterface
|
||||
* @param bool $bCanInstantiatePlugins internal use, let this value to true
|
||||
* @param string|null $sFilterInstanceOf [optional] if given, only instance of this string will be returned
|
||||
* @param bool $bCanInstantiatePlugins internal use, let this value to true
|
||||
*
|
||||
* @return array classes=>instance implementing the given interface
|
||||
*/
|
||||
public function EnumPlugins($sInterface, $sFilterInstanceOf = null, $bCanInstantiatePlugins = true)
|
||||
{
|
||||
$aPlugins = array();
|
||||
if (array_key_exists($sInterface, self::$m_aExtensionClasses))
|
||||
{
|
||||
$aAllPlugins = array_values(self::$m_aExtensionClasses[$sInterface]);
|
||||
if (array_key_exists($sInterface, self::$m_aExtensionClasses)) {
|
||||
$aAllPlugins = self::$m_aExtensionClasses[$sInterface];
|
||||
|
||||
if (is_null($sFilterInstanceOf))
|
||||
{
|
||||
if (is_null($sFilterInstanceOf)) {
|
||||
return $aAllPlugins;
|
||||
};
|
||||
|
||||
$aPlugins = array();
|
||||
foreach ($aAllPlugins as $instance)
|
||||
{
|
||||
if ($instance instanceof $sFilterInstanceOf)
|
||||
{
|
||||
$aPlugins[] = $instance;
|
||||
foreach ($aAllPlugins as $sPluginClass => $instance) {
|
||||
if ($instance instanceof $sFilterInstanceOf) {
|
||||
$aPlugins[$sPluginClass] = $instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,19 +286,16 @@ abstract class TabularBulkExport extends BulkExport
|
||||
$aSampleRow = array();
|
||||
foreach($aAuthorizedClasses as $sAlias => $sClass)
|
||||
{
|
||||
if (count($aAuthorizedClasses) > 1 )
|
||||
{
|
||||
if (count($aAuthorizedClasses) > 1) {
|
||||
$sShortAlias = $sAlias.'.';
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$sShortAlias = '';
|
||||
}
|
||||
|
||||
foreach ($aAllAttCodes[$sAlias] as $sAttCodeEx)
|
||||
{
|
||||
$oObj = $aRow[$sAlias];
|
||||
$aSampleRow[$sShortAlias.$sAttCodeEx] = $oObj ? $this->GetSampleData($oObj, $sAttCodeEx) : '';
|
||||
if (isset($aAllAttCodes[$sAlias])) {
|
||||
foreach ($aAllAttCodes[$sAlias] as $sAttCodeEx) {
|
||||
$oObj = $aRow[$sAlias];
|
||||
$aSampleRow[$sShortAlias.$sAttCodeEx] = $oObj ? $this->GetSampleData($oObj, $sAttCodeEx) : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
$aSampleData[] = $aSampleRow;
|
||||
|
||||
@@ -558,7 +558,7 @@ class TriggerOnObjectUpdate extends TriggerOnObject
|
||||
* Class TriggerOnObjectMention
|
||||
*
|
||||
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
|
||||
* @since 2.7.0
|
||||
* @since 3.0.0
|
||||
*/
|
||||
class TriggerOnObjectMention extends TriggerOnObject
|
||||
{
|
||||
@@ -584,37 +584,11 @@ class TriggerOnObjectMention extends TriggerOnObject
|
||||
MetaModel::Init_InheritAttributes();
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'filter', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'action_list')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list
|
||||
// Search criteria
|
||||
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
|
||||
}
|
||||
|
||||
// TODO 3.0.0: Clean this up. What was the intention?
|
||||
// public function IsTargetObject($iObjectId, $aChanges = array())
|
||||
// {
|
||||
// if (!parent::IsTargetObject($iObjectId, $aChanges))
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// // Check the attribute
|
||||
// $oAttCodeSet = $this->Get('target_attcodes');
|
||||
// $aAttCodes = $oAttCodeSet->GetValues();
|
||||
// if (empty($aAttCodes))
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// foreach($aAttCodes as $sAttCode)
|
||||
// {
|
||||
// if (array_key_exists($sAttCode, $aChanges))
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -128,11 +128,13 @@ class ValueSetObjects extends ValueSetDefinition
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use SetCondition instead
|
||||
*
|
||||
* @param \DBSearch $oFilter
|
||||
* @deprecated use SetCondition
|
||||
*/
|
||||
public function AddCondition(DBSearch $oFilter)
|
||||
{
|
||||
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use SetCondition instead');
|
||||
$this->SetCondition($oFilter);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ This is a brief description of the SASS 7-1 system and how to use it.
|
||||
SCSS files are structured following the [7-1 pattern](https://sass-guidelin.es/#the-7-1-pattern). \
|
||||
@rveitch made a great summary with the following, which can also be found [here](https://gist.github.com/rveitch/84cea9650092119527bc).
|
||||
|
||||
_Note: Folders with an * are customizations we made to the original 7-1 pattern to best fit our needs_
|
||||
|
||||
```
|
||||
css/backoffice/
|
||||
|
|
||||
@@ -42,11 +44,20 @@ css/backoffice/
|
||||
| |– _forms.scss # Forms
|
||||
| ... # Etc…
|
||||
|
|
||||
|- *application/ # Elements that are not usable as a standalone (like componants and layouts are) and very application (the backoffice) specific
|
||||
| |- display-block
|
||||
| |- tabular-fields
|
||||
| ...
|
||||
|
|
||||
|– pages/
|
||||
| |– _home.scss # Home specific styles
|
||||
| |– _contact.scss # Contact specific styles
|
||||
| ... # Etc…
|
||||
|
|
||||
|- *blocks-integrations # Specific rules for the integration of a block with another one, those kind of rules should never be in the block partial directly
|
||||
| |- _panel-with-datatable.scss # Changes the negative margins of the datatable so it overlaps the panel's original padding
|
||||
| ...
|
||||
|
|
||||
|– themes/
|
||||
| |– _theme.scss # Default theme
|
||||
| |– _admin.scss # Admin theme
|
||||
@@ -68,6 +79,8 @@ To avoid common errors, files should be imported in the final file in the follow
|
||||
- Base
|
||||
- Components
|
||||
- Layout
|
||||
- \*Application
|
||||
- Pages
|
||||
- \*Block integrations
|
||||
- Themes
|
||||
- Shame file
|
||||
38
css/backoffice/_fallback.scss
Normal file
38
css/backoffice/_fallback.scss
Normal file
@@ -0,0 +1,38 @@
|
||||
/*!
|
||||
* Copyright (C) 2013-2021 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
//==========================================================================
|
||||
// Here are the rules that make standard HTML tags fallback to the theme
|
||||
// styles so then can be displayed correctly even if they don't have any
|
||||
// CSS classes
|
||||
//
|
||||
// Be very specific about what each piece of code is doing
|
||||
// ==========================================================================
|
||||
|
||||
// Rules:
|
||||
// ---------------
|
||||
// 1. If it’s a hack, it goes in _shame.scss not here.
|
||||
// 2. See rule #1
|
||||
|
||||
fieldset {
|
||||
@extend .ibo-fieldset;
|
||||
}
|
||||
|
||||
legend {
|
||||
@extend .ibo-fieldset-legend;
|
||||
}
|
||||
@@ -8,16 +8,16 @@
|
||||
// @see http://csswizardry.com/2013/04/shame-css/
|
||||
// Thanks https://github.com/heroheman/shepherd/blob/master/sass/_shame.scss
|
||||
// ==========================================================================
|
||||
|
||||
//
|
||||
// ==========================================================================
|
||||
// because hacks happen.
|
||||
//
|
||||
// be very specific about what each piece of code is doing, and
|
||||
// how to better fix it later
|
||||
// ==========================================================================
|
||||
|
||||
//
|
||||
// Try: $ git blame _shame.scss
|
||||
|
||||
//
|
||||
// Rules:
|
||||
// ---------------
|
||||
// 1. If it’s a hack, it goes in _shame.scss.
|
||||
@@ -32,11 +32,11 @@
|
||||
// Example:
|
||||
// ---------------
|
||||
// Nav specificity fix.
|
||||
|
||||
//
|
||||
// Someone used an ID in the header code (`#header a{}`) which trumps the
|
||||
// nav selectors (`.site-nav a{}`). Use !important to override it until I
|
||||
// have time to refactor the header stuff.
|
||||
|
||||
//
|
||||
// .site-nav a { color:#BADA55!important; }
|
||||
|
||||
// N°2847 - Recolor svg illustrations with iTop's primary color
|
||||
|
||||
@@ -4,4 +4,3 @@
|
||||
*/
|
||||
|
||||
@import "block-csv";
|
||||
@import "block-indirect-links";
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
.ibo-block-indirect-links--edit--dialog{
|
||||
overflow:hidden !important;
|
||||
> form{
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-block-indirect-links--edit--dialog .ibo-datatable--selection-validation-buttons-toolbar{
|
||||
position:absolute;
|
||||
bottom: 5px;
|
||||
background-color:white;
|
||||
}
|
||||
@@ -11,7 +11,7 @@
|
||||
padding: 5px;
|
||||
border-width: 1px 1px 0;
|
||||
border-style: groove groove none;
|
||||
background: $ibo-body-background-color;
|
||||
background: $ibo-color-white-200;
|
||||
}
|
||||
|
||||
td {
|
||||
|
||||
@@ -6,7 +6,12 @@
|
||||
@import "dashlet-within-dashboard";
|
||||
@import "add-to-dashboard";
|
||||
@import "caselog-entry-form-within-activity-panel";
|
||||
@import "panel-with-tab-container";
|
||||
@import "panel-with-datatable";
|
||||
@import "panel-with-tab-container";
|
||||
@import "panel-within-main-content";
|
||||
@import "panel-within-modal";
|
||||
@import "object-details-with-tab-container";
|
||||
@import "medallion-with-blocklist";
|
||||
@import "input-within-datatable";
|
||||
@import "jquery-blockui-within-dialog";
|
||||
@import "jquery-blockui-within-datatable";
|
||||
@@ -0,0 +1,13 @@
|
||||
$ibo-input-within-datatable--attribute-set-item--padding-x: $ibo-input-set--item--padding-x !default;
|
||||
$ibo-input-within-datatable--attribute-set-item--box-shadow: $ibo-elevation-100 !default;
|
||||
|
||||
.ibo-datatable {
|
||||
.attribute-set {
|
||||
.attribute-set-item {
|
||||
display: inline;
|
||||
margin: 0;
|
||||
padding: 0 $ibo-input-within-datatable--attribute-set-item--padding-x;
|
||||
box-shadow: $ibo-input-within-datatable--attribute-set-item--box-shadow;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
$ibo-vendors-blockui--blockoverlay--within--datatable--background-color: $ibo-color-white-100 !default;
|
||||
|
||||
.ibo-datatable .blockUI.blockOverlay{
|
||||
background-color: $ibo-vendors-blockui--blockoverlay--within--datatable--background-color;
|
||||
}
|
||||
.ibo-datatable .blockUI.blockMsg{
|
||||
font-size: 2em;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
/*!
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
$ibo-vendors-blockui--blockoverlay--within--dialog--background-color: $ibo-vendors-jqueryui--ui-dialog--background-color !default;
|
||||
|
||||
.ui-dialog .blockUI.blockOverlay{
|
||||
background-color: $ibo-vendors-blockui--blockoverlay--within--dialog--background-color;
|
||||
}
|
||||
@@ -2,10 +2,16 @@
|
||||
text-align: center;
|
||||
}
|
||||
.ibo-blocklist--medallion{
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
> .ibo-medallion-icon--image{
|
||||
margin: 0 auto;
|
||||
~ .ibo-medallion-icon--description{
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
> .ibo-medallion-icon--description{
|
||||
flex-grow: 1;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,8 @@
|
||||
// Note: We use the child ">" selector to ensure this applies only the child tab container, not another one that would be nested
|
||||
.ibo-object-details.ibo-has-medallion-icon {
|
||||
> .ibo-panel--body {
|
||||
> .ibo-tab-container {
|
||||
// Only for horizontal tabs
|
||||
> .ibo-tab-container:not(.ibo-is-vertical) {
|
||||
> .ibo-tab-container--tabs-list {
|
||||
// Align tab toggler's title with the panel's title
|
||||
padding-left: calc(#{$ibo-object-details--icon--spacing--as-medallion} + #{$ibo-object-details--icon--size} + #{$ibo-object-details--icon--spacing--as-medallion} - #{$ibo-tab-container--tab-toggler--padding-x});
|
||||
@@ -11,15 +11,18 @@
|
||||
|
||||
$ibo-panel-with-tab-container--padding-top: -1 * ($ibo-panel--body--padding-top - $ibo-panel--highlight--height) !default;
|
||||
$ibo-panel-with-tab-container--margin-x: -1 * $ibo-panel--body--padding-x !default;
|
||||
$ibo-panel-with-tab-container--margin-bottom: -1 * $ibo-panel--body--padding-bottom !default;
|
||||
|
||||
// Note: We use the child ">" selector to ensure this applies only the child tab container, not another one that would be nested
|
||||
$ibo-panel-with-tab-container--tab-toggler--font-size--is-sticking: $ibo-font-size-100 !default;
|
||||
|
||||
// Note: We use the child ">" selector to ensure this applies only to the child tab container, not another one that would be nested
|
||||
.ibo-panel {
|
||||
> .ibo-panel--body {
|
||||
> .ibo-tab-container {
|
||||
margin-top: $ibo-panel-with-tab-container--padding-top;
|
||||
margin-left: $ibo-panel-with-tab-container--margin-x;
|
||||
margin-right: $ibo-panel-with-tab-container--margin-x;
|
||||
height: 80vh;
|
||||
margin-bottom: $ibo-panel-with-tab-container--margin-bottom;
|
||||
|
||||
> .ibo-tab-container--tab-container-list {
|
||||
height: 100%;
|
||||
@@ -58,4 +61,27 @@ $ibo-panel-with-tab-container--margin-x: -1 * $ibo-panel--body--padding-x !defau
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sticky header rules */
|
||||
&.ibo-has-sticky-header {
|
||||
> .ibo-panel--body {
|
||||
> .ibo-tab-container {
|
||||
> .ibo-tab-container--tabs-list.ibo-is-sticking {
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
&:not(.ibo-is-vertical){
|
||||
> .ibo-tab-container--tabs-list.ibo-is-sticking {
|
||||
padding-left: 0;
|
||||
|
||||
.ibo-tab-container--tab-toggler,
|
||||
.ibo-tab-container--extra-tabs-list-toggler {
|
||||
font-size: $ibo-panel-with-tab-container--tab-toggler--font-size--is-sticking;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*!
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-panel-within-main-content--sticky-sentinel-top--top: -1 * $ibo-main-content--padding-top !default;
|
||||
$ibo-panel-within-main-content--sticky-sentinel-top--height: $ibo-main-content--padding-top !default;
|
||||
|
||||
$ibo-panel-within-main-content--header--top--is-sticky: -1 * $ibo-main-content--padding-top !default;
|
||||
|
||||
#ibo-main-content {
|
||||
/*
|
||||
* Careful: Here we get all the "descendants" instead of the first closest panel in the main content, which could cause some glitches in the future.
|
||||
* For now we decided to stay that way for the following reasons, if that changes in the future keep the repercussions on descendants panels in mind.
|
||||
* - We don't have nested sticky panels (yet)
|
||||
* - We don't want to hardcode the main content's markup selector if not necessary as it could change in the future (and is already different in read-only vs edition)
|
||||
* - Unlike in JS, there no easy way to find the closest descendant
|
||||
*/
|
||||
.ibo-panel.ibo-has-sticky-header {
|
||||
/* Stickable header rules */
|
||||
> .ibo-sticky-sentinel-top {
|
||||
top: $ibo-panel-within-main-content--sticky-sentinel-top--top;
|
||||
height: $ibo-panel-within-main-content--sticky-sentinel-top--height;
|
||||
}
|
||||
> .ibo-panel--header.ibo-is-sticking {
|
||||
top: $ibo-panel-within-main-content--header--top--is-sticky;
|
||||
}
|
||||
}
|
||||
}
|
||||
29
css/backoffice/blocks-integrations/_panel-within-modal.scss
Normal file
29
css/backoffice/blocks-integrations/_panel-within-modal.scss
Normal file
@@ -0,0 +1,29 @@
|
||||
/*!
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
$ibo-panel-within-modal--sticky-sentinel-top--top: -1 * $ibo-vendors-jqueryui--ui-dialog-content--padding-y !default;
|
||||
$ibo-panel-within-modal--sticky-sentinel-top--height: $ibo-vendors-jqueryui--ui-dialog-content--padding-y !default;
|
||||
|
||||
$ibo-panel-within-modal--header--top--is-sticky: -1 * $ibo-vendors-jqueryui--ui-dialog-content--padding-y !default;
|
||||
|
||||
.ui-dialog-content {
|
||||
/*
|
||||
* Careful: Here we get all the "descendants" instead of the first closest panel in the modal, which could cause some glitches in the future.
|
||||
* For now we decided to stay that way for the following reasons, if that changes in the future keep the repercussions on descendants panels in mind.
|
||||
* - We don't have nested sticky panels (yet)
|
||||
* - We don't want to hardcode the modal's markup selector if not necessary as it could change in the future (and is already different in read-only vs edition)
|
||||
* - Unlike in JS, there no easy way to find the closest descendant
|
||||
*/
|
||||
.ibo-panel.ibo-has-sticky-header {
|
||||
/* Sticky header rules */
|
||||
> .ibo-sticky-sentinel-top {
|
||||
top: $ibo-panel-within-modal--sticky-sentinel-top--top;
|
||||
height: $ibo-panel-within-modal--sticky-sentinel-top--height;
|
||||
}
|
||||
> .ibo-panel--header.ibo-is-sticking {
|
||||
top: $ibo-panel-within-modal--header--top--is-sticky;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ $ibo-alert-colors: (
|
||||
border-radius: $ibo-alert--border-radius;
|
||||
overflow: hidden; /* To force highlight color to be cropped by the border radius */
|
||||
|
||||
@extend %ibo-font-ral-med-150;
|
||||
@extend %ibo-font-size-150;
|
||||
|
||||
&::before {
|
||||
display: block;
|
||||
@@ -93,7 +93,7 @@ $ibo-alert-colors: (
|
||||
.ibo-alert--title {
|
||||
cursor: pointer;
|
||||
|
||||
@extend %ibo-font-ral-bol-150;
|
||||
@extend %ibo-font-weight-700;
|
||||
}
|
||||
|
||||
&.ibo-is-opened {
|
||||
|
||||
@@ -37,7 +37,7 @@ $ibo-breadcrumbs--item-separator--text-color: $ibo-color-grey-500 !default;
|
||||
}
|
||||
.ibo-breadcrumbs--item{
|
||||
color: $ibo-breadcrumbs--item--text-color;
|
||||
@extend %ibo-font-ral-nor-100;
|
||||
@extend %ibo-font-ral-med-100;
|
||||
|
||||
&:not(:last-child){
|
||||
&::after{
|
||||
@@ -59,7 +59,7 @@ $ibo-breadcrumbs--item-separator--text-color: $ibo-color-grey-500 !default;
|
||||
}
|
||||
.ibo-breadcrumbs--item-icon{
|
||||
margin-right: $ibo-breadcrumbs--item-icon--margin-x;
|
||||
@extend %ibo-font-ral-nor-150;
|
||||
@extend %ibo-font-size-150;
|
||||
transition: all 0.1s linear;
|
||||
|
||||
> span{
|
||||
|
||||
@@ -84,7 +84,7 @@ $ibo-collapsible-section--body--border-color: $ibo-color-grey-400 !default;
|
||||
|
||||
.ibo-collapsible-section--title {
|
||||
color: $ibo-collapsible-section--title--color;
|
||||
@extend %ibo-font-ral-med-250;
|
||||
@extend %ibo-font-size-250;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,25 @@ $ibo-datatable--toolbar--text-color: $ibo-color-grey-700 !default;
|
||||
$ibo-datatable--toolbar--elements-spacing: 1rem !default;
|
||||
$ibo-datatable--toolbar--table-spacing: 18px !default;
|
||||
|
||||
$ibo-datatable-panel--body--padding: $ibo-panel--body--padding-top 0px $ibo-panel--body--padding-bottom !default;
|
||||
$ibo-datatable--selection-validation-buttons-toolbar--margin-top: 10px !default;
|
||||
$ibo-list-column--max-height: 150px;
|
||||
$ibo-datatable-header--text-color: $ibo-base-variable--text-color !default;
|
||||
|
||||
$ibo-datatable-panel--table-spacing: 48px !default;
|
||||
$ibo-datatable-panel--body--padding: $ibo-panel--body--padding-top 0px $ibo-panel--body--padding-bottom !default;
|
||||
|
||||
$ibo-datatable--selection-validation-buttons-toolbar--margin-top: 10px !default;
|
||||
$ibo-list-column--max-height: 150px !default;
|
||||
|
||||
$ibo-datatable--sort-order--color: $ibo-color-orange-600 !default;
|
||||
|
||||
$ibo-fieldsorter--selected--background-color: $ibo-color-blue-200 !default;
|
||||
|
||||
|
||||
/* CSS variables (can be changed directly from the browser) */
|
||||
:root {
|
||||
--ibo-datatable-panel--table-spacing: #{$ibo-datatable-panel--table-spacing};
|
||||
}
|
||||
|
||||
/* Rules */
|
||||
.ibo-datatable--toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -20,7 +35,7 @@ $ibo-list-column--max-height: 150px;
|
||||
padding: $ibo-datatable--toolbar--padding-y $ibo-datatable--toolbar--padding-x;
|
||||
|
||||
color: $ibo-datatable--toolbar--text-color;
|
||||
@extend %ibo-font-ral-med-100;
|
||||
@extend %ibo-font-size-100;
|
||||
|
||||
/* Above the table */
|
||||
&:first-child {
|
||||
@@ -42,6 +57,10 @@ $ibo-list-column--max-height: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-datatable-header {
|
||||
color: $ibo-datatable-header--text-color;
|
||||
}
|
||||
|
||||
/* TODO 3.0.0: The lines below need to be refactored / placed in the right places (integrations, dedicated block / page, ...) */
|
||||
.ibo-datatable-panel > .ibo-panel--body {
|
||||
padding: $ibo-datatable-panel--body--padding;
|
||||
@@ -49,16 +68,41 @@ $ibo-list-column--max-height: 150px;
|
||||
|
||||
// For cancel / OK / next... selection validation buttons
|
||||
.ibo-datatable--selection-validation-buttons-toolbar {
|
||||
clear: both;
|
||||
margin-top: $ibo-datatable--selection-validation-buttons-toolbar--margin-top;
|
||||
clear: both;
|
||||
margin-top: $ibo-datatable--selection-validation-buttons-toolbar--margin-top;
|
||||
}
|
||||
|
||||
.ibo-list-column {
|
||||
max-height: $ibo-list-column--max-height;
|
||||
overflow-y: auto;
|
||||
max-height: $ibo-list-column--max-height;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.ibo-datatable .ibo-field-badge {
|
||||
margin: 0;
|
||||
padding: 0px 4px;
|
||||
}
|
||||
|
||||
// Datatables configure this list dialog
|
||||
// Could be in a separate component, but is only used in datatables as of now
|
||||
|
||||
.ibo-sort-order{
|
||||
&::after{
|
||||
@extend %fa-solid-base;
|
||||
color: $ibo-datatable--sort-order--color;
|
||||
}
|
||||
&.ibo-is-descending::after{
|
||||
content: '\f0dd';
|
||||
}
|
||||
&.ibo-is-ascending::after{
|
||||
content: '\f0de';
|
||||
}
|
||||
&.ibo-is-none::after{
|
||||
content: '\f0dc';
|
||||
}
|
||||
}
|
||||
|
||||
.itop-fieldsorter{
|
||||
>.selected{
|
||||
background-color: $ibo-fieldsorter--selected--background-color;
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,15 @@ $ibo-field-badge--margin: 0 !default;
|
||||
$ibo-field-badge--padding: 4px 10px !default;
|
||||
$ibo-field-badge--border-radius: $ibo-border-radius-300 !default;
|
||||
|
||||
.ibo-field-badge {
|
||||
@extend %ibo-font-ral-med-100;
|
||||
$ibo-field-badge--label--spacing-x: 0.5rem !default;
|
||||
|
||||
.ibo-field-badge {
|
||||
margin: $ibo-field-badge--margin;
|
||||
padding: $ibo-field-badge--padding;
|
||||
border-radius: $ibo-field-badge--border-radius;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.ibo-field-badge--decoration + .ibo-field-badge--label {
|
||||
margin-left: $ibo-field-badge--label--spacing-x;
|
||||
}
|
||||
@@ -31,15 +31,28 @@ $ibo-field--value-decoration--spacing-x: 0.5rem !default;
|
||||
|
||||
/* SCSS rules */
|
||||
.ibo-field {
|
||||
@extend %ibo-font-ral-nor-150;
|
||||
@extend %ibo-font-size-150;
|
||||
|
||||
/* Avoid value to overflow from its container with very long strings (typically URLs) */
|
||||
/* Note: Some types of attribute must be excluding as it can alter their rendering */
|
||||
&:not([data-attribute-type="AttributeBlob"]):not([data-attribute-type="AttributeFile"]):not([data-attribute-type="AttributeImage"]):not(.ibo-input-file-select--container) {
|
||||
/* We need the rule to apply for the class and all its descendants */
|
||||
.ibo-field--value {
|
||||
* {
|
||||
word-break: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& ~ .ibo-field {
|
||||
margin-top: $ibo-field--sibling-spacing;
|
||||
}
|
||||
}
|
||||
|
||||
/* Large field = Label on top, value below */
|
||||
.ibo-field-large {
|
||||
display: inherit;
|
||||
display: block;
|
||||
|
||||
.ibo-field--label {
|
||||
position: relative; /* Necessary for fullscreen toggler */
|
||||
@@ -49,7 +62,38 @@ $ibo-field--value-decoration--spacing-x: 0.5rem !default;
|
||||
max-width: initial;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* For custom fields (eg. request templates) only */
|
||||
.ibo-field-small .ibo-field--label {
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
padding-right: 10px;
|
||||
min-width: 100px;
|
||||
max-width: 145px;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
/* Fullscreen mode */
|
||||
&.ibo-is-fullscreen {
|
||||
background-color: $ibo-field--background-color--is-fullscreen;
|
||||
|
||||
.ibo-field--label {
|
||||
position: fixed;
|
||||
width: $ibo-field--label--width--is-fullscreen;
|
||||
min-width: initial;
|
||||
max-width: initial;
|
||||
padding: $ibo-field--label--padding-y--is-fullscreen $ibo-field--label--padding-x--is-fullscreen;
|
||||
background-color: $ibo-field--label--background-color--is-fullscreen;
|
||||
border-bottom: $ibo-field--label--border-bottom--is-fullscreen;
|
||||
}
|
||||
|
||||
.ibo-field--value {
|
||||
padding: $ibo-field--value--padding-top--is-fullscreen $ibo-field--value--padding-x--is-fullscreen $ibo-field--value--padding-bottom--is-fullscreen $ibo-field--value--padding-x--is-fullscreen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Small field = Label on the left, value on the right */
|
||||
.ibo-field-small {
|
||||
display: table;
|
||||
width: 100%;
|
||||
@@ -61,10 +105,26 @@ $ibo-field--value-decoration--spacing-x: 0.5rem !default;
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-field--fullscreen-toggler {
|
||||
width: $ibo-field--fullscreen-toggler--size;
|
||||
height: $ibo-field--fullscreen-toggler--size;
|
||||
border-radius: $ibo-field--fullscreen-toggler--border-radius;
|
||||
cursor: pointer;
|
||||
|
||||
@extend %ibo-hyperlink-inherited-colors;
|
||||
@extend %ibo-fully-centered-content;
|
||||
@extend %ibo-font-size-100;
|
||||
|
||||
&:hover {
|
||||
background-color: $ibo-field--fullscreen-toggler--background-color--on-hover;
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-field--label {
|
||||
min-width: 100px;
|
||||
max-width: 145px;
|
||||
width: 30%;
|
||||
@extend %ibo-font-weight-600;
|
||||
|
||||
> .ibo-has-description {
|
||||
&::after {
|
||||
@@ -78,20 +138,6 @@ $ibo-field--value-decoration--spacing-x: 0.5rem !default;
|
||||
}
|
||||
}
|
||||
}
|
||||
.ibo-field--fullscreen-toggler {
|
||||
width: $ibo-field--fullscreen-toggler--size;
|
||||
height: $ibo-field--fullscreen-toggler--size;
|
||||
border-radius: $ibo-field--fullscreen-toggler--border-radius;
|
||||
cursor: pointer;
|
||||
|
||||
@extend %ibo-hyperlink-inherited-colors;
|
||||
@extend %ibo-fully-centered-content;
|
||||
@extend %ibo-font-ral-nor-100;
|
||||
|
||||
&:hover {
|
||||
background-color: $ibo-field--fullscreen-toggler--background-color--on-hover;
|
||||
}
|
||||
}
|
||||
.ibo-field--value {
|
||||
display: table;
|
||||
width: 100%;
|
||||
@@ -106,7 +152,6 @@ $ibo-field--value-decoration--spacing-x: 0.5rem !default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ibo-field--comments {
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
@@ -114,6 +159,31 @@ $ibo-field--value-decoration--spacing-x: 0.5rem !default;
|
||||
|
||||
> input[type="checkbox"] {
|
||||
margin-left: 5px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
> .multi_values, > .mono_value {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
/********************************************************/
|
||||
/* Shameful zone, CSS classes not following conventions */
|
||||
/* To be reworked */
|
||||
/********************************************************/
|
||||
.mailto, .tel {
|
||||
white-space: nowrap;
|
||||
|
||||
.text_decoration {
|
||||
margin-right: $ibo-field--value-decoration--spacing-x;
|
||||
font-size: 0.9em; /* Mind the "em" instead of "rem" as we want the decoration to be size relatively to the text itself, not the page */
|
||||
}
|
||||
}
|
||||
|
||||
.object-ref-icon, .object-ref-icon-disabled {
|
||||
&.text_decoration {
|
||||
margin-right: $ibo-field--value-decoration--spacing-x;
|
||||
@extend %ibo-font-size-100;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,39 +205,3 @@ $ibo-field--value-decoration--spacing-x: 0.5rem !default;
|
||||
.multi_values {
|
||||
background-color: #c33;
|
||||
}
|
||||
|
||||
/* Fullscreen mode */
|
||||
.ibo-field-large {
|
||||
&.ibo-is-fullscreen {
|
||||
background-color: $ibo-field--background-color--is-fullscreen;
|
||||
|
||||
.ibo-field--label {
|
||||
position: fixed;
|
||||
width: $ibo-field--label--width--is-fullscreen;
|
||||
min-width: initial;
|
||||
max-width: initial;
|
||||
padding: $ibo-field--label--padding-y--is-fullscreen $ibo-field--label--padding-x--is-fullscreen;
|
||||
background-color: $ibo-field--label--background-color--is-fullscreen;
|
||||
border-bottom: $ibo-field--label--border-bottom--is-fullscreen;
|
||||
}
|
||||
|
||||
.ibo-field--value {
|
||||
padding: $ibo-field--value--padding-top--is-fullscreen $ibo-field--value--padding-x--is-fullscreen $ibo-field--value--padding-bottom--is-fullscreen $ibo-field--value--padding-x--is-fullscreen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mailto, .tel {
|
||||
white-space: nowrap;
|
||||
|
||||
.text_decoration {
|
||||
margin-right: $ibo-field--value-decoration--spacing-x;
|
||||
}
|
||||
}
|
||||
|
||||
.object-ref-icon, .object-ref-icon-disabled {
|
||||
&.text_decoration {
|
||||
margin-right: $ibo-field--value-decoration--spacing-x;
|
||||
@extend %ibo-font-ral-nor-100;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,5 +24,5 @@ $ibo-fieldset--legend--border-bottom-style: solid !default;
|
||||
padding-bottom: $ibo-fieldset--legend--padding-bottom;
|
||||
border-bottom: $ibo-fieldset--legend--border-bottom-size $ibo-fieldset--legend--border-bottom-style $ibo-fieldset--legend--border-bottom-color;
|
||||
|
||||
@extend %ibo-font-ral-med-250;
|
||||
@extend %ibo-font-size-250;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ $ibo-input-file-select--file-name-margin-left: 10px !default;
|
||||
|
||||
.ibo-input-file-select--file-name {
|
||||
margin-left: $ibo-input-file-select--file-name-margin-left;
|
||||
@extend %ibo-font-ral-med-150;
|
||||
@extend %ibo-font-size-150;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,7 @@ $ibo-global-search--compartment--placeholder-hint--text-color: $ibo-color-grey-7
|
||||
padding: $ibo-global-search--drawer--padding-y $ibo-global-search--drawer--padding-x;
|
||||
background-color: $ibo-global-search--drawer--background-color;
|
||||
box-shadow: none;
|
||||
@extend %ibo-font-ral-nor-100;
|
||||
@extend %ibo-font-size-100;
|
||||
}
|
||||
.ibo-global-search--compartment-title{
|
||||
margin-bottom: $ibo-global-search--compartment-title--margin-bottom;
|
||||
|
||||
@@ -79,11 +79,9 @@ img.ibo-navigation-menu--notifications--item--image:not([src=""]) ~ i.ibo-naviga
|
||||
align-items: center;
|
||||
float: right;
|
||||
align-self: center;
|
||||
@extend %ibo-font-ral-nor-100;
|
||||
margin-left: $ibo-navigation-menu--notifications--item--bottom-text--margin-left;
|
||||
}
|
||||
.ibo-navigation-menu--notifications--item--content{
|
||||
@extend %ibo-font-ral-nor-150;
|
||||
padding: $ibo-navigation-menu--notifications--item--content--padding-y $ibo-navigation-menu--notifications--item--content--padding-x;
|
||||
img{
|
||||
max-height: $ibo-navigation-menu--notifications--item--content--img--max-height;
|
||||
@@ -108,7 +106,7 @@ img.ibo-navigation-menu--notifications--item--image:not([src=""]) ~ i.ibo-naviga
|
||||
margin-right: $ibo-navigation-menu--notifications-show-all-multiple--ibo-popover-menu--indicator--margin-right;
|
||||
}
|
||||
.ibo-navigation-menu--notifications-show-all-multiple--counter{
|
||||
@extend %ibo-font-ral-bol-200;
|
||||
@extend %ibo-font-weight-600;
|
||||
}
|
||||
}
|
||||
.ibo-navigation-menu--notifications-dismiss-all--icon{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user