mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-15 08:24:10 +01:00
Compare commits
415 Commits
3.0.3-desi
...
support/3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8141723869 | ||
|
|
8cb701bda3 | ||
|
|
1b29746806 | ||
|
|
fb9c317256 | ||
|
|
1aef576403 | ||
|
|
96e1388dde | ||
|
|
69c8791fc5 | ||
|
|
cddc452693 | ||
|
|
0904a21e3f | ||
|
|
1f1a2b660f | ||
|
|
33a906f11a | ||
|
|
82d11eeb47 | ||
|
|
2596a150bf | ||
|
|
142d6c8993 | ||
|
|
c4fc0ed982 | ||
|
|
320922a13d | ||
|
|
d3b9965283 | ||
|
|
f03d731b1d | ||
|
|
63cf78f64d | ||
|
|
8be7628668 | ||
|
|
f632cf3155 | ||
|
|
62caf16153 | ||
|
|
163a3afc0f | ||
|
|
d98e35d918 | ||
|
|
f8b54be896 | ||
|
|
c6f3e36451 | ||
|
|
53dc452d61 | ||
|
|
ccaf2dc5b7 | ||
|
|
46738d4ba4 | ||
|
|
5d5df5ad1a | ||
|
|
61469a28b9 | ||
|
|
dbcbb187b2 | ||
|
|
93bba66323 | ||
|
|
cab6394cba | ||
|
|
32140b360f | ||
|
|
e657052d17 | ||
|
|
d85767a838 | ||
|
|
e5a8bd61b0 | ||
|
|
eeec57536b | ||
|
|
514e0b80a5 | ||
|
|
35f4ab4941 | ||
|
|
16ff6341d0 | ||
|
|
ac826cb9f1 | ||
|
|
9dab8679d6 | ||
|
|
f737bcb9a0 | ||
|
|
4c78488644 | ||
|
|
77cc4672b0 | ||
|
|
b65e931c4c | ||
|
|
dfbfab7005 | ||
|
|
aa831b632c | ||
|
|
6cb3519308 | ||
|
|
cfb9fae648 | ||
|
|
f4e791734f | ||
|
|
6653ab0668 | ||
|
|
7ab258ba03 | ||
|
|
b5af30a93f | ||
|
|
ce5c05234d | ||
|
|
bbfa601ab1 | ||
|
|
83764deedb | ||
|
|
172b1cb1ff | ||
|
|
e1374a0e6b | ||
|
|
ca356859a3 | ||
|
|
5efe294895 | ||
|
|
18d0b88531 | ||
|
|
3139a0b610 | ||
|
|
e0170ccc7e | ||
|
|
367aac3e04 | ||
|
|
3b78885f38 | ||
|
|
f14b4c32be | ||
|
|
6a30d6caa9 | ||
|
|
a51242dc36 | ||
|
|
64ba706083 | ||
|
|
ed562c9f73 | ||
|
|
85c576a986 | ||
|
|
ff1305165e | ||
|
|
65c706fdfe | ||
|
|
5a34c76cc4 | ||
|
|
bdfd956825 | ||
|
|
da99a250bf | ||
|
|
f4b9a9a5fe | ||
|
|
5c46b4ef4a | ||
|
|
a97935ca01 | ||
|
|
efd7fb0f59 | ||
|
|
a4edf8cb21 | ||
|
|
dbd5ba0377 | ||
|
|
5d6f293956 | ||
|
|
65e6c84477 | ||
|
|
a337ef3d88 | ||
|
|
986c24d777 | ||
|
|
763112c179 | ||
|
|
a5efd981d8 | ||
|
|
289ca7b505 | ||
|
|
2922b22478 | ||
|
|
2af05a437e | ||
|
|
71d9536bc4 | ||
|
|
6377a738c5 | ||
|
|
a9f8dcc5e8 | ||
|
|
969a301cbb | ||
|
|
c325294e17 | ||
|
|
a29b0a8e33 | ||
|
|
f78b57521a | ||
|
|
da490739be | ||
|
|
48de13b5cf | ||
|
|
b867faa355 | ||
|
|
e878938e25 | ||
|
|
7453cc184f | ||
|
|
f3abe1ff13 | ||
|
|
473cf004b6 | ||
|
|
24f1cf8ca1 | ||
|
|
102a4a0c75 | ||
|
|
f6fec506b1 | ||
|
|
3b9f281afd | ||
|
|
ec465174f7 | ||
|
|
31bd763b90 | ||
|
|
5c12151c26 | ||
|
|
9777ac1a5c | ||
|
|
dd27a3ebb4 | ||
|
|
54439ad529 | ||
|
|
8f7bf00551 | ||
|
|
c020de59a7 | ||
|
|
aa53de467d | ||
|
|
bc6efc99ed | ||
|
|
bb3ab76205 | ||
|
|
0b1bdfff55 | ||
|
|
77c0cdf5aa | ||
|
|
af9fb74c54 | ||
|
|
5d6c4939f6 | ||
|
|
51d0d16a11 | ||
|
|
b0634c9fbc | ||
|
|
c951a33646 | ||
|
|
ed694b09b0 | ||
|
|
3868d57d28 | ||
|
|
1b3a2c8470 | ||
|
|
618d8e6468 | ||
|
|
f54d1273c9 | ||
|
|
01a955a16f | ||
|
|
a5aac0caad | ||
|
|
31e29506fa | ||
|
|
3f3b0cbe55 | ||
|
|
dab03e5b5d | ||
|
|
87582a021b | ||
|
|
8ef04bedf2 | ||
|
|
9eeb4b8751 | ||
|
|
ae3d0f9444 | ||
|
|
2ed0666c3b | ||
|
|
a61b117f71 | ||
|
|
9830178a47 | ||
|
|
83ac219ec9 | ||
|
|
c140ebcb6b | ||
|
|
b181914d25 | ||
|
|
7a0a4e377b | ||
|
|
7fffbb60e9 | ||
|
|
b8892e9651 | ||
|
|
8cde0ce5c5 | ||
|
|
2fd9523c16 | ||
|
|
48c4e2d13d | ||
|
|
a4f6f6e877 | ||
|
|
6042e7f74d | ||
|
|
94c604a6af | ||
|
|
f84e2060be | ||
|
|
6995a3c641 | ||
|
|
4ee70cb95a | ||
|
|
9865bf0779 | ||
|
|
f63f3bb547 | ||
|
|
d5449cca42 | ||
|
|
181c180824 | ||
|
|
5d38d22c50 | ||
|
|
974c155855 | ||
|
|
99d69493d1 | ||
|
|
c9bb628c30 | ||
|
|
08e8d15d78 | ||
|
|
bed1db9c51 | ||
|
|
7e3e8e43a8 | ||
|
|
7b59df216b | ||
|
|
cb5eab812e | ||
|
|
484a0bb6b6 | ||
|
|
c9b73a7fe2 | ||
|
|
6f1de11c59 | ||
|
|
e02b6ee14a | ||
|
|
3b2da39469 | ||
|
|
c5b43f3157 | ||
|
|
fc22d91232 | ||
|
|
3068a6a360 | ||
|
|
083a0b79bf | ||
|
|
e22220b4fe | ||
|
|
b10bcb976d | ||
|
|
154a20b757 | ||
|
|
79c24dfc96 | ||
|
|
b9d960e89e | ||
|
|
8540ec644a | ||
|
|
5b19593ede | ||
|
|
0915081f50 | ||
|
|
a23d629e31 | ||
|
|
47ccd7589f | ||
|
|
7521fc3006 | ||
|
|
c955fe00b7 | ||
|
|
5a43448644 | ||
|
|
a2b9583379 | ||
|
|
77409eed99 | ||
|
|
09be84f69d | ||
|
|
d9bdcfeae3 | ||
|
|
d725ba3d84 | ||
|
|
8a3d81c430 | ||
|
|
83a70daf68 | ||
|
|
cbb37f27d7 | ||
|
|
e21dc4d21c | ||
|
|
a49a4e6c2b | ||
|
|
8fa9336568 | ||
|
|
15148f7d1d | ||
|
|
7e8589ba95 | ||
|
|
fba668207f | ||
|
|
798cd10d6b | ||
|
|
442721bcb5 | ||
|
|
1a9049d277 | ||
|
|
c0931af91a | ||
|
|
29e9a06dc1 | ||
|
|
d6415042ae | ||
|
|
90006667fe | ||
|
|
fd351df08b | ||
|
|
7419749ba6 | ||
|
|
73bed04555 | ||
|
|
8893cdac1d | ||
|
|
7f245a15be | ||
|
|
b5c46ccd4a | ||
|
|
7fbc211c43 | ||
|
|
cf774cdb90 | ||
|
|
722a58491c | ||
|
|
037dfe1df6 | ||
|
|
0b26d45014 | ||
|
|
b9c566238a | ||
|
|
4fd8177165 | ||
|
|
0cc0f39d9e | ||
|
|
a2cdf214f0 | ||
|
|
4f75d012e5 | ||
|
|
013173019f | ||
|
|
fadfd94bac | ||
|
|
9469681a0c | ||
|
|
da27ddba82 | ||
|
|
c72cb7e70e | ||
|
|
9df92665e0 | ||
|
|
3647291475 | ||
|
|
6dc6392fab | ||
|
|
e793b02f8b | ||
|
|
fc6e98b534 | ||
|
|
8ecebee511 | ||
|
|
2690fa3315 | ||
|
|
35cd965360 | ||
|
|
83a5b98f82 | ||
|
|
e5dd51f637 | ||
|
|
4923418f58 | ||
|
|
0a6c82dfe1 | ||
|
|
2dd7f5cada | ||
|
|
24c0f4950f | ||
|
|
d4dbbc59d4 | ||
|
|
dc0cd44c79 | ||
|
|
f3c4fcb0f5 | ||
|
|
6046f44f56 | ||
|
|
6c6131ce03 | ||
|
|
343e87a8d4 | ||
|
|
e76728b2bf | ||
|
|
f65c690462 | ||
|
|
ecf8bc42fa | ||
|
|
ea8509db1f | ||
|
|
df25ce76b6 | ||
|
|
e946fc65fc | ||
|
|
dbe2f66539 | ||
|
|
0d8ff7bbac | ||
|
|
61a9a4ac65 | ||
|
|
1f4dcc4f9e | ||
|
|
e86309669e | ||
|
|
6ebcd44bb1 | ||
|
|
bf768311c2 | ||
|
|
d130959692 | ||
|
|
a8c689c6c0 | ||
|
|
1990ccb5d8 | ||
|
|
e107be56e4 | ||
|
|
f6653e1594 | ||
|
|
65bb76b9e3 | ||
|
|
d951d3b872 | ||
|
|
ccceb870e3 | ||
|
|
ed6df77cbb | ||
|
|
1ad28312ec | ||
|
|
f002aa04cd | ||
|
|
b86d70623e | ||
|
|
fe3467309d | ||
|
|
851ab9c356 | ||
|
|
aef3c2e609 | ||
|
|
f04fc546b5 | ||
|
|
13ad98b9b3 | ||
|
|
4be54fdd65 | ||
|
|
6d13397ba1 | ||
|
|
d64a91d4ce | ||
|
|
c0c8a13864 | ||
|
|
d2eef06276 | ||
|
|
880a824f2f | ||
|
|
7aa478d6ff | ||
|
|
734a788340 | ||
|
|
e5b6e2eb8c | ||
|
|
1682a85cc0 | ||
|
|
cd9beec313 | ||
|
|
8295eaed90 | ||
|
|
829b648dd2 | ||
|
|
5475b9fbbe | ||
|
|
6f8e7c7002 | ||
|
|
772368ef8a | ||
|
|
a57b6471c9 | ||
|
|
bc7c1b4744 | ||
|
|
046e857768 | ||
|
|
4d8246c4d8 | ||
|
|
5c61d725e1 | ||
|
|
00b070b3cf | ||
|
|
2c4cad4dac | ||
|
|
89145593ef | ||
|
|
b2e80d37dd | ||
|
|
6432678de9 | ||
|
|
71ed784c60 | ||
|
|
da45651121 | ||
|
|
d388ce9a06 | ||
|
|
47e71d8838 | ||
|
|
2b5973ec67 | ||
|
|
78396d8e4a | ||
|
|
40d63a2fa4 | ||
|
|
556b9ad89a | ||
|
|
9afc22bd8f | ||
|
|
a010239efb | ||
|
|
264a8cd70a | ||
|
|
aa1834170b | ||
|
|
f94d67ab35 | ||
|
|
3048c8c41f | ||
|
|
246e4a9f50 | ||
|
|
8907525b78 | ||
|
|
6d58adb6dd | ||
|
|
764a170cd0 | ||
|
|
5a33fb7a6a | ||
|
|
2cca57c7fa | ||
|
|
52820925b1 | ||
|
|
1824111de8 | ||
|
|
5a0b5364d6 | ||
|
|
76eed2eba0 | ||
|
|
5d5589dd64 | ||
|
|
1ec671ef61 | ||
|
|
b8199a6e2c | ||
|
|
9f38eec40a | ||
|
|
5f5537b8b9 | ||
|
|
72716b7ec8 | ||
|
|
e288af4ddb | ||
|
|
4f999de844 | ||
|
|
f47133bc28 | ||
|
|
ea49c0a87c | ||
|
|
6cc971849b | ||
|
|
0fbd41a884 | ||
|
|
70e6f707c4 | ||
|
|
e76ada641f | ||
|
|
2405810864 | ||
|
|
fff46d99fc | ||
|
|
3a891f707c | ||
|
|
8b6ea43ebe | ||
|
|
90cf7502e8 | ||
|
|
c596fa2967 | ||
|
|
a45177410e | ||
|
|
b8f61362f5 | ||
|
|
e3ba826e5d | ||
|
|
17d22219d2 | ||
|
|
a49025f371 | ||
|
|
9e96ea2873 | ||
|
|
1172159745 | ||
|
|
cdcc069099 | ||
|
|
8c639fc23a | ||
|
|
da5a825c7e | ||
|
|
b9230ad402 | ||
|
|
959ac7e3be | ||
|
|
1884596ecd | ||
|
|
584cfa8cbf | ||
|
|
eebc61385d | ||
|
|
3c15186685 | ||
|
|
fa038ded3d | ||
|
|
e7ea1b831c | ||
|
|
f15ac75f8f | ||
|
|
c6edbf982d | ||
|
|
5aea7ccbc9 | ||
|
|
e28dbebbd5 | ||
|
|
4aff65f98b | ||
|
|
8aba578cfa | ||
|
|
9d3e389011 | ||
|
|
7e7f8577e8 | ||
|
|
3c94974d9d | ||
|
|
d6e5069dd5 | ||
|
|
f839638e0b | ||
|
|
740ff8c649 | ||
|
|
cfe227e0c7 | ||
|
|
ed79c8f099 | ||
|
|
4560f751d1 | ||
|
|
46e869d1f4 | ||
|
|
fbd72b2783 | ||
|
|
778118cfb4 | ||
|
|
096ed9a63a | ||
|
|
06eb79d4f4 | ||
|
|
4e95ca3c7b | ||
|
|
4c626d0782 | ||
|
|
1114ed9562 | ||
|
|
1ddfaf0b61 | ||
|
|
c6fb03547f | ||
|
|
34368fe795 | ||
|
|
db46298cb8 | ||
|
|
064e8ee511 | ||
|
|
424e7b37d7 | ||
|
|
ca7aa482ab | ||
|
|
368b3f4ef7 | ||
|
|
dc8e6f314a | ||
|
|
8ffddeff01 | ||
|
|
21d37fb237 | ||
|
|
fca4006811 | ||
|
|
c3b00939dd | ||
|
|
75df33f606 | ||
|
|
0001e8ffc4 |
83
.github/pull_request_template.md
vendored
Normal file
83
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
<!--
|
||||
|
||||
IMPORTANT: Please follow the guidelines within this PR template before submitting it, it will greatly help us process your PR. 🙏
|
||||
|
||||
Any PRs not following the guidelines or with missing information will not be considered.
|
||||
|
||||
-->
|
||||
|
||||
## Base information
|
||||
| Question | Answer
|
||||
|---------------------------------------------------------------|--------
|
||||
| Related to a SourceForge thead / Another PR / Combodo ticket? | <!-- Put the URL -->
|
||||
| Type of change? | Bug fix / Enhancement / Translations
|
||||
|
||||
|
||||
## Symptom (bug) / Objective (enhancement)
|
||||
<!--
|
||||
If it's a bug
|
||||
- Explain the symptom in details
|
||||
- If possible put error messages, logs or screenshots (you can paste image directly in this editor).
|
||||
|
||||
If it's an enhancement
|
||||
- Describe what is blocking you, what is the objective with as much details as possible.
|
||||
- Add screenshots if it's related to UI.
|
||||
-->
|
||||
|
||||
|
||||
## Reproduction procedure (bug)
|
||||
<!--
|
||||
Remove this section only if it's NOT a bug.
|
||||
|
||||
Otherwise, explain step by step how to reproduce the issue on a standard iTop Community.
|
||||
|
||||
If it requires a custom datamodel, provide the minimal XML delta to reproduce it on a standard iTop Community.
|
||||
-->
|
||||
|
||||
1. On iTop x.y.z <!-- Put complete iTop version (eg. 3.1.0-2) -->
|
||||
2. With PHP x.y.z <!-- Put complete PHP version (eg. 8.1.24) -->
|
||||
2. First go there
|
||||
2. Then do that
|
||||
3. ...
|
||||
4. Finally, see that...
|
||||
|
||||
|
||||
## Cause (bug)
|
||||
<!--
|
||||
Remove this section only if it's NOT a bug.
|
||||
|
||||
Otherwise, explain what is the cause of the issue (where in the code and why)
|
||||
-->
|
||||
|
||||
|
||||
## Proposed solution (bug and enhancement)
|
||||
<!--
|
||||
Explain in details how you are proposing to solve this:
|
||||
- What did you do in the code and why
|
||||
- If you changed something in the UI, put before / after screenshots (you can paste image directly in this editor)
|
||||
-->
|
||||
|
||||
|
||||
## Checklist before requesting a review
|
||||
<!--
|
||||
Don't remove these lines, check them once done.
|
||||
-->
|
||||
- [ ] I have performed a self-review of my code
|
||||
- [ ] I have tested all changes I made on an iTop instance
|
||||
- [ ] I have added a unit test, otherwise I have explained why I couldn't
|
||||
- [ ] Is the PR clear and detailed enough so anyone can understand digging in the code?
|
||||
|
||||
## Checklist of things to do before PR is ready to merge
|
||||
<!--
|
||||
Things that needs to be done in the PR before it can be considered as ready to be merged
|
||||
|
||||
Examples:
|
||||
- Changes requested in the review
|
||||
- Unit test to add
|
||||
- Dictionary entries to translate
|
||||
- ...
|
||||
-->
|
||||
|
||||
- [ ] ...
|
||||
- [ ] ...
|
||||
- [ ] ...
|
||||
16
.github/workflows/action.yml
vendored
Normal file
16
.github/workflows/action.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: Add PRs to Combodo PRs Dashboard
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
add-to-project:
|
||||
name: Add PR to Combodo Project
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/add-to-project@v1.0.2
|
||||
with:
|
||||
project-url: https://github.com/orgs/Combodo/projects/5
|
||||
github-token: ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -37,7 +37,9 @@ tests/*/vendor/*
|
||||
|
||||
# iTop extensions
|
||||
/extensions/**
|
||||
!/extensions/.htaccess
|
||||
!/extensions/readme.txt
|
||||
!/extensions/web.config
|
||||
|
||||
# all logs but listing prevention
|
||||
/log/**
|
||||
@@ -45,8 +47,10 @@ tests/*/vendor/*
|
||||
!/log/index.php
|
||||
!/log/web.config
|
||||
|
||||
# PHPUnit cache file
|
||||
# PHPUnit: Cache file, local XML working copies
|
||||
/tests/php-unit-tests/.phpunit.result.cache
|
||||
/tests/php-unit-tests/phpunit.xml
|
||||
/tests/php-unit-tests/postbuild_integration.xml
|
||||
|
||||
|
||||
# Jetbrains
|
||||
|
||||
@@ -27,7 +27,7 @@ $iTopFolder = __DIR__."/../../../";
|
||||
require_once("$iTopFolder/approot.inc.php");
|
||||
require_once(APPROOT."/application/utils.inc.php");
|
||||
|
||||
if (php_sapi_name() !== 'cli')
|
||||
if (PHP_SAPI !== 'cli')
|
||||
{
|
||||
throw new \Exception('This script can only run from CLI');
|
||||
}
|
||||
@@ -48,4 +48,4 @@ if (!file_exists($sCssFile))
|
||||
{
|
||||
fwrite(STDERR, "Failed to compile $sCssFile, exiting.");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ $iTopFolder = __DIR__ . "/../../" ;
|
||||
require_once ("$iTopFolder/approot.inc.php");
|
||||
require_once (APPROOT."/setup/setuputils.class.inc.php");
|
||||
|
||||
if (php_sapi_name() !== 'cli')
|
||||
if (PHP_SAPI !== 'cli')
|
||||
{
|
||||
throw new \Exception('This script can only run from CLI');
|
||||
}
|
||||
@@ -70,4 +70,4 @@ if (false === empty($aMissing)) {
|
||||
echo "Some new tests dirs exists !\n"
|
||||
.' They must be declared either in the allowed or denied list in '.iTopComposer::class." (see N°2651).\n"
|
||||
.' List of dirs:'."\n".var_export($aMissing, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,17 +19,24 @@
|
||||
* The target license file path is in `$xmlFilePath`
|
||||
*/
|
||||
|
||||
$iTopFolder = __DIR__ . "/../../" ;
|
||||
$xmlFilePath = $iTopFolder . "setup/licenses/community-licenses.xml";
|
||||
$iTopFolder = __DIR__."/../../";
|
||||
$xmlFilePath = $iTopFolder."setup/licenses/community-licenses.xml";
|
||||
|
||||
function get_scope($product_node)
|
||||
{
|
||||
$jqExec = shell_exec("jq -V"); // a param is mandatory otherwise the script will freeze
|
||||
if ((null === $jqExec) || (false === $jqExec)) {
|
||||
echo "/!\ JQ is required but cannot be launched :( \n";
|
||||
echo "Check this script PHPDoc block for instructions\n";
|
||||
die(-1);
|
||||
}
|
||||
|
||||
|
||||
function get_scope($product_node) {
|
||||
$scope = $product_node->getAttribute("scope");
|
||||
|
||||
if ($scope === "")
|
||||
{ //put iTop first
|
||||
if ($scope === "") { //put iTop first
|
||||
return "aaaaaaaaa";
|
||||
}
|
||||
|
||||
return $scope;
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,8 @@ We would like to give a special thank you 🤗 to the people from the community
|
||||
- Rosenke, Stephan
|
||||
- Seki, Shoji
|
||||
- Shilov, Vladimir
|
||||
- Stukalov, Ilya (a.k.a [@ilya](https://www.github.com/ilya)-stukalov)
|
||||
- Stukalov, Ilya (a.k.a [@ilya](https://www.github.com/ilya-stukalov))
|
||||
- Tarjányi, Csaba (a.k.a [@tacsaby](https://github.com/tacsaby))
|
||||
- Tulio, Marco
|
||||
- Turrubiates, Miguel
|
||||
|
||||
|
||||
@@ -432,6 +432,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
UR_ACTION_BULK_DELETE => 'bd',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array $aUsersProfilesList Cache of users' profiles. Hash array of user ID => [profile ID => profile friendlyname, profile ID => profile friendlyname, ...]
|
||||
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6887
|
||||
*/
|
||||
private $aUsersProfilesList = [];
|
||||
|
||||
// Installation: create the very first user
|
||||
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
|
||||
{
|
||||
@@ -490,6 +496,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
}
|
||||
|
||||
protected $m_aUserOrgs = array(); // userid -> array of orgid
|
||||
protected $m_aAdministrators = null; // [user id]
|
||||
|
||||
// Built on demand, could be optimized if necessary (doing a query for each attribute that needs to be read)
|
||||
protected $m_aObjectActionGrants = array();
|
||||
@@ -546,6 +553,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
|
||||
// Cache
|
||||
$this->m_aObjectActionGrants = array();
|
||||
$this->m_aAdministrators = null;
|
||||
}
|
||||
|
||||
public function LoadCache()
|
||||
@@ -688,12 +696,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
*/
|
||||
private function GetAdministrators()
|
||||
{
|
||||
static $aAdministrators = null;
|
||||
|
||||
if ($aAdministrators === null)
|
||||
if ($this->m_aAdministrators === null)
|
||||
{
|
||||
// Find all administrators
|
||||
$aAdministrators = array();
|
||||
$this->m_aAdministrators = array();
|
||||
$oAdministratorsFilter = new DBObjectSearch('User');
|
||||
$oLnkFilter = new DBObjectSearch('URP_UserProfile');
|
||||
$oExpression = new FieldExpression('profileid', 'URP_UserProfile');
|
||||
@@ -706,10 +712,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
$oSet->OptimizeColumnLoad(array('User' => array('login')));
|
||||
while($oUser = $oSet->Fetch())
|
||||
{
|
||||
$aAdministrators[] = $oUser->GetKey();
|
||||
$this->m_aAdministrators[] = $oUser->GetKey();
|
||||
}
|
||||
}
|
||||
return $aAdministrators;
|
||||
return $this->m_aAdministrators;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -746,8 +752,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
$sAction = self::$m_aActionCodes[$iActionCode];
|
||||
|
||||
$bStatus = null;
|
||||
// Cache user's profiles
|
||||
if(false === array_key_exists($iUser, $this->aUsersProfilesList)){
|
||||
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
|
||||
}
|
||||
// Call the API of UserRights because it caches the list for us
|
||||
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
|
||||
foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
|
||||
{
|
||||
$bGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
|
||||
if (!is_null($bGrant))
|
||||
@@ -873,11 +883,16 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
// Note: this code is VERY close to the code of IsActionAllowed()
|
||||
$iUser = $oUser->GetKey();
|
||||
|
||||
// Cache user's profiles
|
||||
if(false === array_key_exists($iUser, $this->aUsersProfilesList)){
|
||||
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
|
||||
}
|
||||
|
||||
// Note: The object set is ignored because it was interesting to optimize for huge data sets
|
||||
// and acceptable to consider only the root class of the object set
|
||||
$bStatus = null;
|
||||
// Call the API of UserRights because it caches the list for us
|
||||
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
|
||||
foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
|
||||
{
|
||||
$bGrant = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
|
||||
if (!is_null($bGrant))
|
||||
@@ -906,8 +921,9 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out which attribute is corresponding the the dimension 'owner org'
|
||||
* returns null if no such attribute has been found (no filtering should occur)
|
||||
* @param string $sClass
|
||||
* @return string|null Find out which attribute is corresponding the dimension 'owner org'
|
||||
* returns null if no such attribute has been found (no filtering should occur)
|
||||
*/
|
||||
public static function GetOwnerOrganizationAttCode($sClass)
|
||||
{
|
||||
|
||||
@@ -586,10 +586,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
/**
|
||||
* Read and cache organizations allowed to the given user
|
||||
*
|
||||
* @param $oUser
|
||||
* @param $sClass (not used here but can be used in overloads)
|
||||
* @param User $oUser
|
||||
* @param string $sClass (not used here but can be used in overloads)
|
||||
*
|
||||
* @return array
|
||||
* @return array keys of the User allowed org
|
||||
* @throws \CoreException
|
||||
* @throws \Exception
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/AjaxPage.php, now loadable using autoloader
|
||||
* @deprecated 3.0.0 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
|
||||
*/
|
||||
|
||||
@@ -1949,6 +1949,8 @@ class RestUtils
|
||||
*
|
||||
* @return DBObject The object found
|
||||
* @throws Exception If the input structure is not valid or it could not find exactly one object
|
||||
*
|
||||
* @see DBObject::CheckChangedExtKeysValues() generic method to check that we can access the linked object isn't used in that use case because values can be literal, OQL, friendlyname
|
||||
*/
|
||||
public static function FindObjectFromKey($sClass, $key, $bAllowNullValue = false)
|
||||
{
|
||||
@@ -2035,8 +2037,16 @@ class RestUtils
|
||||
elseif (is_string($key))
|
||||
{
|
||||
// OQL
|
||||
$oSearch = DBObjectSearch::FromOQL($key);
|
||||
}
|
||||
try {
|
||||
$oSearch = DBObjectSearch::FromOQL($key);
|
||||
} catch (Exception $e) {
|
||||
throw new CoreOqlException('Query failed to execute', [
|
||||
'query' => $key,
|
||||
'exception_class' => get_class($e),
|
||||
'exception_message' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("Wrong format for key");
|
||||
@@ -2185,4 +2195,28 @@ class RestUtils
|
||||
interface iModuleExtension
|
||||
{
|
||||
public function __construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* KPI logging extensibility point
|
||||
*
|
||||
* KPI Logger extension
|
||||
*/
|
||||
interface iKPILoggerExtension
|
||||
{
|
||||
/**
|
||||
* Init the statistics collected
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function InitStats();
|
||||
|
||||
/**
|
||||
* Add a new KPI to the stats
|
||||
*
|
||||
* @param \Combodo\iTop\Core\Kpi\KpiLogData $oKpiLogData
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function LogOperation($oKpiLogData);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CaptureWebPage.php, now loadable using autoloader
|
||||
* @deprecated 3.0.0 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
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CLIPage.php, now loadable using autoloader
|
||||
* @deprecated 3.0.0 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
|
||||
*/
|
||||
|
||||
@@ -1234,7 +1234,7 @@ HTML
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param \CMDBObjectSet $oSet
|
||||
* @param array $aExtraParams
|
||||
* @param array $aExtraParams See possible values in {@see DataTableUIBlockFactory::RenderDataTable()}
|
||||
*
|
||||
* @throws \ApplicationException
|
||||
* @throws \CoreException
|
||||
@@ -3054,11 +3054,23 @@ EOF
|
||||
|
||||
// Hook the cancel button via jQuery so that it can be unhooked easily as well if needed
|
||||
$sDefaultUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_form&class='.$sClass.'&'.$oAppContext->GetForLink();
|
||||
$oPage->add_ready_script("$('#form_{$this->m_iFormId} button.cancel').on('click', function() { BackToDetails('$sClass', $iKey, '$sDefaultUrl', $sJSToken)} );");
|
||||
|
||||
$sCancelButtonOnClickScript = "let fOnClick{$this->m_iFormId}CancelButton = ";
|
||||
if(isset($aExtraParams['js_handlers']['cancel_button_on_click'])){
|
||||
$sCancelButtonOnClickScript .= $aExtraParams['js_handlers']['cancel_button_on_click'];
|
||||
} else {
|
||||
$sCancelButtonOnClickScript .= "function() { BackToDetails('$sClass', $iKey, '$sDefaultUrl', $sJSToken)};";
|
||||
}
|
||||
$sCancelButtonOnClickScript .= "$('#form_{$this->m_iFormId} button.cancel').on('click', fOnClick{$this->m_iFormId}CancelButton);";
|
||||
$oPage->add_ready_script($sCancelButtonOnClickScript);
|
||||
|
||||
|
||||
$iFieldsCount = count($aFieldsMap);
|
||||
$sJsonFieldsMap = json_encode($aFieldsMap);
|
||||
$sState = $this->GetState();
|
||||
$sLifecycleStateForWizardHelper = '';
|
||||
if (MetaModel::HasLifecycle($sClass)) {
|
||||
$sLifecycleStateForWizardHelper = $this->GetState();
|
||||
}
|
||||
$sSessionStorageKey = $sClass.'_'.$iKey;
|
||||
$sTempId = utils::GetUploadTempId($iTransactionId);
|
||||
$oPage->add_ready_script(InlineImage::EnableCKEditorImageUpload($this, $sTempId));
|
||||
@@ -3068,7 +3080,7 @@ EOF
|
||||
sessionStorage.removeItem('$sSessionStorageKey');
|
||||
|
||||
// Create the object once at the beginning of the page...
|
||||
var oWizardHelper$sPrefix = new WizardHelper('$sClass', '$sPrefix', '$sState');
|
||||
var oWizardHelper$sPrefix = new WizardHelper('$sClass', '$sPrefix', '$sLifecycleStateForWizardHelper');
|
||||
oWizardHelper$sPrefix.SetFieldsMap($sJsonFieldsMap);
|
||||
oWizardHelper$sPrefix.SetFieldsCount($iFieldsCount);
|
||||
EOF
|
||||
@@ -3665,7 +3677,7 @@ HTML;
|
||||
if ($oAttDef->GetEditClass() == 'Document') {
|
||||
/** @var \ormDocument $oDocument */
|
||||
$oDocument = $this->Get($sAttCode);
|
||||
if (!$oDocument->IsEmpty()) {
|
||||
if (is_object($oDocument) && !$oDocument->IsEmpty()) {
|
||||
$sFieldAsHtml = $this->GetAsHTML($sAttCode);
|
||||
|
||||
$sDisplayLabel = Dict::S('UI:OpenDocumentInNewWindow_');
|
||||
@@ -4481,7 +4493,9 @@ HTML;
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
|
||||
}
|
||||
|
||||
return $res;
|
||||
@@ -4498,13 +4512,16 @@ HTML;
|
||||
|
||||
protected function DBCloneTracked_Internal($newKey = null)
|
||||
{
|
||||
$oNewObj = parent::DBCloneTracked_Internal($newKey);
|
||||
/** @var cmdbAbstractObject $oNewObj */
|
||||
$oNewObj = MetaModel::GetObject(get_class($this), parent::DBCloneTracked_Internal($newKey));
|
||||
|
||||
// Invoke extensions after insertion (the object must exist, have an id, etc.)
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBInsert($oNewObj, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
|
||||
}
|
||||
|
||||
return $oNewObj;
|
||||
@@ -4532,7 +4549,9 @@ HTML;
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBUpdate');
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
@@ -4578,7 +4597,9 @@ HTML;
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete');
|
||||
}
|
||||
|
||||
return parent::DBDeleteTracked_Internal($oDeletionPlan);
|
||||
@@ -4596,7 +4617,10 @@ HTML;
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
if ($oExtensionInstance->OnIsModified($this))
|
||||
$oKPI = new ExecutionKPI();
|
||||
$bIsModified = $oExtensionInstance->OnIsModified($this);
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnIsModified');
|
||||
if ($bIsModified)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -4650,7 +4674,9 @@ HTML;
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$aNewIssues = $oExtensionInstance->OnCheckToWrite($this);
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToWrite');
|
||||
if (is_array($aNewIssues) && (count($aNewIssues) > 0)) // Some extensions return null instead of an empty array
|
||||
{
|
||||
$this->m_aCheckIssues = array_merge($this->m_aCheckIssues, $aNewIssues);
|
||||
@@ -4698,7 +4724,9 @@ HTML;
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$aNewIssues = $oExtensionInstance->OnCheckToDelete($this);
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToDelete');
|
||||
if (is_array($aNewIssues) && count($aNewIssues) > 0)
|
||||
{
|
||||
$this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues);
|
||||
@@ -5253,6 +5281,11 @@ EOF
|
||||
'errors' => '<p>'.($bResult ? '' : implode('</p><p>', $aErrorsToDisplay)).'</p>',
|
||||
);
|
||||
if ($bResult && (!$bPreview)) {
|
||||
// doing the check will load multiple times same objects :/
|
||||
// but it shouldn't cost too much on execution time
|
||||
// user can mitigate by selecting less extkeys/lnk to set and/or less objects to update 🤷♂️
|
||||
$oObj->CheckChangedExtKeysValues();
|
||||
|
||||
$oObj->DBUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/CSVPage.php, now loadable using autoloader
|
||||
* @deprecated 3.0.0 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
|
||||
*/
|
||||
|
||||
@@ -918,6 +918,11 @@ class RuntimeDashboard extends Dashboard
|
||||
{
|
||||
$bCustomized = false;
|
||||
|
||||
$sDashboardFileSanitized = utils::RealPath(APPROOT.$sDashboardFile, APPROOT);
|
||||
if (false === $sDashboardFileSanitized) {
|
||||
throw new SecurityException('Invalid dashboard file !');
|
||||
}
|
||||
|
||||
// Search for an eventual user defined dashboard
|
||||
$oUDSearch = new DBObjectSearch('UserDashboard');
|
||||
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
|
||||
@@ -929,7 +934,7 @@ class RuntimeDashboard extends Dashboard
|
||||
$sDashboardDefinition = $oUserDashboard->Get('contents');
|
||||
$bCustomized = true;
|
||||
} else {
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFile);
|
||||
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
|
||||
}
|
||||
|
||||
|
||||
@@ -937,7 +942,7 @@ class RuntimeDashboard extends Dashboard
|
||||
$oDashboard = new RuntimeDashboard($sDashBoardId);
|
||||
$oDashboard->FromXml($sDashboardDefinition);
|
||||
$oDashboard->SetCustomFlag($bCustomized);
|
||||
$oDashboard->SetDefinitionFile($sDashboardFile);
|
||||
$oDashboard->SetDefinitionFile($sDashboardFileSanitized);
|
||||
} else {
|
||||
$oDashboard = null;
|
||||
}
|
||||
@@ -1136,7 +1141,7 @@ JS
|
||||
$oToolbar->AddSubBlock($oActionButton);
|
||||
|
||||
$aActions = array();
|
||||
$sFile = addslashes($this->sDefinitionFile);
|
||||
$sFile = addslashes(utils::LocalPath($this->sDefinitionFile));
|
||||
$sJSExtraParams = json_encode($aExtraParams);
|
||||
if ($this->HasCustomDashboard()) {
|
||||
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:EditCustom'), "return EditDashboard('{$this->sId}', '$sFile', $sJSExtraParams)");
|
||||
@@ -1259,12 +1264,12 @@ EOF
|
||||
$sOkButtonLabel = Dict::S('UI:Button:Save');
|
||||
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
|
||||
|
||||
$sId = addslashes($this->sId);
|
||||
$sLayoutClass = addslashes($this->sLayoutClass);
|
||||
$sId = utils::HtmlEntities($this->sId);
|
||||
$sLayoutClass = utils::HtmlEntities($this->sLayoutClass);
|
||||
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
|
||||
$sAutoReloadSec = (string) $this->iAutoReloadSec;
|
||||
$sTitle = addslashes($this->sTitle);
|
||||
$sFile = addslashes($this->GetDefinitionFile());
|
||||
$sTitle = utils::HtmlEntities($this->sTitle);
|
||||
$sFile = utils::HtmlEntities($this->GetDefinitionFile());
|
||||
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
|
||||
$sReloadURL = $this->GetReloadURL();
|
||||
|
||||
|
||||
@@ -386,7 +386,7 @@ EOF;
|
||||
if (!$oPage->IsPrintableVersion())
|
||||
{
|
||||
$sMenuTitle = Dict::S('UI:ConfigureThisList');
|
||||
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><i class="fas fa-tools"></i><i class="fas fa-caret-down"></i><ul>';
|
||||
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li aria-label="'.Dict::S('UI:Menu:Toolkit').'"><i class="fas fa-tools"></i><i class="fas fa-caret-down"></i><ul>';
|
||||
|
||||
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
|
||||
$aActions = array(
|
||||
|
||||
@@ -304,7 +304,7 @@ class DisplayBlock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function GetFilter()
|
||||
{
|
||||
return $this->m_oFilter;
|
||||
@@ -1045,7 +1045,7 @@ JS
|
||||
$aCount = $aCounts[$sStateValue];
|
||||
$sHyperlink = $aCount['link'];
|
||||
$sCountLabel = $aCount['label'];
|
||||
|
||||
|
||||
$oPill = PillFactory::MakeForState($sClass, $sStateValue);
|
||||
// N°5849 - Unencode label for ExternalKey attribute because friendlyname is already html encoded thanks to DBObject::GetName() in AttributeExternalKey::GetAllowedValues(). (A fix in this function may have too much impact).
|
||||
if ($oAttDef instanceof AttributeExternalKey) {
|
||||
@@ -1611,6 +1611,7 @@ JS
|
||||
|
||||
$iTotalCount = 0;
|
||||
$aURLs = array();
|
||||
|
||||
foreach ($aRes as $iRow => $aRow) {
|
||||
$sValue = $aRow['grouped_by_1'];
|
||||
$sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue);
|
||||
@@ -1621,6 +1622,7 @@ JS
|
||||
'value' => (float)$aRow[$sFctVar],
|
||||
);
|
||||
|
||||
|
||||
// Build the search for this subset
|
||||
$oSubsetSearch = $this->m_oFilter->DeepClone();
|
||||
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($sValue));
|
||||
@@ -1637,9 +1639,13 @@ JS
|
||||
|
||||
switch ($sChartType) {
|
||||
case 'bars':
|
||||
$aNames = array();
|
||||
$iMaxNbCharsInLabel = 0;
|
||||
$aNames = [];
|
||||
foreach ($aValues as $idx => $aValue) {
|
||||
$aNames[$idx] = $aValue['label'];
|
||||
if ($iMaxNbCharsInLabel < mb_strlen($aValue['label'])) {
|
||||
$iMaxNbCharsInLabel = mb_strlen($aValue['label']);
|
||||
}
|
||||
}
|
||||
$oBlock = new BlockChartAjaxBars();
|
||||
$oBlock->sJSNames = json_encode($aNames);
|
||||
@@ -1647,21 +1653,31 @@ JS
|
||||
$oBlock->sId = $sId;
|
||||
$oBlock->sJSURLs = $sJSURLs;
|
||||
$oBlock->sURLForRefresh = str_replace("'", "\'", $sUrl);
|
||||
$oBlock->iMaxNbCharsInLabel = $iMaxNbCharsInLabel;
|
||||
break;
|
||||
|
||||
case 'pie':
|
||||
$aColumns = array();
|
||||
$aNames = array();
|
||||
$aColumns = [];
|
||||
$aNames = [];
|
||||
foreach ($aValues as $idx => $aValue) {
|
||||
$aColumns[] = array('series_'.$idx, (float)$aValue['value']);
|
||||
$aNames['series_'.$idx] = $aValue['label'];
|
||||
}
|
||||
|
||||
$iNbLinesToAddForName = 0;
|
||||
if (count($aNames) > 50) {
|
||||
// Calculation of the number of legends line add to the height of the graph to have a maximum of 5 legend columns
|
||||
$iNbLinesIncludedInChartHeight = 10;
|
||||
$iNbLinesToAddForName = ceil(count($aNames) / 5) - $iNbLinesIncludedInChartHeight;
|
||||
}
|
||||
|
||||
$oBlock = new BlockChartAjaxPie();
|
||||
$oBlock->sJSColumns = json_encode($aColumns);
|
||||
$oBlock->sJSNames = json_encode($aNames);
|
||||
$oBlock->sId = $sId;
|
||||
$oBlock->sJSURLs = $sJSURLs;
|
||||
$oBlock->sURLForRefresh = str_replace("'", "\'", $sUrl);
|
||||
$oBlock->iNbLinesToAddForName = $iNbLinesToAddForName;
|
||||
break;
|
||||
}
|
||||
if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/ErrorPage.php, now loadable using autoloader
|
||||
* @deprecated 3.0.0 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
|
||||
*/
|
||||
|
||||
@@ -60,6 +60,24 @@ class CoreCannotSaveObjectException extends CoreException
|
||||
return $sContent;
|
||||
}
|
||||
|
||||
public function getTextMessage()
|
||||
{
|
||||
$sTitle = Dict::S('UI:Error:SaveFailed');
|
||||
$sContent = utils::HtmlEntities($sTitle);
|
||||
|
||||
if (count($this->aIssues) == 1) {
|
||||
$sIssue = reset($this->aIssues);
|
||||
$sContent .= utils::HtmlEntities($sIssue);
|
||||
} else {
|
||||
foreach ($this->aIssues as $sError) {
|
||||
$sContent .= " ".utils::HtmlEntities($sError).", ";
|
||||
}
|
||||
}
|
||||
|
||||
return $sContent;
|
||||
}
|
||||
|
||||
|
||||
public function getIssues()
|
||||
{
|
||||
return $this->aIssues;
|
||||
|
||||
36
application/exceptions/InvalidExternalKeyValueException.php
Normal file
36
application/exceptions/InvalidExternalKeyValueException.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6458 object creation
|
||||
*/
|
||||
class InvalidExternalKeyValueException extends CoreUnexpectedValue
|
||||
{
|
||||
private const ENUM_PARAMS_OBJECT = 'current_object';
|
||||
private const ENUM_PARAMS_ATTCODE = 'attcode';
|
||||
private const ENUM_PARAMS_ATTVALUE = 'attvalue';
|
||||
private const ENUM_PARAMS_USER = 'current_user';
|
||||
|
||||
public function __construct($oObject, $sAttCode, $aContextData = null, $oPrevious = null)
|
||||
{
|
||||
$aContextData[self::ENUM_PARAMS_OBJECT] = get_class($oObject) . '::' . $oObject->GetKey();
|
||||
$aContextData[self::ENUM_PARAMS_ATTCODE] = $sAttCode;
|
||||
$aContextData[self::ENUM_PARAMS_ATTVALUE] = $oObject->Get($sAttCode);
|
||||
|
||||
$oCurrentUser = UserRights::GetUserObject();
|
||||
if (false === is_null($oCurrentUser)) {
|
||||
$aContextData[self::ENUM_PARAMS_USER] = get_class($oCurrentUser) . '::' . $oCurrentUser->GetKey();
|
||||
}
|
||||
|
||||
parent::__construct('Attribute pointing to an object that is either non existing or not readable by the current user', $aContextData, '', $oPrevious);
|
||||
}
|
||||
|
||||
public function GetAttCode(): string
|
||||
{
|
||||
return $this->getContextData()[self::ENUM_PARAMS_ATTCODE];
|
||||
}
|
||||
|
||||
public function GetAttValue(): string
|
||||
{
|
||||
return $this->getContextData()[self::ENUM_PARAMS_ATTVALUE];
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/iTopWebPage.php, now loadable using autoloader
|
||||
* @deprecated 3.0.0 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
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/iTopWizardWebPage.php, now loadable using autoloader
|
||||
* @deprecated 3.0.0 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
|
||||
*/
|
||||
|
||||
@@ -80,6 +80,11 @@ class LoginBasic extends AbstractLoginFSMExtension
|
||||
{
|
||||
if (Session::Get('login_mode') == 'basic')
|
||||
{
|
||||
$iOnExit = LoginWebPage::getIOnExit();
|
||||
if ($iOnExit === LoginWebPage::EXIT_RETURN)
|
||||
{
|
||||
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
|
||||
}
|
||||
LoginWebPage::HTTP401Error();
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
|
||||
@@ -79,7 +79,7 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
{
|
||||
self::ResetLoginSession();
|
||||
$iOnExit = LoginWebPage::getIOnExit();
|
||||
if ($iOnExit == LoginWebPage::EXIT_RETURN)
|
||||
if ($iOnExit === LoginWebPage::EXIT_RETURN)
|
||||
{
|
||||
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
|
||||
}
|
||||
@@ -95,6 +95,12 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
{
|
||||
if (!Session::IsSet('login_mode'))
|
||||
{
|
||||
// N°6358 - if EXIT_RETURN was asked, send an error
|
||||
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
|
||||
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
|
||||
return LoginWebPage::LOGIN_FSM_ERROR;
|
||||
}
|
||||
|
||||
// If no plugin validated the user, exit
|
||||
self::ResetLoginSession();
|
||||
exit();
|
||||
@@ -113,6 +119,11 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
protected function OnConnected(&$iErrorCode)
|
||||
{
|
||||
Session::Unset('login_temp_auth_user');
|
||||
if (is_null(UserRights::GetUserObject())){
|
||||
//N°7085 avoid infinite loop
|
||||
IssueLog::Error("No user logged in. exit");
|
||||
exit(-1);
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
@@ -128,4 +139,4 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,11 @@ class LoginExternal extends AbstractLoginFSMExtension
|
||||
{
|
||||
if (Session::Get('login_mode') == 'external')
|
||||
{
|
||||
$iOnExit = LoginWebPage::getIOnExit();
|
||||
if ($iOnExit === LoginWebPage::EXIT_RETURN)
|
||||
{
|
||||
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
|
||||
}
|
||||
LoginWebPage::HTTP401Error();
|
||||
}
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
|
||||
@@ -44,6 +44,10 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
|
||||
exit;
|
||||
}
|
||||
|
||||
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
// No credentials yet, display the form
|
||||
$oPage = LoginWebPage::NewLoginWebPage();
|
||||
$oPage->DisplayLoginForm($this->bForceFormOnError);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2021 Combodo SARL
|
||||
// Copyright (C) 2010-2023 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
@@ -35,7 +35,7 @@ class LoginWebPage extends NiceWebPage
|
||||
{
|
||||
const EXIT_PROMPT = 0;
|
||||
const EXIT_HTTP_401 = 1;
|
||||
const EXIT_RETURN = 2;
|
||||
const EXIT_RETURN = 2; // Non interactive mode (ajax, rest, ...)
|
||||
|
||||
const EXIT_CODE_OK = 0;
|
||||
const EXIT_CODE_MISSINGLOGIN = 1;
|
||||
@@ -88,7 +88,7 @@ class LoginWebPage extends NiceWebPage
|
||||
parent::__construct($sTitle);
|
||||
$this->SetStyleSheet();
|
||||
$this->no_cache();
|
||||
$this->add_xframe_options();
|
||||
$this->add_http_headers();
|
||||
}
|
||||
|
||||
public function SetStyleSheet()
|
||||
@@ -105,6 +105,7 @@ class LoginWebPage extends NiceWebPage
|
||||
/**
|
||||
* @param $oUser
|
||||
* @param array $aProfiles
|
||||
* @param $sOrigin
|
||||
*
|
||||
* @return array
|
||||
* @throws \CoreException
|
||||
@@ -385,14 +386,20 @@ class LoginWebPage extends NiceWebPage
|
||||
$this->output();
|
||||
}
|
||||
|
||||
public static function ResetSession()
|
||||
public static function ResetSession($bFullCleanup = false)
|
||||
{
|
||||
// Unset all of the session variables.
|
||||
Session::Unset('auth_user');
|
||||
Session::Unset('login_state');
|
||||
Session::Unset('can_logoff');
|
||||
Session::Unset('archive_mode');
|
||||
Session::Unset('impersonate_user');
|
||||
if ($bFullCleanup) {
|
||||
// Unset all of the session variables.
|
||||
foreach (array_keys($_SESSION) as $sKey) {
|
||||
Session::Unset($sKey);
|
||||
}
|
||||
} else {
|
||||
Session::Unset('auth_user');
|
||||
Session::Unset('login_state');
|
||||
Session::Unset('can_logoff');
|
||||
Session::Unset('archive_mode');
|
||||
Session::Unset('impersonate_user');
|
||||
}
|
||||
UserRights::_ResetSessionCache();
|
||||
// If it's desired to kill the session, also delete the session cookie.
|
||||
// Note: This will destroy the session, and not just the session data!
|
||||
@@ -957,7 +964,7 @@ class LoginWebPage extends NiceWebPage
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($iOnExit == self::EXIT_RETURN)
|
||||
if ($iOnExit === self::EXIT_RETURN)
|
||||
{
|
||||
return self::EXIT_CODE_PORTALUSERNOTAUTHORIZED;
|
||||
}
|
||||
@@ -1012,7 +1019,7 @@ class LoginWebPage extends NiceWebPage
|
||||
{
|
||||
if ($bMustBeAdmin && !UserRights::IsAdministrator())
|
||||
{
|
||||
if ($iOnExit == self::EXIT_RETURN)
|
||||
if ($iOnExit === self::EXIT_RETURN)
|
||||
{
|
||||
return self::EXIT_CODE_MUSTBEADMIN;
|
||||
}
|
||||
@@ -1028,7 +1035,7 @@ class LoginWebPage extends NiceWebPage
|
||||
}
|
||||
$iRet = call_user_func(array(self::$sHandlerClass, 'ChangeLocation'), $sRequestedPortalId, $iOnExit);
|
||||
}
|
||||
if ($iOnExit == self::EXIT_RETURN)
|
||||
if ($iOnExit === self::EXIT_RETURN)
|
||||
{
|
||||
return $iRet;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/NiceWebPage.php, now loadable using autoloader
|
||||
* @deprecated 3.0.0 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
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/PDFPage.php, now loadable using autoloader
|
||||
* @deprecated 3.0.0 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
|
||||
*/
|
||||
|
||||
@@ -98,4 +98,10 @@ else
|
||||
Session::Set('itop_env', ITOP_DEFAULT_ENV);
|
||||
}
|
||||
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
|
||||
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
|
||||
try {
|
||||
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
|
||||
}
|
||||
catch (MySQLException $e) {
|
||||
IssueLog::Debug($e->getMessage());
|
||||
throw new MySQLException('Could not connect to the DB server', []);
|
||||
}
|
||||
@@ -211,7 +211,23 @@ class UILinksWidgetDirect
|
||||
$oObj = DBObject::MakeDefaultInstance($sRealClass);
|
||||
$aPrefillParam = array('source_obj' => $oSourceObj);
|
||||
$oObj->PrefillForm('creation_from_editinplace', $aPrefillParam);
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), array('formPrefix' => $this->sInputid, 'noRelations' => true, 'fieldsFlags' => $aFieldFlags));
|
||||
$aFormExtraParams = array(
|
||||
'formPrefix' => $this->sInputid,
|
||||
'noRelations' => true,
|
||||
'fieldsFlags' => $aFieldFlags,
|
||||
'js_handlers' => [
|
||||
'cancel_button_on_click' =>
|
||||
<<<JS
|
||||
function() {
|
||||
// Do nothing, already handled by linksdirectwidget.js
|
||||
};
|
||||
JS
|
||||
,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), $aFormExtraParams);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -100,15 +100,18 @@ class UILinksWidget
|
||||
* @param array $aArgs Extra context arguments
|
||||
* @param DBObject $oCurrentObj The object to which all the elements of the linked set refer to
|
||||
* @param int $iUniqueId A unique identifier of new links
|
||||
* @param boolean $bReadOnly Display link as editable or read-only. Default is false (editable)
|
||||
* @param bool $bReadOnly Display link as editable or read-only. Default is false (editable)
|
||||
* @param bool $bAllowRemoteExtKeyEdit If true, the ext. key to the remote object can be edited, otherwise it will be read-only
|
||||
*
|
||||
* @return array The HTML fragment of the one-row form
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \Exception
|
||||
*
|
||||
* @since 3.1.0 3.0.4 3.0.3-1 N°6124 - Workaround performance problem on the modification of an object with an n:n relation having a large volume
|
||||
*/
|
||||
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false)
|
||||
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false, $bAllowRemoteExtKeyEdit = true)
|
||||
{
|
||||
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
|
||||
$aRow = array();
|
||||
@@ -139,8 +142,11 @@ class UILinksWidget
|
||||
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"$iKey\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$iKey\">";
|
||||
foreach ($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
// N°6124 - Force remote ext. key as read-only if too many items in the linkset
|
||||
$bReadOnlyField = ($sFieldCode === $this->m_sExtKeyToRemote) && (false === $bAllowRemoteExtKeyEdit);
|
||||
|
||||
$sSafeFieldId = $this->GetFieldId($linkObjOrId->GetKey(), $sFieldCode);
|
||||
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $linkObjOrId, $oP, $sNameSuffix, $sSafeFieldId);
|
||||
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $linkObjOrId, $oP, $sNameSuffix, $sSafeFieldId, $bReadOnlyField);
|
||||
$aFieldsMap[$sFieldCode] = $sSafeFieldId;
|
||||
}
|
||||
}
|
||||
@@ -201,8 +207,11 @@ EOF
|
||||
|
||||
foreach($this->m_aEditableFields as $sFieldCode)
|
||||
{
|
||||
// N°6124 - Force remote ext. key as read-only if too many items in the linkset
|
||||
$bReadOnlyField = ($sFieldCode === $this->m_sExtKeyToRemote) && (false === $bAllowRemoteExtKeyEdit);
|
||||
|
||||
$sSafeFieldId = $this->GetFieldId($iUniqueId, $sFieldCode);
|
||||
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $oNewLinkObj, $oP, $sNameSuffix, $sSafeFieldId);
|
||||
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $oNewLinkObj, $oP, $sNameSuffix, $sSafeFieldId, $bReadOnlyField);
|
||||
$aFieldsMap[$sFieldCode] = $sSafeFieldId;
|
||||
|
||||
$sValue = $oNewLinkObj->Get($sFieldCode);
|
||||
@@ -255,11 +264,24 @@ JS
|
||||
return $aRow;
|
||||
}
|
||||
|
||||
private function AddRowForFieldCode(&$aRow, $sFieldCode, &$aArgs, $oLnk, $oP, $sNameSuffix, $sSafeFieldId): void
|
||||
/**
|
||||
* @param $aRow
|
||||
* @param $sFieldCode
|
||||
* @param $aArgs
|
||||
* @param $oLnk
|
||||
* @param $oP
|
||||
* @param $sNameSuffix
|
||||
* @param $sSafeFieldId
|
||||
* @param bool $bReadOnlyField If true, the field will be read-only, otherwise it can be edited
|
||||
*
|
||||
* @return void
|
||||
* @since 3.1.0 3.0.4 3.0.3-1 N°6124 - Workaround performance problem on the modification of an object with an n:n relation having a large volume
|
||||
*/
|
||||
private function AddRowForFieldCode(&$aRow, $sFieldCode, &$aArgs, $oLnk, $oP, $sNameSuffix, $sSafeFieldId, $bReadOnlyField = false): void
|
||||
{
|
||||
if (($sFieldCode === $this->m_sExtKeyToRemote))
|
||||
{
|
||||
// current field is the lnk extkey to the remote class
|
||||
// Current field is the lnk extkey to the remote class
|
||||
$aArgs['replaceDependenciesByRemoteClassFields'] = true;
|
||||
$sRowFieldCode = 'static::key';
|
||||
$aArgs['wizHelperRemote'] = $aArgs['wizHelper'].'_remote';
|
||||
@@ -281,20 +303,31 @@ JS
|
||||
$sDisplayValue = $oLnk->GetEditValue($sFieldCode);
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
|
||||
|
||||
$aRow[$sRowFieldCode] = '<div class="field_container" style="border:none;"><div class="field_data"><div class="field_value">'
|
||||
.cmdbAbstractObject::GetFormElementForField(
|
||||
$oP,
|
||||
$this->m_sLinkedClass,
|
||||
$sFieldCode,
|
||||
$oAttDef,
|
||||
$sValue,
|
||||
$sDisplayValue,
|
||||
$sSafeFieldId,
|
||||
$sNameSuffix,
|
||||
0,
|
||||
$aArgs
|
||||
)
|
||||
.'</div></div></div>';
|
||||
if ($bReadOnlyField) {
|
||||
$sFieldForHtml = $sDisplayValue;
|
||||
} else {
|
||||
$sFieldForHtml = cmdbAbstractObject::GetFormElementForField(
|
||||
$oP,
|
||||
$this->m_sLinkedClass,
|
||||
$sFieldCode,
|
||||
$oAttDef,
|
||||
$sValue,
|
||||
$sDisplayValue,
|
||||
$sSafeFieldId,
|
||||
$sNameSuffix,
|
||||
0,
|
||||
$aArgs
|
||||
);
|
||||
}
|
||||
|
||||
$aRow[$sRowFieldCode] = <<<HTML
|
||||
<div class="field_container" style="border:none;">
|
||||
<div class="field_data">
|
||||
<div class="field_value">$sFieldForHtml</div>
|
||||
</div>
|
||||
</div>
|
||||
HTML
|
||||
;
|
||||
}
|
||||
|
||||
private function GetFieldId($iLnkId, $sFieldCode, $bSafe = true)
|
||||
@@ -374,6 +407,7 @@ JS
|
||||
$oBlock->sRemoteClass = $this->m_sRemoteClass;
|
||||
|
||||
$oValue->Rewind();
|
||||
$bAllowRemoteExtKeyEdit = $oValue->Count() <= utils::GetConfig()->Get('link_set_max_edit_ext_key');
|
||||
$aForm = array();
|
||||
$iMaxAddedId = 0;
|
||||
$iAddedId = -1; // Unique id for new links
|
||||
@@ -398,7 +432,7 @@ JS
|
||||
}
|
||||
|
||||
$iMaxAddedId = max($iMaxAddedId, $key);
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly);
|
||||
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly, $bAllowRemoteExtKeyEdit);
|
||||
}
|
||||
$oBlock->iMaxAddedId = (int) $iMaxAddedId;
|
||||
|
||||
@@ -571,10 +605,11 @@ JS
|
||||
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
|
||||
|
||||
$iAdditionId = $iMaxAddedId + 1;
|
||||
$bAllowRemoteExtKeyEdit = count($aLinkedObjectIds) <= utils::GetConfig()->Get('link_set_max_edit_ext_key');
|
||||
foreach ($aLinkedObjectIds as $iObjectId) {
|
||||
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId, false);
|
||||
if (is_object($oLinkedObj)) {
|
||||
$aRow = $this->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
|
||||
$aRow = $this->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId, false /* Default value */, $bAllowRemoteExtKeyEdit); // Not yet created link get negative Ids
|
||||
$aData = [];
|
||||
foreach ($aRow as $item) {
|
||||
$aData[] = $item;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
use Combodo\iTop\Application\Helper\Session;
|
||||
use Combodo\iTop\Application\UI\Base\iUIBlock;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
|
||||
use Combodo\iTop\Service\Module\ModuleService;
|
||||
use ScssPhp\ScssPhp\Compiler;
|
||||
use ScssPhp\ScssPhp\OutputStyle;
|
||||
use ScssPhp\ScssPhp\ValueConverter;
|
||||
@@ -51,42 +52,51 @@ class utils
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
* @since 2.7.10 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_INTEGER = 'integer';
|
||||
/**
|
||||
* Datamodel class
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
* @since 2.7.10 3.0.0
|
||||
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6606 update PHPDoc
|
||||
* @uses MetaModel::IsValidClass()
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_CLASS = 'class';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6606
|
||||
* @uses class_exists()
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_PHP_CLASS = 'php_class';
|
||||
/**
|
||||
* @var string
|
||||
* @since 2.7.10 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_STRING = 'string';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
* @since 2.7.10 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_CONTEXT_PARAM = 'context_param';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
* @since 2.7.10 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_PARAMETER = 'parameter';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
* @since 2.7.10 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_FIELD_NAME = 'field_name';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
* @since 2.7.10 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_TRANSACTION_ID = 'transaction_id';
|
||||
/**
|
||||
* @var string For XML / HTML node identifiers
|
||||
* @since 3.0.0
|
||||
* @since 2.7.10 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER = 'element_identifier';
|
||||
/**
|
||||
@@ -96,12 +106,13 @@ class utils
|
||||
public const ENUM_SANITIZATION_FILTER_VARIABLE_NAME = 'variable_name';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.0
|
||||
* @since 2.7.10 3.0.0
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_RAW_DATA = 'raw_data';
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.2, 3.1.0 N°4899
|
||||
* @since 3.0.2 3.1.0 N°4899
|
||||
* @since 2.7.10 N°6606
|
||||
*/
|
||||
public const ENUM_SANITIZATION_FILTER_URL = 'url';
|
||||
|
||||
@@ -144,6 +155,8 @@ class utils
|
||||
|
||||
private static $iNextId = 0;
|
||||
|
||||
private static $m_sAppRootUrl = null;
|
||||
|
||||
protected static function LoadParamFile($sParamFile)
|
||||
{
|
||||
if (!file_exists($sParamFile)) {
|
||||
@@ -215,13 +228,8 @@ class utils
|
||||
|
||||
public static function IsModeCLI()
|
||||
{
|
||||
$sSAPIName = php_sapi_name();
|
||||
$sCleanName = strtolower(trim($sSAPIName));
|
||||
if ($sCleanName == 'cli') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
$sCleanName = strtolower(trim(PHP_SAPI));
|
||||
return ($sCleanName === 'cli');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -344,13 +352,13 @@ class utils
|
||||
}
|
||||
return self::Sanitize($retValue, $defaultValue, $sSanitizationFilter);
|
||||
}
|
||||
|
||||
|
||||
public static function ReadPostedParam($sName, $defaultValue = '', $sSanitizationFilter = 'parameter')
|
||||
{
|
||||
$retValue = isset($_POST[$sName]) ? $_POST[$sName] : $defaultValue;
|
||||
return self::Sanitize($retValue, $defaultValue, $sSanitizationFilter);
|
||||
}
|
||||
|
||||
|
||||
public static function Sanitize($value, $defaultValue, $sSanitizationFilter)
|
||||
{
|
||||
if ($value === $defaultValue)
|
||||
@@ -366,7 +374,7 @@ class utils
|
||||
$retValue = $defaultValue;
|
||||
}
|
||||
}
|
||||
return $retValue;
|
||||
return $retValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,6 +393,10 @@ class utils
|
||||
* @since 2.7.0 new 'element_identifier' filter
|
||||
* @since 3.0.0 new utils::ENUM_SANITIZATION_* const
|
||||
* @since 2.7.7, 3.0.2, 3.1.0 N°4899 - new 'url' filter
|
||||
* @since 2.7.10 N°6606 use the utils::ENUM_SANITIZATION_* const
|
||||
* @since 2.7.10 N°6606 new case for ENUM_SANITIZATION_FILTER_PHP_CLASS
|
||||
*
|
||||
* @link https://www.php.net/manual/en/filter.filters.sanitize.php PHP sanitization filters
|
||||
*/
|
||||
protected static function Sanitize_Internal($value, $sSanitizationFilter)
|
||||
{
|
||||
@@ -405,6 +417,13 @@ class utils
|
||||
$retValue = filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
|
||||
break;
|
||||
|
||||
case static::ENUM_SANITIZATION_FILTER_PHP_CLASS:
|
||||
$retValue = $value;
|
||||
if (!class_exists($value)) {
|
||||
$retValue = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case static::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM:
|
||||
case static::ENUM_SANITIZATION_FILTER_PARAMETER:
|
||||
case static::ENUM_SANITIZATION_FILTER_FIELD_NAME:
|
||||
@@ -464,7 +483,8 @@ class utils
|
||||
|
||||
// For URL
|
||||
case static::ENUM_SANITIZATION_FILTER_URL:
|
||||
$retValue = filter_var($value, FILTER_SANITIZE_URL);
|
||||
// N°6350 - returns only valid URLs
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_URL);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -504,11 +524,11 @@ class utils
|
||||
$sMimeType = self::GetFileMimeType($sTmpName);
|
||||
$oDocument = new ormDocument($doc_content, $sMimeType, $sName);
|
||||
break;
|
||||
|
||||
|
||||
case UPLOAD_ERR_NO_FILE:
|
||||
// no file to load, it's a normal case, just return an empty document
|
||||
break;
|
||||
|
||||
|
||||
case UPLOAD_ERR_FORM_SIZE:
|
||||
case UPLOAD_ERR_INI_SIZE:
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadedFileTooBig', ini_get('upload_max_filesize')));
|
||||
@@ -517,7 +537,7 @@ class utils
|
||||
case UPLOAD_ERR_PARTIAL:
|
||||
throw new FileUploadException(Dict::S('UI:Error:UploadedFileTruncated.'));
|
||||
break;
|
||||
|
||||
|
||||
case UPLOAD_ERR_NO_TMP_DIR:
|
||||
throw new FileUploadException(Dict::S('UI:Error:NoTmpDir'));
|
||||
break;
|
||||
@@ -530,7 +550,7 @@ class utils
|
||||
$sName = is_null($sIndex) ? $aFileInfo['name'] : $aFileInfo['name'][$sIndex];
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadStoppedByExtension_FileName', $sName));
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
throw new FileUploadException(Dict::Format('UI:Error:UploadFailedUnknownCause_Code', $sError));
|
||||
break;
|
||||
@@ -636,17 +656,17 @@ class utils
|
||||
|
||||
return $aSelectedObj;
|
||||
}
|
||||
|
||||
|
||||
public static function GetNewTransactionId()
|
||||
{
|
||||
return privUITransaction::GetNewTransactionId();
|
||||
}
|
||||
|
||||
|
||||
public static function IsTransactionValid($sId, $bRemoveTransaction = true)
|
||||
{
|
||||
return privUITransaction::IsTransactionValid($sId, $bRemoveTransaction);
|
||||
}
|
||||
|
||||
|
||||
public static function RemoveTransaction($sId)
|
||||
{
|
||||
return privUITransaction::RemoveTransaction($sId);
|
||||
@@ -831,9 +851,9 @@ class utils
|
||||
$aDateTokens = array_keys($aSpec);
|
||||
$aDateRegexps = array_values($aSpec);
|
||||
}
|
||||
|
||||
|
||||
$sDateRegexp = str_replace($aDateTokens, $aDateRegexps, $sFormat);
|
||||
|
||||
|
||||
if (preg_match('!^(?<head>)'.$sDateRegexp.'(?<tail>)$!', $sDate, $aMatches))
|
||||
{
|
||||
$sYear = isset($aMatches['year']) ? $aMatches['year'] : 0;
|
||||
@@ -850,7 +870,7 @@ class utils
|
||||
}
|
||||
// http://www.spaweditor.com/scripts/regex/index.php
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert an old date/time format specification (using % placeholders)
|
||||
* to a format compatible with DateTime::createFromFormat
|
||||
@@ -969,7 +989,7 @@ class utils
|
||||
*/
|
||||
public static function GetAbsoluteUrlAppRoot($bForceTrustProxy = false)
|
||||
{
|
||||
static $sUrl = null;
|
||||
$sUrl = static::$m_sAppRootUrl;
|
||||
if ($sUrl === null || $bForceTrustProxy)
|
||||
{
|
||||
$sUrl = self::GetConfig()->Get('app_root_url');
|
||||
@@ -990,8 +1010,9 @@ class utils
|
||||
}
|
||||
$sUrl = str_replace(SERVER_NAME_PLACEHOLDER, $sServerName, $sUrl);
|
||||
}
|
||||
static::$m_sAppRootUrl = $sUrl;
|
||||
}
|
||||
return $sUrl;
|
||||
return static::$m_sAppRootUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1342,13 +1363,23 @@ class utils
|
||||
return APPROOT . 'env-' . MetaModel::GetEnvironment() . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string A path to the folder into which data can be written
|
||||
* @internal
|
||||
* @since N°6097 2.7.10 3.0.4 3.1.1
|
||||
*/
|
||||
public static function GetDataPath(): string
|
||||
{
|
||||
return APPROOT.'data/';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string A path to a folder into which any module can store cache data
|
||||
* The corresponding folder is created or cleaned upon code compilation
|
||||
*/
|
||||
public static function GetCachePath()
|
||||
{
|
||||
return APPROOT.'data/cache-'.MetaModel::GetEnvironment().'/';
|
||||
return static::GetDataPath().'cache-'.MetaModel::GetEnvironment().'/';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1396,7 +1427,7 @@ class utils
|
||||
public static function GetPopupMenuItemsBlock(iUIBlock &$oContainerBlock, $iMenuId, $param, &$aActions, $sDataTableId = null)
|
||||
{
|
||||
// 1st - add standard built-in menu items
|
||||
//
|
||||
//
|
||||
switch($iMenuId)
|
||||
{
|
||||
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
|
||||
@@ -1421,7 +1452,7 @@ class utils
|
||||
"mailto:?body=".urlencode($sUrl).' ' // Add an extra space to make it work in Outlook
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_BULK_READ, $param) != UR_ALLOWED_NO)
|
||||
{
|
||||
// Bulk export actions
|
||||
@@ -1435,7 +1466,7 @@ class utils
|
||||
}
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL', '$sContext')");
|
||||
$aResult[] = new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')");
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS:
|
||||
@@ -1449,7 +1480,7 @@ class utils
|
||||
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
|
||||
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
|
||||
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
|
||||
|
||||
|
||||
$aResult = array(
|
||||
new SeparatorPopupMenuItem(),
|
||||
// Static menus: Email this page & CSV Export
|
||||
@@ -1513,7 +1544,7 @@ class utils
|
||||
if (is_object($oMenuItem))
|
||||
{
|
||||
$aActions[$oMenuItem->GetUID()] = $oMenuItem->GetMenuItem();
|
||||
|
||||
|
||||
foreach($oMenuItem->GetLinkedScripts() as $sLinkedScript)
|
||||
{
|
||||
$oContainerBlock->AddJsFileRelPath($sLinkedScript);
|
||||
@@ -1650,7 +1681,7 @@ class utils
|
||||
return $sProposed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Some characters cause troubles with jQuery when used inside DOM IDs, so let's replace them by the safe _ (underscore)
|
||||
* @param string $sId The ID to sanitize
|
||||
@@ -1660,13 +1691,13 @@ class utils
|
||||
{
|
||||
return str_replace(array(':', '[', ']', '+', '-', ' '), '_', $sId);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to execute an HTTP POST request
|
||||
* Source: http://netevil.org/blog/2006/nov/http-post-from-php-without-curl
|
||||
* originaly named after do_post_request
|
||||
* Does not require cUrl but requires openssl for performing https POSTs.
|
||||
*
|
||||
*
|
||||
* @param string $sUrl The URL to POST the data to
|
||||
* @param array $aData The data to POST as an array('param_name' => value)
|
||||
* @param string $sOptionnalHeaders Additional HTTP headers as a string with newlines between headers
|
||||
@@ -1677,11 +1708,11 @@ class utils
|
||||
*
|
||||
* @return string The result of the POST request
|
||||
* @throws Exception with a specific error message depending on the cause
|
||||
*/
|
||||
*/
|
||||
public static function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = array())
|
||||
{
|
||||
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
|
||||
|
||||
|
||||
if (function_exists('curl_init'))
|
||||
{
|
||||
// If cURL is available, let's use it, since it provides a greater control over the various HTTP/SSL options
|
||||
@@ -1714,7 +1745,7 @@ class utils
|
||||
CURLOPT_POSTFIELDS => http_build_query($aData),
|
||||
CURLOPT_HTTPHEADER => $aHTTPHeaders,
|
||||
);
|
||||
|
||||
|
||||
$aAllOptions = $aCurlOptions + $aOptions;
|
||||
$ch = curl_init($sUrl);
|
||||
curl_setopt_array($ch, $aAllOptions);
|
||||
@@ -1740,7 +1771,7 @@ class utils
|
||||
else
|
||||
{
|
||||
// cURL is not available let's try with streams and fopen...
|
||||
|
||||
|
||||
$sData = http_build_query($aData);
|
||||
$aParams = array('http' => array(
|
||||
'method' => 'POST',
|
||||
@@ -1752,7 +1783,7 @@ class utils
|
||||
$aParams['http']['header'] .= $sOptionnalHeaders;
|
||||
}
|
||||
$ctx = stream_context_create($aParams);
|
||||
|
||||
|
||||
$fp = @fopen($sUrl, 'rb', false, $ctx);
|
||||
if (!$fp)
|
||||
{
|
||||
@@ -1793,7 +1824,7 @@ class utils
|
||||
|
||||
/**
|
||||
* Get a standard list of character sets
|
||||
*
|
||||
*
|
||||
* @param array $aAdditionalEncodings Additional values
|
||||
* @return array of iconv code => english label, sorted by label
|
||||
*/
|
||||
@@ -1898,7 +1929,7 @@ class utils
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert (?) plain text to some HTML markup by replacing newlines by <br/> tags
|
||||
* and escaping HTML entities
|
||||
@@ -1911,7 +1942,7 @@ class utils
|
||||
$sText = str_replace("\r", "\n", $sText);
|
||||
return str_replace("\n", '<br/>', htmlentities($sText, ENT_QUOTES, 'UTF-8'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Eventually compiles the SASS (.scss) file into the CSS (.css) file
|
||||
*
|
||||
@@ -1978,7 +2009,7 @@ class utils
|
||||
|
||||
return $sCss->getCss();
|
||||
}
|
||||
|
||||
|
||||
public static function GetImageSize($sImageData)
|
||||
{
|
||||
if (function_exists('getimagesizefromstring')) // PHP 5.4.0 or higher
|
||||
@@ -2035,7 +2066,7 @@ class utils
|
||||
case 'image/png':
|
||||
$img = @imagecreatefromstring($oImage->GetData());
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
// Unsupported image type, return the image as-is
|
||||
//throw new Exception("Unsupported image type: '".$oImage->GetMimeType()."'. Cannot resize the image, original image will be used.");
|
||||
@@ -2049,14 +2080,14 @@ class utils
|
||||
else
|
||||
{
|
||||
// Let's scale the image, preserving the transparency for GIFs and PNGs
|
||||
|
||||
|
||||
$fScale = min($iMaxImageWidth / $iWidth, $iMaxImageHeight / $iHeight);
|
||||
|
||||
$iNewWidth = $iWidth * $fScale;
|
||||
$iNewHeight = $iHeight * $fScale;
|
||||
|
||||
|
||||
$new = imagecreatetruecolor($iNewWidth, $iNewHeight);
|
||||
|
||||
|
||||
// Preserve transparency
|
||||
if(($oImage->GetMimeType() == "image/gif") || ($oImage->GetMimeType() == "image/png"))
|
||||
{
|
||||
@@ -2064,38 +2095,38 @@ class utils
|
||||
imagealphablending($new, false);
|
||||
imagesavealpha($new, true);
|
||||
}
|
||||
|
||||
|
||||
imagecopyresampled($new, $img, 0, 0, 0, 0, $iNewWidth, $iNewHeight, $iWidth, $iHeight);
|
||||
|
||||
|
||||
ob_start();
|
||||
switch ($oImage->GetMimeType())
|
||||
{
|
||||
case 'image/gif':
|
||||
imagegif($new); // send image to output buffer
|
||||
break;
|
||||
|
||||
|
||||
case 'image/jpeg':
|
||||
imagejpeg($new, null, 80); // null = send image to output buffer, 80 = good quality
|
||||
break;
|
||||
|
||||
|
||||
case 'image/png':
|
||||
imagepng($new, null, 5); // null = send image to output buffer, 5 = medium compression
|
||||
break;
|
||||
}
|
||||
$oResampledImage = new ormDocument(ob_get_contents(), $oImage->GetMimeType(), $oImage->GetFileName());
|
||||
@ob_end_clean();
|
||||
|
||||
|
||||
imagedestroy($img);
|
||||
imagedestroy($new);
|
||||
|
||||
|
||||
return $oResampledImage;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a 128 bit UUID in the format: {########-####-####-####-############}
|
||||
*
|
||||
*
|
||||
* Note: this method can be run from the command line as well as from the web server.
|
||||
* Note2: this method is not cryptographically secure! If you need a cryptographically secure value
|
||||
* consider using open_ssl or PHP 7 methods.
|
||||
@@ -2131,26 +2162,9 @@ class utils
|
||||
*/
|
||||
public static function GetCurrentModuleName($iCallDepth = 0)
|
||||
{
|
||||
$sCurrentModuleName = '';
|
||||
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||
$sCallerFile = realpath($aCallStack[$iCallDepth]['file']);
|
||||
|
||||
foreach(GetModulesInfo() as $sModuleName => $aInfo)
|
||||
{
|
||||
if ($aInfo['root_dir'] !== '')
|
||||
{
|
||||
$sRootDir = realpath(APPROOT.$aInfo['root_dir']);
|
||||
|
||||
if(substr($sCallerFile, 0, strlen($sRootDir)) === $sRootDir)
|
||||
{
|
||||
$sCurrentModuleName = $sModuleName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sCurrentModuleName;
|
||||
return ModuleService::GetInstance()->GetCurrentModuleName($iCallDepth + 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* **Warning** : returned result can be invalid as we're using backtrace to find the module dir name
|
||||
*
|
||||
@@ -2170,24 +2184,7 @@ class utils
|
||||
*/
|
||||
public static function GetCurrentModuleDir($iCallDepth)
|
||||
{
|
||||
$sCurrentModuleDir = '';
|
||||
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||
$sCallerFile = realpath($aCallStack[$iCallDepth]['file']);
|
||||
|
||||
foreach(GetModulesInfo() as $sModuleName => $aInfo)
|
||||
{
|
||||
if ($aInfo['root_dir'] !== '')
|
||||
{
|
||||
$sRootDir = realpath(APPROOT.$aInfo['root_dir']);
|
||||
|
||||
if(substr($sCallerFile, 0, strlen($sRootDir)) === $sRootDir)
|
||||
{
|
||||
$sCurrentModuleDir = basename($sRootDir);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sCurrentModuleDir;
|
||||
return ModuleService::GetInstance()->GetCurrentModuleDir($iCallDepth);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2202,14 +2199,9 @@ class utils
|
||||
*/
|
||||
public static function GetCurrentModuleUrl()
|
||||
{
|
||||
$sDir = static::GetCurrentModuleDir(1);
|
||||
if ( $sDir !== '')
|
||||
{
|
||||
return static::GetAbsoluteUrlModulesRoot().'/'.$sDir;
|
||||
}
|
||||
return '';
|
||||
return ModuleService::GetInstance()->GetCurrentModuleUrl(1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $sProperty The name of the property to retrieve
|
||||
* @param mixed $defaultvalue
|
||||
@@ -2217,24 +2209,18 @@ class utils
|
||||
*/
|
||||
public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
|
||||
{
|
||||
$sModuleName = static::GetCurrentModuleName(1);
|
||||
return MetaModel::GetModuleSetting($sModuleName, $sProperty, $defaultvalue);
|
||||
return ModuleService::GetInstance()->GetCurrentModuleSetting($sProperty, $defaultvalue);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $sModuleName
|
||||
* @return string|NULL compiled version of a given module, as it was seen by the compiler
|
||||
*/
|
||||
public static function GetCompiledModuleVersion($sModuleName)
|
||||
{
|
||||
$aModulesInfo = GetModulesInfo();
|
||||
if (array_key_exists($sModuleName, $aModulesInfo))
|
||||
{
|
||||
return $aModulesInfo[$sModuleName]['version'];
|
||||
}
|
||||
return null;
|
||||
return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the given path/url is an http(s) URL
|
||||
* @param string $sPath
|
||||
@@ -2249,7 +2235,7 @@ class utils
|
||||
}
|
||||
return $bRet;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the given URL is a link to download a document/image on the CURRENT iTop
|
||||
* In such a case we can read the content of the file directly in the database (if the users rights allow) and return the ormDocument
|
||||
@@ -2298,7 +2284,7 @@ class utils
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read the content of a file (and retrieve its MIME type) from either:
|
||||
* - an URL pointing to a blob (image/document) on the current iTop server
|
||||
@@ -2342,7 +2328,7 @@ class utils
|
||||
'html' => 'text/html',
|
||||
'exe' => 'application/octet-stream',
|
||||
);
|
||||
|
||||
|
||||
$sData = null;
|
||||
$sMimeType = 'text/plain'; // Default MIME Type: treat the file as a bunch a characters...
|
||||
$sFileName = 'uploaded-file'; // Default name for downloaded-files
|
||||
@@ -2398,7 +2384,7 @@ class utils
|
||||
}
|
||||
$sExtension = strtolower(pathinfo($sPath, PATHINFO_EXTENSION));
|
||||
$sFileName = basename($sPath);
|
||||
|
||||
|
||||
if (array_key_exists($sExtension, $aKnownExtensions))
|
||||
{
|
||||
$sMimeType = $aKnownExtensions[$sExtension];
|
||||
@@ -2412,7 +2398,7 @@ class utils
|
||||
}
|
||||
return $oUploadedDoc;
|
||||
}
|
||||
|
||||
|
||||
protected static function ParseHeaders($aHeaders)
|
||||
{
|
||||
$aCleanHeaders = array();
|
||||
@@ -2437,7 +2423,7 @@ class utils
|
||||
}
|
||||
return $aCleanHeaders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return string a string based on compilation time or (if not available because the datamodel has not been loaded)
|
||||
* the version of iTop. This string is useful to prevent browser side caching of content that may vary at each
|
||||
@@ -2756,7 +2742,7 @@ HTML;
|
||||
|
||||
foreach ($aClassMap as $sPHPClass => $sPHPFile) {
|
||||
$bSkipped = false;
|
||||
|
||||
|
||||
// Check if our class matches name filter, or is in an excluded path
|
||||
if ($sClassNameFilter !== '' && strpos($sPHPClass, $sClassNameFilter) === false) {
|
||||
$bSkipped = true;
|
||||
@@ -2776,7 +2762,7 @@ HTML;
|
||||
$bSkipped = true; // file not found
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(!$bSkipped){
|
||||
try {
|
||||
$oRefClass = new ReflectionClass($sPHPClass);
|
||||
@@ -2886,6 +2872,7 @@ HTML;
|
||||
*
|
||||
* @return bool if string null or empty
|
||||
* @since 3.0.2 N°5302
|
||||
* @since 2.7.10 N°6458 add method in the 2.7 branch
|
||||
*/
|
||||
public static function IsNullOrEmptyString(?string $sString): bool
|
||||
{
|
||||
@@ -2901,6 +2888,7 @@ HTML;
|
||||
*
|
||||
* @return bool if string is not null and not empty
|
||||
* @since 3.0.2 N°5302
|
||||
* @since 2.7.10 N°6458 add method in the 2.7 branch
|
||||
*/
|
||||
public static function IsNotNullOrEmptyString(?string $sString): bool
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/WebPage.php, now loadable using autoloader
|
||||
* @deprecated 3.0.0 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
|
||||
*/
|
||||
|
||||
@@ -347,6 +347,7 @@ class WizardHelper
|
||||
/**
|
||||
* @return string JS code to be executed for fields update
|
||||
* @since 3.0.0 N°3198
|
||||
* @deprecated 3.0.3-2 3.0.4 3.1.1 3.2.0 Use {@see \WizardHelper::AddJsForUpdateFields()} instead
|
||||
*/
|
||||
public function GetJsForUpdateFields()
|
||||
{
|
||||
@@ -359,15 +360,39 @@ class WizardHelper
|
||||
JS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add necessary JS snippets (to the page) to be executed for fields update
|
||||
*
|
||||
* @param \WebPage $oPage
|
||||
* @return void
|
||||
* @since 3.0.3-2 3.0.4 3.1.1 3.2.0 N°6766
|
||||
*/
|
||||
public function AddJsForUpdateFields(WebPage $oPage)
|
||||
{
|
||||
$sWizardHelperJsVar = (!is_null($this->m_aData['m_sWizHelperJsVarName'])) ? utils::Sanitize($this->m_aData['m_sWizHelperJsVarName'], '', utils::ENUM_SANITIZATION_FILTER_PARAMETER) : 'oWizardHelper'.$this->GetFormPrefix();
|
||||
$sWizardHelperJson = $this->ToJSON();
|
||||
|
||||
$oPage->add_script(<<<JS
|
||||
{$sWizardHelperJsVar}.m_oData = {$sWizardHelperJson};
|
||||
{$sWizardHelperJsVar}.UpdateFields();
|
||||
JS
|
||||
);
|
||||
$oPage->add_ready_script(<<<JS
|
||||
if ({$sWizardHelperJsVar}.m_oDependenciesUpdatedPromiseResolve !== null){
|
||||
{$sWizardHelperJsVar}.m_oDependenciesUpdatedPromiseResolve();
|
||||
}
|
||||
JS
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
static function ParseJsonSet($oMe, $sLinkClass, $sExtKeyToMe, $sJsonSet)
|
||||
{
|
||||
$aSet = json_decode($sJsonSet, true); // true means hash array instead of object
|
||||
$oSet = CMDBObjectSet::FromScratch($sLinkClass);
|
||||
foreach ($aSet as $aLinkObj)
|
||||
{
|
||||
foreach ($aSet as $aLinkObj) {
|
||||
$oLink = MetaModel::NewObject($sLinkClass);
|
||||
foreach ($aLinkObj as $sAttCode => $value)
|
||||
{
|
||||
foreach ($aLinkObj as $sAttCode => $value) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($sLinkClass, $sAttCode);
|
||||
if (($oAttDef->IsExternalKey()) && ($value != '') && ($value > 0))
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* @deprecated will be removed in 3.1.0 - moved to sources/application/WebPage/XMLPage.php, now loadable using autoloader
|
||||
* @deprecated 3.0.0 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
|
||||
*/
|
||||
|
||||
@@ -23,6 +23,13 @@ define('ITOP_DESIGN_LATEST_VERSION', '3.0');
|
||||
* @used-by utils::GetItopVersionWikiSyntax()
|
||||
* @used-by iTopModulesPhpVersionIntegrationTest
|
||||
*/
|
||||
define('ITOP_CORE_VERSION', '3.0.3');
|
||||
define('ITOP_CORE_VERSION', '3.0.4');
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.0.4 3.1.0 3.2.0 N°6274 Allow to test if PHPUnit is currently running. Starting with PHPUnit 9.5 we'll be able to replace it with $GLOBALS['phpunit_version']
|
||||
* @since 3.0.4 3.1.1 3.2.0 N°6976 Fix constant name (DeprecatedCallsLog error handler was never set)
|
||||
*/
|
||||
const ITOP_PHPUNIT_RUNNING_CONSTANT_NAME = 'ITOP_PHPUNIT_RUNNING';
|
||||
|
||||
require_once APPROOT.'bootstrap.inc.php';
|
||||
|
||||
@@ -45,6 +45,7 @@ define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance');
|
||||
define('READONLY_MODE_FILE', APPROOT.'data/.readonly');
|
||||
|
||||
$fItopStarted = microtime(true);
|
||||
$iItopInitialMemory = memory_get_usage(true);
|
||||
|
||||
if (!isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false) {
|
||||
require_once APPROOT.'/lib/autoload.php';
|
||||
@@ -67,7 +68,7 @@ if (file_exists(MAINTENANCE_MODE_FILE) && !$bBypassMaintenance)
|
||||
http_response_code(503);
|
||||
// Display message depending on the request
|
||||
include(APPROOT.'application/maintenancemsg.php');
|
||||
$sSAPIName = strtoupper(trim(php_sapi_name()));
|
||||
$sSAPIName = strtoupper(trim(PHP_SAPI));
|
||||
|
||||
switch (true)
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"combodo/tcpdf": "~6.4.4",
|
||||
"firebase/php-jwt": "~6.4.0",
|
||||
"guzzlehttp/guzzle": "^6.5.8",
|
||||
"guzzlehttp/psr7": "~1.9.1",
|
||||
"laminas/laminas-mail": "^2.11",
|
||||
"laminas/laminas-servicemanager": "^3.5",
|
||||
"league/oauth2-google": "^3.0",
|
||||
|
||||
@@ -59,9 +59,16 @@ class DbConnectionWrapper
|
||||
* Use this to register a mock that will handle {@see mysqli::query()}
|
||||
*
|
||||
* @param \mysqli|null $oMysqli
|
||||
* @since 3.0.4 3.1.1 3.2.0 Param $oMysqli becomes nullable
|
||||
*/
|
||||
public static function SetDbConnectionMockForQuery(?mysqli $oMysqli): void
|
||||
public static function SetDbConnectionMockForQuery(?mysqli $oMysqli = null): void
|
||||
{
|
||||
static::$oDbCnxMockableForQuery = $oMysqli;
|
||||
if (is_null($oMysqli)) {
|
||||
// Reset to standard connection
|
||||
static::$oDbCnxMockableForQuery = static::$oDbCnxStandard;
|
||||
}
|
||||
else {
|
||||
static::$oDbCnxMockableForQuery = $oMysqli;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -419,6 +419,7 @@ class MyHelpers
|
||||
//}
|
||||
return $sOutput;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -523,5 +524,3 @@ class Str
|
||||
return (strtolower($sString) == $sString);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -2766,6 +2766,11 @@ class AttributeObjectKey extends AttributeDBFieldVoid
|
||||
return ((int) $proposedValue) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param int|DBObject $proposedValue Object key or valid ({@see MetaModel::IsValidObject()}) datamodel object
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue))
|
||||
@@ -2778,7 +2783,6 @@ class AttributeObjectKey extends AttributeDBFieldVoid
|
||||
}
|
||||
if (MetaModel::IsValidObject($proposedValue))
|
||||
{
|
||||
/** @var \DBObject $proposedValue */
|
||||
return $proposedValue->GetKey();
|
||||
}
|
||||
|
||||
@@ -6167,6 +6171,11 @@ class AttributeDateTime extends AttributeDBField
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*
|
||||
* @param int|string $proposedValue timestamp ({@see DateTime::getTimestamp()) or date as string, following the {@see GetInternalFormat} format.
|
||||
*/
|
||||
public function MakeRealValue($proposedValue, $oHostObj)
|
||||
{
|
||||
if (is_null($proposedValue))
|
||||
@@ -7934,9 +7943,9 @@ class AttributeBlob extends AttributeDefinition
|
||||
}
|
||||
|
||||
/**
|
||||
* Users can provide the document from an URL (including an URL on iTop itself)
|
||||
* for CSV import. Administrators can even provide the path to a local file
|
||||
* {@inheritDoc}
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @param string $proposedValue Can be an URL (including an URL to iTop itself), or a local path (CSV import)
|
||||
*
|
||||
* @see AttributeDefinition::MakeRealValue()
|
||||
*/
|
||||
|
||||
@@ -149,7 +149,9 @@ abstract class BulkExport
|
||||
$this->oSearch = null;
|
||||
$this->iChunkSize = 0;
|
||||
$this->sFormatCode = null;
|
||||
$this->aStatusInfo = array();
|
||||
$this->aStatusInfo = [
|
||||
'show_obsolete_data' => utils::ShowObsoleteData(),
|
||||
];
|
||||
$this->oBulkExportResult = null;
|
||||
$this->sTmpFile = '';
|
||||
$this->bLocalizeOutput = false;
|
||||
@@ -203,15 +205,17 @@ abstract class BulkExport
|
||||
if ($oInfo && ($oInfo->Get('user_id') == UserRights::GetUserId()))
|
||||
{
|
||||
$sFormatCode = $oInfo->Get('format');
|
||||
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
|
||||
$aStatusInfo = json_decode($oInfo->Get('status_info'),true);
|
||||
|
||||
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
|
||||
$oSearch->SetShowObsoleteData($aStatusInfo['show_obsolete_data']);
|
||||
$oBulkExporter = self::FindExporter($sFormatCode, $oSearch);
|
||||
if ($oBulkExporter)
|
||||
{
|
||||
$oBulkExporter->SetFormat($sFormatCode);
|
||||
$oBulkExporter->SetObjectList($oSearch);
|
||||
$oBulkExporter->SetChunkSize($oInfo->Get('chunk_size'));
|
||||
$oBulkExporter->SetStatusInfo(json_decode($oInfo->Get('status_info'), true));
|
||||
$oBulkExporter->SetStatusInfo($aStatusInfo);
|
||||
|
||||
$oBulkExporter->SetLocalizeOutput($oInfo->Get('localize_output'));
|
||||
|
||||
@@ -289,6 +293,7 @@ abstract class BulkExport
|
||||
*/
|
||||
public function SetObjectList(DBSearch $oSearch)
|
||||
{
|
||||
$oSearch->SetShowObsoleteData($this->aStatusInfo['show_obsolete_data']);
|
||||
$this->oSearch = $oSearch;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,12 @@ class CMDBSource
|
||||
const ENUM_DB_VENDOR_MARIADB = 'MariaDB';
|
||||
const ENUM_DB_VENDOR_PERCONA = 'Percona';
|
||||
|
||||
/**
|
||||
* @since 2.7.10 3.0.4 3.1.2 3.0.2 N°6889 constant creation
|
||||
* @internal will be removed in a future version
|
||||
*/
|
||||
const MYSQL_DEFAULT_PORT = 3306;
|
||||
|
||||
/**
|
||||
* Error: 1205 SQLSTATE: HY000 (ER_LOCK_WAIT_TIMEOUT)
|
||||
* Message: Lock wait timeout exceeded; try restarting transaction
|
||||
@@ -213,16 +219,19 @@ class CMDBSource
|
||||
/**
|
||||
* @param string $sDbHost initial value ("p:domain:port" syntax)
|
||||
* @param string $sServer server variable to update
|
||||
* @param int $iPort port variable to update
|
||||
* @param int|null $iPort port variable to update, will return null if nothing is specified in $sDbHost
|
||||
*
|
||||
* @since 2.7.10 3.0.4 3.1.2 3.2.0 N°6889 will return null in $iPort if port isn't present in $sDbHost. Use {@see MYSQL_DEFAULT_PORT} if needed
|
||||
*
|
||||
* @link http://php.net/manual/en/mysqli.persistconns.php documentation for the "p:" prefix (persistent connexion)
|
||||
*/
|
||||
public static function InitServerAndPort($sDbHost, &$sServer, &$iPort)
|
||||
{
|
||||
$aConnectInfo = explode(':', $sDbHost);
|
||||
|
||||
$bUsePersistentConnection = false;
|
||||
if (strcasecmp($aConnectInfo[0], 'p') == 0)
|
||||
if (strcasecmp($aConnectInfo[0], 'p') === 0)
|
||||
{
|
||||
// we might have "p:" prefix to use persistent connections (see http://php.net/manual/en/mysqli.persistconns.php)
|
||||
$bUsePersistentConnection = true;
|
||||
$sServer = $aConnectInfo[0].':'.$aConnectInfo[1];
|
||||
}
|
||||
@@ -240,10 +249,6 @@ class CMDBSource
|
||||
{
|
||||
$iPort = (int)($aConnectInfo[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$iPort = 3306;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -430,6 +435,7 @@ class CMDBSource
|
||||
{
|
||||
self::$m_sDBName = '';
|
||||
}
|
||||
self::_TablesInfoCacheReset(); // reset the table info cache!
|
||||
}
|
||||
|
||||
public static function CreateTable($sQuery)
|
||||
@@ -542,10 +548,9 @@ class CMDBSource
|
||||
/**
|
||||
* @param string $sSQLQuery
|
||||
*
|
||||
* @return \mysqli_result|null
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \CoreException
|
||||
* @return mysqli_result|null
|
||||
* @throws MySQLException
|
||||
* @throws MySQLHasGoneAwayException
|
||||
*
|
||||
* @since 2.7.0 N°679 handles nested transactions
|
||||
*/
|
||||
@@ -606,8 +611,9 @@ class CMDBSource
|
||||
{
|
||||
self::LogDeadLock($e, true);
|
||||
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
|
||||
}
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||
} finally {
|
||||
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
|
||||
}
|
||||
if ($oResult === false) {
|
||||
$aContext = array('query' => $sSql);
|
||||
|
||||
@@ -625,18 +631,24 @@ class CMDBSource
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Exception $e
|
||||
* @param Exception $e
|
||||
* @param bool $bForQuery to get the proper DB connection
|
||||
* @param bool $bCheckMysqliErrno if false won't try to check for mysqli::errno value
|
||||
*
|
||||
* @since 2.7.1
|
||||
* @since 3.0.0 N°4325 add new optional parameter to use the correct DB connection
|
||||
* @since 3.0.4 3.1.1 3.2.0 N°6643 new bCheckMysqliErrno parameter as a workaround for mysqli::errno cannot be mocked
|
||||
*/
|
||||
private static function LogDeadLock(Exception $e, $bForQuery = false)
|
||||
private static function LogDeadLock(Exception $e, $bForQuery = false, $bCheckMysqliErrno = true)
|
||||
{
|
||||
// checks MySQL error code
|
||||
$iMySqlErrorNo = DbConnectionWrapper::GetDbConnection($bForQuery)->errno;
|
||||
if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK))) {
|
||||
return;
|
||||
if ($bCheckMysqliErrno) {
|
||||
$iMySqlErrorNo = DbConnectionWrapper::GetDbConnection($bForQuery)->errno;
|
||||
if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK))) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$iMySqlErrorNo = "N/A";
|
||||
}
|
||||
|
||||
// Get error info
|
||||
@@ -663,7 +675,10 @@ class CMDBSource
|
||||
);
|
||||
DeadLockLog::Info($sMessage, $iMySqlErrorNo, $aLogContext);
|
||||
|
||||
IssueLog::Error($sMessage, LogChannels::DEADLOCK, $e->getMessage());
|
||||
IssueLog::Error($sMessage, LogChannels::DEADLOCK, [
|
||||
'exception.class' => get_class($e),
|
||||
'exception.message' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1153,8 +1168,8 @@ class CMDBSource
|
||||
*/
|
||||
public static function IsSameFieldTypes($sItopGeneratedFieldType, $sDbFieldType)
|
||||
{
|
||||
list($sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
|
||||
list($sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sDbFieldType);
|
||||
[$sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
|
||||
[$sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sDbFieldType);
|
||||
|
||||
if (strcasecmp($sItopFieldDataType, $sDbFieldDataType) !== 0)
|
||||
{
|
||||
@@ -1593,7 +1608,19 @@ class CMDBSource
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
public static function GetClusterNb()
|
||||
{
|
||||
$result = 0;
|
||||
$sSql = "SHOW STATUS LIKE 'wsrep_cluster_size';";
|
||||
$aRows = self::QueryToArray($sSql);
|
||||
if (count($aRows) > 0)
|
||||
{
|
||||
$result = $aRows[0]['Value'];
|
||||
}
|
||||
return intval($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-database.html
|
||||
* @return string query to upgrade database charset and collation if needed, null if not
|
||||
* @throws \MySQLException
|
||||
|
||||
@@ -480,6 +480,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
],
|
||||
'link_set_max_edit_ext_key' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Maximum number of items in the link that allow editing the remote external key. Above that limit, remote external key cannot be edited. Mind that setting this limit too high can have a negative impact on performances.',
|
||||
'default' => 50,
|
||||
'value' => 50,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => true,
|
||||
],
|
||||
'tag_set_item_separator' => [
|
||||
'type' => 'string',
|
||||
'description' => 'Tag set from string: tag label separator',
|
||||
@@ -576,22 +584,22 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'email_transport_smtp.allow_self_signed' => array(
|
||||
'email_transport_smtp.allow_self_signed' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Allow self signed peer certificates',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
'email_transport_smtp.verify_peer' => array(
|
||||
],
|
||||
'email_transport_smtp.verify_peer' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Verify peer certificate',
|
||||
'default' => true,
|
||||
'value' => true,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
),
|
||||
],
|
||||
'email_css' => [
|
||||
'type' => 'string',
|
||||
'description' => 'CSS that will override the standard stylesheet used for the notifications',
|
||||
@@ -981,6 +989,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'log_kpi_generate_legacy_report' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'Generate the legacy KPI report (kpi.html)',
|
||||
'default' => true,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'max_linkset_output' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.',
|
||||
@@ -1427,6 +1443,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'allow_rest_services_via_tokens' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'When set to true, REST endpoint token authorization works even with secure_rest_services set.',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'search_manual_submit' => [
|
||||
'type' => 'array',
|
||||
'description' => 'Force manual submit of search all requests',
|
||||
@@ -1507,6 +1531,14 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'security.enable_header_xcontent_type_options' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If set to false, iTop will stop sending the X-Content-Type-Options HTTP header. This header could trigger CORB protection on certain resources (JSON, XML, HTML, text) therefore blocking them.',
|
||||
'default' => true,
|
||||
'value' => '',
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'security.disable_inline_documents_sandbox' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true then the sandbox for documents displayed in a browser tab will be disabled; enabling scripts and other interactive content. Note that setting this to true will open the application to potential XSS attacks!',
|
||||
@@ -1872,7 +1904,7 @@ class Config
|
||||
}
|
||||
if (strlen($sNoise) > 0)
|
||||
{
|
||||
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
|
||||
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
|
||||
throw new ConfigException('Syntax error in configuration file',
|
||||
array('file' => $sConfigFile, 'error' => '<tt>'.htmlentities($sNoise, ENT_QUOTES, 'UTF-8').'</tt>'));
|
||||
}
|
||||
@@ -2161,6 +2193,24 @@ class Config
|
||||
$this->m_sAllowedLoginTypes = implode('|', $aAllowedLoginTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.7.11 N°7085
|
||||
* Add login mode if not configured already
|
||||
* @param string $sLoginMode
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function AddAllowedLoginTypes($sLoginMode)
|
||||
{
|
||||
$aAllowedLoginTypes = $this->GetAllowedLoginTypes();
|
||||
if (in_array($sLoginMode, $aAllowedLoginTypes)){
|
||||
return;
|
||||
}
|
||||
|
||||
$aAllowedLoginTypes[] = $sLoginMode;
|
||||
$this->SetAllowedLoginTypes($aAllowedLoginTypes);
|
||||
}
|
||||
|
||||
public function SetExternalAuthenticationVariable($sExtAuthVariable)
|
||||
{
|
||||
$this->m_sExtAuthVariable = $sExtAuthVariable;
|
||||
@@ -2682,7 +2732,7 @@ class ConfigPlaceholdersResolver
|
||||
}
|
||||
|
||||
$sPattern = '/\%(env|server)\((\w+)\)(?:\?:(\w*))?\%/'; //3 capturing groups, ie `%env(HTTP_PORT)?:8080%` produce: `env` `HTTP_PORT` and `8080`.
|
||||
|
||||
|
||||
if (! preg_match_all($sPattern, $rawValue, $aMatchesCollection, PREG_SET_ORDER))
|
||||
{
|
||||
return $rawValue;
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is only here for compatibility issues. Will be removed in iTop 3.1.0 (N°3664)
|
||||
* This file is only here for compatibility reasons.
|
||||
* It will be removed in future iTop versions (N°6533)
|
||||
*
|
||||
* @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');
|
||||
|
||||
require_once __DIR__ . '/../approot.inc.php';
|
||||
|
||||
DeprecatedCallsLog::NotifyDeprecatedFile('Classes were moved to /application/exceptions and can be used directly with the autoloader');
|
||||
|
||||
@@ -188,8 +188,8 @@ final class ItopCounter
|
||||
|
||||
if (!$hDBLink)
|
||||
{
|
||||
throw new Exception("Could not connect to the DB server (host=$sDBHost, user=$sDBUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
|
||||
}
|
||||
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
|
||||
}
|
||||
|
||||
return $hDBLink;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use Combodo\iTop\Application\UI\Base\Component\Input\SelectUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\ColumnUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\MultiColumnUIBlockFactory;
|
||||
use Combodo\iTop\Application\Helper\ExportHelper;
|
||||
|
||||
/**
|
||||
* Bulk export: CSV export
|
||||
@@ -114,6 +115,7 @@ class CSVBulkExport extends TabularBulkExport
|
||||
|
||||
case 'csv_options':
|
||||
$oPanel = PanelUIBlockFactory::MakeNeutral(Dict::S('Core:BulkExport:CSVOptions'));
|
||||
$oPanel->AddSubBlock(ExportHelper::GetAlertForExcelMaliciousInjection());
|
||||
|
||||
$oMulticolumn = MultiColumnUIBlockFactory::MakeStandard();
|
||||
$oPanel->AddSubBlock($oMulticolumn);
|
||||
@@ -164,7 +166,7 @@ class CSVBulkExport extends TabularBulkExport
|
||||
|
||||
foreach ($aQualifiers as $sVal => $sLabel) {
|
||||
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "text-qualifier", htmlentities($sVal, ENT_QUOTES, 'UTF-8'), $sLabel, "radio");
|
||||
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawSeparator));
|
||||
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawQualifier));
|
||||
$oRadio->SetBeforeInput(false);
|
||||
$oRadio->GetInput()->AddCSSClass('ibo-input--label-right');
|
||||
$oRadio->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
|
||||
@@ -524,11 +524,10 @@ abstract class DBObject implements iDisplay
|
||||
* Attributes setter
|
||||
*
|
||||
* Set $sAttCode to $value.
|
||||
* The value must be valid according to the type of attribute.
|
||||
* The value must be valid according to the type of attribute : see the different {@see AttributeDefinition::MakeRealValue()} implementations
|
||||
* The value will not be recorded into the DB until DBObject::DBWrite() is called.
|
||||
*
|
||||
* @api
|
||||
* @see DBWrite()
|
||||
*
|
||||
* @param string $sAttCode
|
||||
* @param mixed $value
|
||||
@@ -536,6 +535,8 @@ abstract class DBObject implements iDisplay
|
||||
* @return bool
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
*
|
||||
* @see DBWrite()
|
||||
*/
|
||||
public function Set($sAttCode, $value)
|
||||
{
|
||||
@@ -1581,10 +1582,9 @@ abstract class DBObject implements iDisplay
|
||||
/**
|
||||
* Helper to get the friendly name in a safe manner for displaying inside a web page
|
||||
*
|
||||
* @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)
|
||||
* @since 3.0.0 N°4106 Method should be overloaded anymore for performances reasons. It will be set final in 3.1.0 (N°4107)
|
||||
* @since 3.0.0 N°580 New $sType parameter
|
||||
*
|
||||
*/
|
||||
@@ -1931,7 +1931,7 @@ abstract class DBObject implements iDisplay
|
||||
/** @var \AttributeExternalKey $oAtt */
|
||||
$sTargetClass = $oAtt->GetTargetClass();
|
||||
if (false === MetaModel::IsObjectInDB($sTargetClass, $toCheck)) {
|
||||
return "Target object not found (".$sTargetClass.".::".$toCheck.")";
|
||||
return "Target object not found ({$sTargetClass}::{$toCheck})";
|
||||
}
|
||||
}
|
||||
if ($oAtt->IsHierarchicalKey())
|
||||
@@ -2135,16 +2135,17 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aUniquenessRuleProperties uniqueness rule properties
|
||||
*
|
||||
* @param string $sUniquenessRuleId uniqueness rule ID
|
||||
* @return \DBSearch
|
||||
* @throws \OQLException
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @param string $sUniquenessRuleId uniqueness rule ID
|
||||
* @param array $aUniquenessRuleProperties uniqueness rule properties
|
||||
*
|
||||
* @return \DBSearch
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
* @since 2.6.0 N°659 uniqueness constraint
|
||||
* @api
|
||||
* @since 2.6.0 N°659 uniqueness constraint
|
||||
* @since 2.7.11 3.1.2 3.2.0 N°4314 Fix Uniqueness rules not working with Silo
|
||||
*/
|
||||
protected function GetSearchForUniquenessRule($sUniquenessRuleId, $aUniquenessRuleProperties)
|
||||
{
|
||||
@@ -2173,8 +2174,10 @@ abstract class DBObject implements iDisplay
|
||||
$oUniquenessQuery->AddConditionForInOperatorUsingParam('finalclass', $aChildClassesWithRuleDisabled, false);
|
||||
}
|
||||
|
||||
return $oUniquenessQuery;
|
||||
}
|
||||
$oUniquenessQuery->AllowAllData();
|
||||
|
||||
return $oUniquenessQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sAttCode
|
||||
@@ -2238,7 +2241,6 @@ abstract class DBObject implements iDisplay
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*
|
||||
*/
|
||||
public function DoCheckToWrite()
|
||||
{
|
||||
@@ -2292,7 +2294,6 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @api
|
||||
* @api-advanced
|
||||
*
|
||||
@@ -2308,7 +2309,6 @@ abstract class DBObject implements iDisplay
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*
|
||||
*/
|
||||
final public function CheckToWrite()
|
||||
{
|
||||
@@ -2322,7 +2322,7 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->DoCheckToWrite();
|
||||
$oKPI->ComputeStats('CheckToWrite', get_class($this));
|
||||
$oKPI->ComputeStatsForExtension($this, 'DoCheckToWrite');
|
||||
if (count($this->m_aCheckIssues) == 0)
|
||||
{
|
||||
$this->m_bCheckStatus = true;
|
||||
@@ -2335,6 +2335,87 @@ abstract class DBObject implements iDisplay
|
||||
return array($this->m_bCheckStatus, $this->m_aCheckIssues, $this->m_bSecurityIssue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for extkey attributes values. This will throw exception on non-existing as well as non-accessible objects (silo, scopes).
|
||||
* That's why the test is done for all users including Administrators
|
||||
*
|
||||
* Note that due to perf issues, this isn't called directly by the ORM, but has to be called by consumers when possible.
|
||||
*
|
||||
* @param callable(string, string):bool|null $oIsObjectLoadableCallback Override to check if object is accessible.
|
||||
* Parameters are object class and key
|
||||
* Return value should be false if cannot access object, true otherwise
|
||||
* @return void
|
||||
*
|
||||
* @throws ArchivedObjectException
|
||||
* @throws CoreException if cannot get object attdef list
|
||||
* @throws CoreUnexpectedValue
|
||||
* @throws InvalidExternalKeyValueException
|
||||
* @throws MySQLException
|
||||
* @throws SecurityException if one extkey is pointing to an invalid value
|
||||
*
|
||||
* @link https://github.com/Combodo/iTop/security/advisories/GHSA-245j-66p9-pwmh
|
||||
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6458
|
||||
*
|
||||
* @see \RestUtils::FindObjectFromKey for the same check in the REST endpoint
|
||||
*/
|
||||
final public function CheckChangedExtKeysValues(callable $oIsObjectLoadableCallback = null)
|
||||
{
|
||||
if (is_null($oIsObjectLoadableCallback)) {
|
||||
$oIsObjectLoadableCallback = function ($sClass, $sId) {
|
||||
$oRemoteObject = MetaModel::GetObject($sClass, $sId, false);
|
||||
if (is_null($oRemoteObject)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
$aChanges = $this->ListChanges();
|
||||
$aAttCodesChanged = array_keys($aChanges);
|
||||
foreach ($aAttCodesChanged as $sAttDefCode) {
|
||||
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttDefCode);
|
||||
|
||||
if ($oAttDef instanceof AttributeLinkedSetIndirect) {
|
||||
/** @var ormLinkSet $oOrmSet */
|
||||
$oOrmSet = $this->Get($sAttDefCode);
|
||||
while ($oLnk = $oOrmSet->Fetch()) {
|
||||
$oLnk->CheckChangedExtKeysValues($oIsObjectLoadableCallback);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @noinspection PhpConditionCheckedByNextConditionInspection */
|
||||
/** @noinspection NotOptimalIfConditionsInspection */
|
||||
if (($oAttDef instanceof AttributeHierarchicalKey) || ($oAttDef instanceof AttributeExternalKey)) {
|
||||
$sRemoteObjectClass = $oAttDef->GetTargetClass();
|
||||
$sRemoteObjectKey = $this->Get($sAttDefCode);
|
||||
} else if ($oAttDef instanceof AttributeObjectKey) {
|
||||
$sRemoteObjectClassAttCode = $oAttDef->Get('class_attcode');
|
||||
$sRemoteObjectClass = $this->Get($sRemoteObjectClassAttCode);
|
||||
$sRemoteObjectKey = $this->Get($sAttDefCode);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (utils::IsNullOrEmptyString($sRemoteObjectClass)
|
||||
|| utils::IsNullOrEmptyString($sRemoteObjectKey)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 0 : Undefined ext. key (EG. non-mandatory and no value provided)
|
||||
// < 0 : Non yet persisted object
|
||||
/** @noinspection TypeUnsafeComparisonInspection Non-strict comparison as object ID can be string */
|
||||
if ($sRemoteObjectKey <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (false === $oIsObjectLoadableCallback($sRemoteObjectClass, $sRemoteObjectKey)) {
|
||||
throw new InvalidExternalKeyValueException($this, $sAttDefCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if it is allowed to delete the existing object from the database
|
||||
*
|
||||
@@ -2480,11 +2561,11 @@ abstract class DBObject implements iDisplay
|
||||
* @api
|
||||
* @api-advanced
|
||||
*
|
||||
* @see \DBObject::ListPreviousValuesForUpdatedAttributes() to get previous values anywhere in the CRUD stack
|
||||
* @see https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Asequence_crud iTop CRUD stack documentation
|
||||
* @return array attname => currentvalue List the attributes that have been changed using {@see DBObject::Set()}.
|
||||
* @return array attcode => currentvalue List the attributes that have been changed using {@see DBObject::Set()}.
|
||||
* Reset during {@see DBObject::DBUpdate()}
|
||||
* @throws Exception
|
||||
* @see \DBObject::ListPreviousValuesForUpdatedAttributes() to get previous values anywhere in the CRUD stack
|
||||
* @see https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Asequence_crud iTop CRUD stack documentation
|
||||
* @uses m_aCurrValues
|
||||
*/
|
||||
public function ListChanges()
|
||||
@@ -2776,7 +2857,6 @@ abstract class DBObject implements iDisplay
|
||||
* @throws \Exception
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
*/
|
||||
public function DBInsertNoReload()
|
||||
{
|
||||
@@ -2789,8 +2869,12 @@ abstract class DBObject implements iDisplay
|
||||
$sRootClass = MetaModel::GetRootClass($sClass);
|
||||
|
||||
// Ensure the update of the values (we are accessing the data directly)
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->DoComputeValues();
|
||||
$oKPI->ComputeStatsForExtension($this, 'DoComputeValues');
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->OnInsert();
|
||||
$oKPI->ComputeStatsForExtension($this, 'OnInsert');
|
||||
|
||||
// If not automatically computed, then check that the key is given by the caller
|
||||
if (!MetaModel::IsAutoIncrementKey($sRootClass))
|
||||
@@ -2802,7 +2886,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
// Ultimate check - ensure DB integrity
|
||||
list($bRes, $aIssues) = $this->CheckToWrite();
|
||||
[$bRes, $aIssues] = $this->CheckToWrite();
|
||||
if (!$bRes)
|
||||
{
|
||||
throw new CoreCannotSaveObjectException(array('issues' => $aIssues, 'class' => get_class($this), 'id' => $this->GetKey()));
|
||||
@@ -2917,7 +3001,9 @@ abstract class DBObject implements iDisplay
|
||||
$this->m_aOrigValues[$sAttCode] = $value;
|
||||
}
|
||||
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->AfterInsert();
|
||||
$oKPI->ComputeStatsForExtension($this, 'AfterInsert');
|
||||
|
||||
// Activate any existing trigger
|
||||
$sClass = get_class($this);
|
||||
@@ -3054,8 +3140,6 @@ abstract class DBObject implements iDisplay
|
||||
* Persist an object to the DB, for the first time
|
||||
*
|
||||
* @api
|
||||
* @see DBWrite
|
||||
*
|
||||
* @return int|null inserted object key
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
@@ -3065,10 +3149,12 @@ abstract class DBObject implements iDisplay
|
||||
* @throws \CoreWarning
|
||||
* @throws \MySQLException
|
||||
* @throws \OQLException
|
||||
*
|
||||
* @see DBWrite
|
||||
*/
|
||||
public function DBInsert()
|
||||
{
|
||||
$this->DBInsertNoReload();
|
||||
$this->DBInsertNoReload();
|
||||
|
||||
if (MetaModel::DBIsReadOnly())
|
||||
{
|
||||
@@ -3127,13 +3213,13 @@ abstract class DBObject implements iDisplay
|
||||
* Update an object in DB
|
||||
*
|
||||
* @api
|
||||
* @see DBObject::DBWrite()
|
||||
*
|
||||
* @return int object key
|
||||
*
|
||||
* @throws \CoreException
|
||||
* @throws \CoreCannotSaveObjectException if CheckToWrite() returns issues
|
||||
* @throws \Exception
|
||||
*
|
||||
* @see DBObject::DBWrite()
|
||||
*/
|
||||
public function DBUpdate()
|
||||
{
|
||||
@@ -3141,6 +3227,7 @@ abstract class DBObject implements iDisplay
|
||||
{
|
||||
throw new CoreException("DBUpdate: could not update a newly created object, please call DBInsert instead");
|
||||
}
|
||||
|
||||
// Protect against reentrance (e.g. cascading the update of ticket logs)
|
||||
static $aUpdateReentrance = array();
|
||||
$sKey = get_class($this).'::'.$this->GetKey();
|
||||
@@ -3154,8 +3241,11 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
try
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->DoComputeValues();
|
||||
// Stop watches
|
||||
$oKPI->ComputeStatsForExtension($this, 'DoComputeValues');
|
||||
|
||||
// Stop watches
|
||||
$sState = $this->GetState();
|
||||
if ($sState != '')
|
||||
{
|
||||
@@ -3174,7 +3264,9 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->OnUpdate();
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->OnUpdate();
|
||||
$oKPI->ComputeStatsForExtension($this, 'OnUpdate');
|
||||
|
||||
$aChanges = $this->ListChanges();
|
||||
if (count($aChanges) == 0)
|
||||
@@ -3186,7 +3278,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
|
||||
// Ultimate check - ensure DB integrity
|
||||
list($bRes, $aIssues) = $this->CheckToWrite();
|
||||
[$bRes, $aIssues] = $this->CheckToWrite();
|
||||
if (!$bRes)
|
||||
{
|
||||
throw new CoreCannotSaveObjectException(array(
|
||||
@@ -3370,7 +3462,9 @@ abstract class DBObject implements iDisplay
|
||||
$this->m_aModifiedAtt = array();
|
||||
|
||||
try {
|
||||
$this->AfterUpdate();
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->AfterUpdate();
|
||||
$oKPI->ComputeStatsForExtension($this, 'AfterUpdate');
|
||||
|
||||
// Reload to get the external attributes
|
||||
if ($bNeedReload) {
|
||||
@@ -3510,13 +3604,18 @@ abstract class DBObject implements iDisplay
|
||||
|
||||
/**
|
||||
* Make the current changes persistent - clever wrapper for Insert or Update
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @api
|
||||
*
|
||||
* @return int
|
||||
*
|
||||
* @throws \CoreCannotSaveObjectException
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @throws ArchivedObjectException
|
||||
* @throws CoreCannotSaveObjectException
|
||||
* @throws CoreException
|
||||
* @throws CoreUnexpectedValue
|
||||
* @throws CoreWarning
|
||||
* @throws MySQLException
|
||||
* @throws OQLException
|
||||
*/
|
||||
public function DBWrite()
|
||||
{
|
||||
@@ -4133,7 +4232,7 @@ abstract class DBObject implements iDisplay
|
||||
}
|
||||
}
|
||||
$oDate->modify($sModifier);
|
||||
$this->Set($sAttCode, $oDate->format('Y-m-d H:i:s'));
|
||||
$this->Set($sAttCode, $oDate->getTimestamp());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1103,22 +1103,39 @@ class DBObjectSearch extends DBSearch
|
||||
public function Filter($sClassAlias, DBSearch $oFilter)
|
||||
{
|
||||
// If the conditions are the correct ones for Intersect
|
||||
if (MetaModel::IsParentClass($oFilter->GetFirstJoinedClass(),$this->GetFirstJoinedClass()))
|
||||
{
|
||||
if (MetaModel::IsParentClass($oFilter->GetFirstJoinedClass(), $this->GetFirstJoinedClass())) {
|
||||
return $this->Intersect($oFilter);
|
||||
}
|
||||
|
||||
/** @var \DBObjectSearch $oFilteredSearch */
|
||||
$oFilteredSearch = $this->DeepClone();
|
||||
$oFilterExpression = self::FilterSubClass($oFilteredSearch, $sClassAlias, $oFilter, $this->m_aClasses);
|
||||
if ($oFilterExpression === false)
|
||||
{
|
||||
throw new CoreException("Limitation: cannot filter search");
|
||||
if ($oFilter instanceof DBUnionSearch) {
|
||||
$aFilters = $oFilter->GetSearches();
|
||||
} else {
|
||||
$aFilters = [$oFilter];
|
||||
}
|
||||
|
||||
$oFilteredSearch->AddConditionExpression($oFilterExpression);
|
||||
$aSearches = [];
|
||||
foreach ($aFilters as $oRightFilter) {
|
||||
/** @var \DBObjectSearch $oFilteredSearch */
|
||||
$oFilteredSearch = $this->DeepClone();
|
||||
$oFilterExpression = self::FilterSubClass($oFilteredSearch, $sClassAlias, $oRightFilter, $this->m_aClasses);
|
||||
if ($oFilterExpression === false) {
|
||||
throw new CoreException("Limitation: cannot filter search");
|
||||
}
|
||||
|
||||
return $oFilteredSearch;
|
||||
$oFilteredSearch->AddConditionExpression($oFilterExpression);
|
||||
$aSearches[] = $oFilteredSearch;
|
||||
}
|
||||
|
||||
if (count($aSearches) == 0) {
|
||||
throw new CoreException('Filtering '.$this->ToOQL().' by '.$oFilter->ToOQL().' failed');
|
||||
}
|
||||
|
||||
if (count($aSearches) == 1) {
|
||||
// return a DBObjectSearch
|
||||
return $aSearches[0];
|
||||
}
|
||||
|
||||
return new DBUnionSearch($aSearches);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1184,22 +1201,10 @@ class DBObjectSearch extends DBSearch
|
||||
* @throws \CoreException
|
||||
*/
|
||||
public function Intersect(DBSearch $oFilter)
|
||||
{
|
||||
return $this->IntersectSubClass($oFilter, $this->m_aClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBSearch $oFilter
|
||||
* @param array $aRootClasses classes of the root search (for aliases)
|
||||
*
|
||||
* @return \DBUnionSearch|mixed
|
||||
* @throws \CoreException
|
||||
*/
|
||||
protected function IntersectSubClass(DBSearch $oFilter, $aRootClasses)
|
||||
{
|
||||
if ($oFilter instanceof DBUnionSearch)
|
||||
{
|
||||
// Develop!
|
||||
// Develop!
|
||||
$aFilters = $oFilter->GetSearches();
|
||||
}
|
||||
else
|
||||
@@ -1210,56 +1215,61 @@ class DBObjectSearch extends DBSearch
|
||||
$aSearches = array();
|
||||
foreach ($aFilters as $oRightFilter)
|
||||
{
|
||||
// Limitation: the queried class must be the first declared class
|
||||
if ($oRightFilter->GetFirstJoinedClassAlias() != $oRightFilter->GetClassAlias())
|
||||
{
|
||||
throw new CoreException("Limitation: cannot merge two queries if the queried class ({$oRightFilter->GetClass()} AS {$oRightFilter->GetClassAlias()}) is not the first joined class ({$oRightFilter->GetFirstJoinedClass()} AS {$oRightFilter->GetFirstJoinedClassAlias()})");
|
||||
}
|
||||
|
||||
/** @var \DBObjectSearch $oLeftFilter */
|
||||
$oLeftFilter = $this->DeepClone();
|
||||
$oRightFilter = $oRightFilter->DeepClone();
|
||||
|
||||
$bAllowAllData = ($oLeftFilter->IsAllDataAllowed() && $oRightFilter->IsAllDataAllowed());
|
||||
if ($bAllowAllData)
|
||||
{
|
||||
$oLeftFilter->AllowAllData();
|
||||
}
|
||||
|
||||
if ($oLeftFilter->GetFirstJoinedClass() != $oRightFilter->GetClass())
|
||||
{
|
||||
if (MetaModel::IsParentClass($oLeftFilter->GetFirstJoinedClass(), $oRightFilter->GetClass()))
|
||||
{
|
||||
// Specialize $oLeftFilter
|
||||
$oLeftFilter->ChangeClass($oRightFilter->GetClass(), $oLeftFilter->GetFirstJoinedClassAlias());
|
||||
}
|
||||
elseif (MetaModel::IsParentClass($oRightFilter->GetFirstJoinedClass(), $oLeftFilter->GetClass()))
|
||||
{
|
||||
// Specialize $oRightFilter
|
||||
$oRightFilter->ChangeClass($oLeftFilter->GetFirstJoinedClass());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new CoreException("Attempting to merge a filter of class '{$oLeftFilter->GetClass()}' with a filter of class '{$oRightFilter->GetClass()}'");
|
||||
}
|
||||
}
|
||||
|
||||
$aAliasTranslation = array();
|
||||
$oLeftFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation);
|
||||
$oLeftFilter->MergeWith_InNamespace($oRightFilter, $aRootClasses, $aAliasTranslation);
|
||||
$oRightFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation);
|
||||
$oLeftFilter->TransferConditionExpression($oRightFilter, $aAliasTranslation);
|
||||
$aSearches[] = $oLeftFilter;
|
||||
$aSearches[] = $this->IntersectSubClass($oRightFilter, $this->m_aClasses);
|
||||
}
|
||||
|
||||
if (count($aSearches) == 1)
|
||||
{
|
||||
// return a DBObjectSearch
|
||||
return $aSearches[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return new DBUnionSearch($aSearches);
|
||||
|
||||
return new DBUnionSearch($aSearches);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBObjectSearch $oRightFilter
|
||||
* @param array $aRootClasses classes of the root search (for aliases)
|
||||
*
|
||||
* @return \DBObjectSearch
|
||||
* @throws \CoreException
|
||||
*/
|
||||
protected function IntersectSubClass(DBObjectSearch $oRightFilter, array $aRootClasses): DBObjectSearch
|
||||
{
|
||||
// Limitation: the queried class must be the first declared class
|
||||
if ($oRightFilter->GetFirstJoinedClassAlias() != $oRightFilter->GetClassAlias()) {
|
||||
throw new CoreException("Limitation: cannot merge two queries if the queried class ({$oRightFilter->GetClass()} AS {$oRightFilter->GetClassAlias()}) is not the first joined class ({$oRightFilter->GetFirstJoinedClass()} AS {$oRightFilter->GetFirstJoinedClassAlias()})");
|
||||
}
|
||||
|
||||
/** @var \DBObjectSearch $oLeftFilter */
|
||||
$oLeftFilter = $this->DeepClone();
|
||||
/** @var DBObjectSearch $oRightFilter */
|
||||
$oRightFilter = $oRightFilter->DeepClone();
|
||||
|
||||
$bAllowAllData = ($oLeftFilter->IsAllDataAllowed() && $oRightFilter->IsAllDataAllowed());
|
||||
if ($bAllowAllData) {
|
||||
$oLeftFilter->AllowAllData();
|
||||
}
|
||||
|
||||
if ($oLeftFilter->GetFirstJoinedClass() != $oRightFilter->GetClass()) {
|
||||
if (MetaModel::IsParentClass($oLeftFilter->GetFirstJoinedClass(), $oRightFilter->GetClass())) {
|
||||
// Specialize $oLeftFilter
|
||||
$oLeftFilter->ChangeClass($oRightFilter->GetClass(), $oLeftFilter->GetFirstJoinedClassAlias());
|
||||
} elseif (MetaModel::IsParentClass($oRightFilter->GetFirstJoinedClass(), $oLeftFilter->GetClass())) {
|
||||
// Specialize $oRightFilter
|
||||
$oRightFilter->ChangeClass($oLeftFilter->GetFirstJoinedClass());
|
||||
} else {
|
||||
throw new CoreException("Attempting to merge a filter of class '{$oLeftFilter->GetClass()}' with a filter of class '{$oRightFilter->GetClass()}'");
|
||||
}
|
||||
}
|
||||
|
||||
$aAliasTranslation = array();
|
||||
$oLeftFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation);
|
||||
$oLeftFilter->MergeWith_InNamespace($oRightFilter, $aRootClasses, $aAliasTranslation);
|
||||
$oRightFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation);
|
||||
$oLeftFilter->TransferConditionExpression($oRightFilter, $aAliasTranslation);
|
||||
|
||||
return $oLeftFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -767,7 +767,10 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
|
||||
try
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$this->m_oSQLResult = CMDBSource::Query($sSQL);
|
||||
$sOQL = $this->GetPseudoOQL($this->m_oFilter, $this->GetRealSortOrder(), $this->m_iLimitCount, $this->m_iLimitStart, false);
|
||||
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
|
||||
} catch (MySQLException $e)
|
||||
{
|
||||
// 1116 = ER_TOO_MANY_TABLES
|
||||
@@ -847,8 +850,11 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
{
|
||||
if (is_null($this->m_iNumTotalDBRows))
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), 0, 0, true);
|
||||
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
|
||||
if (!$resQuery) return 0;
|
||||
|
||||
$aRow = CMDBSource::FetchArray($resQuery);
|
||||
@@ -859,6 +865,42 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
return $this->m_iNumTotalDBRows + count($this->m_aAddedObjects); // Does it fix Trac #887 ??
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBSearch $oFilter
|
||||
* @param array $aOrder
|
||||
* @param int $iLimitCount
|
||||
* @param int $iLimitStart
|
||||
* @param bool $bCount
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function GetPseudoOQL($oFilter, $aOrder, $iLimitCount, $iLimitStart, $bCount)
|
||||
{
|
||||
$sOQL = '';
|
||||
if ($bCount) {
|
||||
$sOQL .= 'COUNT ';
|
||||
}
|
||||
$sOQL .= $oFilter->ToOQL();
|
||||
|
||||
if ($iLimitCount > 0) {
|
||||
$sOQL .= ' LIMIT ';
|
||||
if ($iLimitStart > 0) {
|
||||
$sOQL .= "$iLimitStart, ";
|
||||
}
|
||||
$sOQL .= "$iLimitCount";
|
||||
}
|
||||
|
||||
if (count($aOrder) > 0) {
|
||||
$sOQL .= ' ORDER BY ';
|
||||
$aOrderBy = [];
|
||||
foreach ($aOrder as $sAttCode => $bAsc) {
|
||||
$aOrderBy[] = $sAttCode.' '.($bAsc ? 'ASC' : 'DESC');
|
||||
}
|
||||
$sOQL .= implode(', ', $aOrderBy);
|
||||
}
|
||||
return $sOQL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the count exceeds a given limit
|
||||
*
|
||||
@@ -875,8 +917,11 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
{
|
||||
if (is_null($this->m_iNumTotalDBRows))
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true);
|
||||
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
|
||||
if ($resQuery)
|
||||
{
|
||||
$aRow = CMDBSource::FetchArray($resQuery);
|
||||
@@ -887,7 +932,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
{
|
||||
$iCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCount = $this->m_iNumTotalDBRows;
|
||||
@@ -912,8 +957,11 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
{
|
||||
if (is_null($this->m_iNumTotalDBRows))
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true);
|
||||
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
|
||||
if ($resQuery)
|
||||
{
|
||||
$aRow = CMDBSource::FetchArray($resQuery);
|
||||
@@ -924,7 +972,7 @@ class DBObjectSet implements iDBObjectSetIterator
|
||||
{
|
||||
$iCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$iCount = $this->m_iNumTotalDBRows;
|
||||
|
||||
@@ -885,11 +885,11 @@ abstract class DBSearch
|
||||
return;
|
||||
}
|
||||
|
||||
if (count($aColumns) == 0)
|
||||
{
|
||||
$aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass()));
|
||||
// Add the standard id (as first column)
|
||||
array_unshift($aColumns, 'id');
|
||||
if (count($aColumns) == 0)
|
||||
{
|
||||
$aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass()));
|
||||
// Add the standard id (as first column)
|
||||
array_unshift($aColumns, 'id');
|
||||
}
|
||||
|
||||
$aQueryCols = CMDBSource::GetColumns($resQuery, $sSQL);
|
||||
@@ -919,6 +919,55 @@ abstract class DBSearch
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a column ($sAttCode) from the specified class ($sClassAlias - default main class) of the DBsearch object and gives the result as an array
|
||||
* @param string $sAttCode
|
||||
* @param string|null $sClassAlias
|
||||
*
|
||||
* @return array
|
||||
* @throws ConfigException
|
||||
* @throws CoreException
|
||||
* @throws MissingQueryArgument
|
||||
* @throws MySQLException
|
||||
* @throws MySQLHasGoneAwayException
|
||||
*/
|
||||
public function SelectAttributeToArray(string $sAttCode, ?string $sClassAlias = null):array
|
||||
{
|
||||
if(is_null($sClassAlias)) {
|
||||
$sClassAlias = $this->GetClassAlias();
|
||||
}
|
||||
|
||||
$sClass = $this->GetClass();
|
||||
if($sAttCode === 'id'){
|
||||
$aAttToLoad[$sClassAlias]=[];
|
||||
} else {
|
||||
$aAttToLoad[$sClassAlias][$sAttCode] = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
}
|
||||
|
||||
$sSQL = $this->MakeSelectQuery([], [], $aAttToLoad);
|
||||
$resQuery = CMDBSource::Query($sSQL);
|
||||
if (!$resQuery)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
$sColName = $sClassAlias.$sAttCode;
|
||||
|
||||
$aRes = [];
|
||||
while ($aRow = CMDBSource::FetchArray($resQuery))
|
||||
{
|
||||
$aMappedRow = array();
|
||||
if($sAttCode === 'id') {
|
||||
$aMappedRow[$sAttCode] = $aRow[$sColName];
|
||||
} else {
|
||||
$aMappedRow[$sAttCode] = $aAttToLoad[$sClassAlias][$sAttCode]->FromSQLToValue($aRow, $sColName);
|
||||
}
|
||||
$aRes[] = $aMappedRow;
|
||||
}
|
||||
CMDBSource::FreeResult($resQuery);
|
||||
return $aRes;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Construction of the SQL queries
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// 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.
|
||||
@@ -56,10 +56,11 @@ class Dict
|
||||
* @param $sLanguageCode
|
||||
*
|
||||
* @throws \DictExceptionUnknownLanguage
|
||||
* @since 3.0.4 3.1.1 3.2.0 Param $sLanguageCode becomes nullable
|
||||
*/
|
||||
public static function SetUserLanguage($sLanguageCode)
|
||||
public static function SetUserLanguage($sLanguageCode = null)
|
||||
{
|
||||
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
|
||||
if (!is_null($sLanguageCode) && !array_key_exists($sLanguageCode, self::$m_aLanguages))
|
||||
{
|
||||
throw new DictExceptionUnknownLanguage($sLanguageCode);
|
||||
}
|
||||
@@ -115,33 +116,50 @@ class Dict
|
||||
* @return string
|
||||
*/
|
||||
public static function S($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
|
||||
{
|
||||
$aInfo = self::GetLabelAndLangCode($sStringCode, $sDefault, $bUserLanguageOnly);
|
||||
return $aInfo['label'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a localised string from the dictonary with its associated lang code
|
||||
*
|
||||
* @param string $sStringCode The code identifying the dictionary entry
|
||||
* @param string $sDefault Default value if there is no match in the dictionary
|
||||
* @param bool $bUserLanguageOnly True to allow the use of the default language as a fallback, false otherwise
|
||||
*
|
||||
* @return array{
|
||||
* lang: string, label: string
|
||||
* } with localized label string and used lang code
|
||||
*/
|
||||
private static function GetLabelAndLangCode($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
|
||||
{
|
||||
// Attempt to find the string in the user language
|
||||
//
|
||||
$sLangCode = self::GetUserLanguage();
|
||||
self::InitLangIfNeeded($sLangCode);
|
||||
|
||||
if (!array_key_exists($sLangCode, self::$m_aData))
|
||||
if (! array_key_exists($sLangCode, self::$m_aData))
|
||||
{
|
||||
IssueLog::Warning("Cannot find $sLangCode in dictionnaries. default labels displayed");
|
||||
IssueLog::Warning("Cannot find $sLangCode in all registered dictionaries.");
|
||||
// It may happen, when something happens before the dictionaries get loaded
|
||||
return $sStringCode;
|
||||
return [ 'label' => $sStringCode, 'lang' => $sLangCode ];
|
||||
}
|
||||
$aCurrentDictionary = self::$m_aData[$sLangCode];
|
||||
if (is_array($aCurrentDictionary) && array_key_exists($sStringCode, $aCurrentDictionary))
|
||||
{
|
||||
return $aCurrentDictionary[$sStringCode];
|
||||
return [ 'label' => $aCurrentDictionary[$sStringCode], 'lang' => $sLangCode ];
|
||||
}
|
||||
if (!$bUserLanguageOnly)
|
||||
{
|
||||
// Attempt to find the string in the default language
|
||||
//
|
||||
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
|
||||
|
||||
|
||||
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
|
||||
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
{
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => self::$m_sDefaultLanguage ];
|
||||
}
|
||||
// Attempt to find the string in english
|
||||
//
|
||||
@@ -150,17 +168,17 @@ class Dict
|
||||
$aDefaultDictionary = self::$m_aData['EN US'];
|
||||
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
|
||||
{
|
||||
return $aDefaultDictionary[$sStringCode];
|
||||
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => 'EN US' ];
|
||||
}
|
||||
}
|
||||
// Could not find the string...
|
||||
//
|
||||
if (is_null($sDefault))
|
||||
{
|
||||
return $sStringCode;
|
||||
return [ 'label' => $sStringCode, 'lang' => null ];
|
||||
}
|
||||
|
||||
return $sDefault;
|
||||
return [ 'label' => $sDefault, 'lang' => null ];
|
||||
}
|
||||
|
||||
|
||||
@@ -176,19 +194,25 @@ class Dict
|
||||
*/
|
||||
public static function Format($sFormatCode /*, ... arguments ....*/)
|
||||
{
|
||||
$sLocalizedFormat = self::S($sFormatCode);
|
||||
['label' => $sLocalizedFormat, 'lang' => $sLangCode] = self::GetLabelAndLangCode($sFormatCode);
|
||||
|
||||
$aArguments = func_get_args();
|
||||
array_shift($aArguments);
|
||||
|
||||
|
||||
if ($sLocalizedFormat == $sFormatCode)
|
||||
{
|
||||
// Make sure the information will be displayed (ex: an error occuring before the dictionary gets loaded)
|
||||
return $sFormatCode.' - '.implode(', ', $aArguments);
|
||||
}
|
||||
|
||||
return vsprintf($sLocalizedFormat, $aArguments);
|
||||
try{
|
||||
return vsprintf($sLocalizedFormat, $aArguments);
|
||||
} catch(\Throwable $e){
|
||||
\IssueLog::Error("Cannot format dict key", null, ["sFormatCode" => $sFormatCode, "sLangCode" => $sLangCode, 'exception_msg' => $e->getMessage() ]);
|
||||
return $sFormatCode.' - '.implode(', ', $aArguments);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize a the entries for a given language (replaces the former Add() method)
|
||||
* @param string $sLanguageCode Code identifying the language i.e. 'FR-FR', 'EN-US'
|
||||
@@ -198,7 +222,7 @@ class Dict
|
||||
{
|
||||
self::$m_aData[$sLanguageCode] = $aEntries;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the list of available languages
|
||||
* @param hash $aLanguagesList
|
||||
@@ -259,7 +283,7 @@ class Dict
|
||||
{
|
||||
$sDictFile = APPROOT.'env-'.utils::GetCurrentEnvironment().'/dictionaries/'.str_replace(' ', '-', strtolower($sLangCode)).'.dict.php';
|
||||
require_once($sDictFile);
|
||||
|
||||
|
||||
if (self::GetApcService()->function_exists('apc_store')
|
||||
&& (self::$m_sApplicationPrefix !== null))
|
||||
{
|
||||
@@ -269,7 +293,7 @@ class Dict
|
||||
}
|
||||
return $bResult;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enable caching (cached using APC)
|
||||
* @param string $sApplicationPrefix The prefix for uniquely identiying this iTop instance
|
||||
@@ -312,14 +336,14 @@ class Dict
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function MakeStats($sLanguageCode, $sLanguageRef = 'EN US')
|
||||
{
|
||||
$aMissing = array(); // Strings missing for the target language
|
||||
$aUnexpected = array(); // Strings defined for the target language, but not found in the reference dictionary
|
||||
$aNotTranslated = array(); // Strings having the same value in both dictionaries
|
||||
$aOK = array(); // Strings having different values in both dictionaries
|
||||
|
||||
|
||||
foreach (self::$m_aData[$sLanguageRef] as $sStringCode => $sValue)
|
||||
{
|
||||
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageCode]))
|
||||
@@ -327,7 +351,7 @@ class Dict
|
||||
$aMissing[$sStringCode] = $sValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach (self::$m_aData[$sLanguageCode] as $sStringCode => $sValue)
|
||||
{
|
||||
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageRef]))
|
||||
@@ -350,7 +374,7 @@ class Dict
|
||||
}
|
||||
return array($aMissing, $aUnexpected, $aNotTranslated, $aOK);
|
||||
}
|
||||
|
||||
|
||||
public static function Dump()
|
||||
{
|
||||
MyHelpers::var_dump_html(self::$m_aData);
|
||||
@@ -373,7 +397,7 @@ class Dict
|
||||
// No need to actually load the strings since it's only used to know the list of languages
|
||||
// at setup time !!
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Export all the dictionary entries - of the given language - whose code matches the given prefix
|
||||
* missing entries in the current language will be replaced by entries in the default language
|
||||
@@ -386,7 +410,7 @@ class Dict
|
||||
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
|
||||
$aEntries = array();
|
||||
$iLength = strlen($sStartingWith);
|
||||
|
||||
|
||||
// First prefill the array with entries from the default language
|
||||
foreach(self::$m_aData[self::$m_sDefaultLanguage] as $sCode => $sEntry)
|
||||
{
|
||||
@@ -395,7 +419,7 @@ class Dict
|
||||
$aEntries[$sCode] = $sEntry;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Now put (overwrite) the entries for the user language
|
||||
foreach(self::$m_aData[self::GetUserLanguage()] as $sCode => $sEntry)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,7 @@ use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\ColumnUIBlockFactory;
|
||||
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\MultiColumnUIBlockFactory;
|
||||
use Combodo\iTop\Application\Helper\ExportHelper;
|
||||
|
||||
require_once(APPROOT.'application/xlsxwriter.class.php');
|
||||
|
||||
@@ -82,6 +83,7 @@ class ExcelBulkExport extends TabularBulkExport
|
||||
|
||||
case 'xlsx_options':
|
||||
$oPanel = PanelUIBlockFactory::MakeNeutral(Dict::S('Core:BulkExport:XLSXOptions'));
|
||||
$oPanel->AddSubBlock(ExportHelper::GetAlertForExcelMaliciousInjection());
|
||||
|
||||
$oMulticolumn = MultiColumnUIBlockFactory::MakeStandard();
|
||||
$oPanel->AddSubBlock($oMulticolumn);
|
||||
|
||||
@@ -48,10 +48,9 @@ abstract class HTMLSanitizer
|
||||
$sSanitizerClass = 'HTMLDOMSanitizer';
|
||||
} else if (!is_subclass_of($sSanitizerClass, 'HTMLSanitizer')) {
|
||||
if ($sConfigKey === 'html_sanitizer') {
|
||||
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of HTMLSanitizer. Will use HTMLDOMSanitizer as the default sanitizer.');
|
||||
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of '.HTMLSanitizer::class.'. Will use HTMLDOMSanitizer as the default sanitizer.');
|
||||
$sSanitizerClass = 'HTMLDOMSanitizer';
|
||||
}
|
||||
if ($sConfigKey === 'svg_sanitizer') {
|
||||
} else {
|
||||
IssueLog::Error('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of '.HTMLSanitizer::class.' ! Won\'t sanitize string.');
|
||||
|
||||
return $sHTML;
|
||||
|
||||
@@ -1,27 +1,14 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-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
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
use Combodo\iTop\Core\Kpi\KpiLogData;
|
||||
use Combodo\iTop\Service\Module\ModuleService;
|
||||
|
||||
|
||||
/**
|
||||
* Measures operations duration, memory usage, etc. (and some other KPIs)
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
class ExecutionKPI
|
||||
@@ -30,6 +17,8 @@ class ExecutionKPI
|
||||
static protected $m_bEnabled_Memory = false;
|
||||
static protected $m_bBlameCaller = false;
|
||||
static protected $m_sAllowedUser = '*';
|
||||
static protected $m_bGenerateLegacyReport = true;
|
||||
static protected $m_fSlowQueries = 0;
|
||||
|
||||
static protected $m_aStats = []; // Recurrent operations
|
||||
static protected $m_aExecData = []; // One shot operations
|
||||
@@ -86,14 +75,39 @@ class ExecutionKPI
|
||||
return false;
|
||||
}
|
||||
|
||||
static public function SetGenerateLegacyReport($bReportExtensionsOnly)
|
||||
{
|
||||
self::$m_bGenerateLegacyReport = $bReportExtensionsOnly;
|
||||
}
|
||||
|
||||
static public function SetSlowQueries($fSlowQueries)
|
||||
{
|
||||
self::$m_fSlowQueries = $fSlowQueries;
|
||||
}
|
||||
|
||||
static public function GetDescription()
|
||||
{
|
||||
$aFeatures = array();
|
||||
if (self::$m_bEnabled_Duration) $aFeatures[] = 'Duration';
|
||||
if (self::$m_bEnabled_Memory) $aFeatures[] = 'Memory usage';
|
||||
$sFeatures = implode(', ', $aFeatures);
|
||||
$sFeatures = 'Measures: '.implode(', ', $aFeatures);
|
||||
$sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'";
|
||||
return "KPI logging is active for $sFor. Measures: $sFeatures";
|
||||
$sSlowQueries = '';
|
||||
if (self::$m_fSlowQueries > 0) {
|
||||
$sSlowQueries = ". Slow Queries: ".self::$m_fSlowQueries."s";
|
||||
}
|
||||
|
||||
$aExtensions = [];
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||
$aExtensions[] = ModuleService::GetInstance()->GetModuleNameFromObject($oExtensionInstance);
|
||||
}
|
||||
$sExtensions = '';
|
||||
if (count($aExtensions) > 0) {
|
||||
$sExtensions = '. KPI Extensions: ['.implode(', ', $aExtensions).']';
|
||||
}
|
||||
|
||||
return "KPI logging is active for $sFor. $sFeatures$sSlowQueries$sExtensions";
|
||||
}
|
||||
|
||||
static public function ReportStats()
|
||||
@@ -101,7 +115,28 @@ class ExecutionKPI
|
||||
if (!self::IsEnabled()) return;
|
||||
|
||||
global $fItopStarted;
|
||||
global $iItopInitialMemory;
|
||||
$sExecId = microtime(); // id to differentiate the hrefs!
|
||||
$sRequest = $_SERVER['REQUEST_URI'].' ('.$_SERVER['REQUEST_METHOD'].')';
|
||||
if (isset($_POST['operation'])) {
|
||||
$sRequest .= ' operation: '.$_POST['operation'];
|
||||
}
|
||||
|
||||
$fStop = MyHelpers::getmicrotime();
|
||||
if (($fStop - $fItopStarted) > self::$m_fSlowQueries) {
|
||||
// Invoke extensions to log the KPI operation
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
$iCurrentMemory = self::memory_get_usage();
|
||||
$iPeakMemory = self::memory_get_peak_usage();
|
||||
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||
$oKPILogData = new KpiLogData(KpiLogData::TYPE_REQUEST, 'Page', $sRequest, $fItopStarted, $fStop, '', $iItopInitialMemory, $iCurrentMemory, $iPeakMemory);
|
||||
$oExtensionInstance->LogOperation($oKPILogData);
|
||||
}
|
||||
}
|
||||
|
||||
if (!self::$m_bGenerateLegacyReport) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aBeginTimes = array();
|
||||
foreach (self::$m_aExecData as $aOpStats)
|
||||
@@ -114,9 +149,9 @@ class ExecutionKPI
|
||||
|
||||
$sHtml = "<hr/>";
|
||||
$sHtml .= "<div style=\"background-color: grey; padding: 10px;\">";
|
||||
$sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - ".$_SERVER['REQUEST_URI']." (".$_SERVER['REQUEST_METHOD'].")</h3>";
|
||||
$sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - $sRequest</h3>";
|
||||
$oStarted = DateTime::createFromFormat('U.u', $fItopStarted);
|
||||
$sHtml .= "<p>".$oStarted->format('Y-m-d H:i:s.u')."</p>";
|
||||
$sHtml .= '<p>'.$oStarted->format('Y-m-d H:i:s.u').'</p>';
|
||||
$sHtml .= "<p>log_kpi_user_id: ".UserRights::GetUserId()."</p>";
|
||||
$sHtml .= "<div>";
|
||||
$sHtml .= "<table border=\"1\" style=\"$sTableStyle\">";
|
||||
@@ -257,7 +292,7 @@ class ExecutionKPI
|
||||
$sTotalInter = round($fTotalInter, 3);
|
||||
$sMinInter = round($fMinInter, 3);
|
||||
$sMaxInter = round($fMaxInter, 3);
|
||||
if (($fTotalInter >= $fSlowQueries))
|
||||
if (($fTotalInter >= self::$m_fSlowQueries))
|
||||
{
|
||||
if ($bDisplayHeader)
|
||||
{
|
||||
@@ -285,37 +320,19 @@ class ExecutionKPI
|
||||
self::Report($sHtml);
|
||||
}
|
||||
|
||||
public static function InitStats()
|
||||
{
|
||||
// Invoke extensions to initialize the KPI statistics
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||
$oExtensionInstance->InitStats();
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->ResetCounters();
|
||||
self::Push($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stack executions to remove children duration from stats
|
||||
*
|
||||
* @param \ExecutionKPI $oExecutionKPI
|
||||
*/
|
||||
private static function Push(ExecutionKPI $oExecutionKPI)
|
||||
{
|
||||
array_push(self::$m_aExecutionStack, $oExecutionKPI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop current child and count its duration in its parent
|
||||
*
|
||||
* @param float|int $fChildDuration
|
||||
*/
|
||||
private static function Pop(float $fChildDuration = 0)
|
||||
{
|
||||
array_pop(self::$m_aExecutionStack);
|
||||
// Update the parent's children duration
|
||||
$oPrevExecutionKPI = end(self::$m_aExecutionStack);
|
||||
if ($oPrevExecutionKPI) {
|
||||
$oPrevExecutionKPI->m_fChildrenDuration += $fChildDuration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the duration since startup, and reset the counter for the next measure
|
||||
//
|
||||
@@ -323,9 +340,15 @@ class ExecutionKPI
|
||||
{
|
||||
global $fItopStarted;
|
||||
|
||||
if (!self::IsEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aNewEntry = null;
|
||||
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$fStarted = $this->m_fStarted;
|
||||
$fStopped = $this->m_fStarted;
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$fStopped = MyHelpers::getmicrotime();
|
||||
$aNewEntry = array(
|
||||
'op' => $sOperationDesc,
|
||||
@@ -336,6 +359,9 @@ class ExecutionKPI
|
||||
$this->m_fStarted = $fStopped;
|
||||
}
|
||||
|
||||
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
|
||||
$iCurrentMemory = 0;
|
||||
$iPeakMemory = 0;
|
||||
if (self::$m_bEnabled_Memory)
|
||||
{
|
||||
$iCurrentMemory = self::memory_get_usage();
|
||||
@@ -345,40 +371,103 @@ class ExecutionKPI
|
||||
}
|
||||
$aNewEntry['mem_begin'] = $this->m_iInitialMemory;
|
||||
$aNewEntry['mem_end'] = $iCurrentMemory;
|
||||
if (function_exists('memory_get_peak_usage'))
|
||||
{
|
||||
$aNewEntry['mem_peak'] = memory_get_peak_usage();
|
||||
}
|
||||
$iPeakMemory = self::memory_get_peak_usage();
|
||||
$aNewEntry['mem_peak'] = $iPeakMemory;
|
||||
// Reset for the next operation (if the object is recycled)
|
||||
$this->m_iInitialMemory = $iCurrentMemory;
|
||||
}
|
||||
|
||||
if (!is_null($aNewEntry))
|
||||
if (self::$m_bEnabled_Duration || self::$m_bEnabled_Memory) {
|
||||
// Invoke extensions to log the KPI operation
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance)
|
||||
{
|
||||
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
|
||||
$oKPILogData = new KpiLogData(
|
||||
KpiLogData::TYPE_REPORT,
|
||||
'Step',
|
||||
$sOperationDesc,
|
||||
$fStarted,
|
||||
$fStopped,
|
||||
$sExtension,
|
||||
$iInitialMemory,
|
||||
$iCurrentMemory,
|
||||
$iPeakMemory);
|
||||
$oExtensionInstance->LogOperation($oKPILogData);
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_null($aNewEntry) && self::$m_bGenerateLegacyReport)
|
||||
{
|
||||
self::$m_aExecData[] = $aNewEntry;
|
||||
}
|
||||
$this->ResetCounters();
|
||||
}
|
||||
|
||||
public function ComputeStatsForExtension($object, $sMethod)
|
||||
{
|
||||
if (!self::IsEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sSignature = ModuleService::GetInstance()->GetModuleMethodSignature($object, $sMethod);
|
||||
if (utils::StartsWith($sSignature, '[')) {
|
||||
$this->ComputeStats('Extension', $sSignature);
|
||||
}
|
||||
}
|
||||
|
||||
public function ComputeStats($sOperation, $sArguments)
|
||||
{
|
||||
if (!self::IsEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fDuration = 0;
|
||||
if (self::$m_bEnabled_Duration) {
|
||||
$fStopped = MyHelpers::getmicrotime();
|
||||
$fDuration = $fStopped - $this->m_fStarted;
|
||||
$fSelfDuration = $fDuration - $this->m_fChildrenDuration;
|
||||
if (self::$m_bBlameCaller) {
|
||||
self::$m_aStats[$sOperation][$sArguments][] = array(
|
||||
'time' => $fSelfDuration,
|
||||
'callers' => MyHelpers::get_callstack(1),
|
||||
);
|
||||
} else {
|
||||
self::$m_aStats[$sOperation][$sArguments][] = array(
|
||||
'time' => $fSelfDuration,
|
||||
);
|
||||
}
|
||||
}
|
||||
self::Pop($fDuration);
|
||||
$aCallstack = [];
|
||||
if (self::$m_bGenerateLegacyReport) {
|
||||
if (self::$m_bBlameCaller) {
|
||||
$aCallstack = MyHelpers::get_callstack(1);
|
||||
self::$m_aStats[$sOperation][$sArguments][] = [
|
||||
'time' => $fDuration,
|
||||
'callers' => $aCallstack,
|
||||
];
|
||||
} else {
|
||||
self::$m_aStats[$sOperation][$sArguments][] = [
|
||||
'time' => $fDuration
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
|
||||
$iCurrentMemory = 0;
|
||||
$iPeakMemory = 0;
|
||||
if (self::$m_bEnabled_Memory)
|
||||
{
|
||||
$iCurrentMemory = self::memory_get_usage();
|
||||
$iPeakMemory = self::memory_get_peak_usage();
|
||||
}
|
||||
|
||||
// Invoke extensions to log the KPI operation
|
||||
/** @var \iKPILoggerExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
|
||||
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
|
||||
$oKPILogData = new KpiLogData(
|
||||
KpiLogData::TYPE_STATS,
|
||||
$sOperation,
|
||||
$sArguments,
|
||||
$this->m_fStarted,
|
||||
$fStopped,
|
||||
$sExtension,
|
||||
$iInitialMemory,
|
||||
$iCurrentMemory,
|
||||
$iPeakMemory,
|
||||
$aCallstack);
|
||||
$oExtensionInstance->LogOperation($oKPILogData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function ResetCounters()
|
||||
@@ -408,35 +497,7 @@ class ExecutionKPI
|
||||
|
||||
static protected function memory_get_usage()
|
||||
{
|
||||
if (function_exists('memory_get_usage'))
|
||||
{
|
||||
return memory_get_usage(true);
|
||||
}
|
||||
|
||||
// Copied from the PHP manual
|
||||
//
|
||||
//If its Windows
|
||||
//Tested on Win XP Pro SP2. Should work on Win 2003 Server too
|
||||
//Doesn't work for 2000
|
||||
//If you need it to work for 2000 look at http://us2.php.net/manual/en/function.memory-get-usage.php#54642
|
||||
if (substr(PHP_OS,0,3) == 'WIN')
|
||||
{
|
||||
$output = array();
|
||||
exec('tasklist /FI "PID eq ' . getmypid() . '" /FO LIST', $output);
|
||||
|
||||
return preg_replace( '/[\D]/', '', $output[5] ) * 1024;
|
||||
}
|
||||
else
|
||||
{
|
||||
//We now assume the OS is UNIX
|
||||
//Tested on Mac OS X 10.4.6 and Linux Red Hat Enterprise 4
|
||||
//This should work on most UNIX systems
|
||||
$pid = getmypid();
|
||||
exec("ps -eo%mem,rss,pid | grep $pid", $output);
|
||||
$output = explode(" ", $output[0]);
|
||||
//rss is given in 1024 byte units
|
||||
return $output[1] * 1024;
|
||||
}
|
||||
return memory_get_usage(true);
|
||||
}
|
||||
|
||||
static public function memory_get_peak_usage($bRealUsage = false)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2021 Combodo SARL
|
||||
// Copyright (C) 2010-2023 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// 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.
|
||||
@@ -448,7 +448,7 @@ class LogFileNameBuilderFactory
|
||||
/**
|
||||
* File logging
|
||||
*
|
||||
* @copyright Copyright (C) 2010-2021 Combodo SARL
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
* @since 2.7.0 N°2518 N°2793 file log rotation
|
||||
*/
|
||||
@@ -961,7 +961,9 @@ class ToolsLog extends LogAPI
|
||||
|
||||
/**
|
||||
* @see \CMDBSource::LogDeadLock()
|
||||
* @since 2.7.1
|
||||
* @since 2.7.1 PR #139
|
||||
*
|
||||
* @link https://dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks.html
|
||||
*/
|
||||
class DeadLockLog extends LogAPI
|
||||
{
|
||||
@@ -986,10 +988,10 @@ class DeadLockLog extends LogAPI
|
||||
{
|
||||
switch ($iMysqlErrorNo)
|
||||
{
|
||||
case 1205:
|
||||
case CMDBSource::MYSQL_ERRNO_WAIT_TIMEOUT:
|
||||
return self::CHANNEL_WAIT_TIMEOUT;
|
||||
break;
|
||||
case 1213:
|
||||
case CMDBSource::MYSQL_ERRNO_DEADLOCK:
|
||||
return self::CHANNEL_DEADLOCK_FOUND;
|
||||
break;
|
||||
default:
|
||||
@@ -1019,7 +1021,14 @@ class DeadLockLog extends LogAPI
|
||||
|
||||
|
||||
/**
|
||||
* @since 3.0.0 N°3731
|
||||
* Starting with the WARNING level we will log in a dedicated file (/log/deprecated-calls.log) :
|
||||
* - iTop deprecated files or code
|
||||
* - protected trigger_error calls with E_DEPRECATED or E_USER_DEPRECATED
|
||||
*
|
||||
* For the last category, if {@see utils::IsDevelopmentEnvironment()} is true we will do a trigger_error()
|
||||
*
|
||||
* @since 3.0.0 N°3731 first implementation
|
||||
* @link https://www.itophub.io/wiki/page?id=latest:admin:log:channels#deprecated_calls channel used
|
||||
*/
|
||||
class DeprecatedCallsLog extends LogAPI
|
||||
{
|
||||
@@ -1062,16 +1071,23 @@ class DeprecatedCallsLog extends LogAPI
|
||||
* @uses \set_error_handler() to catch deprecated notices
|
||||
*
|
||||
* @since 3.0.0 N°3002 logs deprecated notices in called code
|
||||
* @since 3.0.4 N°6274 do not set handler when in PHPUnit context (otherwise PHP notices won't be caught)
|
||||
*/
|
||||
public static function Enable($sTargetFile = null): void
|
||||
{
|
||||
public static function Enable($sTargetFile = null): void {
|
||||
if (empty($sTargetFile)) {
|
||||
$sTargetFile = APPROOT.'log/deprecated-calls.log';
|
||||
}
|
||||
parent::Enable($sTargetFile);
|
||||
|
||||
if (static::IsLogLevelEnabledSafe(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD)) {
|
||||
set_error_handler([static::class, 'DeprecatedNoticesErrorHandler']);
|
||||
if (
|
||||
(
|
||||
(false === defined(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME))
|
||||
|| (defined(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME) && (constant(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME) !== true))
|
||||
)
|
||||
&& static::IsLogLevelEnabledSafe(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD)
|
||||
) {
|
||||
IssueLog::Trace('Setting '.static::class.' error handler to catch DEPRECATED', static::ENUM_CHANNEL_PHP_LIBMETHOD);
|
||||
set_error_handler([static::class, 'DeprecatedNoticesErrorHandler'], E_DEPRECATED | E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1168,7 +1184,9 @@ class DeprecatedCallsLog extends LogAPI
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \ConfigException
|
||||
* @since 3.0.1 3.1.0 N°4725 silently handles ConfigException
|
||||
* @since 3.0.4 3.1.0 N°4725 remove forgotten throw PHPDoc annotation
|
||||
*
|
||||
* @link https://www.php.net/debug_backtrace
|
||||
* @uses \debug_backtrace()
|
||||
*/
|
||||
@@ -1220,19 +1238,7 @@ class DeprecatedCallsLog extends LogAPI
|
||||
}
|
||||
|
||||
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
|
||||
$iStackDeprecatedMethodLevel = 1; // level 0 = current method, level 1 = method containing the `NotifyDeprecatedPhpMethod` call
|
||||
$sDeprecatedObject = $aStack[$iStackDeprecatedMethodLevel]['class'];
|
||||
$sDeprecatedMethod = $aStack[$iStackDeprecatedMethodLevel]['function'];
|
||||
$sCallerFile = $aStack[$iStackDeprecatedMethodLevel]['file'];
|
||||
$sCallerLine = $aStack[$iStackDeprecatedMethodLevel]['line'];
|
||||
$sMessage = "Call to {$sDeprecatedObject}::{$sDeprecatedMethod} in {$sCallerFile}#L{$sCallerLine}";
|
||||
|
||||
$iStackCallerMethodLevel = $iStackDeprecatedMethodLevel + 1; // level 2 = caller of the deprecated method
|
||||
if (array_key_exists($iStackCallerMethodLevel, $aStack)) {
|
||||
$sCallerObject = $aStack[$iStackCallerMethodLevel]['class'];
|
||||
$sCallerMethod = $aStack[$iStackCallerMethodLevel]['function'];
|
||||
$sMessage .= " ({$sCallerObject}::{$sCallerMethod})";
|
||||
}
|
||||
$sMessage = self::GetMessageFromStack($aStack);
|
||||
|
||||
if (!is_null($sAdditionalMessage)) {
|
||||
$sMessage .= ' : '.$sAdditionalMessage;
|
||||
@@ -1241,6 +1247,44 @@ class DeprecatedCallsLog extends LogAPI
|
||||
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_METHOD);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $aDebugBacktrace data from {@see debug_backtrace()}
|
||||
* @return string message to print to the log
|
||||
*/
|
||||
private static function GetMessageFromStack(array $aDebugBacktrace): string
|
||||
{
|
||||
// level 0 = current method
|
||||
// level 1 = deprecated method, containing the `NotifyDeprecatedPhpMethod` call
|
||||
$sMessage = 'Call' . self::GetMessageForCurrentStackLevel($aDebugBacktrace[1], " to ");
|
||||
|
||||
// level 2 = caller of the deprecated method
|
||||
if (array_key_exists(2, $aDebugBacktrace)) {
|
||||
$sMessage .= ' (from ';
|
||||
$sMessage .= self::GetMessageForCurrentStackLevel($aDebugBacktrace[2]);
|
||||
$sMessage .= ')';
|
||||
}
|
||||
|
||||
return $sMessage;
|
||||
}
|
||||
|
||||
private static function GetMessageForCurrentStackLevel(array $aCurrentLevelDebugTrace, ?string $sPrefix=""): string
|
||||
{
|
||||
$sMessage = "";
|
||||
if (array_key_exists('class', $aCurrentLevelDebugTrace)) {
|
||||
$sDeprecatedObject = $aCurrentLevelDebugTrace['class'];
|
||||
$sDeprecatedMethod = $aCurrentLevelDebugTrace['function'] ?? "";
|
||||
$sMessage = "{$sPrefix}{$sDeprecatedObject}::{$sDeprecatedMethod} in ";
|
||||
}
|
||||
|
||||
if (array_key_exists('file', $aCurrentLevelDebugTrace)) {
|
||||
$sCallerFile = $aCurrentLevelDebugTrace['file'];
|
||||
$sCallerLine = $aCurrentLevelDebugTrace['line'] ?? "";
|
||||
$sMessage .= "{$sCallerFile}#L{$sCallerLine}";
|
||||
}
|
||||
|
||||
return $sMessage;
|
||||
}
|
||||
|
||||
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array()): void
|
||||
{
|
||||
if (true === utils::IsDevelopmentEnvironment()) {
|
||||
@@ -1497,6 +1541,8 @@ class ExceptionLog extends LogAPI
|
||||
*/
|
||||
private static function GetLastEventIssue()
|
||||
{
|
||||
return self::$oLastEventIssue;
|
||||
$oRet = self::$oLastEventIssue;
|
||||
self::$oLastEventIssue = null;
|
||||
return $oRet;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1430,8 +1430,10 @@ abstract class MetaModel
|
||||
*
|
||||
* @return AttributeDefinition[]
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @see GetAttributesList for attcode list
|
||||
*/
|
||||
final static public function ListAttributeDefs($sClass)
|
||||
final public static function ListAttributeDefs($sClass)
|
||||
{
|
||||
self::_check_subclass($sClass);
|
||||
return self::$m_aAttribDefs[$sClass];
|
||||
@@ -1444,8 +1446,10 @@ abstract class MetaModel
|
||||
* @param string[] $aDesiredAttTypes Array of AttributeDefinition classes to filter the list on
|
||||
* @param string|null $sListCode If provided, attributes will be limited to those in this zlist
|
||||
*
|
||||
* @return array
|
||||
* @return string[] list of attcodes
|
||||
* @throws \CoreException
|
||||
*
|
||||
* @see ListAttributeDefs to get AttributeDefinition array instead
|
||||
*/
|
||||
final public static function GetAttributesList(string $sClass, array $aDesiredAttTypes = [], ?string $sListCode = null)
|
||||
{
|
||||
@@ -3002,14 +3006,12 @@ abstract class MetaModel
|
||||
// Build the list of available extensions
|
||||
//
|
||||
$aInterfaces = [
|
||||
'iApplicationUIExtension',
|
||||
'iPreferencesExtension',
|
||||
'iApplicationObjectExtension',
|
||||
'iLoginFSMExtension',
|
||||
'iLoginUIExtension',
|
||||
'iLogoutExtension',
|
||||
'iQueryModifier',
|
||||
'iOnClassInitialization',
|
||||
'iLoginUIExtension',
|
||||
'iPreferencesExtension',
|
||||
'iApplicationUIExtension',
|
||||
'iApplicationObjectExtension',
|
||||
'iPopupMenuExtension',
|
||||
'iPageUIExtension',
|
||||
'iPageUIBlockExtension',
|
||||
@@ -3023,9 +3025,12 @@ abstract class MetaModel
|
||||
'iBackofficeDictEntriesExtension',
|
||||
'iBackofficeDictEntriesPrefixesExtension',
|
||||
'iPortalUIExtension',
|
||||
'iQueryModifier',
|
||||
'iOnClassInitialization',
|
||||
'iModuleExtension',
|
||||
'iKPILoggerExtension',
|
||||
'ModuleHandlerApiInterface',
|
||||
'iNewsroomProvider',
|
||||
'iModuleExtension',
|
||||
];
|
||||
foreach($aInterfaces as $sInterface)
|
||||
{
|
||||
@@ -6511,6 +6516,13 @@ abstract class MetaModel
|
||||
*/
|
||||
public static function Startup($config, $bModelOnly = false, $bAllowCache = true, $bTraceSourceFiles = false, $sEnvironment = 'production')
|
||||
{
|
||||
// Startup on a new environment is not supported
|
||||
static $bStarted = false;
|
||||
if ($bStarted) {
|
||||
return;
|
||||
}
|
||||
$bStarted = true;
|
||||
|
||||
self::$m_sEnvironment = $sEnvironment;
|
||||
|
||||
if (!defined('MODULESROOT'))
|
||||
@@ -6588,7 +6600,9 @@ abstract class MetaModel
|
||||
|
||||
ExecutionKPI::EnableDuration(self::$m_oConfig->Get('log_kpi_duration'));
|
||||
ExecutionKPI::EnableMemory(self::$m_oConfig->Get('log_kpi_memory'));
|
||||
ExecutionKPI::SetAllowedUser(self::$m_oConfig->Get('log_kpi_user_id'));
|
||||
ExecutionKPI::SetAllowedUser(self::$m_oConfig->Get('log_kpi_user_id'));
|
||||
ExecutionKPI::SetGenerateLegacyReport(self::$m_oConfig->Get('log_kpi_generate_legacy_report'));
|
||||
ExecutionKPI::SetSlowQueries(self::$m_oConfig->Get('log_kpi_slow_queries'));
|
||||
|
||||
self::$m_bSkipCheckToWrite = self::$m_oConfig->Get('skip_check_to_write');
|
||||
self::$m_bSkipCheckExtKeys = self::$m_oConfig->Get('skip_check_ext_keys');
|
||||
@@ -6713,6 +6727,7 @@ abstract class MetaModel
|
||||
|
||||
CMDBSource::InitFromConfig(self::$m_oConfig);
|
||||
// Later when timezone implementation is correctly done: CMDBSource::SetTimezone($sDBTimezone);
|
||||
ExecutionKPI::InitStats();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6744,6 +6759,19 @@ abstract class MetaModel
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Used for resetting the configuration during automated tests
|
||||
|
||||
* @param \Config $oConfiguration
|
||||
*
|
||||
* @return void
|
||||
* @since 3.0.4 3.1.1 3.2.0
|
||||
*/
|
||||
public static function SetConfig(Config $oConfiguration)
|
||||
{
|
||||
self::$m_oConfig = $oConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Config
|
||||
*/
|
||||
@@ -6932,7 +6960,13 @@ abstract class MetaModel
|
||||
|
||||
if ($bMustBeFound && empty($aRow))
|
||||
{
|
||||
throw new CoreException("No result for the single row query: '$sSQL'");
|
||||
$sNotFoundErrorMessage = "No result for the single row query";
|
||||
IssueLog::Info($sNotFoundErrorMessage, LogChannels::CMDB_SOURCE, [
|
||||
'class' => $sClass,
|
||||
'key' => $iKey,
|
||||
'sql_query' => $sSQL,
|
||||
]);
|
||||
throw new CoreException($sNotFoundErrorMessage);
|
||||
}
|
||||
|
||||
return $aRow;
|
||||
@@ -7012,25 +7046,21 @@ abstract class MetaModel
|
||||
* $bMustBeFound=false)
|
||||
* @throws CoreException if no result found and $bMustBeFound=true
|
||||
* @throws ArchivedObjectException if archive mode disabled and result is archived and $bMustBeFound=true
|
||||
* @throws \Exception
|
||||
*
|
||||
*/
|
||||
public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null)
|
||||
{
|
||||
$oObject = self::GetObjectWithArchive($sClass, $iKey, $bMustBeFound, $bAllowAllData, $aModifierProperties);
|
||||
|
||||
if (empty($oObject))
|
||||
{
|
||||
if (empty($oObject)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!utils::IsArchiveMode() && $oObject->IsArchived())
|
||||
{
|
||||
if (!utils::IsArchiveMode() && $oObject->IsArchived()) {
|
||||
if ($bMustBeFound) {
|
||||
throw new ArchivedObjectException("The object $sClass::$iKey is archived");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $oObject;
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
* A class to serialize the execution of some code sections
|
||||
* Emulates the API of PECL Mutex class
|
||||
* Relies on MySQL locks because the API sem_get is not always present in the
|
||||
* installed PHP.
|
||||
* installed PHP.
|
||||
*
|
||||
* @link https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html MySQL locking functions documentation
|
||||
*
|
||||
* @copyright Copyright (C) 2013-2021 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
@@ -257,7 +259,7 @@ class iTopMutex
|
||||
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
|
||||
|
||||
if (!$this->hDBLink) {
|
||||
throw new Exception("Could not connect to the DB server (host=$sServer, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
|
||||
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
|
||||
}
|
||||
|
||||
// Make sure that the server variable `wait_timeout` is at least 86400 seconds for this connection,
|
||||
|
||||
@@ -537,7 +537,7 @@ EOF
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception('graphviz not found (executable path: '.$sDotExecutable.')');
|
||||
throw new Exception('graphviz not found');
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
@@ -592,7 +592,7 @@ EOF
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception('graphviz not found (executable path: '.$sDotExecutable.')');
|
||||
throw new Exception('graphviz not found');
|
||||
}
|
||||
return $sHtml;
|
||||
}
|
||||
|
||||
@@ -117,7 +117,9 @@ abstract class Trigger extends cmdbAbstractObject
|
||||
$oAction = MetaModel::GetObject('Action', $iActionId);
|
||||
if ($oAction->IsActive())
|
||||
{
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oAction->DoExecute($this, $aContextArgs);
|
||||
$oKPI->ComputeStatsForExtension($oAction, 'DoExecute');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ abstract class UserRightsAddOnAPI
|
||||
$oSearchSharers->AllowAllData();
|
||||
$oSearchSharers->AddCondition_ReferencedBy($oShareSearch, 'sharing_org_id');
|
||||
$aSharers = array();
|
||||
foreach($oSearchSharers->ToDataArray(array('id')) as $aRow)
|
||||
foreach($oSearchSharers->SelectAttributeToArray('id') as $aRow)
|
||||
{
|
||||
$aSharers[] = $aRow['id'];
|
||||
}
|
||||
@@ -186,7 +186,7 @@ abstract class UserRightsAddOnAPI
|
||||
$oOrgField = new FieldExpression('org_id', $sShareClass);
|
||||
$oSearchShares->AddConditionExpression(new BinaryExpression($oOrgField, 'IN', $oListExpr));
|
||||
$aShared = array();
|
||||
foreach($oSearchShares->ToDataArray(array($sShareAttCode)) as $aRow)
|
||||
foreach($oSearchShares->SelectAttributeToArray($sShareAttCode) as $aRow)
|
||||
{
|
||||
$aShared[] = $aRow[$sShareAttCode];
|
||||
}
|
||||
@@ -750,14 +750,25 @@ class UserRights
|
||||
protected static $m_aCacheContactPictureAbsUrl = [];
|
||||
/** @var UserRightsAddOnAPI $m_oAddOn */
|
||||
protected static $m_oAddOn;
|
||||
protected static $m_oUser;
|
||||
protected static $m_oRealUser;
|
||||
protected static $m_oUser = null;
|
||||
protected static $m_oRealUser = null;
|
||||
protected static $m_sSelfRegisterAddOn = null;
|
||||
protected static $m_aAdmins = array();
|
||||
protected static $m_aPortalUsers = array();
|
||||
/** @var array array('sName' => $sName, 'bSuccess' => $bSuccess); */
|
||||
private static $m_sLastLoginStatus = null;
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @since 3.0.4 3.1.1 3.2.0
|
||||
*/
|
||||
protected static function ResetCurrentUserData()
|
||||
{
|
||||
self::$m_oUser = null;
|
||||
self::$m_oRealUser = null;
|
||||
self::$m_sLastLoginStatus = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sModuleName
|
||||
*
|
||||
@@ -776,8 +787,7 @@ class UserRights
|
||||
}
|
||||
self::$m_oAddOn = new $sModuleName;
|
||||
self::$m_oAddOn->Init();
|
||||
self::$m_oUser = null;
|
||||
self::$m_oRealUser = null;
|
||||
self::ResetCurrentUserData();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -835,6 +845,8 @@ class UserRights
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current user (as part of the login process)
|
||||
*
|
||||
* @param string $sLogin Login of the concerned user
|
||||
* @param string $sAuthentication
|
||||
*
|
||||
@@ -861,6 +873,19 @@ class UserRights
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset current user and cleanup associated SESSION data
|
||||
*
|
||||
* @return void
|
||||
* @since 3.0.4 3.1.1 3.2.0
|
||||
*/
|
||||
public static function Logoff()
|
||||
{
|
||||
self::ResetCurrentUserData();
|
||||
Dict::SetUserLanguage(null);
|
||||
self::_ResetSessionCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sLogin Login of the user to check the credentials for
|
||||
* @param string $sPassword
|
||||
@@ -1085,9 +1110,7 @@ class UserRights
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current user login or an empty string if nobody connected.
|
||||
*
|
||||
* @return string
|
||||
* @return string connected {@see User} login field value, otherwise empty string
|
||||
*/
|
||||
public static function GetUser()
|
||||
{
|
||||
@@ -1535,9 +1558,9 @@ class UserRights
|
||||
|
||||
/**
|
||||
* @param string $sClass
|
||||
* @param int $iActionCode
|
||||
* @param \DBObjectSet $oInstanceSet
|
||||
* @param \User $oUser
|
||||
* @param int $iActionCode see UR_ACTION_* constants
|
||||
* @param DBObjectSet $oInstanceSet
|
||||
* @param User $oUser
|
||||
*
|
||||
* @return int (UR_ALLOWED_YES|UR_ALLOWED_NO|UR_ALLOWED_DEPENDS)
|
||||
* @throws \CoreException
|
||||
|
||||
@@ -6,10 +6,6 @@
|
||||
$ibo-attachment--datatable--icon-preview--max-height: 44px !default;
|
||||
$ibo-attachment--datatable--icon-preview--max-width: $ibo-attachment--datatable--icon-preview--max-height !default;
|
||||
|
||||
$ibo-attachment--datatable--line-height: $ibo-attachment--datatable--icon-preview--max-height !default;
|
||||
|
||||
$ibo-attachment--datatable--first-column--line-height: 0px !default;
|
||||
|
||||
$ibo-attachment--drag-in--border: 2px $ibo-color-grey-400 dashed !default;
|
||||
$ibo-attachment--upload-file--drop-zone-hint--max-height: 200px !default;
|
||||
$ibo-attachment--upload-file--drop-zone-hint--margin: 22px $ibo-spacing-0 !default;
|
||||
@@ -33,10 +29,7 @@ $ibo-attachment--tab-header--drop-in--icon--color: $ibo-color-blue-600 !default;
|
||||
max-width: $ibo-attachment--datatable--icon-preview--max-width;
|
||||
}
|
||||
.ibo-attachment--datatable tbody tr td {
|
||||
line-height: $ibo-attachment--datatable--line-height;
|
||||
}
|
||||
.ibo-attachment--datatable tbody tr td:nth-child(1){
|
||||
line-height: $ibo-attachment--datatable--first-column--line-height;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.ibo-attachment--upload-file--drop-zone-hint{
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -58,6 +58,9 @@ $progress-bar-error-bg-color: #F56565 !default;
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes progress_bar_color_ongoing {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
||||
'CAS:Error:UserNotAllowed' => 'User not allowed~~',
|
||||
'CAS:Login:SignIn' => 'Sign in with CAS~~',
|
||||
'CAS:Login:SignInTooltip' => 'Click here to authenticate yourself with the CAS server~~',
|
||||
'CAS:Error:UserNotAllowed' => 'Nem engedélyezett felhasználó',
|
||||
'CAS:Login:SignIn' => 'Bejelentkezés CAS szerverrel',
|
||||
'CAS:Login:SignInTooltip' => 'Kattintson ide az azonosításhoz a CAS szerveren',
|
||||
));
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'authent-cas/3.0.3',
|
||||
'authent-cas/3.0.4',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -49,6 +49,11 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
|
||||
protected function OnReadCredentials(&$iErrorCode)
|
||||
{
|
||||
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
|
||||
// Not allowed if not already connected
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
|
||||
if (empty(Session::Get('login_mode')) || Session::Get('login_mode') == static::LOGIN_MODE)
|
||||
{
|
||||
static::InitCASClient();
|
||||
@@ -114,6 +119,10 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
|
||||
if (Session::Get('login_mode') == static::LOGIN_MODE)
|
||||
{
|
||||
Session::Unset('phpCAS');
|
||||
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
|
||||
// don't display the login page
|
||||
return LoginWebPage::LOGIN_FSM_CONTINUE;
|
||||
}
|
||||
if ($iErrorCode != LoginWebPage::EXIT_CODE_MISSINGLOGIN)
|
||||
{
|
||||
$oLoginWebPage = new LoginWebPage();
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'authent-external/3.0.3',
|
||||
'authent-external/3.0.4',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -21,5 +21,5 @@
|
||||
*/
|
||||
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
||||
'Class:UserLDAP' => 'LDAP felhasználó',
|
||||
'Class:UserLDAP+' => '',
|
||||
'Class:UserLDAP+' => 'LDAP vagy AD felhasználó',
|
||||
));
|
||||
|
||||
@@ -9,7 +9,7 @@ if (function_exists('ldap_connect'))
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'authent-ldap/3.0.3',
|
||||
'authent-ldap/3.0.4',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -51,7 +51,7 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire' => 'One-time Password~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
|
||||
@@ -36,7 +36,7 @@ Dict::Add('DA DA', 'Danish', 'Dansk', array(
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire' => 'One-time Password~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
|
||||
@@ -20,26 +20,26 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
||||
'Class:UserLocal' => ITOP_APPLICATION_SHORT.' felhasználó',
|
||||
'Class:UserLocal+' => '',
|
||||
'Class:UserLocal/Attribute:password' => 'Jelszó',
|
||||
'Class:UserLocal/Attribute:password+' => '',
|
||||
'Class:UserLocal' => ITOP_APPLICATION_SHORT.' felhasználó',
|
||||
'Class:UserLocal+' => 'Rendszeren belül létrehozott felhasználó',
|
||||
'Class:UserLocal/Attribute:password' => 'Jelszó',
|
||||
'Class:UserLocal/Attribute:password+' => '',
|
||||
|
||||
'Class:UserLocal/Attribute:expiration' => 'Password expiration~~',
|
||||
'Class:UserLocal/Attribute:expiration+' => 'Password expiration status (requires an extension to have an effect)~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Can expire~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:can_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:never_expire' => 'Never expire~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'Expired~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire' => 'One-time Password~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
'Class:UserLocal/Attribute:expiration' => 'Jelszó lejárati ideje',
|
||||
'Class:UserLocal/Attribute:expiration+' => 'Jelszó lejárati státusz (bővítmény szükséges hozzá)',
|
||||
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Lejár',
|
||||
'Class:UserLocal/Attribute:expiration/Value:can_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:never_expire' => 'Soha nem jár le',
|
||||
'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'Lejárt',
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire' => 'Egyszeri jelszó',
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'A felhasználó nem változtathat jelszót.',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Jelszó megújítás ideje',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'A jelszó legutóbbi módosításának időpontja',
|
||||
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'A jelszónak legalább 8 karakterből kell állnia, és tartalmaznia kell nagybetűket, kisbetűket, numerikus és speciális karaktereket.',
|
||||
|
||||
'UserLocal:password:expiration' => 'The fields below require an extension~~',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'Setting password expiration to "One-time password" is not allowed for your own User~~',
|
||||
'UserLocal:password:expiration' => 'Az alábbi mezőkhöz egy bővítmény szükséges',
|
||||
'Class:UserLocal/Error:OneTimePasswordChangeIsNotAllowed' => 'A jelszó lejárati idejének beállítása "Egyszeri jelszóra" nem engedélyezett a saját felhasználó számára.',
|
||||
));
|
||||
|
||||
@@ -49,7 +49,7 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire' => 'One-time Password~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
|
||||
@@ -36,7 +36,7 @@ Dict::Add('JA JP', 'Japanese', '日本語', array(
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire' => 'One-time Password~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
|
||||
@@ -48,7 +48,7 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire' => 'One-time Password~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
|
||||
@@ -50,7 +50,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
|
||||
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire' => 'One-time Password~~',
|
||||
'Class:UserLocal/Attribute:expiration/Value:otp_expire+' => 'Password cannot be changed by the user.~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewed on~~',
|
||||
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
|
||||
|
||||
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// 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.
|
||||
@@ -69,7 +69,7 @@ class UserLocal extends UserInternal
|
||||
const EXPIRE_NEVER = 'never_expire';
|
||||
const EXPIRE_FORCE = 'force_expire';
|
||||
const EXPIRE_ONE_TIME_PWD = 'otp_expire';
|
||||
|
||||
|
||||
/** @var UserLocalPasswordValidity|null */
|
||||
protected $m_oPasswordValidity = null;
|
||||
|
||||
@@ -160,7 +160,7 @@ class UserLocal extends UserInternal
|
||||
|
||||
/**
|
||||
* Use with care!
|
||||
*/
|
||||
*/
|
||||
public function SetPassword($sNewPassword)
|
||||
{
|
||||
$this->Set('password', $sNewPassword);
|
||||
@@ -197,19 +197,39 @@ class UserLocal extends UserInternal
|
||||
|
||||
protected function OnWrite()
|
||||
{
|
||||
if (empty($this->m_oPasswordValidity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (array_key_exists('password_renewed_date', $this->ListChanges()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($this->m_oPasswordValidity))
|
||||
{
|
||||
//password unchanged
|
||||
if (is_null($this->Get('password_renewed_date')))
|
||||
{
|
||||
//initialize password_renewed_date with User creation date
|
||||
$sKey = $this->GetKey();
|
||||
$sOql = <<<OQL
|
||||
SELECT CMDBChangeOpCreate AS ccc
|
||||
JOIN CMDBChange AS c ON ccc.change = c.id
|
||||
WHERE ccc.objclass="UserLocal" AND ccc.objkey="$sKey"
|
||||
OQL;
|
||||
$oCmdbChangeOpSearch = \DBObjectSearch::FromOQL($sOql);
|
||||
$oSet = new \DBObjectSet($oCmdbChangeOpSearch);
|
||||
$oCMDBChangeOpCreate = $oSet->Fetch();
|
||||
if (! is_null($oCMDBChangeOpCreate))
|
||||
{
|
||||
$oUserCreationDateTime = \DateTime::createFromFormat(AttributeDateTime::GetInternalFormat(), $oCMDBChangeOpCreate->Get('date'));
|
||||
$sCreationDate = $oUserCreationDateTime->format(\AttributeDate::GetInternalFormat());
|
||||
$this->Set('password_renewed_date', $sCreationDate);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$sNow = date(\AttributeDate::GetInternalFormat());
|
||||
$this->Set('password_renewed_date', $sNow);
|
||||
|
||||
|
||||
// Reset the "force" expiration flag when the user updates her/his own password!
|
||||
if ($this->IsCurrentUser())
|
||||
{
|
||||
@@ -294,7 +314,7 @@ class UserLocal extends UserInternal
|
||||
{
|
||||
$this->m_aCheckIssues[] = $this->m_oPasswordValidity->getPasswordValidityMessage();
|
||||
}
|
||||
|
||||
|
||||
// A User cannot force a one-time password on herself/himself
|
||||
if ($this->IsCurrentUser()) {
|
||||
if (array_key_exists('expiration', $this->ListChanges()) && ($this->Get('expiration') == self::EXPIRE_ONE_TIME_PWD)) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'authent-local/3.0.3',
|
||||
'authent-local/3.0.4',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -22,5 +22,5 @@
|
||||
*/
|
||||
|
||||
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
||||
'theme:darkmoon' => 'Dark moon~~',
|
||||
));
|
||||
'theme:darkmoon' => 'Dark moon',
|
||||
));
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'combodo-backoffice-darkmoon-theme/3.0.3',
|
||||
'combodo-backoffice-darkmoon-theme/3.0.4',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
// Database inconsistencies
|
||||
Dict::Add('CS CZ', 'Czech', 'Čeština', array(
|
||||
// Dictionary entries go here
|
||||
'Menu:DBToolsMenu' => 'DB Tools~~',
|
||||
'Menu:DBToolsMenu' => 'Database integrity~~',
|
||||
'DBTools:Class' => 'Class~~',
|
||||
'DBTools:Title' => 'Database Maintenance Tools~~',
|
||||
'DBTools:Title' => 'Database integrity check~~',
|
||||
'DBTools:ErrorsFound' => 'Errors Found~~',
|
||||
'DBTools:Indication' => 'Important: after fixing errors in the database you\'ll have to run the analysis again as new inconsistencies will be generated~~',
|
||||
'DBTools:Disclaimer' => 'DISCLAIMER: BACKUP YOUR DATABASE BEFORE RUNNING THE FIXES~~',
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
// Database inconsistencies
|
||||
Dict::Add('DA DA', 'Danish', 'Dansk', array(
|
||||
// Dictionary entries go here
|
||||
'Menu:DBToolsMenu' => 'DB Tools~~',
|
||||
'Menu:DBToolsMenu' => 'Database integrity~~',
|
||||
'DBTools:Class' => 'Class~~',
|
||||
'DBTools:Title' => 'Database Maintenance Tools~~',
|
||||
'DBTools:Title' => 'Database integrity check~~',
|
||||
'DBTools:ErrorsFound' => 'Errors Found~~',
|
||||
'DBTools:Indication' => 'Important: after fixing errors in the database you\'ll have to run the analysis again as new inconsistencies will be generated~~',
|
||||
'DBTools:Disclaimer' => 'DISCLAIMER: BACKUP YOUR DATABASE BEFORE RUNNING THE FIXES~~',
|
||||
|
||||
@@ -22,73 +22,73 @@
|
||||
*/
|
||||
// Database inconsistencies
|
||||
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
||||
// Dictionary entries go here
|
||||
'Menu:DBToolsMenu' => 'DB Tools~~',
|
||||
'DBTools:Class' => 'Class~~',
|
||||
'DBTools:Title' => 'Database Maintenance Tools~~',
|
||||
'DBTools:ErrorsFound' => 'Errors Found~~',
|
||||
'DBTools:Indication' => 'Important: after fixing errors in the database you\'ll have to run the analysis again as new inconsistencies will be generated~~',
|
||||
'DBTools:Disclaimer' => 'DISCLAIMER: BACKUP YOUR DATABASE BEFORE RUNNING THE FIXES~~',
|
||||
'DBTools:Error' => 'Error~~',
|
||||
'DBTools:Count' => 'Count~~',
|
||||
'DBTools:SQLquery' => 'SQL query~~',
|
||||
'DBTools:FixitSQLquery' => 'SQL query To Fix it (indication)~~',
|
||||
'DBTools:SQLresult' => 'SQL result~~',
|
||||
'DBTools:NoError' => 'The database is OK~~',
|
||||
'DBTools:HideIds' => 'Error List~~',
|
||||
'DBTools:ShowIds' => 'Detailed view~~',
|
||||
'DBTools:ShowReport' => 'Report~~',
|
||||
'DBTools:IntegrityCheck' => 'Integrity check~~',
|
||||
'DBTools:FetchCheck' => 'Fetch Check (long)~~',
|
||||
'DBTools:SelectAnalysisType' => 'Select analysis type~~',
|
||||
// Dictionary entries go here
|
||||
'Menu:DBToolsMenu' => 'DB eszközök',
|
||||
'DBTools:Class' => 'Osztály',
|
||||
'DBTools:Title' => 'Adatbázis karbantartó eszközök',
|
||||
'DBTools:ErrorsFound' => 'Hibák vannak',
|
||||
'DBTools:Indication' => 'Fontos: az adatbázisban lévő hibák kijavítása után újra kell futtatni az elemzést, mivel új következetlenségek keletkezhetnek.',
|
||||
'DBTools:Disclaimer' => 'A JAVÍTÁSOK FUTTATÁSA ELŐTT MINDIG KÉSZÍTSEN BIZTONSÁGI MENTÉST AZ ADATBÁZISÁRÓL.',
|
||||
'DBTools:Error' => 'Hiba',
|
||||
'DBTools:Count' => 'Sorszám',
|
||||
'DBTools:SQLquery' => 'SQL lekérdezés',
|
||||
'DBTools:FixitSQLquery' => 'SQL lekérdezés To Fix it (indication)',
|
||||
'DBTools:SQLresult' => 'SQL eredmény',
|
||||
'DBTools:NoError' => 'Az adatbázis OK',
|
||||
'DBTools:HideIds' => 'Hibalista',
|
||||
'DBTools:ShowIds' => 'Részletes nézet',
|
||||
'DBTools:ShowReport' => 'Jelentés',
|
||||
'DBTools:IntegrityCheck' => 'Integritás ellenőrzés',
|
||||
'DBTools:FetchCheck' => 'Lehívás ellenőrzés (hosszú)',
|
||||
'DBTools:SelectAnalysisType' => 'Válasszon elemzés típust',
|
||||
|
||||
'DBTools:Analyze' => 'Analyze~~',
|
||||
'DBTools:Details' => 'Show Details~~',
|
||||
'DBTools:ShowAll' => 'Show All Errors~~',
|
||||
'DBTools:Analyze' => 'Elemzés',
|
||||
'DBTools:Details' => 'Részletek megjelenítése',
|
||||
'DBTools:ShowAll' => 'Minden hiba megjelenítése',
|
||||
|
||||
'DBTools:Inconsistencies' => 'Database inconsistencies~~',
|
||||
'DBTools:DetailedErrorTitle' => '%2$s error(s) in class %1$s: %3$s~~',
|
||||
'DBTools:Inconsistencies' => 'Adatbázis inkonzisztenciák',
|
||||
'DBTools:DetailedErrorTitle' => '%2$s hiba a %1$s osztályban: %3$s',
|
||||
|
||||
'DBAnalyzer-Integrity-OrphanRecord' => 'Orphan record in `%1$s`, it should have its counterpart in table `%2$s`~~',
|
||||
'DBAnalyzer-Integrity-InvalidExtKey' => 'Invalid external key %1$s (column: `%2$s.%3$s`)~~',
|
||||
'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)~~',
|
||||
'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)~~',
|
||||
'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~',
|
||||
'DBAnalyzer-Integrity-HKInvalid' => 'Broken hierarchical key `%1$s`~~',
|
||||
'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~',
|
||||
'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~',
|
||||
'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~',
|
||||
'DBAnalyzer-Integrity-OrphanRecord' => 'Árva rekord a %1$s -ban, kell hogy legyen megfelelője a %2$s táblázatban',
|
||||
'DBAnalyzer-Integrity-InvalidExtKey' => 'Érvénytelen a %1$s külső kulcs (oszlop: `%2$s.%3$s`)',
|
||||
'DBAnalyzer-Integrity-MissingExtKey' => 'Hiányzik a %1$s külső külcs (oszlop: %2$s.%3$s)',
|
||||
'DBAnalyzer-Integrity-InvalidValue' => '%1$s értéke érvénytelen (oszlop: %2$s.%3$s)',
|
||||
'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Néhány felhasználónak egyáltalán nincs fiókja',
|
||||
'DBAnalyzer-Integrity-HKInvalid' => 'Sérült a %1$s hierarchikus kulcs',
|
||||
'DBAnalyzer-Fetch-Count-Error' => 'Lekérési hiba a %1$s -nál, %2$d bejegyzés lekérve / %3$d megszámlálva',
|
||||
'DBAnalyzer-Integrity-FinalClass' => 'A %2$s.%1$s mezőnek ugyanolyan értékűnek kell lennie mint a %3$s.%1$s',
|
||||
'DBAnalyzer-Integrity-RootFinalClass' => '%2$s.%1$s mezőnek érvényes osztályt kell tartalmaznia',
|
||||
));
|
||||
|
||||
// Database Info
|
||||
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
||||
'DBTools:DatabaseInfo' => 'Database Information~~',
|
||||
'DBTools:Base' => 'Base~~',
|
||||
'DBTools:Size' => 'Size~~',
|
||||
'DBTools:DatabaseInfo' => 'Adatbázis információ',
|
||||
'DBTools:Base' => 'Bázis',
|
||||
'DBTools:Size' => 'Méret',
|
||||
));
|
||||
|
||||
// Lost attachments
|
||||
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
||||
'DBTools:LostAttachments' => 'Lost attachments~~',
|
||||
'DBTools:LostAttachments:Disclaimer' => 'Here you can search your database for lost or misplaced attachments. This is NOT a data recovery tool, is does not retrieve deleted data.~~',
|
||||
'DBTools:LostAttachments' => 'Elveszett mellékletek',
|
||||
'DBTools:LostAttachments:Disclaimer' => 'Itt kereshet az adatbázisban elveszett vagy elkeveredett mellékletek után. Ez NEM egy adat-visszaállítási eszköz, nem állítja vissza a törölt adatokat.',
|
||||
|
||||
'DBTools:LostAttachments:Button:Analyze' => 'Analyze~~',
|
||||
'DBTools:LostAttachments:Button:Restore' => 'Restore~~',
|
||||
'DBTools:LostAttachments:Button:Restore:Confirm' => 'This action cannot be undone, please confirm that you want to restore the selected files.~~',
|
||||
'DBTools:LostAttachments:Button:Busy' => 'Please wait...~~',
|
||||
'DBTools:LostAttachments:Button:Analyze' => 'Elemzés',
|
||||
'DBTools:LostAttachments:Button:Restore' => 'Visszaállítás',
|
||||
'DBTools:LostAttachments:Button:Restore:Confirm' => 'Ez a művelet nem vonható vissza, kérjük, erősítse meg, hogy vissza kívánja-e állítani a kiválasztott fájlokat.',
|
||||
'DBTools:LostAttachments:Button:Busy' => 'Kérem várjon...',
|
||||
|
||||
'DBTools:LostAttachments:Step:Analyze' => 'First, search for lost/misplaced attachments by analyzing the database.~~',
|
||||
'DBTools:LostAttachments:Step:Analyze' => 'Először az adatbázis elemzésével keresse meg az elveszett/áthelyezett mellékleteket.',
|
||||
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analyze results:~~',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Great! Every thing seems to be at the right place.~~',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Some attachments (%1$d) seem to be misplaced. Take a look at the following list and check the ones you would like to move.~~',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Filename~~',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Current location~~',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Move to...~~',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults' => 'Elemzés eredménye:',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Nagyszerű! Úgy tűnik, minden a helyén van.',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Úgy tűnik, hogy néhány melléklet (%1$d) rossz helyen van. Nézze meg az alábbi listát, és ellenőrizze azokat, amelyeket szeretne áthelyezni.',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Fájlnév',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Jelenlegi helye',
|
||||
'DBTools:LostAttachments:Step:AnalyzeResults:Item:TargetLocation' => 'Áthelyezés...',
|
||||
|
||||
'DBTools:LostAttachments:Step:RestoreResults' => 'Restore results:~~',
|
||||
'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d attachments were restored.~~',
|
||||
'DBTools:LostAttachments:Step:RestoreResults' => 'Visszaállítás eredménye:',
|
||||
'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d melléklet lett visszaállítva.',
|
||||
|
||||
'DBTools:LostAttachments:StoredAsInlineImage' => 'Stored as inline image~~',
|
||||
'DBTools:LostAttachments:History' => 'Attachment "%1$s" restored with DB tools~~'
|
||||
'DBTools:LostAttachments:StoredAsInlineImage' => 'Soron belüli képként tárolva',
|
||||
'DBTools:LostAttachments:History' => 'A %1$s melléklet visszaállítva a DB eszközzel'
|
||||
));
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
// Database inconsistencies
|
||||
Dict::Add('IT IT', 'Italian', 'Italiano', array(
|
||||
// Dictionary entries go here
|
||||
'Menu:DBToolsMenu' => 'DB Tools~~',
|
||||
'Menu:DBToolsMenu' => 'Database integrity~~',
|
||||
'DBTools:Class' => 'Class~~',
|
||||
'DBTools:Title' => 'Database Maintenance Tools~~',
|
||||
'DBTools:Title' => 'Database integrity check~~',
|
||||
'DBTools:ErrorsFound' => 'Errors Found~~',
|
||||
'DBTools:Indication' => 'Important: after fixing errors in the database you\'ll have to run the analysis again as new inconsistencies will be generated~~',
|
||||
'DBTools:Disclaimer' => 'DISCLAIMER: BACKUP YOUR DATABASE BEFORE RUNNING THE FIXES~~',
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
// Database inconsistencies
|
||||
Dict::Add('JA JP', 'Japanese', '日本語', array(
|
||||
// Dictionary entries go here
|
||||
'Menu:DBToolsMenu' => 'DB Tools~~',
|
||||
'Menu:DBToolsMenu' => 'Database integrity~~',
|
||||
'DBTools:Class' => 'Class~~',
|
||||
'DBTools:Title' => 'Database Maintenance Tools~~',
|
||||
'DBTools:Title' => 'Database integrity check~~',
|
||||
'DBTools:ErrorsFound' => 'Errors Found~~',
|
||||
'DBTools:Indication' => 'Important: after fixing errors in the database you\'ll have to run the analysis again as new inconsistencies will be generated~~',
|
||||
'DBTools:Disclaimer' => 'DISCLAIMER: BACKUP YOUR DATABASE BEFORE RUNNING THE FIXES~~',
|
||||
|
||||
@@ -12,7 +12,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
|
||||
// Dictionary entries go here
|
||||
'Menu:DBToolsMenu' => 'Инструменты БД',
|
||||
'DBTools:Class' => 'Класс',
|
||||
'DBTools:Title' => 'Инструменты обслуживания базы данных~~',
|
||||
'DBTools:Title' => 'Инструменты обслуживания базы данных',
|
||||
'DBTools:ErrorsFound' => 'Найденные ошибки',
|
||||
'DBTools:Indication' => 'Important: after fixing errors in the database you\'ll have to run the analysis again as new inconsistencies will be generated~~',
|
||||
'DBTools:Disclaimer' => 'DISCLAIMER: BACKUP YOUR DATABASE BEFORE RUNNING THE FIXES~~',
|
||||
|
||||
@@ -25,7 +25,7 @@ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
|
||||
// Dictionary entries go here
|
||||
'Menu:DBToolsMenu' => 'Database integrity~~',
|
||||
'DBTools:Class' => 'Class~~',
|
||||
'DBTools:Title' => 'Database Maintenance Tools~~',
|
||||
'DBTools:Title' => 'Database integrity check~~',
|
||||
'DBTools:ErrorsFound' => 'Errors Found~~',
|
||||
'DBTools:Indication' => 'Important: after fixing errors in the database you\'ll have to run the analysis again as new inconsistencies will be generated~~',
|
||||
'DBTools:Disclaimer' => 'DISCLAIMER: BACKUP YOUR DATABASE BEFORE RUNNING THE FIXES~~',
|
||||
|
||||
@@ -23,9 +23,9 @@
|
||||
// Database inconsistencies
|
||||
Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
|
||||
// Dictionary entries go here
|
||||
'Menu:DBToolsMenu' => 'DB Tools~~',
|
||||
'Menu:DBToolsMenu' => 'Database integrity~~',
|
||||
'DBTools:Class' => 'Class~~',
|
||||
'DBTools:Title' => 'Database Maintenance Tools~~',
|
||||
'DBTools:Title' => 'Database integrity check~~',
|
||||
'DBTools:ErrorsFound' => 'Errors Found~~',
|
||||
'DBTools:Indication' => 'Important: after fixing errors in the database you\'ll have to run the analysis again as new inconsistencies will be generated~~',
|
||||
'DBTools:Disclaimer' => 'DISCLAIMER: BACKUP YOUR DATABASE BEFORE RUNNING THE FIXES~~',
|
||||
|
||||
@@ -56,7 +56,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all~~',
|
||||
'DBAnalyzer-Integrity-HKInvalid' => 'Broken hierarchical key `%1$s`~~',
|
||||
'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~',
|
||||
'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value than `%3$s`.`%1$s`~~',
|
||||
'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~',
|
||||
'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~',
|
||||
));
|
||||
|
||||
@@ -90,5 +90,5 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'DBTools:LostAttachments:Step:RestoreResults:Results' => '%1$d/%2$d 的附件被还原.',
|
||||
|
||||
'DBTools:LostAttachments:StoredAsInlineImage' => 'Stored as inline image~~',
|
||||
'DBTools:LostAttachments:History' => '附件 "%1$s" restored with DB 工具~~'
|
||||
'DBTools:LostAttachments:History' => '附件 "%1$s" restored with DB 工具'
|
||||
));
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'combodo-db-tools/3.0.3',
|
||||
'combodo-db-tools/3.0.4',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -26,9 +26,9 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'Attachments:History_File_Removed' => 'Attachment %1$s removed.',
|
||||
'Attachments:AddAttachment' => 'Add attachment: ',
|
||||
'Attachments:UploadNotAllowedOnThisSystem' => 'File upload in NOT allowed on this system.',
|
||||
'Attachment:Max_Go' => '(Maximum file size: %1$s Go)',
|
||||
'Attachment:Max_Mo' => '(Maximum file size: %1$s Mo)',
|
||||
'Attachment:Max_Ko' => '(Maximum file size: %1$s Ko)',
|
||||
'Attachment:Max_Go' => '(Maximum file size: %1$s GB)',
|
||||
'Attachment:Max_Mo' => '(Maximum file size: %1$s MB)',
|
||||
'Attachment:Max_Ko' => '(Maximum file size: %1$s KB)',
|
||||
'Attachments:NoAttachment' => 'No attachment. ',
|
||||
'Attachments:PreviewNotAvailable' => 'Preview not available for this type of attachment.',
|
||||
'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s',
|
||||
|
||||
@@ -21,26 +21,24 @@
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
||||
'Attachments:TabTitle_Count' => 'Attachments (%1$d)~~',
|
||||
'Attachments:EmptyTabTitle' => 'Attachments~~',
|
||||
'Attachments:FieldsetTitle' => 'Attachments~~',
|
||||
'Attachments:DeleteBtn' => 'Delete~~',
|
||||
'Attachments:History_File_Added' => 'Attachment %1$s added.~~',
|
||||
'Attachments:History_File_Removed' => 'Attachment %1$s removed.~~',
|
||||
'Attachments:AddAttachment' => 'Add attachment: ~~',
|
||||
'Attachments:UploadNotAllowedOnThisSystem' => 'File upload in NOT allowed on this system.~~',
|
||||
'Attachment:Max_Go' => '(Maximum file size: %1$s Go)~~',
|
||||
'Attachment:Max_Mo' => '(Maximum file size: %1$s Mo)~~',
|
||||
'Attachment:Max_Ko' => '(Maximum file size: %1$s Ko)~~',
|
||||
'Attachments:NoAttachment' => 'No attachment. ~~',
|
||||
'Attachments:PreviewNotAvailable' => 'Preview not available for this type of attachment.~~',
|
||||
'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s~~',
|
||||
'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached.
|
||||
Either you have pushed an empty file,
|
||||
or ask your iTop administrator if the iTop server disk is full.~~',
|
||||
'Attachments:Render:Icons' => 'Display as icons~~',
|
||||
'Attachments:Render:Table' => 'Display as list~~',
|
||||
'UI:Attachments:DropYourFileHint' => 'Drop files anywhere in this area~~',
|
||||
'Attachments:TabTitle_Count' => 'Mellékletek (%1$d)',
|
||||
'Attachments:EmptyTabTitle' => 'Mellékletek',
|
||||
'Attachments:FieldsetTitle' => 'Mellékletek',
|
||||
'Attachments:DeleteBtn' => 'Törlés',
|
||||
'Attachments:History_File_Added' => '%1$s melléklet hozzáadva',
|
||||
'Attachments:History_File_Removed' => '%1$s melléklet eltávolítva',
|
||||
'Attachments:AddAttachment' => 'Melléklet hozzáadása: ',
|
||||
'Attachments:UploadNotAllowedOnThisSystem' => 'A fájlfeltöltés nem engedélyezett ezen a rendszeren',
|
||||
'Attachment:Max_Go' => '(Maximum fájlméret: %1$s GB)',
|
||||
'Attachment:Max_Mo' => '(Maximum fájlméret: %1$s MB)',
|
||||
'Attachment:Max_Ko' => '(Maximum fájlméret: %1$s KB)',
|
||||
'Attachments:NoAttachment' => 'Nincs melléklet. ',
|
||||
'Attachments:PreviewNotAvailable' => 'Az előnézet nem érhető el ilyen típusú melléklethez',
|
||||
'Attachments:Error:FileTooLarge' => 'Túl nagy a %1$s fájl a feltöltéshez.',
|
||||
'Attachments:Error:UploadedFileEmpty' => 'A kapott fájl üres, ezért nem csatolható. Vagy egy üres fájlt húzott be, vagy kérdezze meg a rendszergazdát, hátha az iTop szerver lemeze telt meg.',
|
||||
'Attachments:Render:Icons' => 'Mutassa ikonként',
|
||||
'Attachments:Render:Table' => 'Mutassa listaként',
|
||||
'UI:Attachments:DropYourFileHint' => 'Húzza a fájlokat erre a területre',
|
||||
));
|
||||
|
||||
//
|
||||
@@ -48,40 +46,40 @@ or ask your iTop administrator if the iTop server disk is full.~~',
|
||||
//
|
||||
|
||||
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
||||
'Class:Attachment' => 'Attachment~~',
|
||||
'Class:Attachment+' => '~~',
|
||||
'Class:Attachment/Attribute:expire' => 'Expire~~',
|
||||
'Class:Attachment/Attribute:expire+' => '~~',
|
||||
'Class:Attachment/Attribute:temp_id' => 'Temporary id~~',
|
||||
'Class:Attachment/Attribute:temp_id+' => '~~',
|
||||
'Class:Attachment/Attribute:item_class' => 'Item class~~',
|
||||
'Class:Attachment/Attribute:item_class+' => '~~',
|
||||
'Class:Attachment/Attribute:item_id' => 'Item~~',
|
||||
'Class:Attachment/Attribute:item_id+' => '~~',
|
||||
'Class:Attachment/Attribute:item_org_id' => 'Item organization~~',
|
||||
'Class:Attachment/Attribute:item_org_id+' => '~~',
|
||||
'Class:Attachment/Attribute:contents' => 'Contents~~',
|
||||
'Class:Attachment/Attribute:contents+' => '~~',
|
||||
'Class:Attachment' => 'Mellékletek',
|
||||
'Class:Attachment+' => '~~',
|
||||
'Class:Attachment/Attribute:expire' => 'Lejárat',
|
||||
'Class:Attachment/Attribute:expire+' => '~~',
|
||||
'Class:Attachment/Attribute:temp_id' => 'Átmeneti azonosító',
|
||||
'Class:Attachment/Attribute:temp_id+' => '~~',
|
||||
'Class:Attachment/Attribute:item_class' => 'Elem típus',
|
||||
'Class:Attachment/Attribute:item_class+' => '~~',
|
||||
'Class:Attachment/Attribute:item_id' => 'Elem',
|
||||
'Class:Attachment/Attribute:item_id+' => '~~',
|
||||
'Class:Attachment/Attribute:item_org_id' => 'Elem szervezeti egység',
|
||||
'Class:Attachment/Attribute:item_org_id+' => '~~',
|
||||
'Class:Attachment/Attribute:contents' => 'Tartalom',
|
||||
'Class:Attachment/Attribute:contents+' => '~~',
|
||||
));
|
||||
|
||||
|
||||
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
||||
'Attachments:File:Thumbnail' => 'Icon~~',
|
||||
'Attachments:File:Name' => 'File name~~',
|
||||
'Attachments:File:Date' => 'Upload date~~',
|
||||
'Attachments:File:Uploader' => 'Uploaded by~~',
|
||||
'Attachments:File:Size' => 'Size~~',
|
||||
'Attachments:File:MimeType' => 'Type~~',
|
||||
'Attachments:File:Thumbnail' => 'Ikon',
|
||||
'Attachments:File:Name' => 'Fájlnév',
|
||||
'Attachments:File:Date' => 'Feltöltés dátuma',
|
||||
'Attachments:File:Uploader' => 'Feltöltötte ',
|
||||
'Attachments:File:Size' => 'Méret',
|
||||
'Attachments:File:MimeType' => 'Típus',
|
||||
));
|
||||
//
|
||||
// Class: Attachment
|
||||
//
|
||||
|
||||
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
|
||||
'Class:Attachment/Attribute:creation_date' => 'Creation date~~',
|
||||
'Class:Attachment/Attribute:creation_date+' => '~~',
|
||||
'Class:Attachment/Attribute:user_id' => 'User id~~',
|
||||
'Class:Attachment/Attribute:user_id+' => '~~',
|
||||
'Class:Attachment/Attribute:contact_id' => 'Contact id~~',
|
||||
'Class:Attachment/Attribute:contact_id+' => '~~',
|
||||
'Class:Attachment/Attribute:creation_date' => 'Létrehozás dátuma',
|
||||
'Class:Attachment/Attribute:creation_date+' => '~~',
|
||||
'Class:Attachment/Attribute:user_id' => 'Felhasználó',
|
||||
'Class:Attachment/Attribute:user_id+' => '~~',
|
||||
'Class:Attachment/Attribute:contact_id' => 'Kapcsolattartó',
|
||||
'Class:Attachment/Attribute:contact_id+' => '~~',
|
||||
));
|
||||
|
||||
@@ -29,9 +29,9 @@ Dict::Add('IT IT', 'Italian', 'Italiano', array(
|
||||
'Attachments:History_File_Removed' => 'Attachment %1$s removed.~~',
|
||||
'Attachments:AddAttachment' => 'Add attachment: ~~',
|
||||
'Attachments:UploadNotAllowedOnThisSystem' => 'File upload in NOT allowed on this system.~~',
|
||||
'Attachment:Max_Go' => '(Maximum file size: %1$s Go)~~',
|
||||
'Attachment:Max_Mo' => '(Maximum file size: %1$s Mo)~~',
|
||||
'Attachment:Max_Ko' => '(Maximum file size: %1$s Ko)~~',
|
||||
'Attachment:Max_Go' => '(Maximum file size: %1$s GB)~~',
|
||||
'Attachment:Max_Mo' => '(Maximum file size: %1$s MB)~~',
|
||||
'Attachment:Max_Ko' => '(Maximum file size: %1$s KB)~~',
|
||||
'Attachments:NoAttachment' => 'No attachment. ~~',
|
||||
'Attachments:PreviewNotAvailable' => 'Preview not available for this type of attachment.~~',
|
||||
'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s~~',
|
||||
|
||||
@@ -29,9 +29,9 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
|
||||
'Attachments:History_File_Removed' => 'Attachment %1$s removed.~~',
|
||||
'Attachments:AddAttachment' => 'Add attachment: ~~',
|
||||
'Attachments:UploadNotAllowedOnThisSystem' => 'File upload in NOT allowed on this system.~~',
|
||||
'Attachment:Max_Go' => '(Maximum file size: %1$s Go)~~',
|
||||
'Attachment:Max_Mo' => '(Maximum file size: %1$s Mo)~~',
|
||||
'Attachment:Max_Ko' => '(Maximum file size: %1$s Ko)~~',
|
||||
'Attachment:Max_Go' => '(Maximum file size: %1$s GB)~~',
|
||||
'Attachment:Max_Mo' => '(Maximum file size: %1$s MB)~~',
|
||||
'Attachment:Max_Ko' => '(Maximum file size: %1$s KB)~~',
|
||||
'Attachments:NoAttachment' => 'No attachment. ~~',
|
||||
'Attachments:PreviewNotAvailable' => 'Preview not available for this type of attachment.~~',
|
||||
'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s~~',
|
||||
|
||||
@@ -52,7 +52,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
|
||||
'Class:Attachment/Attribute:item_class+' => '~~',
|
||||
'Class:Attachment/Attribute:item_id' => '项目',
|
||||
'Class:Attachment/Attribute:item_id+' => '~~',
|
||||
'Class:Attachment/Attribute:item_org_id' => 'Item 组织~~',
|
||||
'Class:Attachment/Attribute:item_org_id' => 'Item 组织',
|
||||
'Class:Attachment/Attribute:item_org_id+' => '',
|
||||
'Class:Attachment/Attribute:contents' => '内容',
|
||||
'Class:Attachment/Attribute:contents+' => '',
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-attachments/3.0.3',
|
||||
'itop-attachments/3.0.4',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -137,6 +137,9 @@ try
|
||||
* As a result we're setting a token file to make sure the restore is called by an authenticated user with the correct rights !
|
||||
*/
|
||||
case 'restore_get_token':
|
||||
$oPage = new JsonPage();
|
||||
$oPage->SetOutputDataOnly(true);
|
||||
|
||||
$sEnvironment = utils::ReadParam('environment', 'production', false, 'raw_data');
|
||||
$oRestoreMutex = new iTopMutex('restore.'.$sEnvironment);
|
||||
if ($oRestoreMutex->IsLocked())
|
||||
@@ -149,12 +152,7 @@ try
|
||||
$sTokenFile = APPROOT.'/data/restore.'.$sToken.'.tok';
|
||||
file_put_contents($sTokenFile, $sFile);
|
||||
|
||||
$oPage->add_ready_script(
|
||||
<<<JS
|
||||
$("#restore_token").val('$sToken');
|
||||
JS
|
||||
);
|
||||
|
||||
$oPage->SetData(['token' => $sToken]);
|
||||
$oPage->output();
|
||||
break;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user