mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-03 16:24:12 +02:00
Compare commits
455 Commits
3.1.0-beta
...
feature/68
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a1b8c8145 | ||
|
|
ca47f5458c | ||
|
|
3aec6bff79 | ||
|
|
3027bb5092 | ||
|
|
83313ce1d5 | ||
|
|
893efa8e2d | ||
|
|
8aa3dcdaa7 | ||
|
|
1ea9659227 | ||
|
|
306d8136ef | ||
|
|
cbb37f27d7 | ||
|
|
e78fa18359 | ||
|
|
31e58d7597 | ||
|
|
1385a3dc03 | ||
|
|
3b21ed8fe8 | ||
|
|
789f0c826b | ||
|
|
0ad8db565d | ||
|
|
ac070b0cbe | ||
|
|
e21dc4d21c | ||
|
|
a49a4e6c2b | ||
|
|
95aa4afe75 | ||
|
|
74004fa375 | ||
|
|
d2fc87c6f9 | ||
|
|
a1a83a1ba3 | ||
|
|
e7d11e4bec | ||
|
|
5264a1f10d | ||
|
|
f0199a4cf2 | ||
|
|
f8877ef3e7 | ||
|
|
00b1156526 | ||
|
|
39d2ba8d1b | ||
|
|
322adcb180 | ||
|
|
77e7685c90 | ||
|
|
3c14e5e032 | ||
|
|
f58ec7e38e | ||
|
|
73fd0b06b2 | ||
|
|
8fa9336568 | ||
|
|
15148f7d1d | ||
|
|
7e8589ba95 | ||
|
|
fba668207f | ||
|
|
798cd10d6b | ||
|
|
442721bcb5 | ||
|
|
1a9049d277 | ||
|
|
c0931af91a | ||
|
|
29e9a06dc1 | ||
|
|
d6415042ae | ||
|
|
90006667fe | ||
|
|
fd351df08b | ||
|
|
7419749ba6 | ||
|
|
e3c26c97f2 | ||
|
|
d6e132fbda | ||
|
|
73bed04555 | ||
|
|
8893cdac1d | ||
|
|
7f245a15be | ||
|
|
b5c46ccd4a | ||
|
|
7fbc211c43 | ||
|
|
cf774cdb90 | ||
|
|
722a58491c | ||
|
|
037dfe1df6 | ||
|
|
0b26d45014 | ||
|
|
44e826543d | ||
|
|
74c4c642eb | ||
|
|
af3be19175 | ||
|
|
50f5ab6be4 | ||
|
|
941412a365 | ||
|
|
a9bd62dc43 | ||
|
|
4f336abeb8 | ||
|
|
ee49d64120 | ||
|
|
40d1ae0b1c | ||
|
|
b9c566238a | ||
|
|
4fd8177165 | ||
|
|
58257fa25e | ||
|
|
c597c34e5c | ||
|
|
0cc0f39d9e | ||
|
|
a2cdf214f0 | ||
|
|
cb3d8120bd | ||
|
|
189fd1d9e3 | ||
|
|
4f75d012e5 | ||
|
|
013173019f | ||
|
|
ace88cb709 | ||
|
|
ed8d4df5ef | ||
|
|
8ca20e3135 | ||
|
|
4e13cce770 | ||
|
|
31a1370028 | ||
|
|
f36f3aa05b | ||
|
|
f408e14d4b | ||
|
|
d0d90d7c69 | ||
|
|
aa618468d1 | ||
|
|
fadfd94bac | ||
|
|
9469681a0c | ||
|
|
543daac06d | ||
|
|
800d1b03c1 | ||
|
|
d4ab55dd9a | ||
|
|
da27ddba82 | ||
|
|
c72cb7e70e | ||
|
|
9df92665e0 | ||
|
|
9b970a472f | ||
|
|
819baa3951 | ||
|
|
c78024394e | ||
|
|
593131a7ea | ||
|
|
4267f2b855 | ||
|
|
ba8f18e1d4 | ||
|
|
ba13d24206 | ||
|
|
148f708360 | ||
|
|
239c51bb53 | ||
|
|
ab3a4a2468 | ||
|
|
3647291475 | ||
|
|
7d0feab078 | ||
|
|
a472d83e3d | ||
|
|
6dc6392fab | ||
|
|
e793b02f8b | ||
|
|
aae2db8ad8 | ||
|
|
c5cb84f976 | ||
|
|
f3d488de26 | ||
|
|
12c0edc530 | ||
|
|
ebc15db75e | ||
|
|
61565b25a3 | ||
|
|
dd28aca260 | ||
|
|
7619d055dd | ||
|
|
2a6e5b21a9 | ||
|
|
f01997f2ad | ||
|
|
fc6e98b534 | ||
|
|
8ecebee511 | ||
|
|
b5e26061e1 | ||
|
|
14f6ce3626 | ||
|
|
14fa20b428 | ||
|
|
2690fa3315 | ||
|
|
35cd965360 | ||
|
|
34a2a6ce03 | ||
|
|
133fc29ad5 | ||
|
|
83a5b98f82 | ||
|
|
e5dd51f637 | ||
|
|
db940e3bba | ||
|
|
2f6bcc3534 | ||
|
|
4923418f58 | ||
|
|
0a6c82dfe1 | ||
|
|
9f3a6be255 | ||
|
|
f89d843ab3 | ||
|
|
2dd7f5cada | ||
|
|
24c0f4950f | ||
|
|
d4dbbc59d4 | ||
|
|
dc0cd44c79 | ||
|
|
b6ac92d192 | ||
|
|
d7df249586 | ||
|
|
f3c4fcb0f5 | ||
|
|
9a8e9a0b01 | ||
|
|
f6a7e6b4e1 | ||
|
|
ce1c517a9f | ||
|
|
2829fb291f | ||
|
|
6110abfc7f | ||
|
|
6046f44f56 | ||
|
|
6c6131ce03 | ||
|
|
178c922407 | ||
|
|
343e87a8d4 | ||
|
|
d7e5d6fb7f | ||
|
|
75b6a3ede8 | ||
|
|
993606f102 | ||
|
|
bdf0b4daa9 | ||
|
|
44c189223e | ||
|
|
7fdbb59c30 | ||
|
|
5acf38ac36 | ||
|
|
e76728b2bf | ||
|
|
3e258f32cc | ||
|
|
3c51d6fb98 | ||
|
|
7cfe1389aa | ||
|
|
7292a8540b | ||
|
|
f65c690462 | ||
|
|
ecf8bc42fa | ||
|
|
3818bc3f98 | ||
|
|
faba812fc1 | ||
|
|
add433d702 | ||
|
|
9c99cb35e5 | ||
|
|
d8ec900933 | ||
|
|
9d392ad167 | ||
|
|
ea8509db1f | ||
|
|
df25ce76b6 | ||
|
|
e946fc65fc | ||
|
|
29ba5e73fa | ||
|
|
acf449c03a | ||
|
|
d203e075a8 | ||
|
|
dbe2f66539 | ||
|
|
ecb4cb7435 | ||
|
|
3200b5dc86 | ||
|
|
e20d4cecd5 | ||
|
|
0d8ff7bbac | ||
|
|
36177e09f7 | ||
|
|
48eb022824 | ||
|
|
03c9ffc033 | ||
|
|
61a9a4ac65 | ||
|
|
9ae62adff1 | ||
|
|
38962e68ee | ||
|
|
483dbb4a5d | ||
|
|
1f4dcc4f9e | ||
|
|
e86309669e | ||
|
|
6d6f55acf7 | ||
|
|
6ebcd44bb1 | ||
|
|
f8fb51fea0 | ||
|
|
bf768311c2 | ||
|
|
d797436786 | ||
|
|
73a2c935a7 | ||
|
|
b508c0d983 | ||
|
|
351893bbdd | ||
|
|
59e4bb028f | ||
|
|
6d895371ec | ||
|
|
42acbe66c3 | ||
|
|
ab91631e68 | ||
|
|
22a9299d97 | ||
|
|
b9acd0a9a6 | ||
|
|
c761364bed | ||
|
|
cc4af0a027 | ||
|
|
3366bae0ab | ||
|
|
d79dacf8a3 | ||
|
|
03b484c349 | ||
|
|
70081ecf33 | ||
|
|
575ba1cd7b | ||
|
|
d130959692 | ||
|
|
a8c689c6c0 | ||
|
|
1990ccb5d8 | ||
|
|
e107be56e4 | ||
|
|
0f8e87e001 | ||
|
|
d92d2b5e9e | ||
|
|
ebd0136773 | ||
|
|
f6653e1594 | ||
|
|
65bb76b9e3 | ||
|
|
efc36e12bc | ||
|
|
5a0b12f078 | ||
|
|
22d04cb9fc | ||
|
|
d8da119c56 | ||
|
|
a24d50d7e3 | ||
|
|
e5716e66bb | ||
|
|
f238593966 | ||
|
|
d951d3b872 | ||
|
|
ccceb870e3 | ||
|
|
ed6df77cbb | ||
|
|
1ad28312ec | ||
|
|
f002aa04cd | ||
|
|
b86d70623e | ||
|
|
fe3467309d | ||
|
|
851ab9c356 | ||
|
|
aef3c2e609 | ||
|
|
c6c2bb33ed | ||
|
|
0d0ef86550 | ||
|
|
5212e15cc4 | ||
|
|
f04fc546b5 | ||
|
|
ca0bd674a7 | ||
|
|
caf3076b12 | ||
|
|
260b2e0bd6 | ||
|
|
90413ae4fe | ||
|
|
c4c400d852 | ||
|
|
b7466e3206 | ||
|
|
f2812cdbe5 | ||
|
|
6cc4cc4fb6 | ||
|
|
95a2fdec99 | ||
|
|
d7495af207 | ||
|
|
13ad98b9b3 | ||
|
|
4be54fdd65 | ||
|
|
6d13397ba1 | ||
|
|
48e7e0309a | ||
|
|
6880c49a32 | ||
|
|
2ce9b2afaf | ||
|
|
d64a91d4ce | ||
|
|
c0c8a13864 | ||
|
|
1b48b4a1e1 | ||
|
|
5ffa41bc16 | ||
|
|
d2eef06276 | ||
|
|
0516100aae | ||
|
|
77b14c516e | ||
|
|
880a824f2f | ||
|
|
5b4079ab7e | ||
|
|
f7f1b5f399 | ||
|
|
85f66f5e0c | ||
|
|
a5c980113b | ||
|
|
d284d629a4 | ||
|
|
18efbfa803 | ||
|
|
7aa478d6ff | ||
|
|
c52195e6cd | ||
|
|
97700dbf15 | ||
|
|
c25c69d746 | ||
|
|
734a788340 | ||
|
|
a61734a6f8 | ||
|
|
eb1eb15791 | ||
|
|
750ecd4804 | ||
|
|
781c15d1ee | ||
|
|
a84077782d | ||
|
|
26048150d3 | ||
|
|
e5b6e2eb8c | ||
|
|
a1499a04d4 | ||
|
|
87b6ea4def | ||
|
|
fb23bddeb2 | ||
|
|
aa6fa4c674 | ||
|
|
72873a3343 | ||
|
|
19e7fc9cb9 | ||
|
|
a0a66f3b54 | ||
|
|
5ef25ccb77 | ||
|
|
1682a85cc0 | ||
|
|
cd9beec313 | ||
|
|
8295eaed90 | ||
|
|
3c9c13a371 | ||
|
|
86a7cefa68 | ||
|
|
829b648dd2 | ||
|
|
5475b9fbbe | ||
|
|
6f8e7c7002 | ||
|
|
96d04e35f9 | ||
|
|
67ca554261 | ||
|
|
629a8412dc | ||
|
|
f89953f39e | ||
|
|
772368ef8a | ||
|
|
19a83bc944 | ||
|
|
2e049aa244 | ||
|
|
a57b6471c9 | ||
|
|
bc7c1b4744 | ||
|
|
a59dacf679 | ||
|
|
8b93a404ef | ||
|
|
ded204344d | ||
|
|
12c78697f4 | ||
|
|
046e857768 | ||
|
|
4d8246c4d8 | ||
|
|
5c61d725e1 | ||
|
|
b3121d46c4 | ||
|
|
0c7195f1a3 | ||
|
|
00b070b3cf | ||
|
|
2c4cad4dac | ||
|
|
f60d03ddd3 | ||
|
|
9c37d5c23e | ||
|
|
89145593ef | ||
|
|
b2e80d37dd | ||
|
|
6432678de9 | ||
|
|
3d8345b92c | ||
|
|
952194b385 | ||
|
|
bfb452dd69 | ||
|
|
9ab928e92e | ||
|
|
64baeba1c7 | ||
|
|
71ed784c60 | ||
|
|
da45651121 | ||
|
|
d388ce9a06 | ||
|
|
47e71d8838 | ||
|
|
2b5973ec67 | ||
|
|
e58918f53e | ||
|
|
ea56939b7a | ||
|
|
6caddb65e9 | ||
|
|
125715af3f | ||
|
|
8df0d66fb6 | ||
|
|
ea8e7c5131 | ||
|
|
a80aa20b23 | ||
|
|
06e5e0b102 | ||
|
|
df1cb0b6e3 | ||
|
|
fd336ffd92 | ||
|
|
be1c559ad4 | ||
|
|
2dce37f289 | ||
|
|
22b5eef4ce | ||
|
|
63426e459a | ||
|
|
8393172ad2 | ||
|
|
cc06afc6df | ||
|
|
5247f5b3ea | ||
|
|
78396d8e4a | ||
|
|
f1ee22cbed | ||
|
|
9b409b117f | ||
|
|
39305468f8 | ||
|
|
32fd75bc4b | ||
|
|
efadf2cc79 | ||
|
|
40d63a2fa4 | ||
|
|
baa6dedbcf | ||
|
|
556b9ad89a | ||
|
|
9afc22bd8f | ||
|
|
ef0b0f88c9 | ||
|
|
a010239efb | ||
|
|
264a8cd70a | ||
|
|
a2a0b2cd0b | ||
|
|
aa1834170b | ||
|
|
f94d67ab35 | ||
|
|
3048c8c41f | ||
|
|
246e4a9f50 | ||
|
|
4c9ea0c9d4 | ||
|
|
e654daf4a5 | ||
|
|
8292b16387 | ||
|
|
340b982d77 | ||
|
|
3e187282b0 | ||
|
|
722a4a2c4d | ||
|
|
8907525b78 | ||
|
|
6d58adb6dd | ||
|
|
03e7e0e79f | ||
|
|
83529da319 | ||
|
|
9d38b4d1d6 | ||
|
|
40dc3deabb | ||
|
|
56688fbbff | ||
|
|
764a170cd0 | ||
|
|
c30da57818 | ||
|
|
e36b9eb664 | ||
|
|
6cc2d49cd5 | ||
|
|
d085f15b6d | ||
|
|
6606af71ff | ||
|
|
52049b7837 | ||
|
|
f40674ec85 | ||
|
|
b46d4db8ff | ||
|
|
12dbd0ed3d | ||
|
|
0d9f33ec4c | ||
|
|
71704404d0 | ||
|
|
3c8bea8921 | ||
|
|
b755f65a8a | ||
|
|
3bd3081359 | ||
|
|
5a33fb7a6a | ||
|
|
29a90fe244 | ||
|
|
011116029b | ||
|
|
f95cb50002 | ||
|
|
2c8db92504 | ||
|
|
98d4fa4331 | ||
|
|
4a39a5e51d | ||
|
|
8373a03d82 | ||
|
|
befbe2dfa9 | ||
|
|
86ca7fcb7a | ||
|
|
ccf1d7ecfb | ||
|
|
178e82a039 | ||
|
|
452cc77168 | ||
|
|
7fc46fbc50 | ||
|
|
f338d3bdc8 | ||
|
|
d41e54bf34 | ||
|
|
aa75456e6e | ||
|
|
db6e4137b1 | ||
|
|
a206af1813 | ||
|
|
77868e356b | ||
|
|
60e302162c | ||
|
|
f36c8c5cdd | ||
|
|
d062ba8196 | ||
|
|
c3469e43bc | ||
|
|
68ee12bf5e | ||
|
|
47400d94ba | ||
|
|
b642dbe3d6 | ||
|
|
83564849e0 | ||
|
|
3e73038709 | ||
|
|
dcfefc1588 | ||
|
|
4db092ea52 | ||
|
|
96f1bd3646 | ||
|
|
f889c53d71 | ||
|
|
814916fb21 | ||
|
|
9e667c968e | ||
|
|
a8a3385969 | ||
|
|
87e04547bd | ||
|
|
6fe41796d9 | ||
|
|
99a4e5e861 | ||
|
|
3d3a751072 | ||
|
|
216a1b95b1 | ||
|
|
2074a0fa0d | ||
|
|
bfd078c2a3 | ||
|
|
85a879b587 | ||
|
|
9b9ba3c440 | ||
|
|
4d43d83b95 | ||
|
|
63487226a9 | ||
|
|
7b5ef52865 | ||
|
|
4550dfb1d4 | ||
|
|
1a5ffc23ce | ||
|
|
3a9198bf1c | ||
|
|
2242a80808 | ||
|
|
86148ecf57 | ||
|
|
2c4a7c5a77 | ||
|
|
2cca57c7fa | ||
|
|
e52fa28089 | ||
|
|
0001e8ffc4 |
Binary file not shown.
|
After Width: | Height: | Size: 3.0 MiB |
@@ -62,6 +62,14 @@ gitGraph
|
||||
commit id: "2022-12-28" tag: "2.7.8"
|
||||
checkout support/3.0
|
||||
commit id: "2023-04-12" tag: "3.0.3"
|
||||
checkout develop
|
||||
commit id: "2023-06-19" tag: "3.1.0-beta" type: REVERSE
|
||||
commit id: "2023-07-26" tag: "3.1.0-1" type: HIGHLIGHT
|
||||
branch support/3.1 order: 840
|
||||
checkout support/3.1
|
||||
commit id: "2023-08-09" tag: "3.1.0-2"
|
||||
checkout support/2.7
|
||||
commit id: "2023-08-10" tag: "2.7.9"
|
||||
```
|
||||
|
||||
To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start).
|
||||
To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start).
|
||||
|
||||
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
|
||||
|
||||
@@ -140,7 +140,7 @@ When your code is working, please:
|
||||
* Pull request description: mind to add all the information useful to understand why you're suggesting this modification and anything necessary to dive into your work. Especially:
|
||||
- Bugfixes: exact steps to reproduce the bug (given/when/then), description of the bug cause and what solution is implemented
|
||||
- Enhancements: use cases, implementation details if needed
|
||||
* Mind to check the "[Allow edits from maintainers](https://docs.github.com/en/github-ae@latest/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork)" option !
|
||||
* Mind to check the "[Allow edits from maintainers](https://docs.github.com/en/github-ae@latest/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork)" option ! (note that if you are working with an org fork, this option [won't be available](https://github.com/orgs/community/discussions/5634))
|
||||
|
||||
|
||||
## 🙏 We are thankful
|
||||
@@ -161,4 +161,4 @@ We have one sticker per contribution type. You might get multiple stickers with
|
||||
|
||||
Here is the design of each stickers for year 2022:
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -90,6 +90,7 @@ We would like to give a special thank you 🤗 to the people from the community
|
||||
- Dvořák, Lukáš
|
||||
- Goethals, Stefan
|
||||
- Gumble, David
|
||||
- Ji, Leeb (冀利斌) (a.k.a [@chileeb](https://github.com/chileeb))
|
||||
- Kaltefleiter, Lars (a.k.a [@larhip](https://www.github.com/larhip))
|
||||
- Khamit, Shamil
|
||||
- Kincel, Martin
|
||||
@@ -107,6 +108,7 @@ We would like to give a special thank you 🤗 to the people from the community
|
||||
- Rudner, Björn (a.k.a [@rudnerbjoern](https://github.com/rudnerbjoern))
|
||||
- Seki, Shoji
|
||||
- Shilov, Vladimir
|
||||
- Stetina, Pavel (a.k.a [@Stetinac](https://github.com/Stetinac))
|
||||
- Stukalov, Ilya (a.k.a [@ilya](https://www.github.com/ilya)-stukalov)
|
||||
- Tulio, Marco
|
||||
- Turrubiates, Miguel
|
||||
|
||||
@@ -228,6 +228,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
"db_table" => "priv_urp_userprofile",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"is_link" => true, /** @since 3.1.0 N°6482 */
|
||||
'uniqueness_rules' => array(
|
||||
'no_duplicate' => array(
|
||||
'attributes' => array(
|
||||
@@ -501,6 +502,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();
|
||||
@@ -557,6 +559,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
|
||||
|
||||
// Cache
|
||||
$this->m_aObjectActionGrants = array();
|
||||
$this->m_aAdministrators = null;
|
||||
}
|
||||
|
||||
public function LoadCache()
|
||||
@@ -699,12 +702,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');
|
||||
@@ -717,10 +718,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -334,6 +334,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
|
||||
"db_table" => "priv_urp_userprofile",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"is_link" => true, /** @since 3.1.0 N°6482 */
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
|
||||
@@ -277,6 +277,7 @@ class URP_UserProfile extends UserRightsBaseClass
|
||||
"db_table" => "priv_urp_userprofile",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"is_link" => true, /** @since 3.1.0 N°6482 */
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
//MetaModel::Init_InheritAttributes();
|
||||
|
||||
@@ -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-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -238,7 +238,7 @@ class ApplicationContext
|
||||
{
|
||||
$aContextInputBlocks = [];
|
||||
foreach ($this->aValues as $sName => $sValue) {
|
||||
$aContextInputBlocks[] = InputUIBlockFactory::MakeForHidden("c[$sName]", utils::EscapeHtml($sValue));
|
||||
$aContextInputBlocks[] = InputUIBlockFactory::MakeForHidden("c[$sName]", $sValue);
|
||||
}
|
||||
return $aContextInputBlocks;
|
||||
}
|
||||
|
||||
@@ -2246,3 +2246,27 @@ 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);
|
||||
}
|
||||
@@ -34,15 +34,15 @@ class AuditCategory extends cmdbAbstractObject
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "application, grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_auditcategory",
|
||||
"db_key_field" => "id",
|
||||
"category" => "application,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('name'),
|
||||
"db_table" => "priv_auditcategory",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-folder.svg'),
|
||||
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-folder.svg'),
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
@@ -35,7 +35,7 @@ class AuditDomain extends cmdbAbstractObject
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "application, grant_by_profile",
|
||||
"category" => "application,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"complementary_name_attcode" => array('description'),
|
||||
@@ -86,15 +86,26 @@ class lnkAuditCategoryToAuditDomain extends cmdbAbstractObject
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "application, grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('category_id', 'domain_id'),
|
||||
"db_table" => "priv_link_audit_category_domain",
|
||||
"db_key_field" => "id",
|
||||
"category" => "application, grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array('category_id', 'domain_id'),
|
||||
"db_table" => "priv_link_audit_category_domain",
|
||||
"db_key_field" => "id",
|
||||
"db_finalclass_field" => "",
|
||||
"is_link" => true,
|
||||
"is_link" => true,
|
||||
'uniqueness_rules' => array(
|
||||
'no_duplicate' => array(
|
||||
'attributes' => array(
|
||||
0 => 'category_id',
|
||||
1 => 'domain_id',
|
||||
),
|
||||
'filter' => '',
|
||||
'disabled' => false,
|
||||
'is_blocking' => true,
|
||||
),
|
||||
),
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", array("targetclass" => "AuditCategory", "jointype" => '', "allowed_values" => null, "sql" => "category_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
|
||||
|
||||
@@ -35,7 +35,7 @@ class AuditRule extends cmdbAbstractObject
|
||||
{
|
||||
$aParams = array
|
||||
(
|
||||
"category" => "application, grant_by_profile",
|
||||
"category" => "application,grant_by_profile",
|
||||
"key_type" => "autoincrement",
|
||||
"name_attcode" => "name",
|
||||
"state_attcode" => "",
|
||||
|
||||
@@ -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-2023 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-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -47,6 +47,7 @@ use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
|
||||
use Combodo\iTop\Renderer\Console\ConsoleFormRenderer;
|
||||
use Combodo\iTop\Service\Links\LinkSetDataTransformer;
|
||||
use Combodo\iTop\Service\Links\LinkSetModel;
|
||||
use Combodo\iTop\Service\TemporaryObjects\TemporaryObjectHelper;
|
||||
|
||||
|
||||
define('OBJECT_PROPERTIES_TAB', 'ObjectProperties');
|
||||
@@ -187,9 +188,6 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
/** @var array initial attributes flags cache [attcode]['flags'] */
|
||||
protected $aInitialAttributesFlags;
|
||||
|
||||
protected $iUpdateLoopCount;
|
||||
|
||||
const MAX_UPDATE_LOOP_COUNT = 10;
|
||||
|
||||
/**
|
||||
* @var array First level classname, second level id, value number of calls done
|
||||
@@ -227,7 +225,6 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
$this->sDisplayMode = static::DEFAULT_DISPLAY_MODE;
|
||||
$this->bAllowWrite = false;
|
||||
$this->bAllowDelete = false;
|
||||
$this->iUpdateLoopCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -747,7 +744,11 @@ HTML
|
||||
$oPage->SetCurrentTab($sTabCode, $oAttDef->GetLabel().$sCount, $sTabDescription);
|
||||
|
||||
$aArgs = array('this' => $this);
|
||||
$bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE));
|
||||
|
||||
$sEditWhen = $oAttDef->GetEditWhen();
|
||||
$bIsEditableBasedOnEditWhen = ($sEditWhen === LINKSET_EDITWHEN_ALWAYS || $sEditWhen === LINKSET_EDITWHEN_ON_HOST_EDITION);
|
||||
|
||||
$bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE)) || !$bIsEditableBasedOnEditWhen;
|
||||
if ($bEditMode && (!$bReadOnly)) {
|
||||
$sInputId = $this->m_iFormId.'_'.$sAttCode;
|
||||
$sDisplayValue = ''; // not used
|
||||
@@ -1169,7 +1170,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
|
||||
@@ -2822,33 +2823,33 @@ JS
|
||||
}
|
||||
}
|
||||
// Custom operation for the form ?
|
||||
if (isset($aExtraParams['custom_operation'])) {
|
||||
$sOperation = $aExtraParams['custom_operation'];
|
||||
} else {
|
||||
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
|
||||
$sOperation = 'apply_modify';
|
||||
} else {
|
||||
$sOperation = 'apply_new';
|
||||
}
|
||||
}
|
||||
if (isset($aExtraParams['custom_operation'])) {
|
||||
$sOperation = $aExtraParams['custom_operation'];
|
||||
} else {
|
||||
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
|
||||
$sOperation = 'apply_modify';
|
||||
} else {
|
||||
$sOperation = 'apply_new';
|
||||
}
|
||||
}
|
||||
|
||||
$oContentBlock = new UIContentBlock();
|
||||
$oPage->AddUiBlock($oContentBlock);
|
||||
$oContentBlock = new UIContentBlock();
|
||||
$oPage->AddUiBlock($oContentBlock);
|
||||
|
||||
$oForm = new Form("form_{$this->m_iFormId}");
|
||||
$oForm->SetAction($sFormAction);
|
||||
$sOnSubmitForm = "let bOnSubmitForm = OnSubmit('form_{$this->m_iFormId}');";
|
||||
if (isset($aExtraParams['js_handlers']['form_on_submit'])) {
|
||||
$oForm->SetOnSubmitJsCode($sOnSubmitForm.$aExtraParams['js_handlers']['form_on_submit']);
|
||||
} else {
|
||||
$oForm->SetOnSubmitJsCode($sOnSubmitForm."return bOnSubmitForm;");
|
||||
}
|
||||
$oContentBlock->AddSubBlock($oForm);
|
||||
$oForm = new Form("form_{$this->m_iFormId}");
|
||||
$oForm->SetAction($sFormAction);
|
||||
$sOnSubmitForm = "let bOnSubmitForm = OnSubmit('form_{$this->m_iFormId}');";
|
||||
if (isset($aExtraParams['js_handlers']['form_on_submit'])) {
|
||||
$oForm->SetOnSubmitJsCode($sOnSubmitForm . $aExtraParams['js_handlers']['form_on_submit']);
|
||||
} else {
|
||||
$oForm->SetOnSubmitJsCode($sOnSubmitForm . "return bOnSubmitForm;");
|
||||
}
|
||||
$oContentBlock->AddSubBlock($oForm);
|
||||
|
||||
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
|
||||
// The object already exists in the database, it's a modification
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('id', $iKey, "{$sPrefix}_id"));
|
||||
}
|
||||
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
|
||||
// The object already exists in the database, it's a modification
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('id', $iKey, "{$sPrefix}_id"));
|
||||
}
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', $sOperation));
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $sClass));
|
||||
|
||||
@@ -2857,6 +2858,11 @@ JS
|
||||
$oPage->SetTransactionId($iTransactionId);
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', $iTransactionId));
|
||||
|
||||
// Add temporary object watchdog (only on root form)
|
||||
if (!utils::IsXmlHttpRequest()) {
|
||||
$oPage->add_ready_script(TemporaryObjectHelper::GetWatchDogJS($iTransactionId));
|
||||
}
|
||||
|
||||
// TODO 3.0.0: Is this (the if condition, not the code inside) still necessary?
|
||||
if (isset($aExtraParams['wizard_container']) && $aExtraParams['wizard_container']) {
|
||||
$sClassLabel = MetaModel::GetName($sClass);
|
||||
@@ -2867,34 +2873,34 @@ JS
|
||||
}
|
||||
}
|
||||
|
||||
$oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null);
|
||||
$oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null);
|
||||
|
||||
$oCancelButton = ButtonUIBlockFactory::MakeForCancel();
|
||||
$oCancelButton->AddCSSClasses(['action', 'cancel']);
|
||||
$oToolbarButtons->AddSubBlock($oCancelButton);
|
||||
$oApplyButton = ButtonUIBlockFactory::MakeForPrimaryAction($sApplyButton, null, null, true);
|
||||
$oApplyButton->AddCSSClass('action');
|
||||
$oToolbarButtons->AddSubBlock($oApplyButton);
|
||||
$bAreTransitionsHidden = isset($aExtraParams['hide_transitions']) && $aExtraParams['hide_transitions'] === true;
|
||||
$aTransitions = $this->EnumTransitions();
|
||||
if (!isset($aExtraParams['custom_operation']) && !$bAreTransitionsHidden && count($aTransitions)) {
|
||||
// Transitions are displayed only for the standard new/modify actions, not for modify_all or any other case...
|
||||
$oSetToCheckRights = DBObjectSet::FromObject($this);
|
||||
$oCancelButton = ButtonUIBlockFactory::MakeForCancel();
|
||||
$oCancelButton->AddCSSClasses(['action', 'cancel']);
|
||||
$oToolbarButtons->AddSubBlock($oCancelButton);
|
||||
$oApplyButton = ButtonUIBlockFactory::MakeForPrimaryAction($sApplyButton, null, null, true);
|
||||
$oApplyButton->AddCSSClass('action');
|
||||
$oToolbarButtons->AddSubBlock($oApplyButton);
|
||||
$bAreTransitionsHidden = isset($aExtraParams['hide_transitions']) && $aExtraParams['hide_transitions'] === true;
|
||||
$aTransitions = $this->EnumTransitions();
|
||||
if (!isset($aExtraParams['custom_operation']) && !$bAreTransitionsHidden && count($aTransitions)) {
|
||||
// Transitions are displayed only for the standard new/modify actions, not for modify_all or any other case...
|
||||
$oSetToCheckRights = DBObjectSet::FromObject($this);
|
||||
|
||||
$oTransitionPopoverMenu = new PopoverMenu();
|
||||
$sTPMSectionId = 'transitions';
|
||||
$oTransitionPopoverMenu->AddSection($sTPMSectionId);
|
||||
$aStimuli = Metamodel::EnumStimuli($sClass);
|
||||
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
|
||||
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass,
|
||||
$sStimulusCode, $oSetToCheckRights) : UR_ALLOWED_NO;
|
||||
switch ($iActionAllowed) {
|
||||
case UR_ALLOWED_YES:
|
||||
// Button to be displayed on its own on large screens
|
||||
$oButton = ButtonUIBlockFactory::MakeForPrimaryAction($aStimuli[$sStimulusCode]->GetLabel(), 'next_action', $sStimulusCode, true);
|
||||
$oButton->AddCSSClass('action');
|
||||
$oButton->SetColor(Button::ENUM_COLOR_SCHEME_NEUTRAL);
|
||||
$oToolbarButtons->AddSubBlock($oButton);
|
||||
$oTransitionPopoverMenu = new PopoverMenu();
|
||||
$sTPMSectionId = 'transitions';
|
||||
$oTransitionPopoverMenu->AddSection($sTPMSectionId);
|
||||
$aStimuli = Metamodel::EnumStimuli($sClass);
|
||||
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
|
||||
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass,
|
||||
$sStimulusCode, $oSetToCheckRights) : UR_ALLOWED_NO;
|
||||
switch ($iActionAllowed) {
|
||||
case UR_ALLOWED_YES:
|
||||
// Button to be displayed on its own on large screens
|
||||
$oButton = ButtonUIBlockFactory::MakeForPrimaryAction($aStimuli[$sStimulusCode]->GetLabel(), 'next_action', $sStimulusCode, true);
|
||||
$oButton->AddCSSClass('action');
|
||||
$oButton->SetColor(Button::ENUM_COLOR_SCHEME_NEUTRAL);
|
||||
$oToolbarButtons->AddSubBlock($oButton);
|
||||
|
||||
// Button to be displayed in a grouped button on smaller screens
|
||||
$oTPMPopupMenuItem = new JSPopupMenuItem('next_action--'.$oButton->GetId(), $oButton->GetLabel(), "$(`#{$oButton->GetId()}`).trigger(`click`);");
|
||||
@@ -3049,16 +3055,21 @@ JS
|
||||
|
||||
$oPage->SetCurrentTab('');
|
||||
|
||||
// Static fields values for wizard helper serialization
|
||||
$aWizardHelperStaticValues = [];
|
||||
|
||||
// Add as hidden inputs values that we want displayed if they're readonly
|
||||
if(isset($aExtraParams['forceFieldsSubmission'])){
|
||||
$aExtraFlags = $aExtraParams['fieldsFlags'] ?? [];
|
||||
foreach ($aExtraParams['forceFieldsSubmission'] as $sAttCode) {
|
||||
if(FormHelper::GetAttributeFlagsForObject($this, $sAttCode, $aExtraFlags) & OPT_ATT_READONLY) {
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('attr_'.$sPrefix.$sAttCode, $this->Get($sAttCode)));
|
||||
$aWizardHelperStaticValues[$sAttCode] = $this->Get($sAttCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$sWizardHelperStaticValues = json_encode($aWizardHelperStaticValues);
|
||||
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $sClass));
|
||||
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', $iTransactionId));
|
||||
foreach ($aExtraParams as $sName => $value) {
|
||||
@@ -3101,6 +3112,7 @@ JS
|
||||
var oWizardHelper$sPrefix = new WizardHelper('$sClass', '$sPrefix', '$sLifecycleStateForWizardHelper');
|
||||
oWizardHelper$sPrefix.SetFieldsMap($sJsonFieldsMap);
|
||||
oWizardHelper$sPrefix.SetFieldsCount($iFieldsCount);
|
||||
oWizardHelper$sPrefix.SetStaticValues($sWizardHelperStaticValues);
|
||||
EOF
|
||||
);
|
||||
$oPage->add_ready_script(
|
||||
@@ -4513,16 +4525,12 @@ HTML;
|
||||
*/
|
||||
public function DBInsertNoReload()
|
||||
{
|
||||
$this->LogCRUDEnter(__METHOD__);
|
||||
try {
|
||||
$res = parent::DBInsertNoReload();
|
||||
|
||||
$this->SetWarningsAsSessionMessages('create');
|
||||
|
||||
// Invoke extensions after insertion (the object must exist, have an id, etc.)
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) {
|
||||
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
|
||||
}
|
||||
} finally {
|
||||
if (static::IsCrudStackEmpty()) {
|
||||
// Avoid signaling the current object that links were modified
|
||||
@@ -4530,9 +4538,25 @@ HTML;
|
||||
static::FireEventDbLinksChangedForAllObjects();
|
||||
}
|
||||
}
|
||||
$this->LogCRUDExit(__METHOD__);
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function PostInsertActions(): void
|
||||
{
|
||||
parent::PostInsertActions();
|
||||
|
||||
// Invoke extensions after insertion (the object must exist, have an id, etc.)
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins(iApplicationObjectExtension::class) as $oExtensionInstance) {
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBInsert()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* Attaches InlineImages to the current object
|
||||
@@ -4544,13 +4568,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;
|
||||
@@ -4558,57 +4585,41 @@ HTML;
|
||||
|
||||
public function DBUpdate()
|
||||
{
|
||||
$this->LogCRUDEnter(__METHOD__);
|
||||
|
||||
try {
|
||||
if (count($this->ListChanges()) === 0) {
|
||||
$this->LogCRUDExit(__METHOD__);
|
||||
return $this->GetKey();
|
||||
}
|
||||
$res = parent::DBUpdate();
|
||||
|
||||
$this->SetWarningsAsSessionMessages('update');
|
||||
|
||||
// Protection against reentrance (e.g. cascading the update of ticket logs)
|
||||
// Note: This is based on the fix made on r 3190 in DBObject::DBUpdate()
|
||||
if (!MetaModel::StartReentranceProtection($this)) {
|
||||
$sClass = get_class($this);
|
||||
$sKey = $this->GetKey();
|
||||
IssueLog::Debug("CRUD: DBUpdate $sClass::$sKey Rejected (reentrance)", LogChannels::DM_CRUD);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
try {
|
||||
// Invoke extensions after the update (could be before)
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) {
|
||||
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
MetaModel::StopReentranceProtection($this);
|
||||
}
|
||||
|
||||
$aChanges = $this->ListChanges();
|
||||
if (count($aChanges) != 0) {
|
||||
$this->iUpdateLoopCount++;
|
||||
if ($this->iUpdateLoopCount > self::MAX_UPDATE_LOOP_COUNT) {
|
||||
$sClass = get_class($this);
|
||||
$sKey = $this->GetKey();
|
||||
$aPlugins = [];
|
||||
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) {
|
||||
$aPlugins[] = get_class($oExtensionInstance);
|
||||
}
|
||||
$sPlugins = implode(', ', $aPlugins);
|
||||
IssueLog::Error("CRUD: DBUpdate $sClass::$sKey Update loop detected plugins: $sPlugins", LogChannels::DM_CRUD);
|
||||
} else {
|
||||
return $this->DBUpdate();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (static::IsCrudStackEmpty()) {
|
||||
static::FireEventDbLinksChangedForAllObjects();
|
||||
}
|
||||
}
|
||||
$this->LogCRUDExit(__METHOD__);
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function PostUpdateActions(array $aChanges): void
|
||||
{
|
||||
parent::PostUpdateActions($aChanges);
|
||||
|
||||
// Invoke extensions after the update (could be before)
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach (MetaModel::EnumPlugins(iApplicationObjectExtension::class) as $oExtensionInstance) {
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBUpdate()");
|
||||
$oKPI = new ExecutionKPI();
|
||||
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBUpdate');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sMessageIdPrefix
|
||||
*
|
||||
@@ -4628,6 +4639,7 @@ HTML;
|
||||
|
||||
public function DBDelete(&$oDeletionPlan = null)
|
||||
{
|
||||
$this->LogCRUDEnter(__METHOD__);
|
||||
try {
|
||||
parent::DBDelete($oDeletionPlan);
|
||||
} finally {
|
||||
@@ -4637,6 +4649,7 @@ HTML;
|
||||
static::FireEventDbLinksChangedForAllObjects();
|
||||
}
|
||||
}
|
||||
$this->LogCRUDExit(__METHOD__);
|
||||
|
||||
return $oDeletionPlan;
|
||||
}
|
||||
@@ -4647,7 +4660,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);
|
||||
@@ -4665,9 +4680,15 @@ HTML;
|
||||
/** @var \iApplicationObjectExtension $oExtensionInstance */
|
||||
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
|
||||
{
|
||||
if ($oExtensionInstance->OnIsModified($this))
|
||||
{
|
||||
$sExtensionClass = get_class($oExtensionInstance);
|
||||
$oKPI = new ExecutionKPI();
|
||||
$bIsModified = $oExtensionInstance->OnIsModified($this);
|
||||
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnIsModified');
|
||||
if ($bIsModified) {
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified() -> true");
|
||||
return true;
|
||||
} else {
|
||||
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified() -> false");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4719,7 +4740,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);
|
||||
@@ -4767,7 +4790,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);
|
||||
@@ -6021,14 +6046,16 @@ JS
|
||||
// - we have a EVENT_DB_LINKS_CHANGED listener on Ticket that will update impacted items, so it will create new lnkApplicationSolutionToFunctionalCI
|
||||
// We want to avoid launching the listener twice, first here, and secondly after saving the Ticket in the listener
|
||||
// By disabling the event to be fired, we can remove the current object from the attribute !
|
||||
/** @noinspection PhpRedundantOptionalArgumentInspection */
|
||||
$oObject = MetaModel::GetObject($sClass, $sId, true);
|
||||
self::SetEventDBLinksChangedBlocked(true);
|
||||
MetaModel::StartReentranceProtection($oObject);
|
||||
$oObject->FireEvent(EVENT_DB_LINKS_CHANGED);
|
||||
MetaModel::StopReentranceProtection($oObject);
|
||||
if ($oObject->IsModified()) {
|
||||
$oObject->DBUpdate();
|
||||
$oObject = MetaModel::GetObject($sClass, $sId, false);
|
||||
// N°6408 The object can have been deleted
|
||||
if (!is_null($oObject)) {
|
||||
self::SetEventDBLinksChangedBlocked(true);
|
||||
MetaModel::StartReentranceProtection($oObject);
|
||||
$oObject->FireEvent(EVENT_DB_LINKS_CHANGED);
|
||||
MetaModel::StopReentranceProtection($oObject);
|
||||
if (count($oObject->ListChanges()) !== 0) {
|
||||
$oObject->DBUpdate();
|
||||
}
|
||||
}
|
||||
self::RemoveObjectAwaitingEventDbLinksChanged($sClass, $sId);
|
||||
cmdbAbstractObject::SetEventDBLinksChangedBlocked(false);
|
||||
|
||||
@@ -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-2023 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();
|
||||
|
||||
|
||||
@@ -667,7 +667,7 @@ class DashletUnknown extends Dashlet
|
||||
*/
|
||||
public function GetPropertiesFields(DesignerForm $oForm)
|
||||
{
|
||||
$oField = new DesignerLongTextField('xml', Dict::S('UI:DashletUnknown:Prop-XMLConfiguration'), $this->sOriginalDashletXML);
|
||||
$oField = new DesignerXMLField('xml', Dict::S('UI:DashletUnknown:Prop-XMLConfiguration'), $this->sOriginalDashletXML);
|
||||
$oForm->AddField($oField);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
|
||||
<classes>
|
||||
<class id="AbstractResource" _delta="define">
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
@@ -100,20 +100,11 @@
|
||||
<enable_class>URP_Profiles</enable_class>
|
||||
<enable_action>UR_ACTION_MODIFY</enable_action>
|
||||
</menu>
|
||||
<menu id="AuditCategories" xsi:type="DashboardMenuNode" _delta="define">
|
||||
<menu id="AuditCategories" xsi:type="OQLMenuNode" _delta="define">
|
||||
<rank>20</rank>
|
||||
<parent>AdminTools</parent>
|
||||
<definition>
|
||||
<layout>DashboardLayoutOneCol</layout>
|
||||
<title>Menu:WelcomeMenuPage</title>
|
||||
<cells>
|
||||
<cell id="0">
|
||||
<rank>0</rank>
|
||||
<dashlets>
|
||||
</dashlets>
|
||||
</cell>
|
||||
</cells>
|
||||
</definition>
|
||||
<oql><![CDATA[SELECT AuditCategory]]></oql>
|
||||
<do_search>1</do_search>
|
||||
<enable_class>AuditCategory</enable_class>
|
||||
<enable_action>UR_ACTION_MODIFY</enable_action>
|
||||
</menu>
|
||||
@@ -523,10 +514,18 @@
|
||||
<description>The object containing the document</description>
|
||||
<type>DBObject</type>
|
||||
</event_datum>
|
||||
<event_datum id="att_code">
|
||||
<description>The optional object attribute code hosting the document</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="document">
|
||||
<description>The document downloaded</description>
|
||||
<type>ormDocument</type>
|
||||
</event_datum>
|
||||
<event_datum id="content_disposition">
|
||||
<description>The content disposition of the document ("inline" or "attachment")</description>
|
||||
<type>string</type>
|
||||
</event_datum>
|
||||
<event_datum id="debug_info">
|
||||
<description>Debug string</description>
|
||||
<type>string</type>
|
||||
|
||||
@@ -1246,6 +1246,10 @@ JS
|
||||
} else {
|
||||
$oBlock = DashletFactory::MakeForDashletBadge($sClassIconUrl, $sHyperlink, $iCount, $sClassLabel, null, null, $aRefreshParams);
|
||||
}
|
||||
$sClassDescription = MetaModel::GetClassDescription($sClass);
|
||||
if (utils::IsNotNullOrEmptyString($sClassDescription)) {
|
||||
$oBlock->SetClassDescription($sClassDescription);
|
||||
}
|
||||
|
||||
return $oBlock;
|
||||
}
|
||||
@@ -1871,7 +1875,13 @@ class MenuBlock extends DisplayBlock
|
||||
/** @var array $aToolkitActions Any "legacy" toolkit menu item, which are now displayed in the same menu as the $aRegularActions, after them */
|
||||
$aToolkitActions = [];
|
||||
|
||||
if (!isset($aExtraParams['selection_mode']) || ($aExtraParams['selection_mode'] == "")) {
|
||||
// Display menu actions only if...
|
||||
if (
|
||||
// ... NOT in a selection mode
|
||||
(!isset($aExtraParams['selection_mode']) || ($aExtraParams['selection_mode'] == ""))
|
||||
// ... "menu" parameter is NOT EXPLICITLY disabled
|
||||
&& (!isset($aExtraParams['menu']) || $aExtraParams['menu'] === "1" || $aExtraParams['menu'] === true)
|
||||
) {
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sContext = $oAppContext->GetForLink();
|
||||
if (utils::IsNotNullOrEmptyString($sContext)) {
|
||||
@@ -2032,7 +2042,7 @@ class MenuBlock extends DisplayBlock
|
||||
}
|
||||
|
||||
//----------------------------------------------------
|
||||
// Any style but NOT "listInObject" (linksets) actions
|
||||
// Any style but NOT \DisplayBlock::ENUM_STYLE_LIST_IN_OBJECT (linksets) actions
|
||||
//----------------------------------------------------
|
||||
if ($this->m_sStyle !== static::ENUM_STYLE_LIST_IN_OBJECT) {
|
||||
switch ($iSetCount) {
|
||||
|
||||
@@ -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-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -1110,13 +1110,41 @@ $('#$sId').on('change keyup validate', function() { ValidateWithPattern('$sId',
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$sValue = "<textarea $sCSSClasses id=\"$sId\" name=\"$sName\">".utils::EscapeHtml($this->defaultValue)."</textarea>";
|
||||
$sValue = "<textarea $sCSSClasses id=\"$sId\" name=\"$sName\">".$this->PrepareValueForRendering()."</textarea>";
|
||||
}
|
||||
else {
|
||||
$sValue = "<div $sCSSClasses id=\"$sId\">".utils::EscapeHtml($this->defaultValue)."</div>";
|
||||
$sValue = "<div $sCSSClasses id=\"$sId\">".$this->PrepareValueForRendering()."</div>";
|
||||
}
|
||||
return array('label' => $this->sLabel, 'value' => $sValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null The value itself as expected for rendering. May it be encoded, escaped or else.
|
||||
* @since 3.1.0 N°6405
|
||||
*/
|
||||
protected function PrepareValueForRendering(): ?string
|
||||
{
|
||||
return utils::EscapeHtml($this->defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class DesignerXMLField
|
||||
*
|
||||
* Field to display XML content
|
||||
*
|
||||
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
|
||||
* @since 3.1.0 N°6405
|
||||
*/
|
||||
class DesignerXMLField extends DesignerLongTextField
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function PrepareValueForRendering(): ?string
|
||||
{
|
||||
return utils::EscapeHtml($this->defaultValue, true);
|
||||
}
|
||||
}
|
||||
|
||||
class DesignerIntegerField extends DesignerFormField
|
||||
|
||||
@@ -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-2023 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-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -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-2023 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-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -74,6 +74,7 @@ abstract class Query extends cmdbAbstractObject
|
||||
"default_value" => 0,
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array(),
|
||||
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
|
||||
)));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime("export_last_date", array(
|
||||
@@ -82,6 +83,7 @@ abstract class Query extends cmdbAbstractObject
|
||||
"default_value" => null,
|
||||
"is_null_allowed" => true,
|
||||
"depends_on" => array(),
|
||||
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
|
||||
)));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalKey("export_last_user_id",
|
||||
@@ -93,14 +95,16 @@ abstract class Query extends cmdbAbstractObject
|
||||
"depends_on"=>array(),
|
||||
"display_style"=>'select',
|
||||
"always_load_in_tables"=>false,
|
||||
"on_target_delete"=>DEL_SILENT
|
||||
"on_target_delete"=>DEL_SILENT,
|
||||
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
|
||||
)));
|
||||
|
||||
MetaModel::Init_AddAttribute(new AttributeExternalField("export_last_user_contact",
|
||||
array(
|
||||
"allowed_values"=>null,
|
||||
"extkey_attcode"=> "export_last_user_id",
|
||||
"target_attcode"=>"contactid"
|
||||
"target_attcode"=>"contactid",
|
||||
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
|
||||
)));
|
||||
|
||||
// Display lists
|
||||
@@ -292,7 +296,7 @@ class QueryOQL extends Query
|
||||
}
|
||||
catch
|
||||
(OQLException $e) {
|
||||
$oAlert = AlertUIBlockFactory::MakeForFailure(Dict::Format('UI:RunQuery:Error'), $e->getHtmlDesc())
|
||||
$oAlert = AlertUIBlockFactory::MakeForFailure(Dict::S('UI:RunQuery:Error'), $e->getHtmlDesc())
|
||||
->SetIsClosable(false)
|
||||
->SetIsCollapsible(false);
|
||||
$oAlert->AddCSSClass('mb-5');
|
||||
|
||||
@@ -99,4 +99,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', []);
|
||||
}
|
||||
@@ -163,13 +163,11 @@ class UIExtKeyWidget
|
||||
$oPage->add_linked_script('../js/extkeywidget.js');
|
||||
$oPage->add_linked_script('../js/forms-json-utils.js');
|
||||
|
||||
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
|
||||
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_MODIFY) && $bAllowTargetCreation);
|
||||
$bExtensions = true;
|
||||
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
|
||||
$sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
|
||||
|
||||
|
||||
|
||||
$sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL());
|
||||
if ($this->bSearchMode) {
|
||||
$sWizHelper = 'null';
|
||||
@@ -977,6 +975,10 @@ HTML
|
||||
// Remove blob edition from creation form @see N°5863 to allow blob edition in modal context
|
||||
FormHelper::DisableAttributeBlobInputs($this->sTargetClass, $aFormExtraParams);
|
||||
|
||||
if(FormHelper::HasMandatoryAttributeBlobInputs($oNewObj)){
|
||||
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal());
|
||||
}
|
||||
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), $aFormExtraParams);
|
||||
$oPage->add(<<<HTML
|
||||
</div>
|
||||
@@ -1070,18 +1072,27 @@ JS
|
||||
{
|
||||
$oObj = MetaModel::NewObject($this->sTargetClass);
|
||||
$aErrors = $oObj->UpdateObjectFromPostedForm($this->iId);
|
||||
if (count($aErrors) == 0)
|
||||
{
|
||||
$oObj->DBInsert();
|
||||
if (count($aErrors) == 0) {
|
||||
|
||||
// Retrieve JSON data
|
||||
$sJSON = utils::ReadParam('json', '{}', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
|
||||
$oJSON = json_decode($sJSON);
|
||||
|
||||
$oObj->SetContextSection('temporary_objects', [
|
||||
'create' => [
|
||||
'transaction_id' => utils::ReadParam('root_transaction_id', '', false, utils::ENUM_SANITIZATION_FILTER_TRANSACTION_ID),
|
||||
'host_class' => $oJSON->m_sClass,
|
||||
'host_att_code' => $this->sAttCode,
|
||||
],
|
||||
]);
|
||||
$oObj->DBInsertNoReload();
|
||||
|
||||
return array('name' => $oObj->GetName(), 'id' => $oObj->GetKey());
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return array('error' => implode(' ', $aErrors), 'id' => 0);
|
||||
}
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
catch (Exception $e) {
|
||||
return array('error' => $e->getMessage(), 'id' => 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +143,10 @@ JS
|
||||
|
||||
// Remove blob edition from creation form @see N°5863 to allow blob edition in modal context
|
||||
FormHelper::DisableAttributeBlobInputs($sRealClass, $aFormExtraParams);
|
||||
|
||||
if(FormHelper::HasMandatoryAttributeBlobInputs($oObj)){
|
||||
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal());
|
||||
}
|
||||
|
||||
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), $aFormExtraParams);
|
||||
}
|
||||
|
||||
@@ -178,17 +178,18 @@ class UILinksWidget
|
||||
|
||||
$oDisplayBlock = new DisplayBlock($oFilter, 'search', false);
|
||||
$oBlock->AddSubBlock($oDisplayBlock->GetDisplay($oPage, "SearchFormToAdd_{$sLinkedSetId}",
|
||||
array(
|
||||
'menu' => false,
|
||||
[
|
||||
'menu' => false,
|
||||
'result_list_outer_selector' => "SearchResultsToAdd_{$sLinkedSetId}",
|
||||
'table_id' => "add_{$sLinkedSetId}",
|
||||
'table_inner_id' => "ResultsToAdd_{$sLinkedSetId}",
|
||||
'selection_mode' => true,
|
||||
'json' => $sJson,
|
||||
'cssCount' => '#count_'.$this->m_sAttCode.$this->m_sNameSuffix,
|
||||
'query_params' => $oFilter->GetInternalParams(),
|
||||
'hidden_criteria' => $sAlreadyLinkedExpression,
|
||||
)));
|
||||
'table_id' => "add_{$sLinkedSetId}",
|
||||
'table_inner_id' => "ResultsToAdd_{$sLinkedSetId}",
|
||||
'selection_mode' => true,
|
||||
'json' => $sJson,
|
||||
'cssCount' => '#count_'.$this->m_sAttCode.$this->m_sNameSuffix,
|
||||
'query_params' => $oFilter->GetInternalParams(),
|
||||
'hidden_criteria' => $sAlreadyLinkedExpression,
|
||||
'submit_on_load' => false,
|
||||
]));
|
||||
|
||||
$oBlock->AddForm();
|
||||
}
|
||||
|
||||
@@ -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,22 +52,31 @@ 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';
|
||||
/**
|
||||
@@ -81,22 +91,22 @@ class utils
|
||||
public const ENUM_SANITIZATION_FILTER_OPERATION = 'operation';
|
||||
/**
|
||||
* @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';
|
||||
/**
|
||||
@@ -106,12 +116,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';
|
||||
|
||||
@@ -154,6 +165,8 @@ class utils
|
||||
|
||||
private static $iNextId = 0;
|
||||
|
||||
private static $m_sAppRootUrl = null;
|
||||
|
||||
protected static function LoadParamFile($sParamFile)
|
||||
{
|
||||
if (!file_exists($sParamFile)) {
|
||||
@@ -395,6 +408,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)
|
||||
{
|
||||
@@ -415,6 +432,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_ROUTE:
|
||||
case static::ENUM_SANITIZATION_FILTER_OPERATION:
|
||||
@@ -480,6 +504,7 @@ class utils
|
||||
|
||||
// For URL
|
||||
case static::ENUM_SANITIZATION_FILTER_URL:
|
||||
// N°6350 - returns only valid URLs
|
||||
$retValue = filter_var($value, FILTER_VALIDATE_URL);
|
||||
break;
|
||||
|
||||
@@ -1023,7 +1048,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');
|
||||
@@ -1044,8 +1069,9 @@ class utils
|
||||
}
|
||||
$sUrl = str_replace(SERVER_NAME_PLACEHOLDER, $sServerName, $sUrl);
|
||||
}
|
||||
static::$m_sAppRootUrl = $sUrl;
|
||||
}
|
||||
return $sUrl;
|
||||
return static::$m_sAppRootUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1396,13 +1422,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().'/';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1970,6 +2006,7 @@ SQL;
|
||||
|
||||
/**
|
||||
* @param string $sValue
|
||||
* @param bool $bDoubleEncode Whether to double encode the value or not
|
||||
*
|
||||
* @return string passed value with only characters having a special meaning in HTML escaped as entities
|
||||
* Since 3.0.0 we were using for this {@link HtmlEntities} but it was overkill and leads to double escaping !
|
||||
@@ -1977,14 +2014,15 @@ SQL;
|
||||
* @uses \htmlspecialchars()
|
||||
* @link https://www.php.net/manual/fr/function.htmlspecialchars.php
|
||||
* @since 3.0.0 N°3623
|
||||
* @since 3.1.0 N°6405 Add $bDoubleEncode parameter
|
||||
*/
|
||||
public static function EscapeHtml($sValue)
|
||||
public static function EscapeHtml($sValue, bool $bDoubleEncode = false)
|
||||
{
|
||||
return htmlspecialchars(
|
||||
$sValue ?? '',
|
||||
ENT_QUOTES | ENT_DISALLOWED | ENT_HTML5,
|
||||
WebPage::PAGES_CHARSET,
|
||||
false
|
||||
$bDoubleEncode
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2045,6 +2083,9 @@ SQL;
|
||||
*/
|
||||
public static function TextToHtml($sText)
|
||||
{
|
||||
if (static::IsNullOrEmptyString($sText)){
|
||||
return '';
|
||||
}
|
||||
$sText = str_replace("\r\n", "\n", $sText);
|
||||
$sText = str_replace("\r", "\n", $sText);
|
||||
|
||||
@@ -2260,24 +2301,7 @@ SQL;
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2299,24 +2323,7 @@ SQL;
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2331,12 +2338,7 @@ SQL;
|
||||
*/
|
||||
public static function GetCurrentModuleUrl()
|
||||
{
|
||||
$sDir = static::GetCurrentModuleDir(1);
|
||||
if ( $sDir !== '')
|
||||
{
|
||||
return static::GetAbsoluteUrlModulesRoot().'/'.$sDir;
|
||||
}
|
||||
return '';
|
||||
return ModuleService::GetInstance()->GetCurrentModuleUrl(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2346,8 +2348,7 @@ SQL;
|
||||
*/
|
||||
public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
|
||||
{
|
||||
$sModuleName = static::GetCurrentModuleName(1);
|
||||
return MetaModel::GetModuleSetting($sModuleName, $sProperty, $defaultvalue);
|
||||
return ModuleService::GetInstance()->GetCurrentModuleSetting($sProperty, $defaultvalue);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2356,12 +2357,7 @@ SQL;
|
||||
*/
|
||||
public static function GetCompiledModuleVersion($sModuleName)
|
||||
{
|
||||
$aModulesInfo = GetModulesInfo();
|
||||
if (array_key_exists($sModuleName, $aModulesInfo))
|
||||
{
|
||||
return $aModulesInfo[$sModuleName]['version'];
|
||||
}
|
||||
return null;
|
||||
return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2686,24 +2682,26 @@ SQL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the local path relative to the iTop installation of an existing file
|
||||
* Returns the local path relative to the iTop installation (APPROOT or the given base path)
|
||||
* Dir separator is changed to '/' for consistency among the different OS
|
||||
*
|
||||
* @param string $sAbsolutePath absolute path
|
||||
* @param string $sBasePath Base path for the resulting local path (default APPROOT)
|
||||
*
|
||||
* @return false|string
|
||||
* @return false|string The generated local path or false if absolute path is not under the base path
|
||||
* @since 3.1.1 Added base path defaulted to previous version APPROOT
|
||||
*/
|
||||
final public static function LocalPath($sAbsolutePath)
|
||||
final public static function LocalPath($sAbsolutePath, string $sBasePath = APPROOT)
|
||||
{
|
||||
$sRootPath = realpath(APPROOT);
|
||||
$sRootPath = realpath($sBasePath);
|
||||
$sFullPath = realpath($sAbsolutePath);
|
||||
if (($sFullPath === false) || !self::StartsWith($sFullPath, $sRootPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$sLocalPath = substr($sFullPath, strlen($sRootPath.DIRECTORY_SEPARATOR));
|
||||
$sLocalPath = str_replace(DIRECTORY_SEPARATOR, '/', $sLocalPath);
|
||||
return $sLocalPath;
|
||||
|
||||
return str_replace(DIRECTORY_SEPARATOR, '/', $sLocalPath);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2895,7 +2893,7 @@ HTML;
|
||||
|
||||
// Add already loaded classes
|
||||
$aCurrentClasses = array_fill_keys(get_declared_classes(), '');
|
||||
$aClassMap = array_merge($aClassMap, $aCurrentClasses);
|
||||
$aClassMap = array_merge($aCurrentClasses, $aClassMap);
|
||||
|
||||
foreach ($aClassMap as $sPHPClass => $sPHPFile) {
|
||||
$bSkipped = false;
|
||||
@@ -2920,11 +2918,12 @@ HTML;
|
||||
$bSkipped = true; // file not found
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(!$bSkipped){
|
||||
try {
|
||||
$oRefClass = new ReflectionClass($sPHPClass);
|
||||
if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable()) {
|
||||
if ($oRefClass->implementsInterface($sInterface) &&
|
||||
!$oRefClass->isInterface() && !$oRefClass->isAbstract() && !$oRefClass->isTrait()) {
|
||||
$aMatchingClasses[] = $sPHPClass;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
@@ -3369,5 +3368,22 @@ HTML;
|
||||
{
|
||||
return in_array($sTrait, self::TraitsUsedByClass($sClass, true));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get stack trace as string array.
|
||||
*
|
||||
* @return array
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public static function GetStackTraceAsArray(): array
|
||||
{
|
||||
$e = new Exception();
|
||||
$aTrace = explode("\n", $e->getTraceAsString());
|
||||
// Remove call to this method
|
||||
array_shift($aTrace);
|
||||
// Remove Main
|
||||
array_pop($aTrace);
|
||||
|
||||
return $aTrace;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -351,6 +351,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()
|
||||
{
|
||||
@@ -363,6 +364,32 @@ 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
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Function with an old pattern of code
|
||||
* @deprecated 3.1.0
|
||||
@@ -371,11 +398,9 @@ JS;
|
||||
{
|
||||
$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-2023 Combodo SARL
|
||||
*/
|
||||
|
||||
@@ -11,7 +11,7 @@ define('APPCONF', APPROOT.'conf/');
|
||||
*
|
||||
* @see ITOP_CORE_VERSION to get full iTop core version
|
||||
*/
|
||||
define('ITOP_DESIGN_LATEST_VERSION', '3.1');
|
||||
define('ITOP_DESIGN_LATEST_VERSION', '3.2');
|
||||
|
||||
/**
|
||||
* Constant containing the iTop core version, whatever application was built
|
||||
@@ -23,7 +23,7 @@ define('ITOP_DESIGN_LATEST_VERSION', '3.1');
|
||||
* @used-by utils::GetItopVersionWikiSyntax()
|
||||
* @used-by iTopModulesPhpVersionIntegrationTest
|
||||
*/
|
||||
define('ITOP_CORE_VERSION', '3.1.0');
|
||||
define('ITOP_CORE_VERSION', '3.2.0');
|
||||
|
||||
/**
|
||||
* @since 3.0.4 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']
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -59,9 +59,17 @@ 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
|
||||
* @since 3.1.0-4 N°6848 backport of restoring cnx on null parameter value
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
101
core/TemporaryObjectDescriptor.php
Normal file
101
core/TemporaryObjectDescriptor.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2023 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class TemporaryObjectDescriptor
|
||||
*
|
||||
* Descriptor to track a temporary object.
|
||||
*
|
||||
* @experimental do not use, this feature will be part of a future version
|
||||
*
|
||||
* @since 3.1
|
||||
*/
|
||||
class TemporaryObjectDescriptor extends DBObject
|
||||
{
|
||||
public static function Init()
|
||||
{
|
||||
$aParams = array(
|
||||
'category' => 'core',
|
||||
'key_type' => 'autoincrement',
|
||||
'name_attcode' => array('item_class', 'temp_id'),
|
||||
'image_attcode' => '',
|
||||
'state_attcode' => '',
|
||||
'reconc_keys' => array(''),
|
||||
'db_table' => 'priv_temporary_object_descriptor',
|
||||
'db_key_field' => 'id',
|
||||
'db_finalclass_field' => '',
|
||||
'style' => new ormStyle(null, null, null, null, null, null),
|
||||
'indexes' => array(
|
||||
1 =>
|
||||
array(
|
||||
0 => 'temp_id',
|
||||
),
|
||||
2 =>
|
||||
array(
|
||||
0 => 'item_class',
|
||||
1 => 'item_id',
|
||||
),
|
||||
),
|
||||
);
|
||||
MetaModel::Init_Params($aParams);
|
||||
MetaModel::Init_InheritAttributes();
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime('expiration_date', array('sql' => 'expiration_date', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString('temp_id', array('sql' => 'temp_id', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString('item_class', array('sql' => 'item_class', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeObjectKey('item_id', array('class_attcode' => 'item_class', 'sql' => 'item_id', 'is_null_allowed' => true, 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeDateTime('creation_date', array('sql' => 'creation_date', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString('host_class', array('sql' => 'host_class', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeObjectKey('host_id', array('class_attcode' => 'host_class', 'sql' => 'host_id', 'is_null_allowed' => true, 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString('host_att_code', array('sql' => 'host_att_code', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnum("operation", array("allowed_values" => new ValueSetEnum('create,delete'), "sql" => "operation", "default_value" => "create", "is_null_allowed" => true, "depends_on" => array())));
|
||||
|
||||
MetaModel::Init_SetZListItems('details', array(
|
||||
0 => 'temp_id',
|
||||
1 => 'item_class',
|
||||
2 => 'item_id',
|
||||
3 => 'creation_date',
|
||||
4 => 'expiration_date',
|
||||
5 => 'meta',
|
||||
));
|
||||
MetaModel::Init_SetZListItems('standard_search', array(
|
||||
0 => 'temp_id',
|
||||
1 => 'item_class',
|
||||
2 => 'item_id',
|
||||
));
|
||||
MetaModel::Init_SetZListItems('list', array(
|
||||
0 => 'temp_id',
|
||||
1 => 'item_class',
|
||||
2 => 'item_id',
|
||||
3 => 'creation_date',
|
||||
4 => 'expiration_date',
|
||||
));;
|
||||
}
|
||||
|
||||
|
||||
public function DBInsertNoReload()
|
||||
{
|
||||
$this->SetCurrentDateIfNull('creation_date');
|
||||
|
||||
return parent::DBInsertNoReload();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set/Update all of the '_item' fields
|
||||
*
|
||||
* @param object $oItem Container item
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function SetItem($oItem, $bUpdateOnChange = false)
|
||||
{
|
||||
$sClass = get_class($oItem);
|
||||
$iItemId = $oItem->GetKey();
|
||||
|
||||
$this->Set('item_class', $sClass);
|
||||
$this->Set('item_id', $iItemId);
|
||||
}
|
||||
}
|
||||
@@ -650,6 +650,9 @@ class ActionEmail extends ActionNotification
|
||||
$aMessageContent['subject'] = 'TEST['.$aMessageContent['subject'].']';
|
||||
$aMessageContent['body'] = $sTestBody;
|
||||
$aMessageContent['to'] = $this->Get('test_recipient');
|
||||
// N°6677 Ensure emails in test are never sent to cc'd and bcc'd addresses
|
||||
$aMessageContent['cc'] = '';
|
||||
$aMessageContent['bcc'] = '';
|
||||
}
|
||||
// Note: N°4849 We pass the "References" identifier instead of the "Message-ID" on purpose as we want notifications emails to group around the triggering iTop object, not just the users' replies to the notification
|
||||
$aMessageContent['in_reply_to'] = $aMessageContent['references'];
|
||||
|
||||
@@ -9,9 +9,7 @@ use Combodo\iTop\Application\UI\Links\Set\BlockLinkSetDisplayAsProperty;
|
||||
use Combodo\iTop\Form\Field\LabelField;
|
||||
use Combodo\iTop\Form\Field\TextAreaField;
|
||||
use Combodo\iTop\Form\Form;
|
||||
use Combodo\iTop\Form\Validator\LinkedSetValidator;
|
||||
use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator;
|
||||
use Combodo\iTop\Form\Validator\Validator;
|
||||
use Combodo\iTop\Form\Validator\CustomRegexpValidator;
|
||||
use Combodo\iTop\Renderer\BlockRenderer;
|
||||
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
|
||||
use Combodo\iTop\Service\Links\LinkSetModel;
|
||||
@@ -93,8 +91,11 @@ define('LINKSET_EDITMODE_ACTIONS', 2); // Show the usual 'Actions' popup menu
|
||||
define('LINKSET_EDITMODE_INPLACE', 3); // The "linked" objects can be created/modified/deleted in place
|
||||
define('LINKSET_EDITMODE_ADDREMOVE', 4); // The "linked" objects can be added/removed in place
|
||||
|
||||
define('LINKSET_RELATIONTYPE_PROPERTY', 'property');
|
||||
define('LINKSET_RELATIONTYPE_LINK', 'link');
|
||||
define('LINKSET_EDITWHEN_NEVER', 0); // The linkset cannot be edited at all from inside this object
|
||||
define('LINKSET_EDITWHEN_ON_HOST_EDITION', 1); // The only possible action is to open a new window to create a new object
|
||||
define('LINKSET_EDITWHEN_ON_HOST_DISPLAY', 2); // Show the usual 'Actions' popup menu
|
||||
define('LINKSET_EDITWHEN_ALWAYS', 3); // Show the usual 'Actions' popup menu
|
||||
|
||||
|
||||
define('LINKSET_DISPLAY_STYLE_PROPERTY', 'property');
|
||||
define('LINKSET_DISPLAY_STYLE_TAB', 'tab');
|
||||
@@ -796,7 +797,7 @@ abstract class AttributeDefinition
|
||||
public function HasAValue($proposedValue): bool
|
||||
{
|
||||
// Default implementation, we don't really know what type $proposedValue will be
|
||||
return is_null($proposedValue);
|
||||
return !(is_null($proposedValue));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1126,7 +1127,7 @@ abstract class AttributeDefinition
|
||||
|
||||
// Validation pattern
|
||||
if ($this->GetValidationPattern() !== '') {
|
||||
$oFormField->AddValidator(new Validator($this->GetValidationPattern()));
|
||||
$oFormField->AddValidator(new CustomRegexpValidator($this->GetValidationPattern()));
|
||||
}
|
||||
|
||||
// Description
|
||||
@@ -1155,6 +1156,13 @@ abstract class AttributeDefinition
|
||||
$oFormField->AddMetadata('value-raw', (string)$oObject->Get($this->GetCode()));
|
||||
}
|
||||
|
||||
// We don't want to invalidate field because of old untouched values that are no longer valid
|
||||
$aModifiedAttCodes = $oObject->ListChanges();
|
||||
$bAttributeHasBeenModified = array_key_exists($this->GetCode(), $aModifiedAttCodes);
|
||||
if (false === $bAttributeHasBeenModified) {
|
||||
$oFormField->SetValidationDisabled(true);
|
||||
}
|
||||
|
||||
return $oFormField;
|
||||
}
|
||||
|
||||
@@ -1697,20 +1705,19 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
|
||||
/**
|
||||
* @return string see LINKSET_EDITMODE_* constants
|
||||
* @since 3.1.0 N°5563 relations are edited using new attributes in details mode, but as nothing changed in edit mode we are still using edit_mode attribute
|
||||
*/
|
||||
public function GetEditMode()
|
||||
{
|
||||
return $this->GetOptional('edit_mode', LINKSET_EDITMODE_ACTIONS);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string see LINKSET_RELATIONTYPE_* constants
|
||||
* @since 3.1.0 N°5563
|
||||
* @return int see LINKSET_EDITWHEN_* constants
|
||||
* @since 3.1.1 3.2.0 N°6385
|
||||
*/
|
||||
public function GetRelationType()
|
||||
public function GetEditWhen(): int
|
||||
{
|
||||
return $this->GetOptional('relation_type', LINKSET_RELATIONTYPE_LINK);
|
||||
return $this->GetOptional('edit_when', LINKSET_EDITWHEN_ALWAYS);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1737,15 +1744,6 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
return $this->GetOptional('with_php_constraint', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
* @since 3.1.0 N°5563
|
||||
*/
|
||||
public function GetReadOnly()
|
||||
{
|
||||
return $this->GetOptional('read_only', false);
|
||||
}
|
||||
|
||||
public function GetLinkedClass()
|
||||
{
|
||||
return $this->Get('linked_class');
|
||||
@@ -1772,7 +1770,53 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
}
|
||||
|
||||
/** @inheritDoc * */
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true): string
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if($this->GetDisplayStyle() === LINKSET_DISPLAY_STYLE_TAB){
|
||||
return $this->GetAsHTMLForTab($sValue, $oHostObject, $bLocalize);
|
||||
}
|
||||
else{
|
||||
return $this->GetAsHTMLForProperty($sValue, $oHostObject, $bLocalize);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetAsHTMLForTab($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (is_object($sValue) && ($sValue instanceof ormLinkSet))
|
||||
{
|
||||
$sValue->Rewind();
|
||||
$aItems = array();
|
||||
while ($oObj = $sValue->Fetch())
|
||||
{
|
||||
// Show only relevant information (hide the external key to the current object)
|
||||
$aAttributes = array();
|
||||
foreach(MetaModel::ListAttributeDefs($this->GetLinkedClass()) as $sAttCode => $oAttDef)
|
||||
{
|
||||
if ($sAttCode == $this->GetExtKeyToMe())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if ($oAttDef->IsExternalField())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
$sAttValue = $oObj->GetAsHTML($sAttCode);
|
||||
if (strlen($sAttValue) > 0)
|
||||
{
|
||||
$aAttributes[] = $sAttValue;
|
||||
}
|
||||
}
|
||||
$sAttributes = implode(', ', $aAttributes);
|
||||
$aItems[] = $sAttributes;
|
||||
}
|
||||
|
||||
return implode('<br/>', $aItems);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function GetAsHTMLForProperty($sValue, $oHostObject = null, $bLocalize = true): string
|
||||
{
|
||||
try {
|
||||
|
||||
@@ -2447,8 +2491,6 @@ class AttributeLinkedSet extends AttributeDefinition
|
||||
$oFormField->SetLnkAttributesToDisplay($aLnkAttributesToDisplay);
|
||||
}
|
||||
|
||||
$oFormField->AddValidator(new LinkedSetValidator());
|
||||
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
return $oFormField;
|
||||
@@ -2558,15 +2600,6 @@ class AttributeLinkedSetIndirect extends AttributeLinkedSet
|
||||
return $this->GetOptional("duplicates", false);
|
||||
} // The same object may be linked several times... or not...
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
* @since 3.1.0 N°5563
|
||||
*/
|
||||
public function GetReadOnly()
|
||||
{
|
||||
return $this->GetOptional('read_only', false);
|
||||
}
|
||||
|
||||
public function GetTrackingLevel()
|
||||
{
|
||||
return $this->GetOptional('tracking_level',
|
||||
@@ -3873,6 +3906,12 @@ class AttributeApplicationLanguage extends AttributeString
|
||||
{
|
||||
$aLanguageCodes[$sLangCode] = $aInfo['description'].' ('.$aInfo['localized_description'].')';
|
||||
}
|
||||
|
||||
// N°6462 This should be sorted directly in \Dict during the compilation but we can't for 2 reasons:
|
||||
// - Additional languages can be added on the fly even though it is not recommended
|
||||
// - Formatting is done at run time (just above)
|
||||
natcasesort($aLanguageCodes);
|
||||
|
||||
$aParams["allowed_values"] = new ValueSetEnum($aLanguageCodes);
|
||||
parent::__construct($sCode, $aParams);
|
||||
}
|
||||
@@ -4163,7 +4202,7 @@ class AttributePassword extends AttributeString implements iAttributeNoGroupBy
|
||||
|
||||
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
|
||||
{
|
||||
if (strlen($sValue) == 0)
|
||||
if (utils::IsNullOrEmptyString($sValue))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
@@ -7196,14 +7235,27 @@ class AttributeExternalKey extends AttributeDBFieldVoid
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (MetaModel::IsValidObject($proposedValue))
|
||||
{
|
||||
if (MetaModel::IsValidObject($proposedValue)) {
|
||||
return $proposedValue->GetKey();
|
||||
}
|
||||
|
||||
return (int)$proposedValue;
|
||||
}
|
||||
|
||||
/** @inheritdoc @since 3.1 */
|
||||
public function WriteExternalValues(DBObject $oHostObject): void
|
||||
{
|
||||
$sTargetKey = $oHostObject->Get($this->GetCode());
|
||||
$oFilter = DBSearch::FromOQL('SELECT `'.TemporaryObjectDescriptor::class.'` WHERE item_class=:class AND item_id=:id');
|
||||
$oSet = new DBObjectSet($oFilter, [], ['class' => $this->GetTargetClass(), 'id' => $sTargetKey]);
|
||||
while ($oTemporaryObjectDescriptor = $oSet->Fetch()) {
|
||||
$oTemporaryObjectDescriptor->Set('host_class', get_class($oHostObject));
|
||||
$oTemporaryObjectDescriptor->Set('host_id', $oHostObject->GetKey());
|
||||
$oTemporaryObjectDescriptor->Set('host_att_code', $this->GetCode());
|
||||
$oTemporaryObjectDescriptor->DBUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public function GetMaximumComboLength()
|
||||
{
|
||||
return $this->GetOptional('max_combo_length', MetaModel::GetConfig()->Get('max_combo_length'));
|
||||
@@ -7267,6 +7319,7 @@ class AttributeExternalKey extends AttributeDBFieldVoid
|
||||
|
||||
public function MakeFormField(DBObject $oObject, $oFormField = null)
|
||||
{
|
||||
/** @var \Combodo\iTop\Form\Field\Field $oFormField */
|
||||
if ($oFormField === null) {
|
||||
// Later : We should check $this->Get('display_style') and create a Radio / Select / ... regarding its value
|
||||
$sFormFieldClass = static::GetFormFieldClass();
|
||||
@@ -7297,19 +7350,12 @@ class AttributeExternalKey extends AttributeDBFieldVoid
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
$oSearch = DBSearch::FromOQL($this->GetValuesDef()->GetFilterExpression());
|
||||
$oSearch->SetInternalParams(array('this' => $oObject));
|
||||
$oFormField->SetSearch($oSearch);
|
||||
}
|
||||
|
||||
// If ExtKey is mandatory, we add a validator to ensure that the value 0 is not selected
|
||||
if ($oObject->GetAttributeFlags($this->GetCode()) & OPT_ATT_MANDATORY)
|
||||
{
|
||||
$oFormField->AddValidator(new NotEmptyExtKeyValidator());
|
||||
}
|
||||
|
||||
parent::MakeFormField($oObject, $oFormField);
|
||||
|
||||
return $oFormField;
|
||||
@@ -7935,6 +7981,17 @@ class AttributeExternalField extends AttributeDefinition
|
||||
return $oExtAttDef->MakeRealValue($proposedValue, $oHostObj);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @since 3.1.0 N°6271 Delegate to remote attribute to ensure cascading computed values
|
||||
*/
|
||||
public function GetSQLValues($value)
|
||||
{
|
||||
$oExtAttDef = $this->GetExtAttDef();
|
||||
|
||||
return $oExtAttDef->GetSQLValues($value);
|
||||
}
|
||||
|
||||
public function ScalarToSQL($value)
|
||||
{
|
||||
// This one could be used in case of filtering only
|
||||
@@ -8343,7 +8400,7 @@ class AttributeBlob extends AttributeDefinition
|
||||
$aValues[$this->GetCode().'_data'] = '';
|
||||
$aValues[$this->GetCode().'_mimetype'] = '';
|
||||
$aValues[$this->GetCode().'_filename'] = '';
|
||||
$aValues[$this->GetCode().'_downloads_count'] = \ormDocument::DEFAULT_DOWNLOADS_COUNT;
|
||||
$aValues[$this->GetCode().'_downloads_count'] = ormDocument::DEFAULT_DOWNLOADS_COUNT;
|
||||
}
|
||||
|
||||
return $aValues;
|
||||
@@ -8533,6 +8590,22 @@ class AttributeBlob extends AttributeDefinition
|
||||
return utils::IsNotNullOrEmptyString($proposedValue->GetData()) && utils::IsNotNullOrEmptyString($proposedValue->GetFileName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @param \ormDocument $original
|
||||
* @param \ormDocument $value
|
||||
* @since N°6502
|
||||
*/
|
||||
public function RecordAttChange(DBObject $oObject, $original, $value): void
|
||||
{
|
||||
// N°6502 Don't record history if only the download count has changed
|
||||
if ($original->EqualsExceptDownloadsCount($value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent::RecordAttChange($oObject, $original, $value);
|
||||
}
|
||||
|
||||
protected function GetChangeRecordAdditionalData(CMDBChangeOp $oMyChangeOp, DBObject $oObject, $original, $value): void
|
||||
{
|
||||
if (is_null($original)) {
|
||||
@@ -8692,7 +8765,7 @@ class AttributeImage extends AttributeBlob
|
||||
return 'data:'.$value->GetMimeType().';base64,'.base64_encode($value->GetData());
|
||||
}
|
||||
|
||||
return $value->GetDownloadURL(get_class($oHostObject), $oHostObject->GetKey(), $this->GetCode());
|
||||
return $value->GetDisplayURL(get_class($oHostObject), $oHostObject->GetKey(), $this->GetCode());
|
||||
}
|
||||
|
||||
public static function GetFormFieldClass()
|
||||
@@ -11302,6 +11375,9 @@ class AttributeClassAttCodeSet extends AttributeSet
|
||||
}
|
||||
$aAllowedAttributes[$sAttCode] = $sLabel;
|
||||
}
|
||||
// N°6460 Always sort on the labels, not on the datamodel definition order
|
||||
natcasesort($aAllowedAttributes);
|
||||
|
||||
return $aAllowedAttributes;
|
||||
}
|
||||
|
||||
@@ -13135,8 +13211,10 @@ class AttributeCustomFields extends AttributeDefinition
|
||||
// In that case we need additional keys : see \TemplateFieldsHandler::DoBuildForm
|
||||
$aRequestTemplateValues = $proposedValue->GetValues();
|
||||
if (false === array_key_exists('current_template_id', $aRequestTemplateValues)) {
|
||||
$aRequestTemplateValues['current_template_id'] = $aRequestTemplateValues['template_id'];
|
||||
$aRequestTemplateValues['current_template_data'] = $aRequestTemplateValues['template_data'];
|
||||
$oCurrentValue = $oHostObject->Get($this->GetCode());
|
||||
if(is_object($oCurrentValue)){
|
||||
$aRequestTemplateValues = array_merge($oCurrentValue->GetValues(), $aRequestTemplateValues);
|
||||
}
|
||||
$proposedValue = new ormCustomFieldsValue($oHostObject, $this->GetCode(), $aRequestTemplateValues);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ MetaModel::IncludeModule('synchro/synchrodatasource.class.inc.php');
|
||||
MetaModel::IncludeModule('core/backgroundtask.class.inc.php');
|
||||
MetaModel::IncludeModule('core/inlineimage.class.inc.php');
|
||||
MetaModel::IncludeModule('core/counter.class.inc.php');
|
||||
MetaModel::IncludeModule('core/TemporaryObjectDescriptor.php');
|
||||
|
||||
MetaModel::IncludeModule('webservices/webservices.basic.php');
|
||||
|
||||
|
||||
@@ -37,6 +37,14 @@ abstract class CellChangeSpec
|
||||
return $this->m_proposedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.1.0 N°5305
|
||||
*/
|
||||
public function SetDisplayableValue(string $sDisplayableValue)
|
||||
{
|
||||
$this->m_proposedValue = $sDisplayableValue;
|
||||
}
|
||||
|
||||
public function GetOql()
|
||||
{
|
||||
return $this->m_sOql;
|
||||
@@ -136,6 +144,12 @@ class CellStatus_SearchIssue extends CellStatus_Issue
|
||||
/** @var string|null $m_sTargetClass */
|
||||
private $m_sTargetClass;
|
||||
|
||||
/**
|
||||
* @since 3.1.0 N°5305
|
||||
* @var string $sAllowedValuesSearch
|
||||
*/
|
||||
private $sAllowedValuesSearch;
|
||||
|
||||
/**
|
||||
* CellStatus_SearchIssue constructor.
|
||||
* @since 3.1.0 N°5305
|
||||
@@ -144,13 +158,15 @@ class CellStatus_SearchIssue extends CellStatus_Issue
|
||||
* @param string $sReason : main message
|
||||
* @param null $sClass : used for additional message that provides allowed values for current class $sClass
|
||||
* @param null $sAllowedValues : used for additional message that provides allowed values $sAllowedValues for current class
|
||||
* @param string|null $sAllowedValuesSearch : used to search all allowed values
|
||||
*/
|
||||
public function __construct($sSerializedSearch, $sReason, $sClass=null, $sAllowedValues=null)
|
||||
public function __construct($sSerializedSearch, $sReason, $sClass=null, $sAllowedValues=null, string $sAllowedValuesSearch=null)
|
||||
{
|
||||
parent::__construct(null, null, $sReason);
|
||||
$this->sSerializedSearch = $sSerializedSearch;
|
||||
$this->m_sAllowedValues = $sAllowedValues;
|
||||
$this->m_sTargetClass = $sClass;
|
||||
$this->sAllowedValuesSearch = $sAllowedValuesSearch;
|
||||
}
|
||||
|
||||
public function GetDisplayableValue()
|
||||
@@ -182,6 +198,17 @@ class CellStatus_SearchIssue extends CellStatus_Issue
|
||||
rawurlencode($this->sSerializedSearch)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 3.1.0 N°5305
|
||||
* @return null|string
|
||||
*/
|
||||
public function GetAllowedValuesLinkUrl(): ?string
|
||||
{
|
||||
return sprintf("UI.php?operation=search&filter=%s",
|
||||
rawurlencode($this->sAllowedValuesSearch)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CellStatus_NullIssue extends CellStatus_Issue
|
||||
@@ -745,6 +772,7 @@ class BulkChange
|
||||
$oDbSearchWithoutAnyCondition->AllowAllData(false);
|
||||
$oExtObjectSetWithCurrentUserPermissions = new CMDBObjectSet($oDbSearchWithoutAnyCondition);
|
||||
$iCurrentUserRightsObjectCount = $oExtObjectSetWithCurrentUserPermissions->Count();
|
||||
$sAllowedValuesOql = $oDbSearchWithoutAnyCondition->serialize();
|
||||
|
||||
if ($iCurrentUserRightsObjectCount === 0){
|
||||
// No objects visible by current user
|
||||
@@ -785,7 +813,7 @@ class BulkChange
|
||||
if ($iAllowAllDataObjectCount != $iCurrentUserRightsObjectCount) {
|
||||
// No match and some objects NOT visible by current user. including current search maybe...
|
||||
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch-SomeObjectNotVisibleForCurrentUser', $oDbSearchWithConditions->GetClass());
|
||||
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues);
|
||||
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues, $sAllowedValuesOql);
|
||||
}
|
||||
|
||||
// No match. This is not linked to any right issue
|
||||
@@ -796,7 +824,7 @@ class BulkChange
|
||||
}
|
||||
$value =implode(" ", $aCurrentValueFields);
|
||||
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch', $value);
|
||||
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues);
|
||||
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues, $sAllowedValuesOql);
|
||||
}
|
||||
|
||||
protected function PrepareMissingObject(&$oTargetObj, &$aErrors)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -469,7 +469,10 @@ abstract class CMDBObject extends DBObject
|
||||
*/
|
||||
public function DBDelete(&$oDeletionPlan = null)
|
||||
{
|
||||
return $this->DBDeleteTracked_Internal($oDeletionPlan);
|
||||
$this->LogCRUDEnter(__METHOD__);
|
||||
$oDeletionPlan = $this->DBDeleteTracked_Internal($oDeletionPlan);
|
||||
$this->LogCRUDExit(__METHOD__);
|
||||
return $oDeletionPlan;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -519,15 +522,6 @@ abstract class CMDBObject extends DBObject
|
||||
utils::PopArchiveMode();
|
||||
}
|
||||
}
|
||||
|
||||
public function DBUpdate()
|
||||
{
|
||||
if (count($this->ListChanges()) === 0) {
|
||||
$this->InitPreviousValuesForUpdatedAttributes();
|
||||
return $this->GetKey();
|
||||
}
|
||||
return parent::DBUpdate(); // TODO: Change the autogenerated stub
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -431,6 +431,7 @@ class CMDBSource
|
||||
{
|
||||
self::$m_sDBName = '';
|
||||
}
|
||||
self::_TablesInfoCacheReset(); // reset the table info cache!
|
||||
}
|
||||
|
||||
public static function CreateTable($sQuery)
|
||||
@@ -607,8 +608,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);
|
||||
|
||||
@@ -626,18 +628,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
|
||||
@@ -664,7 +672,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(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,7 +29,7 @@ define('ITOP_APPLICATION_SHORT', 'iTop');
|
||||
*
|
||||
* @see ITOP_CORE_VERSION to get iTop core version
|
||||
*/
|
||||
define('ITOP_VERSION', '3.1.0-dev');
|
||||
define('ITOP_VERSION', '3.2.0-dev');
|
||||
|
||||
define('ITOP_VERSION_NAME', 'Fullmoon');
|
||||
define('ITOP_REVISION', 'svn');
|
||||
@@ -137,7 +137,7 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'log_purge.max_keep_days' => [
|
||||
'log_purge.max_keep_days' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Optional purge number of days to keep logs.',
|
||||
'default' => 365,
|
||||
@@ -145,7 +145,7 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'event_service.debug.filter_events' => [
|
||||
'event_service.debug.filter_events' => [
|
||||
'type' => 'array',
|
||||
'description' => 'List of events name to filter Event Service debug messages',
|
||||
'default' => [],
|
||||
@@ -153,7 +153,7 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'event_service.debug.filter_sources' => [
|
||||
'event_service.debug.filter_sources' => [
|
||||
'type' => 'array',
|
||||
'description' => 'List of event sources to filter Event Service debug messages',
|
||||
'default' => '',
|
||||
@@ -161,6 +161,38 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'temporary_object.force_creation' => [
|
||||
'type' => 'bool',
|
||||
'description' => 'If true, all the objects created by the external key are temporary',
|
||||
'default' => false,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'temporary_object.lifetime' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Seconds for temporary objects created',
|
||||
'default' => 300,
|
||||
'value' => 300,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'temporary_object.watchdog_interval' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Seconds between watchdog signals',
|
||||
'default' => 60,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'temporary_object.garbage_interval' => [
|
||||
'type' => 'integer',
|
||||
'description' => 'Seconds between garbage collections',
|
||||
'default' => 60,
|
||||
'value' => false,
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'app_env_label' => [
|
||||
'type' => 'string',
|
||||
'description' => 'Label displayed to describe the current application environment, defaults to the environment name (e.g. "production")',
|
||||
@@ -185,7 +217,7 @@ class Config
|
||||
'source_of_value' => '',
|
||||
'show_in_conf_sample' => false,
|
||||
],
|
||||
'db_host' => [
|
||||
'db_host' => [
|
||||
'type' => 'string',
|
||||
'default' => null,
|
||||
'value' => '',
|
||||
@@ -624,22 +656,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',
|
||||
@@ -1037,6 +1069,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.',
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ class CSVBulkExport extends TabularBulkExport
|
||||
$aSep['other'] = Dict::S('UI:CSVImport:SeparatorOther').' <input type="text" size="3" name="other-separator" value="'.utils::EscapeHtml($sOtherSeparator).'"/>';
|
||||
|
||||
foreach ($aSep as $sVal => $sLabel) {
|
||||
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "separator", utils::EscapeHtml($sVal), $sLabel, "radio");
|
||||
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "separator", $sVal, $sLabel, "radio");
|
||||
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawSeparator));
|
||||
$oRadio->SetBeforeInput(false);
|
||||
$oRadio->GetInput()->AddCSSClass('ibo-input--label-right');
|
||||
@@ -163,8 +163,8 @@ class CSVBulkExport extends TabularBulkExport
|
||||
$aQualifiers['other'] = Dict::S('UI:CSVImport:QualifierOther').' <input type="text" size="3" name="other-text-qualifier" value="'.utils::EscapeHtml($sOtherQualifier).'"/>';
|
||||
|
||||
foreach ($aQualifiers as $sVal => $sLabel) {
|
||||
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "text-qualifier", utils::EscapeHtml($sVal), $sLabel, "radio");
|
||||
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawSeparator));
|
||||
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "text-qualifier", $sVal, $sLabel, "radio");
|
||||
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawQualifier));
|
||||
$oRadio->SetBeforeInput(false);
|
||||
$oRadio->GetInput()->AddCSSClass('ibo-input--label-right');
|
||||
$oRadio->GetInput()->AddCSSClass('ibo-input-checkbox');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
|
||||
<user_rights>
|
||||
<profiles>
|
||||
<profile id="1024" _delta="define">
|
||||
@@ -58,6 +58,7 @@
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>addon/userrights,grant_by_profile</category>
|
||||
<is_link>1</is_link>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="userid" xsi:type="AttributeExternalKey">
|
||||
@@ -218,6 +219,19 @@
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="AuditDomain" _delta="define">
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
<properties>
|
||||
<category>application, grant_by_profile</category>
|
||||
</properties>
|
||||
<fields>
|
||||
<field id="name" xsi:type="AttributeString"/>
|
||||
<field id="description" xsi:type="AttributeString"/>
|
||||
<field id="icon" xsi:type="AttributeImage"/>
|
||||
<field id="categories_list" xsi:type="AttributeLinkedSet"/>
|
||||
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="Query" _delta="define">
|
||||
<!-- Generated by toolkit/export-class-to-meta.php -->
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
@@ -268,8 +282,13 @@
|
||||
<field id="obj_attcode" xsi:type="AttributeString"/>
|
||||
</fields>
|
||||
</class>
|
||||
<class id="DefaultWorkingTimeComputer" _delta="define">
|
||||
<interfaces>
|
||||
<interface id="iWorkingTimeComputer"/>
|
||||
</interfaces>
|
||||
</class>
|
||||
</classes>
|
||||
<attribute_properties_definition _delta="define">
|
||||
<attribute_properties_definition _delta="define">
|
||||
<properties>
|
||||
<property id="sql">
|
||||
<php_param>sql</php_param>
|
||||
@@ -488,6 +507,12 @@
|
||||
<type>boolean</type>
|
||||
<default>false</default>
|
||||
</property>
|
||||
<property id="create_temporary_object">
|
||||
<php_param>create_temporary_object</php_param>
|
||||
<mandatory>false</mandatory>
|
||||
<type>boolean</type>
|
||||
<default>false</default>
|
||||
</property>
|
||||
<property id="on_target_delete">
|
||||
<php_param>on_target_delete</php_param>
|
||||
<mandatory>false</mandatory>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -1,27 +1,14 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2023 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-2023 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)
|
||||
{
|
||||
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)
|
||||
|
||||
@@ -583,6 +583,12 @@ class LogChannels
|
||||
*/
|
||||
public const DM_CRUD = 'DMCRUD';
|
||||
|
||||
/**
|
||||
* @var string Everything related to the datamodel CRUD
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public const WEB_REQUEST = 'WebRequest';
|
||||
|
||||
/**
|
||||
* @var string Everything related to the event service
|
||||
* @since 3.1.0
|
||||
@@ -606,6 +612,8 @@ class LogChannels
|
||||
|
||||
public const PORTAL = 'portal';
|
||||
|
||||
public const TEMPORARY_OBJECTS = 'TemporaryObjects';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @since 3.1.0
|
||||
@@ -1130,7 +1138,7 @@ class DeprecatedCallsLog extends LogAPI
|
||||
parent::Enable($sTargetFile);
|
||||
|
||||
if (
|
||||
(false === defined(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME))
|
||||
(false === defined('ITOP_PHPUNIT_RUNNING_CONSTANT_NAME'))
|
||||
&& static::IsLogLevelEnabledSafe(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD)
|
||||
) {
|
||||
set_error_handler([static::class, 'DeprecatedNoticesErrorHandler'], E_DEPRECATED | E_USER_DEPRECATED);
|
||||
@@ -1406,7 +1414,7 @@ class LogFileRotationProcess implements iScheduledProcess
|
||||
$iMaxDays = MetaModel::GetConfig()->Get(LogAPI::ENUM_CONFIG_PARAM_PURGE_MAX_KEEP_DAYS);
|
||||
|
||||
// Files iterator (*.*)
|
||||
$oIterator = new \GlobIterator(APPROOT.'log'.DIRECTORY_SEPARATOR.'/*.*');
|
||||
$oIterator = new \GlobIterator(APPROOT.'log'.DIRECTORY_SEPARATOR.'*.*');
|
||||
$aLogFiles = iterator_to_array($oIterator);
|
||||
|
||||
// Reference date
|
||||
@@ -1418,6 +1426,11 @@ class LogFileRotationProcess implements iScheduledProcess
|
||||
// File real path
|
||||
$sFileRealPath = $oLogFile->getRealPath();
|
||||
|
||||
// Check file extension
|
||||
if(!in_array($oLogFile->getExtension(), ['log','sql','xml'])){
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compute number of days since last modification
|
||||
$oDateFileLastModification = new DateTime();
|
||||
$oDateFileLastModification->setTimestamp($oLogFile->getMTime());
|
||||
@@ -1658,6 +1671,8 @@ class ExceptionLog extends LogAPI
|
||||
*/
|
||||
private static function GetLastEventIssue()
|
||||
{
|
||||
return self::$oLastEventIssue;
|
||||
$oRet = self::$oLastEventIssue;
|
||||
self::$oLastEventIssue = null;
|
||||
return $oRet;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2927,52 +2927,7 @@ abstract class MetaModel
|
||||
}
|
||||
|
||||
self::$m_sTablePrefix = $sTablePrefix;
|
||||
|
||||
// Build the list of available extensions
|
||||
//
|
||||
$aInterfaces = [
|
||||
'iApplicationUIExtension',
|
||||
'iPreferencesExtension',
|
||||
'iApplicationObjectExtension',
|
||||
'iLoginFSMExtension',
|
||||
'iLoginUIExtension',
|
||||
'iLogoutExtension',
|
||||
'iQueryModifier',
|
||||
'iOnClassInitialization',
|
||||
'iPopupMenuExtension',
|
||||
'iPageUIExtension',
|
||||
'iPageUIBlockExtension',
|
||||
'iBackofficeLinkedScriptsExtension',
|
||||
'iBackofficeEarlyScriptExtension',
|
||||
'iBackofficeScriptExtension',
|
||||
'iBackofficeInitScriptExtension',
|
||||
'iBackofficeReadyScriptExtension',
|
||||
'iBackofficeLinkedStylesheetsExtension',
|
||||
'iBackofficeStyleExtension',
|
||||
'iBackofficeDictEntriesExtension',
|
||||
'iBackofficeDictEntriesPrefixesExtension',
|
||||
'iPortalUIExtension',
|
||||
'ModuleHandlerApiInterface',
|
||||
'iNewsroomProvider',
|
||||
'iModuleExtension',
|
||||
];
|
||||
foreach($aInterfaces as $sInterface)
|
||||
{
|
||||
self::$m_aExtensionClassNames[$sInterface] = array();
|
||||
}
|
||||
|
||||
foreach(get_declared_classes() as $sPHPClass)
|
||||
{
|
||||
$oRefClass = new ReflectionClass($sPHPClass);
|
||||
$oExtensionInstance = null;
|
||||
foreach($aInterfaces as $sInterface)
|
||||
{
|
||||
if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable())
|
||||
{
|
||||
self::$m_aExtensionClassNames[$sInterface][$sPHPClass] = $sPHPClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
self::InitExtensions();
|
||||
|
||||
// Initialize the classes (declared attributes, etc.)
|
||||
//
|
||||
@@ -6343,6 +6298,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;
|
||||
|
||||
try {
|
||||
@@ -6421,7 +6383,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');
|
||||
@@ -6540,6 +6504,7 @@ abstract class MetaModel
|
||||
|
||||
CMDBSource::InitFromConfig(self::$m_oConfig);
|
||||
// Later when timezone implementation is correctly done: CMDBSource::SetTimezone($sDBTimezone);
|
||||
ExecutionKPI::InitStats();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6571,6 +6536,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
|
||||
*/
|
||||
@@ -6759,7 +6737,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;
|
||||
@@ -6852,25 +6836,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;
|
||||
@@ -7642,6 +7622,57 @@ abstract class MetaModel
|
||||
unset(self::$m_aReentranceProtection[get_class($oObject)][$oObject->GetKey()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For test purpose
|
||||
* @throws \ReflectionException
|
||||
* @since 3.1.0
|
||||
*/
|
||||
public static function InitExtensions()
|
||||
{
|
||||
// Build the list of available extensions
|
||||
//
|
||||
$aInterfaces = [
|
||||
'iLoginFSMExtension',
|
||||
'iLogoutExtension',
|
||||
'iLoginUIExtension',
|
||||
'iPreferencesExtension',
|
||||
'iApplicationUIExtension',
|
||||
'iApplicationObjectExtension',
|
||||
'iPopupMenuExtension',
|
||||
'iPageUIExtension',
|
||||
'iPageUIBlockExtension',
|
||||
'iBackofficeLinkedScriptsExtension',
|
||||
'iBackofficeEarlyScriptExtension',
|
||||
'iBackofficeScriptExtension',
|
||||
'iBackofficeInitScriptExtension',
|
||||
'iBackofficeReadyScriptExtension',
|
||||
'iBackofficeLinkedStylesheetsExtension',
|
||||
'iBackofficeStyleExtension',
|
||||
'iBackofficeDictEntriesExtension',
|
||||
'iBackofficeDictEntriesPrefixesExtension',
|
||||
'iPortalUIExtension',
|
||||
'iQueryModifier',
|
||||
'iOnClassInitialization',
|
||||
'iModuleExtension',
|
||||
'iKPILoggerExtension',
|
||||
'ModuleHandlerApiInterface',
|
||||
'iNewsroomProvider',
|
||||
];
|
||||
foreach ($aInterfaces as $sInterface) {
|
||||
self::$m_aExtensionClassNames[$sInterface] = array();
|
||||
}
|
||||
|
||||
foreach (get_declared_classes() as $sPHPClass) {
|
||||
$oRefClass = new ReflectionClass($sPHPClass);
|
||||
$oExtensionInstance = null;
|
||||
foreach ($aInterfaces as $sInterface) {
|
||||
if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable()) {
|
||||
self::$m_aExtensionClassNames[$sInterface][$sPHPClass] = $sPHPClass;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -257,7 +257,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,
|
||||
|
||||
@@ -3526,6 +3526,7 @@ class CharConcatWSExpression extends CharConcatExpression
|
||||
$aRes = array();
|
||||
foreach ($this->m_aExpressions as $oExpr)
|
||||
{
|
||||
// TODO: Seems weird, this should rather be $aRes[] = $oExpr->Evaluate($aArgs);
|
||||
$aRes .= $oExpr->Evaluate($aArgs);
|
||||
}
|
||||
return implode($this->m_separator, $aRes);
|
||||
|
||||
@@ -86,6 +86,33 @@ class ormDocument
|
||||
{
|
||||
return ($this->m_data == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \ormDocument $oCompared
|
||||
*
|
||||
* @return bool True if the current ormDocument is equals to $oCompared EXCEPT for its download count. False if any other property is different OR if count is the same.
|
||||
* @since 3.1.0 N°6502
|
||||
*/
|
||||
public function EqualsExceptDownloadsCount(ormDocument $oCompared): bool
|
||||
{
|
||||
// First checking equality on others properties
|
||||
if ($oCompared->GetData() !== $this->GetData()) {
|
||||
return false;
|
||||
}
|
||||
if ($oCompared->GetMimeType() !== $this->GetMimeType()) {
|
||||
return false;
|
||||
}
|
||||
if ($oCompared->GetFileName() !== $this->GetFileName()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finally check equality of the download count
|
||||
if ($oCompared->GetDownloadsCount() === $this->GetDownloadsCount()) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function GetMimeType()
|
||||
{
|
||||
|
||||
@@ -68,8 +68,6 @@ class PDFBulkExport extends HTMLBulkExport
|
||||
|
||||
//page format
|
||||
$oSelectFormat = SelectUIBlockFactory::MakeForSelectWithLabel("page_size", Dict::S('Core:BulkExport:PDFPageSize'));
|
||||
$oSelectFormat->SetIsLabelBefore(false);
|
||||
//$oSelectFormat->AddCSSClass('ibo-input-checkbox');
|
||||
$oFieldSetFormat->AddSubBlock($oSelectFormat);
|
||||
|
||||
$aPossibleFormat = ['A3', 'A4', 'Letter'];
|
||||
@@ -79,10 +77,7 @@ class PDFBulkExport extends HTMLBulkExport
|
||||
}
|
||||
$oFieldSetFormat->AddSubBlock(new Html('</br>'));
|
||||
|
||||
$oSelectOrientation = SelectUIBlockFactory::MakeForSelectWithLabel("page_orientation",
|
||||
Dict::S('Core:BulkExport:PDFPageOrientation'));
|
||||
$oSelectOrientation->SetIsLabelBefore(false);
|
||||
//$oSelectOrientation->AddCSSClass('ibo-input-checkbox');
|
||||
$oSelectOrientation = SelectUIBlockFactory::MakeForSelectWithLabel("page_orientation", Dict::S('Core:BulkExport:PDFPageOrientation'));
|
||||
$oFieldSetFormat->AddSubBlock($oSelectOrientation);
|
||||
|
||||
$aPossibleOrientation = ['P', 'L'];
|
||||
|
||||
@@ -98,4 +98,14 @@ class PluginManager
|
||||
|
||||
return $oInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* For test purpose
|
||||
* @return void
|
||||
* @since 3.1.0
|
||||
*/
|
||||
protected static function ResetPlugins()
|
||||
{
|
||||
self::$m_aExtensionClasses = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,12 @@
|
||||
|
||||
class SimpleCrypt
|
||||
{
|
||||
/**
|
||||
* @var \SimpleCrypt
|
||||
* @since 3.1.0 N°5388
|
||||
*/
|
||||
protected $oEngine;
|
||||
|
||||
public static function GetNewDefaultParams()
|
||||
{
|
||||
if(function_exists('sodium_crypto_secretbox_open') && function_exists('random_bytes')){
|
||||
@@ -62,6 +68,7 @@ class SimpleCrypt
|
||||
$sEngineName = 'SimpleCrypt' . $sEngineName . 'Engine';
|
||||
return $sEngineName::GetNewDefaultParams();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param string $sEngineName Engine for encryption. Values: Simple, Mcrypt, Sodium or OpenSSL
|
||||
|
||||
@@ -218,8 +218,8 @@ class SQLObjectQueryBuilder
|
||||
continue;
|
||||
}
|
||||
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
|
||||
// Skip this attribute if not made of SQL columns
|
||||
if (count($oAttDef->GetSQLExpressions()) == 0)
|
||||
// Skip this attribute if not made of SQL columns nor in current table
|
||||
if (count($oAttDef->GetSQLExpressions()) == 0 || $oAttDef->IsExternalField())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ abstract class Trigger extends cmdbAbstractObject
|
||||
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("action_list",
|
||||
array("linked_class" => "lnkTriggerAction", "ext_key_to_me" => "trigger_id", "ext_key_to_remote" => "action_id", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array())));
|
||||
$aTags = ContextTag::GetTags();
|
||||
MetaModel::Init_AddAttribute(new AttributeEnumSet("context", array("allowed_values" => null, "possible_values" => new ValueSetEnumPadded($aTags), "sql" => "context", "depends_on" => array(), "is_null_allowed" => true, "max_items" => 12)));
|
||||
MetaModel::Init_AddAttribute(new AttributeEnumSet("context", array("allowed_values" => null, "possible_values" => new ValueSetEnumPadded($aTags, true), "sql" => "context", "depends_on" => array(), "is_null_allowed" => true, "max_items" => 12)));
|
||||
// "complement" is a computed field, fed by Trigger sub-classes, in general in ComputeValues method, for eg. the TriggerOnObject fed it with target_class info
|
||||
MetaModel::Init_AddAttribute(new AttributeString("complement", array("allowed_values" => null, "sql" => "complement", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
|
||||
|
||||
@@ -121,7 +121,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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -761,14 +761,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
|
||||
*
|
||||
@@ -787,8 +798,7 @@ class UserRights
|
||||
}
|
||||
self::$m_oAddOn = new $sModuleName;
|
||||
self::$m_oAddOn->Init();
|
||||
self::$m_oUser = null;
|
||||
self::$m_oRealUser = null;
|
||||
self::ResetCurrentUserData();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -855,6 +865,8 @@ class UserRights
|
||||
*/
|
||||
public static function Login($sLogin, $sAuthentication = 'any')
|
||||
{
|
||||
static::Logoff();
|
||||
|
||||
$oUser = self::FindUser($sLogin, $sAuthentication);
|
||||
if (is_null($oUser))
|
||||
{
|
||||
@@ -872,6 +884,17 @@ class UserRights
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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
|
||||
|
||||
@@ -97,7 +97,7 @@ abstract class ValueSetDefinition
|
||||
public function SortValues(array &$aValues): void
|
||||
{
|
||||
// Sort alphabetically on values
|
||||
asort($aValues);
|
||||
natcasesort($aValues);
|
||||
}
|
||||
|
||||
abstract protected function LoadValues($aArgs);
|
||||
@@ -465,11 +465,11 @@ class ValueSetEnum extends ValueSetDefinition
|
||||
{
|
||||
protected $m_values;
|
||||
/**
|
||||
* @var bool $bSortByValue If true, values will be sorted at runtime, otherwise it is sorted at compile time in a predefined order.
|
||||
* @var bool $bSortByValues If true, values will be sorted at runtime (on their values, not their keys), otherwise it is sorted at compile time in a predefined order.
|
||||
* {@see \MFCompiler::CompileAttributeEnumValues()} for complete reasons.
|
||||
* @since 3.1.0 N°1646
|
||||
*/
|
||||
protected bool $bSortByValue;
|
||||
protected bool $bSortByValues;
|
||||
|
||||
/**
|
||||
* @param array|string $Values
|
||||
@@ -477,20 +477,20 @@ class ValueSetEnum extends ValueSetDefinition
|
||||
*
|
||||
* @since 3.1.0 N°1646 Add $bLocalizedSort parameter
|
||||
*/
|
||||
public function __construct($Values, bool $bSortByValue = false)
|
||||
public function __construct($Values, bool $bSortByValues = false)
|
||||
{
|
||||
$this->m_values = $Values;
|
||||
$this->bSortByValue = $bSortByValue;
|
||||
$this->bSortByValues = $bSortByValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see \ValueSetEnum::$bSortByValue
|
||||
* @see \ValueSetEnum::$bSortByValues
|
||||
* @return bool
|
||||
* @since 3.1.0 N°1646
|
||||
*/
|
||||
public function IsSortedByValues(): bool
|
||||
{
|
||||
return $this->bSortByValue;
|
||||
return $this->bSortByValues;
|
||||
}
|
||||
|
||||
// Helper to export the data model
|
||||
@@ -507,8 +507,8 @@ class ValueSetEnum extends ValueSetDefinition
|
||||
public function SortValues(array &$aValues): void
|
||||
{
|
||||
// Force sort by values only if necessary
|
||||
if ($this->bSortByValue) {
|
||||
asort($aValues);
|
||||
if ($this->bSortByValues) {
|
||||
natcasesort($aValues);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -548,9 +548,13 @@ class ValueSetEnum extends ValueSetDefinition
|
||||
|
||||
class ValueSetEnumPadded extends ValueSetEnum
|
||||
{
|
||||
public function __construct($Values)
|
||||
/**
|
||||
* @inheritDoc
|
||||
* @since 3.1.0 N°6448 Add $bSortByValues parameter
|
||||
*/
|
||||
public function __construct($Values, bool $bSortByValues = false)
|
||||
{
|
||||
parent::__construct($Values);
|
||||
parent::__construct($Values, $bSortByValues);
|
||||
if (is_string($Values))
|
||||
{
|
||||
$this->LoadValues(null);
|
||||
@@ -562,6 +566,7 @@ class ValueSetEnumPadded extends ValueSetEnum
|
||||
$aPaddedValues = array();
|
||||
foreach ($this->m_aValues as $sKey => $sVal)
|
||||
{
|
||||
// Pad keys to the min. length required by the \AttributeSet
|
||||
$sKey = str_pad($sKey, 3, '_', STR_PAD_LEFT);
|
||||
$aPaddedValues[$sKey] = $sVal;
|
||||
}
|
||||
@@ -610,7 +615,7 @@ class ValueSetEnumClasses extends ValueSetEnum
|
||||
public function __construct($sCategories = '', $sAdditionalValues = '')
|
||||
{
|
||||
$this->m_sCategories = $sCategories;
|
||||
parent::__construct($sAdditionalValues);
|
||||
parent::__construct($sAdditionalValues, true /* Classes are always sorted alphabetically */);
|
||||
}
|
||||
|
||||
protected function LoadValues($aArgs)
|
||||
|
||||
@@ -20,6 +20,8 @@ $ibo-dashlet-badge--icon--size: 48px !default;
|
||||
|
||||
$ibo-dashlet-badge--action-icon--margin-right: $ibo-spacing-300 !default;
|
||||
|
||||
$ibo-dashlet-badge--body--tooltip-title--margin-bottom: $ibo-spacing-500 !default;
|
||||
|
||||
/* CSS variables (can be changed directly from the browser) */
|
||||
:root {
|
||||
--ibo-dashlet-badge--min-width: #{$ibo-dashlet-badge--min-width};
|
||||
@@ -74,18 +76,27 @@ $ibo-dashlet-badge--action-icon--margin-right: $ibo-spacing-300 !default;
|
||||
@extend %ibo-hyperlink-inherited-colors;
|
||||
}
|
||||
}
|
||||
.ibo-dashlet-badge--action-list-count{
|
||||
margin-right: $ibo-dashlet-badge--action-list-count--margin-right;
|
||||
@extend %ibo-font-ral-bol-450;
|
||||
|
||||
.ibo-dashlet-badge--action-list-count {
|
||||
margin-right: $ibo-dashlet-badge--action-list-count--margin-right;
|
||||
@extend %ibo-font-ral-bol-450;
|
||||
}
|
||||
.ibo-dashlet-badge--action-list-label{
|
||||
display: inline-block;
|
||||
@extend %ibo-text-truncated-with-ellipsis;
|
||||
|
||||
.ibo-dashlet-badge--action-list-label {
|
||||
display: inline-block;
|
||||
@extend %ibo-text-truncated-with-ellipsis;
|
||||
}
|
||||
.ibo-dashlet-badge--action-create{
|
||||
@extend %ibo-baseline-centered-content;
|
||||
@extend %ibo-font-size-150;
|
||||
|
||||
.ibo-dashlet-badge--action-create {
|
||||
@extend %ibo-baseline-centered-content;
|
||||
@extend %ibo-font-size-150;
|
||||
}
|
||||
.ibo-dashlet-badge--action-create-icon{
|
||||
margin-right: $ibo-dashlet-badge--action-icon--margin-right;
|
||||
|
||||
.ibo-dashlet-badge--action-create-icon {
|
||||
margin-right: $ibo-dashlet-badge--action-icon--margin-right;
|
||||
}
|
||||
|
||||
.ibo-dashlet-badge--body--tooltip-title {
|
||||
@extend %ibo-font-weight-600;
|
||||
margin-bottom: $ibo-dashlet-badge--body--tooltip-title--margin-bottom;
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ $body-overflow-x: hidden !default;
|
||||
$body-overflow-y: auto !default;
|
||||
|
||||
|
||||
/*N°5786 - Avoid strong text to always be grey (default Bulma color for this var.) so strong text can keep its color. This is mostly for text within the .ibo-is-html-content. */
|
||||
$text-strong: inherit !default;
|
||||
|
||||
/**
|
||||
* customize Bulma content variables
|
||||
* See https://bulma.io/documentation/elements/content/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
|
||||
<constants>
|
||||
</constants>
|
||||
<classes>
|
||||
|
||||
@@ -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.1.0',
|
||||
'authent-cas/3.2.0',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -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.1.0',
|
||||
'authent-external/3.2.0',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
|
||||
<classes>
|
||||
<class id="UserLDAP" _delta="define">
|
||||
<parent>cmdbAbstractObject</parent>
|
||||
@@ -355,4 +355,26 @@
|
||||
</presentation>
|
||||
</class>
|
||||
</classes>
|
||||
<meta>
|
||||
<classes>
|
||||
<class id="UserInternal" _delta="define_if_not_exists">
|
||||
<fields>
|
||||
<field id="contactid" xsi:type="AttributeExternalKey">
|
||||
<target_class>Contact</target_class>
|
||||
</field>
|
||||
<field id="first_name" xsi:type="AttributeExternalField"/>
|
||||
<field id="last_name" xsi:type="AttributeExternalField"/>
|
||||
<field id="status" xsi:type="AttributeEnum"/>
|
||||
<field id="org_id" xsi:type="AttributeExternalField"/>
|
||||
<field id="email" xsi:type="AttributeExternalField"/>
|
||||
<field id="login" xsi:type="AttributeString"/>
|
||||
<field id="language" xsi:type="AttributeApplicationLanguage"/>
|
||||
<field id="status" xsi:type="AttributeEnum"/>
|
||||
<field id="allowed_org_list" xsi:type="AttributeLinkedSetIndirect"/>
|
||||
<field id="profile_list" xsi:type="AttributeLinkedSetIndirect"/>
|
||||
<field id="log" xsi:type="AttributeCaseLog"/>
|
||||
</fields>
|
||||
</class>
|
||||
</classes>
|
||||
</meta>
|
||||
</itop_design>
|
||||
|
||||
@@ -25,5 +25,7 @@
|
||||
Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:UserLDAP' => 'LDAP-Benutzer',
|
||||
'Class:UserLDAP+' => 'Benutzer, der via LDAP authentifiziert wird',
|
||||
'UserLDAP:server' => 'LDAP specifics~~',
|
||||
'Class:UserLDAP/Attribute:ldap_server' => 'LDAP-Server',
|
||||
'Class:UserLDAP/Attribute:ldap_server+' => 'Optional: LDAP-Server, der zur Authentifizierung verwendet werden soll, falls mehrere LDAP-Server konfiguriert sind.',
|
||||
'UserLDAP:server' => 'LDAP-Einstellungen',
|
||||
));
|
||||
|
||||
@@ -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.1.0',
|
||||
'authent-ldap/3.2.0',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
|
||||
<module_parameters>
|
||||
<parameters id="authent-local" _delta="define">
|
||||
<password_validation.pattern>^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^\da-zA-Z]).{8,}$</password_validation.pattern>
|
||||
|
||||
@@ -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.1.0',
|
||||
'authent-local/3.2.0',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
|
||||
<branding>
|
||||
<themes>
|
||||
<theme id="darkmoon" _delta="define">
|
||||
|
||||
@@ -21,5 +21,5 @@
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
Dict::Add('CS CZ', 'Czech', 'Čeština', 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.1.0',
|
||||
'combodo-backoffice-darkmoon-theme/3.2.0',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
|
||||
<menus>
|
||||
<menu id="DBToolsMenu" xsi:type="WebPageMenuNode" _delta="define">
|
||||
<rank>30</rank>
|
||||
|
||||
@@ -462,6 +462,12 @@ class DatabaseAnalyzer
|
||||
$aCols = $oAttDef->GetSQLExpressions(); // Workaround a PHP bug: sometimes issuing a Notice if invoking current(somefunc())
|
||||
$sMyAttributeField = current($aCols); // get the first column for the moment
|
||||
$sFilter = "FROM `$sTable` WHERE `$sTable`.`$sMyAttributeField` NOT IN ($sExpectedValues)";
|
||||
if ($oAttDef->IsNullAllowed()) {
|
||||
// NotEmptyToSql should have been in AttributeDefinition, as a workaround the search type is used
|
||||
$sSearchType = $oAttDef->GetSearchType();
|
||||
$sCondition = $this->NotEmptyToSql("`$sTable`.`$sMyAttributeField`", $sSearchType);
|
||||
$sFilter .= " AND $sCondition";
|
||||
}
|
||||
$sDelete = "DELETE `$sTable`";
|
||||
$sSelect = "SELECT DISTINCT `$sTable`.`$sKeyField` AS id, `$sTable`.`$sMyAttributeField` AS value";
|
||||
$sSelWrongRecs = "$sSelect $sFilter";
|
||||
@@ -492,6 +498,26 @@ class DatabaseAnalyzer
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $sRef
|
||||
* @param string $sSearchType
|
||||
*
|
||||
* @return string
|
||||
* @since 3.1.0 N°6442
|
||||
*/
|
||||
private function NotEmptyToSql($sRef, string $sSearchType)
|
||||
{
|
||||
switch ($sSearchType) {
|
||||
case AttributeDefinition::SEARCH_WIDGET_TYPE_NUMERIC:
|
||||
case AttributeDefinition::SEARCH_WIDGET_TYPE_EXTERNAL_FIELD:
|
||||
case AttributeDefinition::SEARCH_WIDGET_TYPE_DATE:
|
||||
case AttributeDefinition::SEARCH_WIDGET_TYPE_DATE_TIME:
|
||||
return "ISNULL({$sRef}) = 0";
|
||||
}
|
||||
|
||||
return "({$sRef} != '')";
|
||||
}
|
||||
|
||||
/**
|
||||
* Check user accounts without profile
|
||||
*
|
||||
|
||||
@@ -43,7 +43,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'DBTools:ShowAll' => 'Alle Fehler anzeigen',
|
||||
'DBTools:Inconsistencies' => 'Datenbank-Inkonsistenzen',
|
||||
'DBTools:DetailedErrorTitle' => '%2$s Fehler(s) in der Klasse %1$s: %3$s',
|
||||
'DBTools:DetailedErrorLimit' => 'List limited to %1$s errors~~',
|
||||
'DBTools:DetailedErrorLimit' => 'Liste auf %1$s Fehler begrenzt',
|
||||
'DBAnalyzer-Integrity-OrphanRecord' => 'Verwaister Eintrag in `%1$s`, er sollte eine Entsprechung in Tabelle `%2$s` haben',
|
||||
'DBAnalyzer-Integrity-InvalidExtKey' => 'Ungültiger Externer Key %1$s (Spalte: `%2$s.%3$s`)',
|
||||
'DBAnalyzer-Integrity-MissingExtKey' => 'Fehlender Externer Key %1$s (Spalte: `%2$s.%3$s`)',
|
||||
|
||||
@@ -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.1.0',
|
||||
'combodo-db-tools/3.2.0',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
|
||||
<classes>
|
||||
<class id="Attachment" _delta="define">
|
||||
<parent>DBObject</parent>
|
||||
|
||||
@@ -74,7 +74,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Attachments:File:Uploader' => 'hochgeladen von',
|
||||
'Attachments:File:Size' => 'Größe',
|
||||
'Attachments:File:MimeType' => 'Typ',
|
||||
'Attachments:File:DownloadsCount' => 'Downloads~~',
|
||||
'Attachments:File:DownloadsCount' => 'Downloads',
|
||||
));
|
||||
//
|
||||
// Class: Attachment
|
||||
@@ -94,6 +94,6 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
//
|
||||
|
||||
Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:TriggerOnAttachmentDownload' => 'Trigger (on object\'s attachment download)~~',
|
||||
'Class:TriggerOnAttachmentDownload+' => 'Trigger on object\'s attachment download of [a child class of] the given class~~',
|
||||
'Class:TriggerOnAttachmentDownload' => 'Trigger (beim Herunterladen eines Attachment eines Objekts)',
|
||||
'Class:TriggerOnAttachmentDownload+' => 'Trigger für das Herunterladen des Attachments der angegebenen Klasse oder einer Unterklasse',
|
||||
));
|
||||
|
||||
@@ -344,6 +344,7 @@ class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExt
|
||||
while ($oAttachment = $oSet->Fetch())
|
||||
{
|
||||
$oTempAttachment = clone $oAttachment;
|
||||
$oTempAttachment->Set('expire', time() + utils::GetConfig()->Get('draft_attachments_lifetime'));
|
||||
$oTempAttachment->Set('item_id', null);
|
||||
$oTempAttachment->Set('temp_id', $sTempId);
|
||||
$oTempAttachment->DBInsert();
|
||||
|
||||
@@ -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.1.0',
|
||||
'itop-attachments/3.2.0',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -102,14 +102,19 @@ if (!function_exists('sys_get_temp_dir'))
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param int $iRefTime Reference date time as a unix timestamp
|
||||
*
|
||||
* @return string Absolute path to the backup file, WITHOUT the file extension (`.tar.gz`)
|
||||
* @throws \Exception
|
||||
*/
|
||||
function MakeArchiveFileName($iRefTime = null)
|
||||
{
|
||||
$sDefaultBackupFileName = sys_get_temp_dir().'/'."__DB__-%Y-%m-%d";
|
||||
$sBackupFile = utils::ReadParam('backup_file', $sDefaultBackupFileName, true, 'raw_data');
|
||||
|
||||
$oBackup = new DBBackup();
|
||||
$oDateTime = $iRefTime !== null ? new DateTime($iRefTime) : new DateTime();
|
||||
$oDateTime = $iRefTime !== null ? DateTime::createFromFormat('U', $iRefTime) : new DateTime();
|
||||
$sBackupFile = $oBackup->MakeName($sBackupFile, $oDateTime);
|
||||
|
||||
return $sBackupFile;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
|
||||
<menus>
|
||||
<menu id="BackupStatus" xsi:type="WebPageMenuNode" _delta="define">
|
||||
<rank>50</rank>
|
||||
|
||||
@@ -235,13 +235,16 @@ class DBRestore extends DBBackup
|
||||
if (in_array($oFileInfo->getFilename(), $aStandardFiles)) {
|
||||
continue;
|
||||
}
|
||||
if (strncmp($oFileInfo->getPathname(), $sDataDir.'/production-modules', strlen($sDataDir.'/production-modules')) == 0) {
|
||||
// Normalize filenames to cope with Windows backslashes
|
||||
$sPath = str_replace('\\', '/', $oFileInfo->getPathname());
|
||||
$sRefPath = str_replace('\\', '/', $sDataDir.'/production-modules');
|
||||
if (strncmp($sPath, $sRefPath, strlen($sRefPath)) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
$aExtraFiles[$oFileInfo->getPathname()] = APPROOT.substr($oFileInfo->getPathname(), strlen($sDataDir));
|
||||
}
|
||||
|
||||
|
||||
return $aExtraFiles;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-backup/3.1.0',
|
||||
'itop-backup/3.2.0',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
|
||||
<constants/>
|
||||
<classes>
|
||||
<class id="lnkFunctionalCIToProviderContract" _delta="define">
|
||||
@@ -197,7 +197,6 @@
|
||||
<count_max>0</count_max>
|
||||
<ext_key_to_remote>providercontract_id</ext_key_to_remote>
|
||||
<duplicates/>
|
||||
<read_only>false</read_only>
|
||||
</field>
|
||||
<field id="services_list" xsi:type="AttributeLinkedSetIndirect" _delta="define">
|
||||
<linked_class>lnkFunctionalCIToService</linked_class>
|
||||
@@ -206,7 +205,6 @@
|
||||
<count_max>0</count_max>
|
||||
<ext_key_to_remote>service_id</ext_key_to_remote>
|
||||
<duplicates/>
|
||||
<read_only>false</read_only>
|
||||
</field>
|
||||
</fields>
|
||||
<presentation>
|
||||
@@ -385,7 +383,6 @@
|
||||
<count_max>0</count_max>
|
||||
<ext_key_to_remote>contract_id</ext_key_to_remote>
|
||||
<duplicates/>
|
||||
<read_only>false</read_only>
|
||||
</field>
|
||||
<field id="services_list" xsi:type="AttributeLinkedSetIndirect" _delta="define">
|
||||
<linked_class>lnkDocumentToService</linked_class>
|
||||
@@ -394,7 +391,6 @@
|
||||
<count_max>0</count_max>
|
||||
<ext_key_to_remote>service_id</ext_key_to_remote>
|
||||
<duplicates/>
|
||||
<read_only>false</read_only>
|
||||
</field>
|
||||
</fields>
|
||||
<methods/>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:lnkFunctionalCIToProviderContract' => 'Verknüpfung FunctionalCI/Provider-Vertrag',
|
||||
'Class:lnkFunctionalCIToProviderContract+' => '',
|
||||
'Class:lnkFunctionalCIToProviderContract/Name' => '%1$s / %2$s~~',
|
||||
'Class:lnkFunctionalCIToProviderContract/Name' => '%1$s / %2$s',
|
||||
'Class:lnkFunctionalCIToProviderContract/Attribute:providercontract_id' => 'Provider-Vertrag',
|
||||
'Class:lnkFunctionalCIToProviderContract/Attribute:providercontract_id+' => '',
|
||||
'Class:lnkFunctionalCIToProviderContract/Attribute:providercontract_name' => 'Provider-Vertragsname',
|
||||
@@ -51,7 +51,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:lnkFunctionalCIToService' => 'Verknüpfung FunctionalCI/Service',
|
||||
'Class:lnkFunctionalCIToService+' => '',
|
||||
'Class:lnkFunctionalCIToService/Name' => '%1$s / %2$s~~',
|
||||
'Class:lnkFunctionalCIToService/Name' => '%1$s / %2$s',
|
||||
'Class:lnkFunctionalCIToService/Attribute:service_id' => 'Service',
|
||||
'Class:lnkFunctionalCIToService/Attribute:service_id+' => '',
|
||||
'Class:lnkFunctionalCIToService/Attribute:service_name' => 'Service-Name',
|
||||
@@ -79,7 +79,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
|
||||
Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:Document/Attribute:contracts_list' => 'Verträge',
|
||||
'Class:Document/Attribute:contracts_list+' => 'All the contracts linked to this document~~',
|
||||
'Class:Document/Attribute:contracts_list+' => 'Alle mit diesem Dokument verknüpften Verträge',
|
||||
'Class:Document/Attribute:services_list' => 'Services',
|
||||
'Class:Document/Attribute:services_list+' => 'All the services linked to this document~~',
|
||||
'Class:Document/Attribute:services_list+' => 'Alle mit diesem Dokument verknüpften Services',
|
||||
));
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-bridge-cmdb-services/3.1.0',
|
||||
'itop-bridge-cmdb-services/3.2.0',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
|
||||
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
|
||||
<constants/>
|
||||
<classes>
|
||||
<class id="lnkFunctionalCIToTicket" _delta="define">
|
||||
@@ -139,7 +139,6 @@
|
||||
<count_max>0</count_max>
|
||||
<ext_key_to_remote>ticket_id</ext_key_to_remote>
|
||||
<duplicates/>
|
||||
<read_only>false</read_only>
|
||||
</field>
|
||||
</fields>
|
||||
</class>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:lnkFunctionalCIToTicket' => 'Verknüpfung FunctionalCI/Ticket',
|
||||
'Class:lnkFunctionalCIToTicket+' => '',
|
||||
'Class:lnkFunctionalCIToTicket/Name' => '%1$s / %2$s~~',
|
||||
'Class:lnkFunctionalCIToTicket/Name' => '%1$s / %2$s',
|
||||
'Class:lnkFunctionalCIToTicket/Attribute:ticket_id' => 'Ticket',
|
||||
'Class:lnkFunctionalCIToTicket/Attribute:ticket_id+' => '',
|
||||
'Class:lnkFunctionalCIToTicket/Attribute:ticket_ref' => 'Referenz',
|
||||
@@ -57,5 +57,5 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
//
|
||||
Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Class:FunctionalCI/Attribute:tickets_list' => 'Tickets',
|
||||
'Class:FunctionalCI/Attribute:tickets_list+' => 'All the tickets for this configuration item~~',
|
||||
'Class:FunctionalCI/Attribute:tickets_list+' => 'Alle Ticket, die mit diesem CI verknüpft sind',
|
||||
));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user