Compare commits

..

220 Commits

Author SHA1 Message Date
XGUI
1eaeeb502b N° 5874 - Quick create: improvement for newcomer
Hide Popular section when empty
2024-11-28 16:55:44 +01:00
xavier.guiboud-ribaud@combodo.com
e7a3e7de90 N°5874 - Quick create: improvement for newcomer
Fix tests
2024-11-27 08:18:49 +01:00
xavier.guiboud-ribaud@combodo.com
d16757f8f0 N°5874 - Quick create: improvement for newcomer
Rework tests for readability
2024-11-27 08:01:30 +01:00
xavier.guiboud-ribaud@combodo.com
b9ad9dcded 5874
Add comments to tests for clarity
Add setUp and tearDown methods on tests
Add classes on history and popular sections
2024-11-25 12:10:10 +01:00
xavier.guiboud-ribaud@combodo.com
44fa08278d 5874
Add comments to tests for clarity
2024-11-22 16:34:09 +01:00
xavier.guiboud-ribaud@combodo.com
585072a478 5874
Change description of parameters and functions
Add comments in twig for clarity
Add default popular category to UserRequest and FunctionalCI classes
Add comments to tests for clarity
2024-11-22 14:02:39 +01:00
XavierGR
dc2064450b N°5874 - Quick create: improvement for newcomer 2024-11-21 15:11:55 +01:00
jf-cbd
0d8a20c35e Merge remote-tracking branch 'origin/support/3.2' into develop 2024-11-19 08:50:29 +01:00
Thomas Casteleyn
1f3780f338 Correctly identify as iTop in cURL requests (#652)
* Correctly identify as iTop in cURL requests (with configuration option)
2024-11-14 15:18:47 +01:00
Eric Espie
85f6195a51 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-11-14 14:34:05 +01:00
Thomas Casteleyn
7e1b1779a9 ️ Add faster way to rename column on the same table (#676)
* Add faster way to rename column on the same table
2024-11-14 14:24:45 +01:00
Benjamin Dalsass
d95e7168aa Merge branch 'support/3.2' into develop 2024-11-13 13:32:53 +01:00
jf-cbd
a7c22c06af Merge remote-tracking branch 'origin/support/3.2' into develop 2024-11-12 09:37:41 +01:00
denis.flaven@combodo.com
a72d1ca1b3 🔨 Replace gitmojis by their UTF-8 equivalent in output. 2024-11-08 16:08:06 +01:00
Stephen Abello
be5e4458ba N°7793 Add breaking changes introduced by adding common SCSS variables between backoffice and end-user portal (#675)
* N°7793 Add breaking changes induce by adding common SCSS variables between backoffice and end-user portal

* N°7793 Handle breaking changes in darkmoon theme
2024-11-08 10:04:56 +01:00
Stephen Abello
317cd585b2 Merge branch 'support/3.2' into develop 2024-11-08 09:45:05 +01:00
jf-cbd
5d8db176f4 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	dictionaries/es_cr.dictionary.itop.core.php
2024-11-07 14:57:54 +01:00
Stephen Abello
c6039f4b51 Merge branch 'support/3.2' into develop 2024-11-06 09:57:03 +01:00
Stephen Abello
d0f9e57bf1 Merge branch 'support/3.2' into develop 2024-11-04 14:40:52 +01:00
jf-cbd
385c60e993 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-10-31 09:41:38 +01:00
Eric Espie
58c68bade9 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-10-29 11:18:45 +01:00
Anne-Cath
eb992c7b45 N°7915 - declaration of DisplayBareProperties function in DocumentFile class has wrong id 2024-10-25 15:40:43 +02:00
Anne-Catherine
0688405f86 N°7348 - Enable to param maximum depth of impact analysis in function Ticket::UpdateImpactedItems (#633) 2024-10-25 15:29:09 +02:00
jf-cbd
dd34fda42e Merge remote-tracking branch 'origin/support/3.2' into develop 2024-10-23 17:56:09 +02:00
jf-cbd
6becd73ac2 Update tests to be minimal and removed hard-coded ITOP_DESIGN_LATEST_VERSION VERSION 2024-10-22 11:47:25 +02:00
jf-cbd
72d6e251b8 🔖 Update tests for XML 3.3.0 version 2024-10-21 18:39:39 +02:00
jf-cbd
9491c9102c 🔖 Update tests for XML 3.3.0 version 2024-10-21 18:28:50 +02:00
jf-cbd
a728cf312d 🔖 Update XML 3.3.0 version 2024-10-21 18:21:37 +02:00
jf-cbd
e321e27899 🔖 Prepare 3.3.0 version 2024-10-21 18:07:54 +02:00
jf-cbd
96e8467e13 Temporary rollback : waiting for datamodel upgrade 2024-10-21 18:01:58 +02:00
jf-cbd
508918a684 Temporary rollback : waiting for datamodel upgrade 2024-10-21 17:56:43 +02:00
jf-cbd
8adef26d18 🔖 Prepare 3.3.0 version 2024-10-21 17:36:45 +02:00
jf-cbd
2d45abd12b 🔖 Prepare 3.3.0 version 2024-10-21 17:34:30 +02:00
jf-cbd
bd5e55aad9 🔖 Prepare 3.3.0 version 2024-10-21 17:26:15 +02:00
Eric Espie
b57e4fce07 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-10-14 09:27:25 +02:00
jf-cbd
b460705831 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	.doc/itop-version-history.md
2024-09-30 11:46:20 +02:00
Eric Espie
58c47f4c0a Merge remote-tracking branch 'origin/support/3.2' into develop 2024-09-26 17:41:05 +02:00
Anne-Catherine
f3a6b064c8 N°824 - Allow OQL JOIN on object ON EventNotification (#642) 2024-09-19 08:58:27 +02:00
Anne-Cath
5e6fb33ed7 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-09-19 08:11:56 +02:00
Anne-Cath
5e7137e0c4 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-09-19 07:51:37 +02:00
BenGrenoble
4cf5e47ec3 N°7796 - itop-ticket and itop-bridge-cmdb-ticket not coherents 2024-09-18 16:06:06 +02:00
Anne-Cath
143a59a19d Merge remote-tracking branch 'origin/support/3.2' into develop 2024-09-18 11:59:16 +02:00
Eric Espie
61b247b156 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-09-11 17:34:34 +02:00
Molkobain
c1aa013053 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-09-06 11:59:46 +02:00
Benjamin Dalsass
a3f9eed6e2 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-08-20 16:55:11 +02:00
jf-cbd
21c9332a7f Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-08-07 18:01:38 +02:00
Benjamin Dalsass
ae6e0a08ea Merge remote-tracking branch 'origin/support/3.2' into develop 2024-08-07 08:33:27 +02:00
Benjamin Dalsass
d9d2e851f4 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-08-07 07:57:33 +02:00
Molkobain
91c63cb12e Merge remote-tracking branch 'origin/support/3.2' into develop 2024-08-05 17:28:36 +02:00
Molkobain
4ff354dd41 Update itop-version-history.md 2024-07-29 10:04:48 +02:00
Molkobain
bd8c325306 📝 Fix version history 2024-07-29 10:02:08 +02:00
Molkobain
9902cedc06 📝 Update version history 2024-07-29 09:56:52 +02:00
Molkobain
b64ee96636 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-29 09:56:15 +02:00
Molkobain
8c1ff2dc1c Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-25 10:53:55 +02:00
Molkobain
f2ab409c9c Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-25 08:32:28 +02:00
Molkobain
db46763e13 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-24 11:02:36 +02:00
Molkobain
7ee1af3cc8 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-24 10:58:05 +02:00
Timothee
32b371eac1 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-16 09:57:16 +02:00
Benjamin Dalsass
f65cadd24c Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-15 10:59:01 +02:00
jf-cbd
40551b36e5 Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-07-12 10:40:09 +02:00
Molkobain
c0056e75d0 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-10 11:39:14 +02:00
jf-cbd
e5825b5fcd Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-07-05 14:06:00 +02:00
Benjamin Dalsass
d3dc59c5da Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-05 09:25:53 +02:00
Molkobain
642a097b4b Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-05 00:19:17 +02:00
jf-cbd
1c615c42b6 Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-07-04 13:56:53 +02:00
jf-cbd
b1f708dcad Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-07-04 11:01:53 +02:00
Benjamin Dalsass
55f202b7cb Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-04 07:57:38 +02:00
jf-cbd
247f1045fe Merge branch 'refs/heads/support/3.2' into develop 2024-07-03 15:52:49 +02:00
jf-cbd
42e78ad3a3 Merge branch 'refs/heads/support/3.2' into develop 2024-07-02 17:21:43 +02:00
odain
113826b2e7 Merge branch 'support/3.2' into develop 2024-07-02 17:13:30 +02:00
odain
7fe8da8590 Merge branch 'support/3.2' into develop 2024-07-02 14:10:54 +02:00
Eric Espie
821251a53f Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-02 10:43:23 +02:00
Molkobain
5334bbb303 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	.doc/itop-version-history.md
2024-06-28 15:08:31 +02:00
Eric Espie
012d5e7ae0 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-06-24 14:36:53 +02:00
Eric Espie
03f4e9f621 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-06-20 11:08:59 +02:00
Romain Quetiez
055968bea9 Update README.md 2024-06-17 14:40:31 +02:00
Romain Quetiez
d0457b73ec Merge branch 'refs/heads/support/3.2' into develop 2024-06-13 18:52:40 +02:00
jf-cbd
7e8ff50886 Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop
# Conflicts:
#	js/ckeditor/src/plugins/maximize/maximize.plugin.ts
2024-06-13 17:53:06 +02:00
Eric Espie
36a6ee4fc9 Merge remote-tracking branch 'origin/feature/3.2-post-beta' into develop 2024-06-12 16:50:02 +02:00
Benjamin Dalsass
db8b00e8df Merge remote-tracking branch 'origin/support/3.2' into develop 2024-06-10 16:15:20 +02:00
Benjamin Dalsass
5d31c372fa Merge remote-tracking branch 'origin/support/3.2' into develop 2024-06-10 10:08:11 +02:00
Benjamin Dalsass
0497122e25 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-06-07 08:55:30 +02:00
Benjamin Dalsass
645d68d5d0 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-06-07 08:20:40 +02:00
Romain Quetiez
f04b8368aa List explicitely the modules when finding inconsistencies in installation.xml 2024-06-04 21:58:18 +02:00
vdumas
f9fd4f1e2a N°6303 - Add search ticket bricks in portal 2024-06-04 21:58:18 +02:00
Benjamin Dalsass
55ed552ba7 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-06-04 10:59:39 +02:00
Benjamin Dalsass
7bfa23fab7 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-06-03 15:51:14 +02:00
Eric Espie
7bd427b4e7 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-30 12:08:40 +02:00
Benjamin Dalsass
a9a9fb1da2 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-30 09:21:58 +02:00
Eric Espie
6653b13144 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-29 18:21:06 +02:00
Benjamin Dalsass
8bea29f0e7 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-28 16:07:36 +02:00
Benjamin Dalsass
d556564ee1 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-28 08:31:18 +02:00
Benjamin Dalsass
2286a9e2a4 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-28 08:17:19 +02:00
Molkobain
30c10cb67f Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-24 23:58:32 +02:00
Benjamin Dalsass
74a42a4d4f Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-24 16:14:44 +02:00
Eric Espie
c31443cb79 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-24 10:18:48 +02:00
Eric Espie
a698aec7a5 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-21 15:08:32 +02:00
Eric Espie
04d2aaf05c Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-21 14:30:30 +02:00
jf-cbd
5103f898e1 Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-05-17 11:23:08 +02:00
Romain Quetiez
68ee3231f4 Merge branch 'support/3.2' into develop
# Conflicts:
#	tests/php-unit-tests/src/Hook/TestsRunStartHook.php
2024-05-16 20:24:38 +02:00
jf-cbd
62a7850a0b Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-04-30 15:12:21 +02:00
jf-cbd
20f1ec42b1 Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-04-30 10:58:35 +02:00
jf-cbd
e51e55b634 Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop
# Conflicts:
#	pages/ajax.render.php
2024-04-30 08:18:57 +02:00
Benjamin Dalsass
bc7db973ad Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-25 09:17:33 +02:00
jf-cbd
0489103e11 Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-04-24 17:13:36 +02:00
Eric Espie
b99249d2d2 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-24 16:11:58 +02:00
jf-cbd
23aed5415c Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-04-23 15:24:52 +02:00
jf-cbd
2adbcf5e6f Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop
# Conflicts:
#	pages/ajax.render.php
2024-04-19 15:37:52 +02:00
jf-cbd
6a99e3c1c9 Revert "Merge branch 'refs/heads/support/3.2' into develop"
This reverts commit 8e1de5bf2d, reversing
changes made to f6808ee522.
2024-04-19 15:34:56 +02:00
jf-cbd
8e1de5bf2d Merge branch 'refs/heads/support/3.2' into develop 2024-04-19 11:28:55 +02:00
Molkobain
f6808ee522 Fix typo in README.md 2024-04-19 11:22:33 +02:00
Molkobain
4f650d3c5b Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-19 09:26:10 +02:00
Molkobain
ba641d96d5 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-18 18:51:05 +02:00
Molkobain
3dc514c67d Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-18 18:19:52 +02:00
Molkobain
5df7c58b1b Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-18 17:03:24 +02:00
Benjamin Dalsass
bb680ac514 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-17 16:05:56 +02:00
Stephen Abello
e18018cacd Merge branch 'support/3.2' into develop 2024-04-16 09:22:58 +02:00
odain
5d91c8832b Merge branch 'support/3.2' into develop 2024-04-15 10:25:01 +02:00
odain
c96c8ac30d Merge branch 'support/3.2' into develop 2024-04-12 17:19:45 +02:00
Molkobain
f930533d8b Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-12 11:08:59 +02:00
odain
29bfb8a8bf Merge branch 'support/3.2' into develop 2024-04-11 18:56:02 +02:00
Molkobain
f04359f398 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-11 17:04:13 +02:00
odain
c24052e976 Merge branch 'support/3.2' into develop 2024-04-10 15:35:17 +02:00
odain
5f8e43fc67 Merge branch 'support/3.2' into develop 2024-04-10 14:26:43 +02:00
jf-cbd
5427b146cf Merge branch 'refs/heads/support/3.2' into develop 2024-04-10 10:36:40 +02:00
odain
6f231ce800 Merge branch 'support/3.2' into develop 2024-04-09 21:37:20 +02:00
odain
d66d1c8739 Merge branch 'support/3.2' into develop 2024-04-09 11:07:21 +02:00
odain
50b4388b8d Merge branch 'support/3.2' into develop 2024-04-09 11:02:32 +02:00
odain
9cc8b75ffb Merge branch 'support/3.2' into develop
# Conflicts:
#	setup/applicationinstaller.class.inc.php
2024-04-09 10:13:45 +02:00
jf-cbd
e715342f7a Merge branch 'refs/heads/support/3.2' into develop 2024-04-08 17:26:40 +02:00
jf-cbd
c86ac4f9a7 Merge branch 'refs/heads/support/3.2' into develop 2024-04-08 17:13:34 +02:00
Molkobain
b79fe06be4 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-08 15:14:30 +02:00
jf-cbd
e5764ac495 Merge branch 'refs/heads/support/3.2' into develop 2024-04-08 14:14:31 +02:00
Molkobain
2ff7cb8956 Add Vincenzo Katriel Giuva (@DarkNight97boss) to the contributors list! 🙌 2024-04-05 16:18:52 +02:00
jf-cbd
3064ab4b25 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-05 15:01:41 +02:00
Molkobain
d2f8d2e903 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-04 15:22:25 +02:00
odain
036c7796e5 Merge branch 'support/3.2' into develop 2024-04-02 15:08:29 +02:00
Molkobain
356dd46537 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-02 10:40:57 +02:00
Molkobain
bb30e1abb8 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-02 10:29:30 +02:00
Stephen Abello
84d225e389 Merge branch 'support/3.2' into develop 2024-03-29 11:20:07 +01:00
Eric Espie
dcf2780cac Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-28 13:33:04 +01:00
Eric Espie
3e421c770b Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-27 11:30:02 +01:00
Anne-Cath
19cb0e873a Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-26 11:41:26 +01:00
Molkobain
ed81dbc4a2 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-21 17:43:41 +01:00
Eric Espie
a93d5b87e1 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-19 17:16:35 +01:00
Molkobain
d40a52e403 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-19 14:55:15 +01:00
Eric Espie
a4a3797cc1 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-19 14:07:55 +01:00
Pierre Goiffon
bd29e5f13a Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-18 18:20:19 +01:00
Pierre Goiffon
ce13ee8a55 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-18 18:17:20 +01:00
Pierre Goiffon
e5ed5fbc6a Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-14 17:28:11 +01:00
jf-cbd
ff7eca2f08 Merge branch 'support/3.2' into develop
# Conflicts:
#	lib/composer/installed.php
2024-03-14 17:17:55 +01:00
Pierre Goiffon
06be217030 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-13 10:08:40 +01:00
Pierre Goiffon
4880052559 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-13 09:28:07 +01:00
Pierre Goiffon
147043857b Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-12 18:39:44 +01:00
Pierre Goiffon
1bfa1a7746 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-12 17:44:49 +01:00
Benjamin Dalsass
3747a6a454 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-12 09:48:37 +01:00
Pierre Goiffon
f1e6246962 N°7299 Remove moment.js (moved to NPM) 2024-03-11 16:56:41 +01:00
Pierre Goiffon
d2c6190b59 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-11 16:40:49 +01:00
Pierre Goiffon
0f8247ce69 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-11 16:32:03 +01:00
Benjamin Dalsass
dae3be99ce Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-11 16:05:34 +01:00
Pierre Goiffon
6e625e0cc9 N°7329 remove js/jquery.autocomplete.js 2024-03-08 16:19:29 +01:00
Pierre Goiffon
a602101695 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	js/jquery-ui-1.11.4.custom.min.js
#	js/jquery-ui.custom.min.js
2024-03-08 16:16:18 +01:00
Pierre Goiffon
48d86fdbf5 N°4797 Remove js/jquery-ui-1.11.4.custom.min.js
File was deprecated in 3.0.0 with N°3184
Use instead JQuery lib in NPM (since 3.2.0 N°5621)
2024-03-08 12:47:14 +01:00
Pierre Goiffon
d6cfde0f94 N°7299 Remove jquery-migrate (moved to NPM) 2024-03-07 15:33:07 +01:00
Pierre Goiffon
1b9b4fdd39 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-07 15:28:27 +01:00
Benjamin Dalsass
8bb551eb10 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-07 14:34:20 +01:00
Molkobain
6177035f84 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-07 13:26:29 +01:00
Pierre Goiffon
e6d3ce5918 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-07 11:12:39 +01:00
Molkobain
9e08143981 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-06 14:47:29 +01:00
Pierre Goiffon
2014a48d22 N°7299 Remove CSS & JS files moved to NPM
Migration was made in 3.2 with N°5621
2024-03-04 11:19:59 +01:00
Pierre Goiffon
b7e1202cf1 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-04 10:43:16 +01:00
Pierre Goiffon
44b994414d Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-04 08:42:18 +01:00
Pierre Goiffon
a7265c2493 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-01 17:26:55 +01:00
jf-cbd
261ec270c5 ✏️ fix a typo 2024-03-01 17:24:39 +01:00
Eric Espie
356be511a8 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-01 09:44:24 +01:00
Anne-Cath
f896e72013 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-29 16:57:24 +01:00
Benjamin Dalsass
187a895265 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-28 17:16:55 +01:00
Pierre Goiffon
6224f8ca51 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-28 16:44:29 +01:00
Pierre Goiffon
3b27e6e466 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-28 15:13:06 +01:00
Anne-Cath
4e53deec9d Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-28 10:37:15 +01:00
Anne-Cath
2cafa78339 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-27 18:22:22 +01:00
Eric Espie
f3f86017b6 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-27 18:14:28 +01:00
Eric Espie
5479080eb2 Merge remote-tracking branch 'origin/support/3.1' into develop 2024-02-27 17:40:56 +01:00
Benjamin Dalsass
3cb3e8a7f9 N°6937 - Symfony 6.4 - Handle Symfony configuration files 2024-02-26 10:38:36 +01:00
Molkobain
6a907f1fed Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-22 16:08:09 +01:00
Benjamin Dalsass
241f845c08 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-21 10:50:44 +01:00
Benjamin Dalsass
623f63abb3 N°7279 - AttributeClass defined in XML datamodel compilation issue 2024-02-21 09:53:57 +01:00
Pierre Goiffon
293adc3a99 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-20 12:12:51 +01:00
Pierre Goiffon
dee9f90a0b Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-20 09:27:51 +01:00
Pierre Goiffon
a7348f0eb9 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-20 09:13:10 +01:00
Molkobain
7457735c04 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-19 20:24:26 +01:00
Molkobain
0f389b2a48 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-19 10:00:21 +01:00
Stephen Abello
bfe01899a4 N°7243 - Add toast notifications to iTop (#614)
* N°7243 - Add toast notifications to iTop

* Apply suggestions from code review

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>

* Apply suggestions from code review

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>

* Apply suggestions from code review

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>

* Apply suggestions from code review

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>

* Apply suggestions from code review

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>

* Apply suggestions from code review

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>

* Update js/pages/backoffice/toolbox.js

* Update js/utils.js

* N°7243 - Move some rules to a dedicated partial and use spacing variables

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2024-02-16 10:59:05 +01:00
Pierre Goiffon
9531762c44 N°7252 Remove unused files in /js 2024-02-15 17:23:02 +01:00
Pierre Goiffon
e59a35e804 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-15 17:20:55 +01:00
Pierre Goiffon
21f5f5fe5e Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-15 14:53:43 +01:00
Pierre Goiffon
e6483fed98 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-15 11:11:59 +01:00
Pierre Goiffon
98bc04697e Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-15 09:51:46 +01:00
Pierre Goiffon
38e9b59b89 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-14 15:17:48 +01:00
Pierre Goiffon
118b22fe56 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-14 15:13:54 +01:00
Pierre Goiffon
8062694331 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-09 13:41:47 +01:00
Pierre Goiffon
e576235307 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-09 12:05:24 +01:00
Pierre Goiffon
e87502e7bf Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-08 11:35:03 +01:00
Molkobain
400ed9e999 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-07 15:05:19 +01:00
Molkobain
5b01ad23c4 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-06 21:53:38 +01:00
Pierre Goiffon
9a06fa4704 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-01 16:12:34 +01:00
Pierre Goiffon
c5555ad365 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-01 10:30:17 +01:00
Pierre Goiffon
9c6db1d730 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-01-30 11:47:04 +01:00
Molkobain
30cfe69e26 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-01-30 09:00:45 +01:00
Pierre Goiffon
e45049a602 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-01-29 17:20:46 +01:00
Pierre Goiffon
82129ff3bd Merge remote-tracking branch 'origin/support/3.2' into develop 2024-01-29 16:31:52 +01:00
Timothee
593b603295 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-01-29 15:43:33 +01:00
Molkobain
54b2e41afd Merge remote-tracking branch 'origin/support/3.2' into develop 2024-01-29 15:07:10 +01:00
Molkobain
ede6e6dd87 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-01-24 15:22:21 +01:00
Molkobain
e18ea88735 N°6648 - Use \utils::GetDataPath() instead of hard-coded paths (#555)
* N°6648 - Use \utils::GetDataPath() instead of hard-coded paths

* Update setup/applicationinstaller.class.inc.php

Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>

---------

Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>
2024-01-22 21:06:08 +01:00
Pierre Goiffon
750dfe746e Merge remote-tracking branch 'origin/support/3.2' into develop 2024-01-22 16:19:59 +01:00
Molkobain
53404f1002 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-01-17 23:06:16 +01:00
jf-cbd
5623784c59 N°7134 - Can't retrieve the list of changes when editing URP_UserProfile 2024-01-17 15:15:11 +01:00
Molkobain
d122de04e4 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-01-16 19:57:16 +01:00
Pierre Goiffon
2fa9774955 📝 Version history: add 2.7.10 and 3.0.4 2024-01-16 18:22:44 +01:00
3401 changed files with 232332 additions and 226511 deletions

101
.doc/README.md Normal file
View File

@@ -0,0 +1,101 @@
# Phpdoc dokuwiki template
This directory contains a template for rendering iTop phpdoc as dokuwiki pages.
Conventional tags that you should use:
* `@internal` : exclude from the documentation.
* `@api` : it means that a method is an api, thus it may be interacted with.
* `@see` : it points to another documented method
* `@link` : external url
* if you point to another page of the wiki, please use relative links.
* `@example` : let you provide example of code
* `@param`, `@return`, `@throws`, ...
## Special instructions
Some iTop specific tags were added :
* `@api-advanced`: it means that a method is an `@api` but mark it also as "complex" to use
* `@overwritable-hook`: used to mark a method as "designed to be extended"
* `@extension-hook`: not used for now
* `@phpdoc-tuning-exclude-inherited`: once this tag is present on a class, it's inherited methods won't be showed.
### known limitations:
#### `@see` tags must be very specific:
* always prefix class members (attributes or methods) with `ClassName::` (do not use self)
* for methods always suffix them with `()`,
* do not reference variables since they are not documented. If you have to, always prefix them with `$`
examples:
```
/**
* @see DBObject
* @see DBObject::Get()
* @see DBObject::$foo
*/
```
#### Do not use inline tags, they do not work properly, example:
```
/**
* This is a texts with an inline tag {@see [FQSEN] [<description>]} it must never be used
*/
```
#### The `@example` tag must respect this very precise syntax
* the sentence in the first line (next to the tag) is the title, it must be enclosed by double quotes
* the following lines are the sample code.
* 💔 since we simply hack the official tag, this syntax must be respected carefully 💔
example:
```
/**
* @example "This is the title of the multiline example"
* $foo = DBObject::Get('foo');
* DBObject::Set('foo', ++$foo);
*/
```
## How content is included into the documentation
**For a class** those requirements have to be respected:
- the file containing the class must be listed in `/phpdoc/files/file[]` of `.doc/phpdoc-objects-manipulation.dist.xml`
- the class **must not** have the tag `@internal`
- the class **must** have at least one of: `@api`, `@api-advanced`, `@overwritable-hook`, `@extension-hook`
Then, **for a method** of an eligible class:
- **public** methods **must** have at least one of: `@api`, `@api-advanced`, `@overwritable-hook`, `@extension-hook`
- **protected** methods **must** have at least one of: `@overwritable-hook`, `@extension-hook`
- **private** methods are **always excluded**
**Class properties** and **constants** are never documented (this is subject to change).
## A note about the rendering engine
:notebook: as spaces are used to mark code, the templates (`.doc/phpdoc-templates/combodo-wiki/*`) have very few indentation, thus they are awful to read (sorry).
## Installation
Note : PHP7 is required. Migrating to PHP8 requires some additional work which is questionable as an alternative way to generate a documentation is being considered.
```
cd .doc
composer require phpdocumentor/phpdocumentor:~2 --dev
```
## Generation
1. Switch to this directory : `cd /path/to/itop/.doc`
2. `composer install`
3. `./bin/build-doc-object-manipulation`
3. `./bin/build-doc-extensions`
4. Get the generated files from `/path/to/itop/data/phpdocumentor/output`
## Dokuwiki requirements
* the template uses the [wrap plugin](https://www.dokuwiki.org/plugin:wrap).
* the generated files have to be placed under an arbitrary directory of `[/path/to/dokuwiki]/data/pages`.
* the html has to be activated [config:htmlok](https://www.dokuwiki.org/config:htmlok)
* the generated files have to be in lowercase

6
.doc/bin/build-doc-extensions Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/sh -x
rm -rf /tmp/phpdoc-twig-cache/ && rm -rf data/phpdocumentor/output/extensions/ && rm -rf data/phpdocumentor/temp/extensions/ && ./vendor/bin/phpdoc -c ./phpdoc-extensions.dist.xml -vvv
# now wee need to lowercase every generated file because dokuwiki can't handle uppercase
cd ../data/phpdocumentor/output/extensions/ && for i in $(ls | grep [A-Z]); do mv -i $i $(echo $i | tr 'A-Z' 'a-z'); done

View File

@@ -0,0 +1,7 @@
#!/bin/sh -x
rm -rf /tmp/phpdoc-twig-cache/ && rm -rf ../data/phpdocumentor/output/objects-manipulation/ && rm -rf ../data/phpdocumentor/temp/objects-manipulation/ && ./vendor/bin/phpdoc -c ./phpdoc-objects-manipulation.dist.xml -vvv
# now wee need to lowercase every generated file because dokuwiki can't handle uppercase
cd ../data/phpdocumentor/output/objects-manipulation/ && for i in $( ls | grep [A-Z] ); do mv -i $i `echo $i | tr 'A-Z' 'a-z'`; done

6
.doc/composer.json Normal file
View File

@@ -0,0 +1,6 @@
{
"require-dev": {
"phpdocumentor/phpdocumentor": "~2",
"jms/serializer": "1.7.*"
}
}

3015
.doc/composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

View File

@@ -0,0 +1,98 @@
# iTop version history
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'base', 'themeVariables': {
'git0': 'lawngreen',
'git3': 'dodgerblue',
'git4': 'grey',
'git5': 'grey',
'git6': 'grey',
'git7': 'grey'
}, 'gitGraph': {'showBranches': true,'mainBranchName': 'develop','rotateCommitLabel': true}} }%%
gitGraph
commit id: "2016-07-06" tag: "2.3.0" type: HIGHLIGHT
branch support/2.3 order: 900
commit id: "2016-07-08" tag: "2.3.1"
commit id: "2016-12-22" tag: "2.3.3"
commit id: "2017-04-14" tag: "2.3.4"
checkout develop
commit id: "2017-07-12" tag: "2.4.0-beta" type: REVERSE
commit id: "2017-11-16" tag: "2.4.0" type: HIGHLIGHT
branch support/2.4 order: 890
commit id: "2018-02-14" tag: "2.4.1"
checkout develop
commit id: "2018-04-25" tag: "2.5.0-beta" type: REVERSE
checkout support/2.4
commit id: "2018-06-14" tag: "2.4.2"
checkout develop
commit id: "2018-06-27" tag: "2.5.0" type: HIGHLIGHT
branch support/2.5 order: 880
checkout develop
commit id: "2019-01-09" tag: "2.6.0" type: HIGHLIGHT
branch support/2.6 order: 870
commit id: "2019-03-28" tag: "2.6.1"
checkout develop
commit id: "2019-12-18" tag: "2.7.0-beta" type: REVERSE
checkout support/2.5
commit id: "2020-01-22" tag: "2.5.4"
checkout support/2.6
commit id: "2020-01-23" tag: "2.6.3"
checkout develop
commit id: "2020-01-29" tag: "2.7.0-beta2" type: REVERSE
commit id: "2020-04-01" tag: "2.7.0-1" type: HIGHLIGHT
checkout support/2.6
commit id: "2020-04-22" tag: "2.6.4"
checkout develop
branch support/2.7 order: 860
commit id: "2020-06-26" tag: "2.7.1"
checkout support/2.7
commit id: "2020-12-09" tag: "2.7.3"
commit id: "2021-03-31" tag: "2.7.4"
checkout develop
commit id: "2021-04-06" tag: "3.0.0-beta" type: REVERSE
checkout support/2.7
commit id: "2021-07-05" tag: "2.7.5"
checkout develop
commit id: "2021-07-05." tag: "3.0.0-beta2" type: REVERSE
checkout support/2.7
commit id: "2021-12-17" tag: "2.7.6"
checkout develop
commit id: "2022-01-04" tag: "3.0.0" type: HIGHLIGHT
branch support/3.0 order: 850
commit id: "2022-04-08" tag: "3.0.1"
checkout support/2.7
commit id: "2022-07-11" tag: "2.7.7"
checkout support/3.0
commit id: "2022-09-12" tag: "3.0.2-1"
checkout develop
checkout support/2.7
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"
checkout support/3.1
commit id: "2023-12-20" tag: "3.1.1"
checkout develop
commit id: "2024-01-15" tag: "Start 3.2" type: HIGHLIGHT
branch support/3.2 order: 830
checkout support/2.7
commit id: "2024-01-17a" tag: "2.7.10"
checkout support/3.0
commit id: "2024-01-17b" tag: "3.0.4"
checkout support/2.7
commit id: "2024-09-28" tag: "2.7.11"
checkout support/3.1
commit id: "2024-09-27" tag: "3.1.2"
checkout support/3.2
commit id: "2024-06-25" tag: "3.2.0-beta1" type: REVERSE
commit id: "2024-08-07" tag: "3.2.0"
```
To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start).

20
.doc/phpdoc-extensions.dist.xml Executable file
View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<phpdoc>
<title><![CDATA[iTop extensions]]></title>
<parser>
<target>../data/phpdocumentor/temp/extensions</target>
</parser>
<transformer>
<target>../data/phpdocumentor/output/extensions</target>
</transformer>
<transformations>
<template name="phpdoc-templates/combodo-wiki"/>
</transformations>
<files>
<file>../application/applicationextension.inc.php</file>
</files>
</phpdoc>

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" ?>
<phpdoc>
<!--
/**
The documentation of this file can be found here : https://docs.phpdoc.org/references/configuration.html
it has to be completed by the CLI parameters documentation which is more comprehensive: https://docs.phpdoc.org/references/commands/project_run.html#usage
usage:
vendor/bin/phpdoc -c phpdoc-objects-manipulation.dist.xml
*/
-->
<title><![CDATA[iTop's objects manipulation API]]></title>
<parser>
<default-package-name>iTopORM</default-package-name>
<target>../data/phpdocumentor/temp/objects-manipulation</target>
<visibility>public,protected</visibility>
<markers>
<!--<item>TODO</item>-->
<!--<item>FIXME</item>-->
</markers>
<extensions>
<extension>php</extension>
</extensions>
</parser>
<transformer>
<target>../data/phpdocumentor/output/objects-manipulation</target>
</transformer>
<transformations>
<template name="phpdoc-templates/combodo-wiki"/>
</transformations>
<!--<logging>-->
<!--<level>warn</level>-->
<!--<paths>-->
<!--&lt;!&ndash;<default>data/phpdocumentor/log/objects-manipulation/{DATE}.log</default>&ndash;&gt;-->
<!--&lt;!&ndash;<errors>data/phpdocumentor/log/objects-manipulation/{DATE}.errors.log</errors>&ndash;&gt;-->
<!--<default>{APP_ROOT}/data/log/{DATE}.log</default>-->
<!--<errors>{APP_ROOT}/data/log/{DATE}.errors.log</errors>-->
<!--</paths>-->
<!--</logging>-->
<files>
<file>../core/dbobject.class.php</file>
<file>../core/dbobjectsearch.class.php</file>
<file>../core/metamodel.class.php</file>
<file>../core/dbobjectset.class.php</file>
<file>../core/dbsearch.class.php</file>
<file>../core/dbunionsearch.class.php</file>
</files>
</phpdoc>

View File

View File

@@ -0,0 +1,136 @@
{% extends 'layout.txt.twig' %}
{% block content %}
<wrap button>[[start|🔙 Back]]</wrap>
{% if node.tags['internal'] is defined %}
====== {{ node.name }} ======
<WRAP alert>This class is "internal", and thus is not documented!</WRAP>
{% elseif node.tags['api'] is not defined and node.tags['api-advanced'] is not defined and node.tags['overwritable-hook'] is not defined and node.tags['extension-hook'] is not defined %}
====== {{ node.name }} ======
<WRAP alert>This class is neither "api", "api-advanced", "overwritable-hook" or "extension-hook", and thus is not documented!</WRAP>
{% else %}
====== {{ node.name }} ======
{% if node.deprecated %}<wrap danger>deprecated</wrap>{% endif %}
{% if node.abstract %}<wrap warning>abstract</wrap>{% endif %}
{% if node.final %}<wrap notice>final</wrap>{% endif %}
{% include 'includes/wrap-tags.txt.twig' with {structure:node, wrap: 'safety', wrapTags: ['api', 'api-advanced', 'overwritable-hook', 'extension-hook']} %}
{% if node.deprecated %}
=== **<del>Deprecated</del>**===
//{{ node.tags.deprecated[0].description }}//
{% endif %}
== {{ node.summary|replace({"\n":""})|raw }} ==
<html>{{ node.description|markdown|raw }}</html>
{% include 'includes/code-examples.txt.twig' with {structure:node, title_level: '====='} %}
{% set class = node.parent %}
{% block hierarchy_element %}
{% if class and class.name is defined and class.name|trim != '' %}
==== parent ====
{% set child = class %}
{% set class = class.parent %}
{{ block('hierarchy_element') }}
[[{{ child.name }}|{{ child.name }}]]
{% endif %}
{% endblock %}
{% for interface in node.interfaces|sort_asc %}
{% if loop.first %}
==== Implements ====
{% endif %}
{% if loop.length > 1 %} * {% endif %}{{ interface.fullyQualifiedStructuralElementName ?: interface }}
{% endfor %}
{% for trait in node.usedTraits|sort_asc %}
{% if loop.first %}
==== Uses traits ====
{% endif %}
{% if loop.length > 1 %} * {% endif %}{{ trait.fullyQualifiedStructuralElementName ?: trait }}
{% endfor %}
{% include 'includes/see-also.txt.twig' with {structure:node, title_level: '==='} %}
{% include 'includes/tags.txt.twig' with {structure:node, title_level: '=====', blacklist: ['link', 'see', 'abstract', 'example', 'method', 'property', 'property-read', 'property-write', 'package', 'subpackage', 'phpdoc-tuning-exclude-inherited', 'api', 'api-advanced', 'overwritable-hook', 'extension-hook', 'copyright', 'license', 'code-example']} %}
{% set methods = node.inheritedMethods.merge(node.methods.merge(node.magicMethods)) %}
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'api'} %}
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'api-advanced'} %}
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'overwritable-hook'} %}
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'extension-hook'} %}
{% include 'includes/code-examples.txt.twig' with {structure:node, title_level: '=====', sub_title_level: '=='} %}
<WRAP clear />
{% for method in methods|sort_asc
if method.visibility == 'public'
and (
method.tags['api'] is defined
or method.tags['api-advanced'] is defined
or method.tags['overwritable-hook'] is defined
or method.tags['extension-hook'] is defined
)
and (
node.tags['phpdoc-tuning-exclude-inherited'] is not defined
or method.parent.name == node.name
)
%}
{%- if loop.first %}
===== Public methods =====
{% endif %}
{{ block('method') }}
{% endfor %}
{% for method in methods|sort_asc if method.visibility == 'protected' and (method.tags['overwritable-hook'] is defined or method.tags['extension-hook'] is defined) %}
{%- if loop.first %}
===== Protected methods =====
{% endif %}
{{ block('method') }}
{% endfor %}
{% set constants = node.inheritedConstants.merge(node.constants) %}
{% if constants|length > 0 %}
===== Constants =====
{% for constant in constants|sort_asc %}
{{ block('constant') }}
{% endfor %}
{% endif %}
{#{% set properties = node.inheritedProperties.merge(node.properties.merge(node.magicProperties)) %}#}
{#{% for property in properties|sort_asc if property.visibility == 'public' %}#}
{#{%- if loop.first %}#}
{#===== Public properties =====#}
{#{% endif %}#}
{#{{ block('property') }}#}
{#{% endfor %}#}
{#{% for property in properties|sort_asc if property.visibility == 'protected' %}#}
{#{%- if loop.first %}#}
{#===== Protected properties =====#}
{#{% endif %}#}
{#{{ block('property') }}#}
{#{% endfor %}#}
{%- endif %} {#{% elseif node.tags['xxx'] is not defined and ... #}
<wrap button>[[start|🔙 Back]]</wrap>
{% endblock %}

View File

@@ -0,0 +1,31 @@
{% block constant %}
<WRAP group box >
<WRAP twothirds column >
==== {{ constant.name }} ====
</WRAP>{# twothirds column#}
<WRAP third column>
{% if constant.deprecated %}<wrap danger>deprecated</wrap> {% endif %}
{% if (node.parent is not null and constant.parent.fullyQualifiedStructuralElementName != node.fullyQualifiedStructuralElementName) %}<wrap notice>inherited</wrap> {% endif %}
</WRAP>{# third column#}
== {{ constant.summary|replace({"\n":""})|raw }} ==
<html>{{ constant.description|markdown|raw }}</html>
{% if constant.deprecated %}
=== Deprecated ===
{{ constant.tags.deprecated[0].description|raw }}
{% endif %}
{% include 'includes/inherited-from.txt.twig' with {structure:constant} %}
{% include 'includes/see-also.txt.twig' with {structure:constant, title_level: '=='} %}
{% include 'includes/uses.txt.twig' with {structure:constant, title_level: '=='} %}
{% include 'includes/tags.txt.twig' with {structure:constant, title_level: '==', blacklist: ['link', 'see', 'var', 'deprecated', 'uses', 'package', 'subpackage', 'todo', 'code-example']} %}
</WRAP>{# group #}
{% endblock %}

View File

@@ -0,0 +1,95 @@
{% block method %}
<WRAP group box >
<WRAP twothirds column >
==== {{ method.name }} ====
</WRAP>{# twothirds column#}
<WRAP third column >
{% include 'includes/wrap-tags.txt.twig' with {structure:method, wrap: 'safety', wrapTags: ['api', 'api-advanced', 'overwritable-hook', 'extension-hook']} %}
{% if method.deprecated %}<wrap danger>deprecated</wrap> {% endif %}
{% if (node.parent is not null and method.parent.fullyQualifiedStructuralElementName != node.fullyQualifiedStructuralElementName) %}<wrap notice>inherited</wrap> {% endif %}
{% if method.abstract %}<wrap warning>abstract</wrap> {% endif %}
{% if method.final %}<wrap notice>final</wrap> {% endif %}
<wrap notice>{{ method.visibility }}</wrap>
{% if method.static %}<wrap warning>static</wrap> {% endif %}
</WRAP>{# third column#}
== {{ method.summary|replace({"\n":""})|raw }} ==
<html>{{ method.description|markdown|raw }}</html>
<code php>{% if method.abstract %}abstract {% endif %}{% if method.final %}final {% endif %}{{ method.visibility }} {% if method.static %}static {% endif %}{{ method.name }}({% for argument in method.arguments %}{{ argument.isVariadic ? '...' }}{{ argument.name }}{{ argument.default ? (' = '~argument.default)|raw }}{% if not loop.last %}, {% endif %}{% endfor %})</code>
<WRAP twothirds column >
=== Parameters ===
{% if method.arguments|length > 0 -%}
^ types ^ name ^ default ^ description ^
{% for argument in method.arguments -%}
| **<nowiki>{{ argument.types|join('|')|raw }}</nowiki>** | {{ argument.name }} {{ argument.isVariadic ? '<small style="color: gray">variadic</small>' }} | <nowiki>{{ argument.default|raw }}</nowiki> | {{ argument.description|trim|replace("\n", ' ')|raw }} |{{ "\r\n" }}
{%- endfor %}
{% else %}
//none//
{% endif %}
{#=== Parameters ===#}
{#{% if method.arguments|length > 0 -%}#}
{#{% for argument in method.arguments -%}#}
{#== {{ argument.name }} ==#}
{#{% set varDesc %}#}
{#<span style="margin:0 10px; 0 20px; font-weight: bold;">{{ argument.types|join('|') }}</span>#}
{#{{ argument.isVariadic ? '<small style="color: gray">variadic</small>' }}#}
{#{{ argument.description|raw }}#}
{#{% endset %}#}
{#<html>{{ varDesc|markdown|raw }}</html>#}
{#{%- endfor %}#}
{#{% else %}#}
{#<wrap tip>This method has no parameter</wrap>#}
{#{% endif %}#}
{% if method.response and method.response.types|join() != 'void' %}
=== Returns ===
<html>{{ ('**' ~ method.response.types|join('|')|trim ~ '** ' ~ method.response.description)|markdown|raw }}</html>
{% endif %}
</WRAP>{# twothirds column#}
<WRAP third column >
{% if method.tags.throws|length > 0 or method.tags.throw|length > 0 %}
=== Throws ===
{% for exception in method.tags.throws -%}
{% if loop.length > 1 %} * {% endif %}''{{ exception.types|join('|')|raw }}'' <nowiki>{{ exception.description|raw }}</nowiki>
{% endfor %}
{% endif %}
{% include 'includes/inherited-from.txt.twig' with {structure:method} %}
{% include 'includes/see-also.txt.twig' with {structure:method, title_level: '==='} %}
{% include 'includes/uses.txt.twig' with {structure:method, title_level: '==='} %}
{% include 'includes/used-by.txt.twig' with {structure:method, title_level: '==='} %}
{% include 'includes/tags-with-description.txt.twig' with {structure:method, title_level: '===', WRAP: 'info', tagsWithDescription: ['api', 'api-advanced', 'overwritable-hook', 'extension-hook']} %}
{% include 'includes/tags.txt.twig' with {structure:method, title_level: '===', blacklist: ['todo', 'link', 'see', 'abstract', 'example', 'param', 'return', 'access', 'deprecated', 'throws', 'throw', 'uses', 'api', 'api-advanced', 'overwritable-hook', 'extension-hook', 'used-by', 'inheritdoc', 'code-example']} %}
</WRAP>{# third column#}
{% include 'includes/code-examples.txt.twig' with {structure:method, title_level: '==='} %}
</WRAP>{# group #}
{% endblock %}

View File

@@ -0,0 +1,49 @@
{% block property %}
<WRAP group box>
<WRAP twothirds column >
==== ${{ property.name }} ====
</WRAP>{# twothirds column#}
<WRAP third column>
{% if property.deprecated %}<wrap danger>deprecated</wrap> {% endif %}
{% if (node.parent is not null and property.parent.fullyQualifiedStructuralElementName != node.fullyQualifiedStructuralElementName) %}<wrap notice>inherited</wrap> {% endif %}
</WRAP>{# third column#}
== {{ property.summary|replace({"\n":""})|raw }} ==
<html>{{ property.description|markdown|raw }}</html>
{% if property.var.0.description %}<html>{{ property.var.0.description|markdown|raw }}</html>{% endif %}
{#{% if property.types %}#}
{#== Type ==#}
{#{% for type in property.types %}#}
{#{% if loop.length > 1 %} * {% endif %}{{ type|raw }} : {{ type.description|raw }}#}
{#{% endfor %}#}
{#{{ property.types|join('|')|raw }}#}
{#{% endif %}#}
{% if property.deprecated %}
== Deprecated ==
{{ property.tags.deprecated[0].description }}
{% endif %}
{% include 'includes/inherited-from.txt.twig' with {structure:property} %}
{% include 'includes/see-also.txt.twig' with {structure:property, title_level: '=='} %}
{% include 'includes/uses.txt.twig' with {structure:property, title_level: ''} %}
{% include 'includes/tags.txt.twig' with {structure:property, title_level: '==', blacklist: ['link', 'see', 'access', 'var', 'deprecated', 'uses', 'todo', 'code-example']} %}
<code php>{{ property.visibility }} ${{ property.name }}{% if property.types %} : {{ property.types|join('|')|raw }}{% endif %}</code>
</WRAP>{# group #}
{% endblock %}

View File

@@ -0,0 +1 @@
{{ node.source|raw }}

View File

@@ -0,0 +1,122 @@
{% extends 'layout.txt.twig' %}
{% block javascripts %}
{% endblock %}
{% block content %}
{#<section class="row-fluid">#}
{#<div class="span2 sidebar">#}
{#{% set namespace = project.namespace %}#}
{#{{ block('sidebarNamespaces') }}#}
{#</div>#}
{#</section>#}
{#<section class="row-fluid">#}
====== {{ node.path|split('/')|slice(0,-1)|join('/') }}{{ node.name }} ======
{{ node.summary }}
<html>{{ node.description|markdown|raw }}</html>
{% if node.traits|length > 0 %}
===== Traits =====
{% for trait in node.traits %}
<tr>
<td>{{ trait|raw }}</td>
<td><em>{{ trait.summary }}</em></td>
</tr>
{% endfor %}
{% endif %}
{% if node.interfaces|length > 0 %}
===== Interfaces =====
{% for interface in node.interfaces %}
<tr>
<td>{{ interface|raw }}</td>
<td><em>{{ interface.summary }}</em></td>
</tr>
{% endfor %}
{% endif %}
{% if node.classes|length > 0 %}
===== Classes =====
{% for class in node.classes %}
{{ class|raw }}
<em>{{ class.summary }}</em>
{% endfor %}
{% endif %}
{% if node.package is not empty and node.package != '\\' %}
===== Package =====
{{ node.subpackage ? (node.package ~ '\\' ~ node.subpackage) : node.package }}
{% endif %}
{% for tagName,tags in node.tags if tagName in ['link', 'see'] %}
{% if loop.first %}
===== See also =====
{% endif %}
{% for tag in tags %}
<dd><a href="{{ tag.reference ?: tag.link }}"><div class="namespace-wrapper">{{ tag.description ?: tag.reference }}</div></a></dd>
{% endfor %}
{% endfor %}
<h2>Tags</h2>
<table class="table table-condensed">
{% for tagName,tags in node.tags if tagName not in ['link', 'see', 'package', 'subpackage'] %}
<tr>
<th>
{{ tagName }}
</th>
<td>
{% for tag in tags %}
{{ tag.description|markdown|raw }}
{% endfor %}
</td>
</tr>
{% else %}
<tr><td colspan="2"><em>None found</em></td></tr>
{% endfor %}
</table>
</aside>
</div>
{% if node.constants|length > 0 %}
<div class="row-fluid">
<section class="span8 content file">
<h2>Constants</h2>
</section>
<aside class="span4 detailsbar"></aside>
</div>
{% for constant in node.constants %}
{{ block('constant') }}
{% endfor %}
{% endif %}
{% if node.functions|length > 0 %}
<div class="row-fluid">
<section class="span8 content file">
<h2>Functions</h2>
</section>
<aside class="span4 detailsbar"></aside>
</div>
{% for method in node.functions %}
{{ block('method') }}
{% endfor %}
{% endif %}
</div>
</section>
<div id="source-view" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="source-view-label" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="source-view-label">{{ node.file.name }}</h3>
</div>
<div class="modal-body">
<pre data-src="{{ path('files/' ~ node.path ~ '.txt')|raw }}" class="language-php line-numbers"></pre>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,42 @@
{% extends 'layout.html.twig' %}
{% block stylesheets %}
<link href="{{ path('css/jquery.iviewer.css') }}" rel="stylesheet" media="all"/>
<style>
#viewer {
position: relative;
width: 100%;
}
.wrapper {
overflow: hidden;
}
</style>
{% endblock %}
{% block javascripts %}
<script src="{{ path('js/jquery.mousewheel.js') }}" type="text/javascript"></script>
<script src="{{ path('js/jquery.iviewer.js') }}" type="text/javascript"></script>
<script type="text/javascript">
$(window).resize(function(){
$("#viewer").height($(window).height() - 100);
});
$(document).ready(function() {
$("#viewer").iviewer({src: '{{ path('graphs/classes.svg') }}', zoom_animation: false});
$('#viewer img').bind('dragstart', function(event){
event.preventDefault();
});
$(window).resize();
});
</script>
{% endblock %}
{% block content %}
<div class="row-fluid">
<div class="span12">
<div class="wrapper">
<div id="viewer" class="viewer"></div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,5 @@
# Fixes a vulnerability in CentOS: http://stackoverflow.com/questions/20533279/prevent-php-from-parsing-non-php-files-such-as-somefile-php-txt
<FilesMatch \.php\.txt$>
RemoveHandler .php
ForceType text/plain
</FilesMatch>

View File

@@ -0,0 +1,34 @@
{% if title_level is not defined %}
{%- set title_level = '==' -%}
{% endif %}
{% if sub_title_level is not defined %}
{%- set sub_title_level = title_level|slice(1) -%}
{% endif %}
{% if sub_title_level == '=' %}
{%- set sub_title_level = '' -%}
{% endif %}
{#{% for tagName,tags in structure.tags if tagName in ['code-example'] %}#}
{#{% if loop.first %}#}
{#{{title_level}} Examples {{title_level}}#}
{#{% endif %}#}
{#{% for tag in tags %}#}
{#{%- set descToken = tag.description|split("\n", 2) -%}#}
{#{%- set title = descToken[0] -%}#}
{#{%- set code = descToken[1] -%}#}
{#{{sub_title_level}} {{ title }} {{sub_title_level}}#}
{#<code php>{{ code|raw }}</code>#}
{#{% endfor %}#}
{#{% endfor %}#}
{% for tagName,tags in structure.tags if tagName in ['example'] %}
{% if loop.first %}
{{title_level}} Examples {{title_level}}
{% endif %}
{% for tag in tags %}
{{ sub_title_level }} {{ tag.filePath|escape }}{{ sub_title_level }}
<code php>{{ tag.description|raw }}</code>
{% endfor %}
{% endfor %}

View File

@@ -0,0 +1,12 @@
{% if title_level is not defined %}
{% set title_level='' %}
{% endif %}
{% if (node.parent is null) %}
{{title_level}} File {{ structure.path }} {{title_level}}
{% endif %}
{% if (node.parent is not null and structure.parent.fullyQualifiedStructuralElementName != node.fullyQualifiedStructuralElementName) %}
{{title_level}} Inherited from {{title_level}}
[[{{structure.parent}}|{{structure.parent}}]]
{% endif %}

View File

@@ -0,0 +1,26 @@
{% for structure in structures|sort_asc if structure.tags['internal'] is not defined and (structure.tags['api'] is defined or structure.tags['api-advanced'] is defined or structure.tags['overwritable-hook'] is defined or structure.tags['extension-hook'] is defined ) %}
{#{{ structure|raw }}#}
{% set structureName = structure|trim('\\', 'left') %}
<WRAP group box>
<WRAP twothirds column >
==== {{ structureName }} ====
</WRAP>{# twothirds column#}
<WRAP third column>
{% if structure.deprecated %}<wrap danger>deprecated</wrap>{% endif %}
{% if structure.abstract %}<wrap warning>abstract</wrap>{% endif %}
{% if structure.final %}<wrap notice>final</wrap>{% endif %}
{% if (node.parent is not null and structure.parent.fullyQualifiedStructuralElementName != node.fullyQualifiedStructuralElementName) %}<wrap notice>inherited</wrap> {% endif %}
{% include 'includes/wrap-tags.txt.twig' with {structure:structure, wrap: 'safety', wrapTags: ['api', 'api-advanced', 'overwritable-hook', 'extension-hook']} %}
</WRAP>{# third column#}
{{ structure.summary|raw }}
[[{{structureName}}|More information]]
</WRAP>{# group #}
{% endfor %}

View File

@@ -0,0 +1,26 @@
{% if title_level is not defined %}
{%- set title_level='==' -%}
{% endif %}
{% for tagName,tags in structure.tags if tagName in ['link', 'see'] %}
{% if loop.first %}
{{title_level}} See also {{title_level}}
{% endif %}
{% for tag in tags %}
{%- set linkTag = tag.reference|trim('\\', 'left') -%}
{% if not('()' in linkTag or '$' in linkTag or node.name in linkTag or '::' in linkTag ) %}
{%- set linkTag = linkTag|lower -%}
{% elseif node.name~'::' in linkTag %}
{%- set linkTag = linkTag|replace({(node.name~'::'): '#'})|lower -%}
{% elseif '::' in linkTag -%}
{%- set linkTag = linkTag|replace({'::': '#'})|lower -%}
{% else %}
{%- set linkTag = '#' ~ linkTag|lower -%}
{%- endif %}
{% if loop.length > 1 %} * {% endif %}{% if tag.reference is not empty -%}
[[{{linkTag}}|{{ (tag.reference)|trim('\\', 'left') }}]] {% if tag.description|trim is not empty %}: {{ tag.description|trim('\\', 'left') }} {% endif %}
{%- else -%}
{#{{ tag.description|trim('\\', 'left') }}#}
{% endif %}
{% endfor %}
{% endfor %}

View File

@@ -0,0 +1,56 @@
{% if tag is not defined -%}
{# Do not display @api if @api-advanced is also present #}
{%- set tag = "api" -%}
{%- endif %}
{% if hidden_by is not defined -%}
{# Do not display @api if @api-advanced is also present #}
{%- set hidden_by = {"api" : "api-advanced"} -%}
{%- endif %}
{% for method in methods|sort_asc
if (method.visibility == 'public')
and (
method.tags[tag] is defined
and (
hidden_by[tag] is not defined or method.tags[hidden_by[tag]] is not defined
)
)
%}
{%- if loop.first %}
{% if tag == 'api' %}
===== API synthesis =====
<WRAP>
List of the public API methods.
When manipulating {{ node.name }}, You can call those methods:
</WRAP>
{% elseif tag == 'api-advanced' %}
===== Advanced API synthesis =====
<WRAP>
List of advanced API methods
Beware they usage is recommended to advanced users only.
</WRAP>
{% elseif tag == 'overwritable-hook' %}
===== overwritable-hook synthesis =====
<WRAP >When inheriting from {{ node.name }},
you can overwrite those methods in order to add custom logic:
</WRAP>
{% elseif tag == 'extension-hook' %}
===== extension-hook synthesis =====
<WRAP >
When inheriting from {{ node.name }},
you can extend the behaviour of iTop by implementing:
</WRAP>
{% endif %}
{% endif %}
{% set sanitizedMethod = method|trim('\\', 'left')|replace({(node.name~'::'): ''}) %}
{% if '::' in sanitizedMethod -%}
{%- if node.tags['phpdoc-tuning-exclude-inherited'] is not defined %}
* [[{{sanitizedMethod|replace({'::': '#'})|lower}}|↪{{sanitizedMethod}}]] — {{ method.summary|replace({"\n":""})|raw }}
{% endif %}
{%- else %}
* [[#{{sanitizedMethod}}|{{sanitizedMethod}}]] — {{ method.summary|replace({"\n":""})|raw }}
{% endif %}
{% endfor %}

View File

@@ -0,0 +1,20 @@
{% if title_level is not defined %}
{% set title_level = '==' %}
{% endif %}
{%- for tagName,tags in structure.tags if tagName in tagsWithDescription -%}
{%- for tag in tags -%}
{%- if tag.description is not empty -%}
{%- if WRAP is defined -%}
<WRAP {{WRAP}}>
{%- endif -%}
{{title_level}} {{ tagName }} {{title_level}}
{{ tag.description|escape }}
{%- if WRAP is defined -%}
</WRAP>
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- endfor -%}

View File

@@ -0,0 +1,22 @@
{% if title_level is not defined %}
{% set title_level='=====' %}
{% endif %}
{% if blacklist is not defined %}
{% set blacklist =['link', 'see', 'abstract', 'example', 'method', 'property', 'property-read', 'property-write', 'package', 'subpackage', 'api', 'api-advanced', 'todo', 'code-example'] %}
{% endif %}
{% if hidden_by is not defined -%}
{# Do not display @api if @api-advanced is also present #}
{%- set hidden_by = {"api" : "api-advanced"} -%}
{%- endif %}
{#^ {% for tagName,tags in structure.tags if tagName not in blacklist -%}#}
{#{{ tagName }} ^#}
{#{%- endfor %}#}
{% for tagName,tags in structure.tags if tagName not in blacklist and (hidden_by[tagName] is not defined or structure.tags[hidden_by[tagName]] is not defined) %}
{%- if loop.first %}
{{title_level}} Tags {{title_level}}
{% endif %}
^ {{ tagName }} | {% for tag in tags %}{{ tag.version ? tag.version ~ ' ' : '' }}{{ tag.description}}{% endfor %} |
{% endfor %}

View File

@@ -0,0 +1,24 @@
{% if title_level is not defined %}
{% set title_level='' %}
{% endif %}
{% for tagName,tags in structure.tags if tagName in ['used-by'] %}
{% if loop.first %}
{{title_level}} Used by {{title_level}}
{% endif %}
{% for tag in tags %}
{% if loop.length > 1 %} * {% endif %}{{ tag.reference ?: tag.link }} : {{ tag.description ?: tag.reference }}
{% endfor %}
{% endfor %}
{#{% for tagName,tags in method.tags if tagName in ['uses'] %}#}
{#{% if loop.first %}#}
{#<dt>Uses</dt>#}
{#{% endif %}#}
{#{% for tag in tags %}#}
{#<dd>{{ tag.reference|raw }}</dd>#}
{#{% endfor %}#}
{#{% endfor %}#}

View File

@@ -0,0 +1,24 @@
{% if title_level is not defined %}
{% set title_level='' %}
{% endif %}
{% for tagName,tags in structure.tags if tagName in ['uses'] %}
{% if loop.first %}
{{title_level}} Uses {{title_level}}
{% endif %}
{% for tag in tags %}
{% if loop.length > 1 %} * {% endif %}{{ tag.reference ?: tag.link }} : {{ tag.description ?: tag.reference }}
{% endfor %}
{% endfor %}
{#{% for tagName,tags in method.tags if tagName in ['uses'] %}#}
{#{% if loop.first %}#}
{#<dt>Uses</dt>#}
{#{% endif %}#}
{#{% for tag in tags %}#}
{#<dd>{{ tag.reference|raw }}</dd>#}
{#{% endfor %}#}
{#{% endfor %}#}

View File

@@ -0,0 +1,11 @@
{% if wrap is not defined -%}
{% set wrap = 'notice' %}
{%- endif -%}
{% if hidden_by is not defined -%}
{# Do not display @api if @api-advanced is also present #}
{%- set hidden_by = {"api" : "api-advanced"} -%}
{%- endif %}
{%- for tagName,tags in structure.tags if tagName in wrapTags and (hidden_by[tagName] is not defined or structure.tags[hidden_by[tagName]] is not defined) %}
<wrap {{wrap}}>{{tagName}}</wrap>
{% endfor %}

View File

@@ -0,0 +1,121 @@
{% extends 'layout.txt.twig' %}
{% block content %}
<wrap button>[[start|🔙 Back]]</wrap>
{% if node.tags['internal'] is defined %}
====== {{ node.name }} ======
<WRAP alert>This interface is "internal", and thus is not documented!</WRAP>
{% elseif node.tags['api'] is not defined and node.tags['api-advanced'] is not defined and node.tags['overwritable-hook'] is not defined and node.tags['extension-hook'] is not defined %}
====== {{ node.name }} ======
<WRAP alert>This interface is neither "api", "overwritable-hook" or "extension-hook", and thus is not documented!</WRAP>
{% else %}
====== {{ node.name }} ======
{% if node.deprecated %}<wrap danger>deprecated</wrap>{% endif %}
{% if node.abstract %}<wrap warning>abstract</wrap>{% endif %}
{% if node.final %}<wrap notice>final</wrap>{% endif %}
{% include 'includes/wrap-tags.txt.twig' with {structure:node, wrap: 'safety', wrapTags: ['api', 'api-advanced', 'overwritable-hook', 'extension-hook']} %}
{% if node.deprecated %}
=== **<del>Deprecated</del>**===
//{{ node.tags.deprecated[0].description }}//
{% endif %}
== {{ node.summary|replace({"\n":""})|raw }} ==
<html>{{ node.description|markdown|raw }}</html>
{% include 'includes/code-examples.txt.twig' with {structure:node, title_level: '====='} %}
{% set class = node.parent %}
{% block hierarchy_element %}
{% if class and class.name is defined and class.name|trim != '' %}
==== parent ====
{% set child = class %}
{% set class = class.parent %}
{{ block('hierarchy_element') }}
[[{{ child.name }}|{{ child.name }}]]
{% endif %}
{% endblock %}
{% for interface in node.interfaces|sort_asc %}
{% if loop.first %}
==== Implements ====
{% endif %}
{% if loop.length > 1 %} * {% endif %}{{ interface.fullyQualifiedStructuralElementName ?: interface }}
{% endfor %}
{% for trait in node.usedTraits|sort_asc %}
{% if loop.first %}
==== Uses traits ====
{% endif %}
{% if loop.length > 1 %} * {% endif %}{{ trait.fullyQualifiedStructuralElementName ?: trait }}
{% endfor %}
{% include 'includes/see-also.txt.twig' with {structure:node, title_level: '==='} %}
{% include 'includes/tags.txt.twig' with {structure:node, title_level: '=====', blacklist: ['link', 'see', 'abstract', 'example', 'method', 'property', 'property-read', 'property-write', 'package', 'subpackage', 'phpdoc-tuning-exclude-inherited', 'api', 'api-advanced', 'overwritable-hook', 'extension-hook', 'copyright', 'license', 'code-example']} %}
{% set methods = node.inheritedMethods.merge(node.methods) %}
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'api'} %}
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'api-advanced'} %}
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'overwritable-hook'} %}
{% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'extension-hook'} %}
<WRAP clear />
{% for method in methods|sort_asc if method.visibility == 'public' %}
{%- if loop.first %}
===== Public methods =====
{% endif %}
{{ block('method') }}
{% endfor %}
{% for method in methods|sort_asc if method.visibility == 'protected' %}
{%- if loop.first %}
===== Protected methods =====
{% endif %}
{{ block('method') }}
{% endfor %}
{% set constants = node.inheritedConstants.merge(node.constants) %}
{% if constants|length > 0 %}
===== Constants =====
{% for constant in constants|sort_asc %}
{{ block('constant') }}
{% endfor %}
{% endif %}
{#{% set properties = node.inheritedProperties.merge(node.properties) %}#}
{#{% for property in properties|sort_asc if property.visibility == 'public' %}#}
{#{%- if loop.first %}#}
{#===== Public properties =====#}
{#{% endif %}#}
{#{{ block('property') }}#}
{#{% endfor %}#}
{#{% for property in properties|sort_asc if property.visibility == 'protected' %}#}
{#{%- if loop.first %}#}
{#===== Protected properties =====#}
{#{% endif %}#}
{#{{ block('property') }}#}
{#{% endfor %}#}
{%- endif %} {#{% elseif node.tags['xxx'] is not defined and ... #}
<wrap button>[[start|🔙 Back]]</wrap>
{% endblock %}

View File

@@ -0,0 +1,5 @@
{% use 'elements/constant.txt.twig' %}
{% use 'elements/property.txt.twig' %}
{% use 'elements/method.txt.twig' %}
{% block content %}{% endblock %}

View File

@@ -0,0 +1,51 @@
{% extends 'layout.txt.twig' %}
{% block content %}
{% set namespace = project.namespace %}
{{ block('sidebarNamespaces') }}
{#{{ node.parent|raw }}#}
{#====== {{ node.parent.fullyQualifiedStructuralElementName }}{{ node.name }} ======#}
{% if node.children|length > 0 %}
=====Namespaces=====
{% include 'includes/namespace-structure-toc.html.twig' with {structures: node.children} %}
----
{% endif %}
{% if node.traits|length > 0 %}
===== Traits =====
{% include 'includes/namespace-structure-toc.html.twig' with {structures: node.traits} %}
----
{%- endif %}
{% if node.interfaces|length > 0 %}
===== Interfaces =====
{% include 'includes/namespace-structure-toc.html.twig' with {structures: node.interfaces} %}
----
{% endif %}
{% if node.classes|length > 0 %}
===== Classes =====
{% include 'includes/namespace-structure-toc.html.twig' with {structures: node.classes} %}
----
{% endif %}
{#{% if node.constants|length > 0 %}#}
{#===== Constants =====#}
{#{% for constant in node.constants|sort_asc %}#}
{# {{ block('constant') }}#}
{#{% endfor %}#}
{#{% endif %}#}
{#{% if node.functions|length > 0 %}#}
{#===== Functions =====#}
{#{% for method in node.functions|sort_asc %}#}
{# {{ block('method') }}#}
{#{% endfor %}#}
{#{% endif %}#}
{% endblock %}

View File

@@ -0,0 +1,49 @@
====== Deprecated elements ======
{#{% for element in project.indexes.elements if element.deprecated %}#}
{#{% if element.file.path != previousPath %}#}
{#<li><a href="#{{ element.file.path }}"><i class="icon-file"></i> {{ element.file.path }}</a></li>#}
{#{% endif %}#}
{#{% set previousPath = element.file.path %}#}
{#{% endfor %}#}
{% for element in project.indexes.elements if element.deprecated %}
{% if element.file.path != previousPath %}
{% if previousPath %}
</WRAP>{# group #}
{% endif %}
{#<a name="{{ element.file.path }}" id="{{ element.file.path }}"></a>#}
===== {{ element.file.path }} ({{ element.tags.deprecated.count }} found)=====
<WRAP group >
<WRAP third column >
Element
</WRAP>{# third column#}
<WRAP third column >
Line
</WRAP>{# third column#}
<WRAP third column >
Description
</WRAP>{# third column#}
{% endif %}
{% for tag in element.tags.deprecated %}
<WRAP group >
<WRAP third column >
{{ element.fullyQualifiedStructuralElementName }}
</WRAP>{# third column#}
<WRAP third column >
{{ element.line }}
</WRAP>{# third column#}
<WRAP third column >
{{ tag.description }}
</WRAP>{# third column#}
{% endfor %}
</WRAP>{# group #}
{% set previousPath = element.file.path %}
{% else %}
<WRAP info>No deprecated elements have been found in this project.</WRAP>
{% endfor %}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<template>
<author>Bruno DA SILVA</author>
<email>contact [at] combodo.com</email>
<version>1.0.0</version>
<copyright>Combodo 2018</copyright>
<description><![CDATA[
Forked from the clean theme of https://github.com/phpDocumentor/phpDocumentor2 provided under the MIT licence.
The original work is copyright "Mike van Riel".
------------------------------------------------------------------------------------------------------------------
To improve performance you can add the following to your .htaccess:
<ifModule mod_deflate.c>
<filesMatch "\.(js|css|html)$">
SetOutputFilter DEFLATE
</filesMatch>
</ifModule>
]]></description>
<transformations>
<transformation writer="twig" query="namespace" source="templates/combodo-wiki/namespace.txt.twig" artifact="start.txt"/>
<transformation writer="twig" query="indexes.classes" source="templates/combodo-wiki/class.txt.twig" artifact="{{name}}.txt"/>
<transformation writer="twig" query="indexes.interfaces" source="templates/combodo-wiki/interface.txt.twig" artifact="{{name}}.txt" />
</transformations>
</template>

View File

@@ -25,7 +25,7 @@ if (false === file_exists($sTcPdfRootFolder)) {
echo $sCurrentScriptFileName.": No TCPDF lib detected, exiting !\n";
return;
}
$sTcPdfFontsFolder = $sTcPdfRootFolder.'/fonts/';
$sTcPdfFontsFolder = $sTcPdfRootFolder.'/Fonts/';
/**

View File

@@ -25,12 +25,34 @@
if (count($argv) === 1) {
echo '⚠ You must pass the base tag/sha1 as parameter';
echo "⚠ You must pass the base tag/sha1 as parameter\n";
exit(1);
}
$sBaseReference = $argv[1];
/**
* Replace the Github emojis codes by their UTF-8 character equivalent
*/
function ReplaceGitmojis(string $sLine)
{
static $aGitmojis = null;
if ($aGitmojis === null) {
$aRawGitmojis = json_decode(trim(file_get_contents(__DIR__.'/gitmojis.json')), true);
if ($aRawGitmojis === false) {
echo "\nFailed to parse ".__DIR__."/gitmojis.json, emoji codes will not be replaced by their unicode equivalent.\n";
} else {
foreach($aRawGitmojis["gitmojis"] as $aGitmoji) {
$aGitmojis[$aGitmoji['code']] = $aGitmoji['emoji'];
}
}
}
if (is_array($aGitmojis)) {
return str_replace(array_keys($aGitmojis), array_values($aGitmojis), $sLine);
}
}
//--- Get log
$sGitLogCommand = 'git log --decorate --pretty="%h;%s" --date-order --no-merges '.$sBaseReference.'..HEAD';
$sGitLogRaw = shell_exec($sGitLogCommand);
@@ -73,5 +95,5 @@ echo "\n";
echo "# Logs line without bug referenced\n";
echo "sha1;subject\n";
foreach ($aLogLineNoBug as $sLogLine) {
echo "$sLogLine\n";
}
echo ReplaceGitmojis($sLogLine)."\n";
}

589
.make/release/gitmojis.json Normal file
View File

@@ -0,0 +1,589 @@
{
"$schema": "https://gitmoji.dev/api/gitmojis/schema",
"gitmojis": [
{
"emoji": "🎨",
"entity": "&#x1f3a8;",
"code": ":art:",
"description": "Improve structure / format of the code.",
"name": "art",
"semver": null
},
{
"emoji": "⚡️",
"entity": "&#x26a1;",
"code": ":zap:",
"description": "Improve performance.",
"name": "zap",
"semver": "patch"
},
{
"emoji": "🔥",
"entity": "&#x1f525;",
"code": ":fire:",
"description": "Remove code or files.",
"name": "fire",
"semver": null
},
{
"emoji": "🐛",
"entity": "&#x1f41b;",
"code": ":bug:",
"description": "Fix a bug.",
"name": "bug",
"semver": "patch"
},
{
"emoji": "🚑️",
"entity": "&#128657;",
"code": ":ambulance:",
"description": "Critical hotfix.",
"name": "ambulance",
"semver": "patch"
},
{
"emoji": "✨",
"entity": "&#x2728;",
"code": ":sparkles:",
"description": "Introduce new features.",
"name": "sparkles",
"semver": "minor"
},
{
"emoji": "📝",
"entity": "&#x1f4dd;",
"code": ":memo:",
"description": "Add or update documentation.",
"name": "memo",
"semver": null
},
{
"emoji": "🚀",
"entity": "&#x1f680;",
"code": ":rocket:",
"description": "Deploy stuff.",
"name": "rocket",
"semver": null
},
{
"emoji": "💄",
"entity": "&#ff99cc;",
"code": ":lipstick:",
"description": "Add or update the UI and style files.",
"name": "lipstick",
"semver": "patch"
},
{
"emoji": "🎉",
"entity": "&#127881;",
"code": ":tada:",
"description": "Begin a project.",
"name": "tada",
"semver": null
},
{
"emoji": "✅",
"entity": "&#x2705;",
"code": ":white_check_mark:",
"description": "Add, update, or pass tests.",
"name": "white-check-mark",
"semver": null
},
{
"emoji": "🔒️",
"entity": "&#x1f512;",
"code": ":lock:",
"description": "Fix security or privacy issues.",
"name": "lock",
"semver": "patch"
},
{
"emoji": "🔐",
"entity": "&#x1f510;",
"code": ":closed_lock_with_key:",
"description": "Add or update secrets.",
"name": "closed-lock-with-key",
"semver": null
},
{
"emoji": "🔖",
"entity": "&#x1f516;",
"code": ":bookmark:",
"description": "Release / Version tags.",
"name": "bookmark",
"semver": null
},
{
"emoji": "🚨",
"entity": "&#x1f6a8;",
"code": ":rotating_light:",
"description": "Fix compiler / linter warnings.",
"name": "rotating-light",
"semver": null
},
{
"emoji": "🚧",
"entity": "&#x1f6a7;",
"code": ":construction:",
"description": "Work in progress.",
"name": "construction",
"semver": null
},
{
"emoji": "💚",
"entity": "&#x1f49a;",
"code": ":green_heart:",
"description": "Fix CI Build.",
"name": "green-heart",
"semver": null
},
{
"emoji": "⬇️",
"entity": "⬇️",
"code": ":arrow_down:",
"description": "Downgrade dependencies.",
"name": "arrow-down",
"semver": "patch"
},
{
"emoji": "⬆️",
"entity": "⬆️",
"code": ":arrow_up:",
"description": "Upgrade dependencies.",
"name": "arrow-up",
"semver": "patch"
},
{
"emoji": "📌",
"entity": "&#x1F4CC;",
"code": ":pushpin:",
"description": "Pin dependencies to specific versions.",
"name": "pushpin",
"semver": "patch"
},
{
"emoji": "👷",
"entity": "&#x1f477;",
"code": ":construction_worker:",
"description": "Add or update CI build system.",
"name": "construction-worker",
"semver": null
},
{
"emoji": "📈",
"entity": "&#x1F4C8;",
"code": ":chart_with_upwards_trend:",
"description": "Add or update analytics or track code.",
"name": "chart-with-upwards-trend",
"semver": "patch"
},
{
"emoji": "♻️",
"entity": "&#x267b;",
"code": ":recycle:",
"description": "Refactor code.",
"name": "recycle",
"semver": null
},
{
"emoji": "",
"entity": "&#10133;",
"code": ":heavy_plus_sign:",
"description": "Add a dependency.",
"name": "heavy-plus-sign",
"semver": "patch"
},
{
"emoji": "",
"entity": "&#10134;",
"code": ":heavy_minus_sign:",
"description": "Remove a dependency.",
"name": "heavy-minus-sign",
"semver": "patch"
},
{
"emoji": "🔧",
"entity": "&#x1f527;",
"code": ":wrench:",
"description": "Add or update configuration files.",
"name": "wrench",
"semver": "patch"
},
{
"emoji": "🔨",
"entity": "&#128296;",
"code": ":hammer:",
"description": "Add or update development scripts.",
"name": "hammer",
"semver": null
},
{
"emoji": "🌐",
"entity": "&#127760;",
"code": ":globe_with_meridians:",
"description": "Internationalization and localization.",
"name": "globe-with-meridians",
"semver": "patch"
},
{
"emoji": "✏️",
"entity": "&#59161;",
"code": ":pencil2:",
"description": "Fix typos.",
"name": "pencil2",
"semver": "patch"
},
{
"emoji": "💩",
"entity": "&#58613;",
"code": ":poop:",
"description": "Write bad code that needs to be improved.",
"name": "poop",
"semver": null
},
{
"emoji": "⏪️",
"entity": "&#9194;",
"code": ":rewind:",
"description": "Revert changes.",
"name": "rewind",
"semver": "patch"
},
{
"emoji": "🔀",
"entity": "&#128256;",
"code": ":twisted_rightwards_arrows:",
"description": "Merge branches.",
"name": "twisted-rightwards-arrows",
"semver": null
},
{
"emoji": "📦️",
"entity": "&#1F4E6;",
"code": ":package:",
"description": "Add or update compiled files or packages.",
"name": "package",
"semver": "patch"
},
{
"emoji": "👽️",
"entity": "&#1F47D;",
"code": ":alien:",
"description": "Update code due to external API changes.",
"name": "alien",
"semver": "patch"
},
{
"emoji": "🚚",
"entity": "&#1F69A;",
"code": ":truck:",
"description": "Move or rename resources (e.g.: files, paths, routes).",
"name": "truck",
"semver": null
},
{
"emoji": "📄",
"entity": "&#1F4C4;",
"code": ":page_facing_up:",
"description": "Add or update license.",
"name": "page-facing-up",
"semver": null
},
{
"emoji": "💥",
"entity": "&#x1f4a5;",
"code": ":boom:",
"description": "Introduce breaking changes.",
"name": "boom",
"semver": "major"
},
{
"emoji": "🍱",
"entity": "&#1F371",
"code": ":bento:",
"description": "Add or update assets.",
"name": "bento",
"semver": "patch"
},
{
"emoji": "♿️",
"entity": "&#9855;",
"code": ":wheelchair:",
"description": "Improve accessibility.",
"name": "wheelchair",
"semver": "patch"
},
{
"emoji": "💡",
"entity": "&#128161;",
"code": ":bulb:",
"description": "Add or update comments in source code.",
"name": "bulb",
"semver": null
},
{
"emoji": "🍻",
"entity": "&#x1f37b;",
"code": ":beers:",
"description": "Write code drunkenly.",
"name": "beers",
"semver": null
},
{
"emoji": "💬",
"entity": "&#128172;",
"code": ":speech_balloon:",
"description": "Add or update text and literals.",
"name": "speech-balloon",
"semver": "patch"
},
{
"emoji": "🗃️",
"entity": "&#128451;",
"code": ":card_file_box:",
"description": "Perform database related changes.",
"name": "card-file-box",
"semver": "patch"
},
{
"emoji": "🔊",
"entity": "&#128266;",
"code": ":loud_sound:",
"description": "Add or update logs.",
"name": "loud-sound",
"semver": null
},
{
"emoji": "🔇",
"entity": "&#128263;",
"code": ":mute:",
"description": "Remove logs.",
"name": "mute",
"semver": null
},
{
"emoji": "👥",
"entity": "&#128101;",
"code": ":busts_in_silhouette:",
"description": "Add or update contributor(s).",
"name": "busts-in-silhouette",
"semver": null
},
{
"emoji": "🚸",
"entity": "&#128696;",
"code": ":children_crossing:",
"description": "Improve user experience / usability.",
"name": "children-crossing",
"semver": "patch"
},
{
"emoji": "🏗️",
"entity": "&#1f3d7;",
"code": ":building_construction:",
"description": "Make architectural changes.",
"name": "building-construction",
"semver": null
},
{
"emoji": "📱",
"entity": "&#128241;",
"code": ":iphone:",
"description": "Work on responsive design.",
"name": "iphone",
"semver": "patch"
},
{
"emoji": "🤡",
"entity": "&#129313;",
"code": ":clown_face:",
"description": "Mock things.",
"name": "clown-face",
"semver": null
},
{
"emoji": "🥚",
"entity": "&#129370;",
"code": ":egg:",
"description": "Add or update an easter egg.",
"name": "egg",
"semver": "patch"
},
{
"emoji": "🙈",
"entity": "&#8bdfe7;",
"code": ":see_no_evil:",
"description": "Add or update a .gitignore file.",
"name": "see-no-evil",
"semver": null
},
{
"emoji": "📸",
"entity": "&#128248;",
"code": ":camera_flash:",
"description": "Add or update snapshots.",
"name": "camera-flash",
"semver": null
},
{
"emoji": "⚗️",
"entity": "&#x2697;",
"code": ":alembic:",
"description": "Perform experiments.",
"name": "alembic",
"semver": "patch"
},
{
"emoji": "🔍️",
"entity": "&#128269;",
"code": ":mag:",
"description": "Improve SEO.",
"name": "mag",
"semver": "patch"
},
{
"emoji": "🏷️",
"entity": "&#127991;",
"code": ":label:",
"description": "Add or update types.",
"name": "label",
"semver": "patch"
},
{
"emoji": "🌱",
"entity": "&#127793;",
"code": ":seedling:",
"description": "Add or update seed files.",
"name": "seedling",
"semver": null
},
{
"emoji": "🚩",
"entity": "&#x1F6A9;",
"code": ":triangular_flag_on_post:",
"description": "Add, update, or remove feature flags.",
"name": "triangular-flag-on-post",
"semver": "patch"
},
{
"emoji": "🥅",
"entity": "&#x1F945;",
"code": ":goal_net:",
"description": "Catch errors.",
"name": "goal-net",
"semver": "patch"
},
{
"emoji": "💫",
"entity": "&#x1f4ab;",
"code": ":dizzy:",
"description": "Add or update animations and transitions.",
"name": "dizzy",
"semver": "patch"
},
{
"emoji": "🗑️",
"entity": "&#x1F5D1;",
"code": ":wastebasket:",
"description": "Deprecate code that needs to be cleaned up.",
"name": "wastebasket",
"semver": "patch"
},
{
"emoji": "🛂",
"entity": "&#x1F6C2;",
"code": ":passport_control:",
"description": "Work on code related to authorization, roles and permissions.",
"name": "passport-control",
"semver": "patch"
},
{
"emoji": "🩹",
"entity": "&#x1FA79;",
"code": ":adhesive_bandage:",
"description": "Simple fix for a non-critical issue.",
"name": "adhesive-bandage",
"semver": "patch"
},
{
"emoji": "🧐",
"entity": "&#x1F9D0;",
"code": ":monocle_face:",
"description": "Data exploration/inspection.",
"name": "monocle-face",
"semver": null
},
{
"emoji": "⚰️",
"entity": "&#x26B0;",
"code": ":coffin:",
"description": "Remove dead code.",
"name": "coffin",
"semver": null
},
{
"emoji": "🧪",
"entity": "&#x1F9EA;",
"code": ":test_tube:",
"description": "Add a failing test.",
"name": "test-tube",
"semver": null
},
{
"emoji": "👔",
"entity": "&#128084;",
"code": ":necktie:",
"description": "Add or update business logic.",
"name": "necktie",
"semver": "patch"
},
{
"emoji": "🩺",
"entity": "&#x1FA7A;",
"code": ":stethoscope:",
"description": "Add or update healthcheck.",
"name": "stethoscope",
"semver": null
},
{
"emoji": "🧱",
"entity": "&#x1f9f1;",
"code": ":bricks:",
"description": "Infrastructure related changes.",
"name": "bricks",
"semver": null
},
{
"emoji": "🧑‍💻",
"entity": "&#129489;&#8205;&#128187;",
"code": ":technologist:",
"description": "Improve developer experience.",
"name": "technologist",
"semver": null
},
{
"emoji": "💸",
"entity": "&#x1F4B8;",
"code": ":money_with_wings:",
"description": "Add sponsorships or money related infrastructure.",
"name": "money-with-wings",
"semver": null
},
{
"emoji": "🧵",
"entity": "&#x1F9F5;",
"code": ":thread:",
"description": "Add or update code related to multithreading or concurrency.",
"name": "thread",
"semver": null
},
{
"emoji": "🦺",
"entity": "&#x1F9BA;",
"code": ":safety_vest:",
"description": "Add or update code related to validation.",
"name": "safety-vest",
"semver": null
}
]
}

View File

@@ -199,7 +199,7 @@ class DatamodelsXmlFiles extends AbstractGlobFileVersionUpdater
libxml_clear_errors();
$oFileXml->formatOutput = true;
$oFileXml->preserveWhiteSpace = false;
$oFileXml->loadXML($sFileContent, LIBXML_BIGLINES);
$oFileXml->loadXML($sFileContent);
$oFileItopFormat = new iTopDesignFormat($oFileXml);

View File

@@ -1,9 +1,5 @@
<p align="center"><a href="https://www.combodo.com/itop-193" target="_blank">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="/images/logos/logo-itop-baseline-light.svg">
<source media="(prefers-color-scheme: light)" srcset="/images/logos/logo-itop-baseline-dark.svg">
<img src="/images/logos/logo-itop-baseline-light.svg" width="350" alt="Logo iTop with baseline" />
</picture>
<img src="https://www.combodo.com/logos/logo-itop-baseline.svg" width=350>
</a></p>
@@ -94,6 +90,7 @@ We would like to give a special thank you 🤗 to the people from the community
- Dejin, Bie (a.k.a [@bdejin](https://github.com/bdejin))
- Dvořák, Lukáš
- Goethals, Stefan
- Giuva, Vincenzo Katriel (a.k.a [@DarkNight97boss](https://github.com/DarkNight97boss))
- Gumble, David
- Ji, Leeb (冀利斌) (a.k.a [@chileeb](https://github.com/chileeb))
- Kaltefleiter, Lars (a.k.a [@larhip](https://www.github.com/larhip))
@@ -110,7 +107,6 @@ We would like to give a special thank you 🤗 to the people from the community
- Raenker, Martin
- Roháč, Richard (a.k.a [@RohacRichard](https://github.com/RohacRichard))
- Rosenke, Stephan
- Rossi, Tommaso (a.k.a [@tomrss](https://www.github.com/tomrss))
- Rudner, Björn (a.k.a [@rudnerbjoern](https://github.com/rudnerbjoern))
- Šafránek, Jaroslav (a.k.a [jkcinik](https://sourceforge.net/u/jkcinik/profile/) on SourceForge)
- Seki, Shoji
@@ -120,7 +116,6 @@ We would like to give a special thank you 🤗 to the people from the community
- Tarjányi, Csaba (a.k.a [@tacsaby](https://github.com/tacsaby))
- Tulio, Marco
- Turrubiates, Miguel
- Vlk, Karel (a.k.a [@vlk-charles](https://www.github.com/vlk-charles))
### Aliases

View File

@@ -1,10 +1,9 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// 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.
@@ -24,29 +23,30 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
class UserRightsMatrixClassGrant extends DBObject
{
public static function Init()
{
$aParams =
[
$aParams = array
(
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => [],
"reconc_keys" => array(),
"db_table" => "priv_ur_matrixclasses",
"db_key_field" => "id",
"db_finalclass_field" => "",
];
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", ["targetclass" => "User", "jointype" => "", "allowed_values" => null, "sql" => "userid", "is_null_allowed" => false, "on_target_delete" => DEL_MANUAL, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalField("login", ["allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "login"]));
MetaModel::Init_AddAttribute(new AttributeString("class", ["allowed_values" => null, "sql" => "class", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login")));
MetaModel::Init_AddAttribute(new AttributeString("class", array("allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("action", ["allowed_values" => null, "sql" => "action", "default_value" => "", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", ["allowed_values" => new ValueSetEnum('yes,no'), "sql" => "permission", "default_value" => "yes", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("action", array("allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
}
}
@@ -54,25 +54,25 @@ class UserRightsMatrixClassStimulusGrant extends DBObject
{
public static function Init()
{
$aParams =
[
$aParams = array
(
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => [],
"reconc_keys" => array(),
"db_table" => "priv_ur_matrixclassesstimulus",
"db_key_field" => "id",
"db_finalclass_field" => "",
];
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", ["targetclass" => "User", "jointype" => "", "allowed_values" => null, "sql" => "userid", "is_null_allowed" => false, "on_target_delete" => DEL_MANUAL, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalField("login", ["allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "login"]));
MetaModel::Init_AddAttribute(new AttributeString("class", ["allowed_values" => null, "sql" => "class", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login")));
MetaModel::Init_AddAttribute(new AttributeString("class", array("allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("stimulus", ["allowed_values" => null, "sql" => "action", "default_value" => "", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", ["allowed_values" => new ValueSetEnum('yes,no'), "sql" => "permission", "default_value" => "yes", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("stimulus", array("allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
}
}
@@ -80,39 +80,42 @@ class UserRightsMatrixAttributeGrant extends DBObject
{
public static function Init()
{
$aParams =
[
$aParams = array
(
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => [],
"reconc_keys" => array(),
"db_table" => "priv_ur_matrixattributes",
"db_key_field" => "id",
"db_finalclass_field" => "",
];
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", ["targetclass" => "User", "jointype" => "", "allowed_values" => null, "sql" => "userid", "is_null_allowed" => false, "on_target_delete" => DEL_MANUAL, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalField("login", ["allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "login"]));
MetaModel::Init_AddAttribute(new AttributeString("class", ["allowed_values" => null, "sql" => "class", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("attcode", ["allowed_values" => null, "sql" => "attcode", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login")));
MetaModel::Init_AddAttribute(new AttributeString("class", array("allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("attcode", array("allowed_values"=>null, "sql"=>"attcode", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("action", ["allowed_values" => null, "sql" => "action", "default_value" => "", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", ["allowed_values" => new ValueSetEnum('yes,no'), "sql" => "permission", "default_value" => "yes", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("action", array("allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
}
}
class UserRightsMatrix extends UserRightsAddOnAPI
{
public static $m_aActionCodes = [
static public $m_aActionCodes = array(
UR_ACTION_READ => 'read',
UR_ACTION_MODIFY => 'modify',
UR_ACTION_DELETE => 'delete',
UR_ACTION_BULK_READ => 'bulk read',
UR_ACTION_BULK_MODIFY => 'bulk modify',
UR_ACTION_BULK_DELETE => 'bulk delete',
];
);
// Installation: create the very first user
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
@@ -146,7 +149,8 @@ class UserRightsMatrix extends UserRightsAddOnAPI
// Users must be added manually
// This procedure will then update the matrix when a new user is found or a new class/attribute appears
$oUserSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT User"));
while ($oUser = $oUserSet->Fetch()) {
while ($oUser = $oUserSet->Fetch())
{
$this->SetupUser($oUser->GetKey());
}
return true;
@@ -154,16 +158,23 @@ class UserRightsMatrix extends UserRightsAddOnAPI
protected function SetupUser($iUserId, $bNewUser = false)
{
foreach (['bizmodel', 'application', 'gui', 'core/cmdb'] as $sCategory) {
foreach (MetaModel::GetClasses($sCategory) as $sClass) {
foreach (self::$m_aActionCodes as $iActionCode => $sAction) {
if ($bNewUser) {
foreach(array('bizmodel', 'application', 'gui', 'core/cmdb') as $sCategory)
{
foreach (MetaModel::GetClasses($sCategory) as $sClass)
{
foreach (self::$m_aActionCodes as $iActionCode => $sAction)
{
if ($bNewUser)
{
$bAddCell = true;
} else {
}
else
{
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassGrant WHERE class = '$sClass' AND action = '$sAction' AND userid = $iUserId"));
$bAddCell = ($oSet->Count() < 1);
}
if ($bAddCell) {
if ($bAddCell)
{
// Create a new entry
$oMyClassGrant = MetaModel::NewObject("UserRightsMatrixClassGrant");
$oMyClassGrant->Set("userid", $iUserId);
@@ -173,14 +184,19 @@ class UserRightsMatrix extends UserRightsAddOnAPI
$iId = $oMyClassGrant->DBInsertNoReload();
}
}
foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus) {
if ($bNewUser) {
foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus)
{
if ($bNewUser)
{
$bAddCell = true;
} else {
}
else
{
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassStimulusGrant WHERE class = '$sClass' AND stimulus = '$sStimulusCode' AND userid = $iUserId"));
$bAddCell = ($oSet->Count() < 1);
}
if ($bAddCell) {
if ($bAddCell)
{
// Create a new entry
$oMyClassGrant = MetaModel::NewObject("UserRightsMatrixClassStimulusGrant");
$oMyClassGrant->Set("userid", $iUserId);
@@ -190,15 +206,21 @@ class UserRightsMatrix extends UserRightsAddOnAPI
$iId = $oMyClassGrant->DBInsertNoReload();
}
}
foreach (MetaModel::GetAttributesList($sClass) as $sAttCode) {
if ($bNewUser) {
foreach (MetaModel::GetAttributesList($sClass) as $sAttCode)
{
if ($bNewUser)
{
$bAddCell = true;
} else {
}
else
{
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixAttributeGrant WHERE class = '$sClass' AND attcode = '$sAttCode' AND userid = $iUserId"));
$bAddCell = ($oSet->Count() < 1);
}
if ($bAddCell) {
foreach (['read', 'modify'] as $sAction) {
if ($bAddCell)
{
foreach (array('read', 'modify') as $sAction)
{
// Create a new entry
$oMyAttGrant = MetaModel::NewObject("UserRightsMatrixAttributeGrant");
$oMyAttGrant->Set("userid", $iUserId);
@@ -239,13 +261,14 @@ class UserRightsMatrix extends UserRightsAddOnAPI
*/
}
public function Init()
{
// Could be loaded in a shared memory (?)
return true;
}
public function GetSelectFilter($oUser, $sClass, $aSettings = [])
public function GetSelectFilter($oUser, $sClass, $aSettings = array())
{
$oNullFilter = new DBObjectSearch($sClass);
return $oNullFilter;
@@ -253,18 +276,21 @@ class UserRightsMatrix extends UserRightsAddOnAPI
public function IsActionAllowed($oUser, $sClass, $iActionCode, $oInstanceSet = null)
{
if (!array_key_exists($iActionCode, self::$m_aActionCodes)) {
if (!array_key_exists($iActionCode, self::$m_aActionCodes))
{
return UR_ALLOWED_NO;
}
$sAction = self::$m_aActionCodes[$iActionCode];
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassGrant WHERE class = '$sClass' AND action = '$sAction' AND userid = '{$oUser->GetKey()}'"));
if ($oSet->Count() < 1) {
if ($oSet->Count() < 1)
{
return UR_ALLOWED_NO;
}
$oGrantRecord = $oSet->Fetch();
switch ($oGrantRecord->Get('permission')) {
switch ($oGrantRecord->Get('permission'))
{
case 'yes':
$iRetCode = UR_ALLOWED_YES;
break;
@@ -278,18 +304,21 @@ class UserRightsMatrix extends UserRightsAddOnAPI
public function IsActionAllowedOnAttribute($oUser, $sClass, $sAttCode, $iActionCode, $oInstanceSet = null)
{
if (!array_key_exists($iActionCode, self::$m_aActionCodes)) {
if (!array_key_exists($iActionCode, self::$m_aActionCodes))
{
return UR_ALLOWED_NO;
}
$sAction = self::$m_aActionCodes[$iActionCode];
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixAttributeGrant WHERE class = '$sClass' AND attcode = '$sAttCode' AND action = '$sAction' AND userid = '{$oUser->GetKey()}'"));
if ($oSet->Count() < 1) {
if ($oSet->Count() < 1)
{
return UR_ALLOWED_NO;
}
$oGrantRecord = $oSet->Fetch();
switch ($oGrantRecord->Get('permission')) {
switch ($oGrantRecord->Get('permission'))
{
case 'yes':
$iRetCode = UR_ALLOWED_YES;
break;
@@ -304,12 +333,14 @@ class UserRightsMatrix extends UserRightsAddOnAPI
public function IsStimulusAllowed($oUser, $sClass, $sStimulusCode, $oInstanceSet = null)
{
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassStimulusGrant WHERE class = '$sClass' AND stimulus = '$sStimulusCode' AND userid = '{$oUser->GetKey()}'"));
if ($oSet->Count() < 1) {
if ($oSet->Count() < 1)
{
return UR_ALLOWED_NO;
}
$oGrantRecord = $oSet->Fetch();
switch ($oGrantRecord->Get('permission')) {
switch ($oGrantRecord->Get('permission'))
{
case 'yes':
$iRetCode = UR_ALLOWED_YES;
break;
@@ -327,3 +358,5 @@ class UserRightsMatrix extends UserRightsAddOnAPI
}
UserRights::SelectModule('UserRightsMatrix');
?>

View File

@@ -1,10 +1,9 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// 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.
@@ -48,7 +47,7 @@ class UserRightsNull extends UserRightsAddOnAPI
return true;
}
public function GetSelectFilter($oUser, $sClass, $aSettings = [])
public function GetSelectFilter($oUser, $sClass, $aSettings = array())
{
$oNullFilter = new DBObjectSearch($sClass);
return $oNullFilter;
@@ -75,3 +74,5 @@ class UserRightsNull extends UserRightsAddOnAPI
}
UserRights::SelectModule('UserRightsNull');
?>

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -30,52 +29,56 @@ class UserRightsBaseClassGUI extends cmdbAbstractObject
}
}
class URP_Profiles extends UserRightsBaseClassGUI
{
public static function Init()
{
$aParams =
[
$aParams = array
(
"category" => "addon/userrights,grant_by_profile,filter",
"key_type" => "autoincrement",
"name_attcode" => "name",
"complementary_name_attcode" => ['description'],
"complementary_name_attcode" => array('description'),
"state_attcode" => "",
"reconc_keys" => [],
"reconc_keys" => array(),
"db_table" => "priv_urp_profiles",
"db_key_field" => "id",
"db_finalclass_field" => "",
];
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("name", ["allowed_values" => null, "sql" => "name", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("description", ["allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("user_list", ["linked_class" => "URP_UserProfile", "ext_key_to_me" => "profileid", "ext_key_to_remote" => "userid", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("user_list", array("linked_class"=>"URP_UserProfile", "ext_key_to_me"=>"profileid", "ext_key_to_remote"=>"userid", "allowed_values"=>null, "count_min"=>1, "count_max"=>0, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', ['name', 'description', 'user_list']); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', ['description']); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', array('name', 'description', 'user_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', ['name','description']); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', ['name','description']);
MetaModel::Init_SetZListItems('standard_search', array('name','description')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array ('name','description'));
}
protected static $m_aCacheProfiles = null;
public static function DoCreateProfile($sName, $sDescription)
{
if (is_null(self::$m_aCacheProfiles)) {
self::$m_aCacheProfiles = [];
if (is_null(self::$m_aCacheProfiles))
{
self::$m_aCacheProfiles = array();
$oFilterAll = new DBObjectSearch('URP_Profiles');
$oSet = new DBObjectSet($oFilterAll);
while ($oProfile = $oSet->Fetch()) {
while ($oProfile = $oSet->Fetch())
{
self::$m_aCacheProfiles[$oProfile->Get('name')] = $oProfile->GetKey();
}
}
$sCacheKey = $sName;
if (isset(self::$m_aCacheProfiles[$sCacheKey])) {
if (isset(self::$m_aCacheProfiles[$sCacheKey]))
{
return self::$m_aCacheProfiles[$sCacheKey];
}
$oNewObj = MetaModel::NewObject("URP_Profiles");
@@ -86,21 +89,27 @@ class URP_Profiles extends UserRightsBaseClassGUI
return $iId;
}
public function GetGrantAsHtml($oUserRights, $sClass, $sAction)
function GetGrantAsHtml($oUserRights, $sClass, $sAction)
{
$bGrant = $oUserRights->GetProfileActionGrant($this->GetKey(), $sClass, $sAction);
if (is_null($bGrant)) {
return '<span class="ibo-user-rights ibo-is-failure">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
} elseif ($bGrant) {
return '<span class="ibo-user-rights ibo-is-success">'.Dict::S('UI:UserManagement:ActionAllowed:Yes').'</span>';
} else {
return '<span class="ibo-user-rights ibo-is-failure">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
if (is_null($bGrant))
{
return '<span style="background-color: #ffdddd;">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
}
elseif ($bGrant)
{
return '<span style="background-color: #ddffdd;">'.Dict::S('UI:UserManagement:ActionAllowed:Yes').'</span>';
}
else
{
return '<span style="background-color: #ffdddd;">'.Dict::S('UI:UserManagement:ActionAllowed:No').'</span>';
}
}
public function DoShowGrantSumary($oPage)
function DoShowGrantSumary($oPage)
{
if ($this->GetRawName() == "Administrator") {
if ($this->GetRawName() == "Administrator")
{
// Looks dirty, but ok that's THE ONE
$oPage->p(Dict::S('UI:UserManagement:AdminProfile+'));
return;
@@ -109,18 +118,21 @@ class URP_Profiles extends UserRightsBaseClassGUI
// Note: for sure, we assume that the instance is derived from UserRightsProfile
$oUserRights = UserRights::GetModuleInstance();
$aDisplayData = [];
foreach (MetaModel::GetClasses('bizmodel,grant_by_profile') as $sClass) {
$aStimuli = [];
foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus) {
$aDisplayData = array();
foreach (MetaModel::GetClasses('bizmodel,grant_by_profile') as $sClass)
{
$aStimuli = array();
foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus)
{
$bGrant = $oUserRights->GetClassStimulusGrant($this->GetKey(), $sClass, $sStimulusCode);
if ($bGrant === true) {
if ($bGrant === true)
{
$aStimuli[] = '<span title="'.$sStimulusCode.': '.utils::EscapeHtml($oStimulus->GetDescription()).'">'.utils::EscapeHtml($oStimulus->GetLabel()).'</span>';
}
}
$sStimuli = implode(', ', $aStimuli);
$aDisplayData[] = [
$aDisplayData[] = array(
'class' => MetaModel::GetName($sClass),
'read' => $this->GetGrantAsHtml($oUserRights, $sClass, 'r'),
'bulkread' => $this->GetGrantAsHtml($oUserRights, $sClass, 'br'),
@@ -129,22 +141,22 @@ class URP_Profiles extends UserRightsBaseClassGUI
'delete' => $this->GetGrantAsHtml($oUserRights, $sClass, 'd'),
'bulkdelete' => $this->GetGrantAsHtml($oUserRights, $sClass, 'bd'),
'stimuli' => $sStimuli,
];
);
}
$aDisplayConfig = [];
$aDisplayConfig['class'] = ['label' => Dict::S('UI:UserManagement:Class'), 'description' => Dict::S('UI:UserManagement:Class+')];
$aDisplayConfig['read'] = ['label' => Dict::S('UI:UserManagement:Action:Read'), 'description' => Dict::S('UI:UserManagement:Action:Read+')];
$aDisplayConfig['bulkread'] = ['label' => Dict::S('UI:UserManagement:Action:BulkRead'), 'description' => Dict::S('UI:UserManagement:Action:BulkRead+')];
$aDisplayConfig['write'] = ['label' => Dict::S('UI:UserManagement:Action:Modify'), 'description' => Dict::S('UI:UserManagement:Action:Modify+')];
$aDisplayConfig['bulkwrite'] = ['label' => Dict::S('UI:UserManagement:Action:BulkModify'), 'description' => Dict::S('UI:UserManagement:Action:BulkModify+')];
$aDisplayConfig['delete'] = ['label' => Dict::S('UI:UserManagement:Action:Delete'), 'description' => Dict::S('UI:UserManagement:Action:Delete+')];
$aDisplayConfig['bulkdelete'] = ['label' => Dict::S('UI:UserManagement:Action:BulkDelete'), 'description' => Dict::S('UI:UserManagement:Action:BulkDelete+')];
$aDisplayConfig['stimuli'] = ['label' => Dict::S('UI:UserManagement:Action:Stimuli'), 'description' => Dict::S('UI:UserManagement:Action:Stimuli+')];
$aDisplayConfig = array();
$aDisplayConfig['class'] = array('label' => Dict::S('UI:UserManagement:Class'), 'description' => Dict::S('UI:UserManagement:Class+'));
$aDisplayConfig['read'] = array('label' => Dict::S('UI:UserManagement:Action:Read'), 'description' => Dict::S('UI:UserManagement:Action:Read+'));
$aDisplayConfig['bulkread'] = array('label' => Dict::S('UI:UserManagement:Action:BulkRead'), 'description' => Dict::S('UI:UserManagement:Action:BulkRead+'));
$aDisplayConfig['write'] = array('label' => Dict::S('UI:UserManagement:Action:Modify'), 'description' => Dict::S('UI:UserManagement:Action:Modify+'));
$aDisplayConfig['bulkwrite'] = array('label' => Dict::S('UI:UserManagement:Action:BulkModify'), 'description' => Dict::S('UI:UserManagement:Action:BulkModify+'));
$aDisplayConfig['delete'] = array('label' => Dict::S('UI:UserManagement:Action:Delete'), 'description' => Dict::S('UI:UserManagement:Action:Delete+'));
$aDisplayConfig['bulkdelete'] = array('label' => Dict::S('UI:UserManagement:Action:BulkDelete'), 'description' => Dict::S('UI:UserManagement:Action:BulkDelete+'));
$aDisplayConfig['stimuli'] = array('label' => Dict::S('UI:UserManagement:Action:Stimuli'), 'description' => Dict::S('UI:UserManagement:Action:Stimuli+'));
$oPage->table($aDisplayConfig, $aDisplayData);
}
public function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
{
parent::DisplayBareRelations($oPage, $bEditMode);
@@ -154,9 +166,10 @@ class URP_Profiles extends UserRightsBaseClassGUI
public static function GetReadOnlyAttributes()
{
return ['name', 'description'];
return array('name', 'description');
}
// returns an array of id => array of column => php value(so-called "real value")
public static function GetPredefinedObjects()
{
@@ -168,13 +181,15 @@ class URP_Profiles extends UserRightsBaseClassGUI
protected function OnDelete()
{
// Don't remove admin profile
if ($this->Get('name') === ADMIN_PROFILE_NAME) {
if ($this->Get('name') === ADMIN_PROFILE_NAME)
{
throw new SecurityException(Dict::Format('UI:Login:Error:AccessAdmin'));
}
// Note: this may break the rule that says: "a user must have at least ONE profile" !
$oLnkSet = $this->Get('user_list');
while ($oLnk = $oLnkSet->Fetch()) {
while($oLnk = $oLnkSet->Fetch())
{
$oLnk->DBDelete();
}
}
@@ -187,10 +202,11 @@ class URP_Profiles extends UserRightsBaseClassGUI
* @param $sTargetState string The target state in which to evalutate the flags, if empty the current state will be used
* @return integer Flags: the binary combination of the flags applicable to this attribute
*/
public function GetAttributeFlags($sAttCode, &$aReasons = [], $sTargetState = '')
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
{
$iFlags = parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
if (MetaModel::GetConfig()->Get('demo_mode')) {
if (MetaModel::GetConfig()->Get('demo_mode'))
{
$aReasons[] = 'Sorry, profiles are read-only in the demonstration mode!';
$iFlags |= OPT_ATT_READONLY;
}
@@ -198,52 +214,52 @@ class URP_Profiles extends UserRightsBaseClassGUI
}
}
class URP_UserProfile extends UserRightsBaseClassGUI
{
public static function Init()
{
$aParams =
[
$aParams = array
(
"category" => "addon/userrights,grant_by_profile,filter",
"key_type" => "autoincrement",
"name_attcode" => ["userlogin", "profile"],
"name_attcode" => array("userlogin", "profile"),
"state_attcode" => "",
"reconc_keys" => [],
"reconc_keys" => array(),
"db_table" => "priv_urp_userprofile",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => true, /** @since 3.1.0 N°6482 */
'uniqueness_rules' => [
'no_duplicate' => [
'attributes' => [
'uniqueness_rules' => array(
'no_duplicate' => array(
'attributes' => array(
0 => 'userid',
1 => 'profileid',
],
),
'filter' => '',
'disabled' => false,
'is_blocking' => true,
],
],
];
),
),
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", ["targetclass" => "User", "jointype" => "", "allowed_values" => null, "sql" => "userid", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalField("userlogin", ["allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "login"]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass" => "User", "jointype" => "", "allowed_values" => null, "sql" => "userid", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("userlogin", array("allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "login")));
MetaModel::Init_AddAttribute(new AttributeExternalKey(
"profileid",
["targetclass" => "URP_Profiles", "jointype" => "", "allowed_values" => null, "sql" => "profileid", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => [], "allow_target_creation" => false]
));
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", ["allowed_values" => null, "extkey_attcode" => 'profileid', "target_attcode" => "name"]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid",
array("targetclass" => "URP_Profiles", "jointype" => "", "allowed_values" => null, "sql" => "profileid", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array(), "allow_target_creation" => false)));
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("allowed_values" => null, "extkey_attcode" => 'profileid', "target_attcode" => "name")));
MetaModel::Init_AddAttribute(new AttributeString("reason", ["allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("reason", array("allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', ['userid', 'profileid', 'reason']); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', ['userid', 'profileid', 'reason']); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', array('userid', 'profileid', 'reason')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('userid', 'profileid', 'reason')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', ['userid', 'profileid']); // Criteria of the std search form
MetaModel::Init_SetZListItems('advanced_search', ['userid', 'profileid']); // Criteria of the advanced search form
MetaModel::Init_SetZListItems('standard_search', array('userid', 'profileid')); // Criteria of the std search form
MetaModel::Init_SetZListItems('advanced_search', array('userid', 'profileid')); // Criteria of the advanced search form
}
public function CheckToDelete(&$oDeletionPlan)
@@ -251,14 +267,15 @@ class URP_UserProfile extends UserRightsBaseClassGUI
if (MetaModel::GetConfig()->Get('demo_mode')) {
// Users deletion is NOT allowed in demo mode
$oDeletionPlan->AddToDelete($this, null);
$oDeletionPlan->SetDeletionIssues($this, ['deletion not allowed in demo mode.'], true);
$oDeletionPlan->SetDeletionIssues($this, array('deletion not allowed in demo mode.'), true);
$oDeletionPlan->ComputeResults();
return false;
}
try {
$this->CheckIfProfileIsAllowed(UR_ACTION_DELETE);
} catch (SecurityException $e) {
}
catch (SecurityException $e) {
// Users deletion is NOT allowed
$oDeletionPlan->AddToDelete($this, null);
$oDeletionPlan->SetDeletionIssues($this, [$e->getMessage()], true);
@@ -275,14 +292,15 @@ class URP_UserProfile extends UserRightsBaseClassGUI
if (MetaModel::GetConfig()->Get('demo_mode')) {
// Users deletion is NOT allowed in demo mode
$oDeletionPlan->AddToDelete($this, null);
$oDeletionPlan->SetDeletionIssues($this, ['deletion not allowed in demo mode.'], true);
$oDeletionPlan->SetDeletionIssues($this, array('deletion not allowed in demo mode.'), true);
$oDeletionPlan->ComputeResults();
return false;
}
try {
$this->CheckIfProfileIsAllowed(UR_ACTION_DELETE);
} catch (SecurityException $e) {
}
catch (SecurityException $e) {
// Users deletion is NOT allowed
$oDeletionPlan->AddToDelete($this, null);
$oDeletionPlan->SetDeletionIssues($this, [$e->getMessage()], true);
@@ -318,26 +336,29 @@ class URP_UserProfile extends UserRightsBaseClassGUI
protected function CheckIfProfileIsAllowed($iActionCode)
{
// When initializing or admin, we need to let everything pass trough
if (!UserRights::IsLoggedIn() || UserRights::IsAdministrator()) {
return;
}
if (!UserRights::IsLoggedIn() || UserRights::IsAdministrator()) { return; }
// Only administrators can manage administrators
$iOrigUserId = $this->GetOriginal('userid');
if (!empty($iOrigUserId)) {
if (!empty($iOrigUserId))
{
$oUser = MetaModel::GetObject('User', $iOrigUserId, true, true);
if (UserRights::IsAdministrator($oUser) && !UserRights::IsAdministrator()) {
if (UserRights::IsAdministrator($oUser) && !UserRights::IsAdministrator())
{
throw new SecurityException(Dict::Format('UI:Login:Error:AccessRestricted'));
}
}
$oUser = MetaModel::GetObject('User', $this->Get('userid'), true, true);
if (UserRights::IsAdministrator($oUser) && !UserRights::IsAdministrator()) {
if (UserRights::IsAdministrator($oUser) && !UserRights::IsAdministrator())
{
throw new SecurityException(Dict::Format('UI:Login:Error:AccessRestricted'));
}
if (!UserRights::IsActionAllowed(get_class($this), $iActionCode, DBObjectSet::FromObject($this))) {
if (!UserRights::IsActionAllowed(get_class($this), $iActionCode, DBObjectSet::FromObject($this)))
{
throw new SecurityException(Dict::Format('UI:Error:ObjectCannotBeUpdated'));
}
if (!UserRights::IsAdministrator() && ($this->Get('profile') === ADMIN_PROFILE_NAME)) {
if (!UserRights::IsAdministrator() && ($this->Get('profile') === ADMIN_PROFILE_NAME))
{
throw new SecurityException(Dict::Format('UI:Login:Error:AccessAdmin'));
}
}
@@ -348,33 +369,33 @@ class URP_UserOrg extends UserRightsBaseClassGUI
{
public static function Init()
{
$aParams =
[
$aParams = array
(
"category" => "addon/userrights,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => ["userlogin", "allowed_org_name"],
"name_attcode" => array("userlogin", "allowed_org_name"),
"state_attcode" => "",
"reconc_keys" => [],
"reconc_keys" => array(),
"db_table" => "priv_urp_userorg",
"db_key_field" => "id",
"db_finalclass_field" => "",
];
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", ["targetclass" => "User", "jointype" => "", "allowed_values" => null, "sql" => "userid", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalField("userlogin", ["allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "login"]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "jointype"=> "", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("userlogin", array("allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login")));
MetaModel::Init_AddAttribute(new AttributeExternalKey("allowed_org_id", ["targetclass" => "Organization", "jointype" => "", "allowed_values" => null, "sql" => "allowed_org_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalField("allowed_org_name", ["allowed_values" => null, "extkey_attcode" => 'allowed_org_id', "target_attcode" => "name"]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("allowed_org_id", array("targetclass"=>"Organization", "jointype"=> "", "allowed_values"=>null, "sql"=>"allowed_org_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("allowed_org_name", array("allowed_values"=>null, "extkey_attcode"=> 'allowed_org_id', "target_attcode"=>"name")));
MetaModel::Init_AddAttribute(new AttributeString("reason", ["allowed_values" => null, "sql" => "reason", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("reason", array("allowed_values"=>null, "sql"=>"reason", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', ['userid', 'allowed_org_id', 'reason']); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', ['allowed_org_id', 'reason']); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', array('userid', 'allowed_org_id', 'reason')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('allowed_org_id', 'reason')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', ['userid', 'allowed_org_id']); // Criteria of the std search form
MetaModel::Init_SetZListItems('advanced_search', ['userid', 'allowed_org_id']); // Criteria of the advanced search form
MetaModel::Init_SetZListItems('standard_search', array('userid', 'allowed_org_id')); // Criteria of the std search form
MetaModel::Init_SetZListItems('advanced_search', array('userid', 'allowed_org_id')); // Criteria of the advanced search form
}
protected function OnInsert()
@@ -397,37 +418,40 @@ class URP_UserOrg extends UserRightsBaseClassGUI
*/
protected function CheckIfOrgIsAllowed()
{
if (!UserRights::IsLoggedIn() || UserRights::IsAdministrator()) {
return;
}
if (!UserRights::IsLoggedIn() || UserRights::IsAdministrator()) { return; }
$oUser = UserRights::GetUserObject();
$oAddon = UserRights::GetModuleInstance();
$aOrgs = $oAddon->GetUserOrgs($oUser, '');
if (count($aOrgs) > 0) {
if (count($aOrgs) > 0)
{
$iOrigOrgId = $this->GetOriginal('allowed_org_id');
if ((!empty($iOrigOrgId) && !in_array($iOrigOrgId, $aOrgs)) || !in_array($this->Get('allowed_org_id'), $aOrgs)) {
if ((!empty($iOrigOrgId) && !in_array($iOrigOrgId, $aOrgs)) || !in_array($this->Get('allowed_org_id'), $aOrgs))
{
throw new SecurityException(Dict::Format('Class:User/Error:OrganizationNotAllowed'));
}
}
}
}
class UserRightsProfile extends UserRightsAddOnAPI
{
public static $m_aActionCodes = [
static public $m_aActionCodes = array(
UR_ACTION_READ => 'r',
UR_ACTION_MODIFY => 'w',
UR_ACTION_DELETE => 'd',
UR_ACTION_BULK_READ => 'br',
UR_ACTION_BULK_MODIFY => 'bw',
UR_ACTION_BULK_DELETE => 'bd',
];
);
/**
* @var array $aUsersProfilesList Cache of users' profiles. Hash array of user ID => [profile ID => profile friendlyname, profile ID => profile friendlyname, ...]
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6887
*/
/**
* @var array $aUsersProfilesList Cache of users' profiles. Hash array of user ID => [profile ID => profile friendlyname, profile ID => profile friendlyname, ...]
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6887
*/
private $aUsersProfilesList = [];
// Installation: create the very first user
@@ -448,7 +472,8 @@ class UserRightsProfile extends UserRightsAddOnAPI
$oContact = MetaModel::NewObject('Person');
$oContact->Set('name', 'My last name');
$oContact->Set('first_name', 'My first name');
if (MetaModel::IsValidAttCode('Person', 'org_id')) {
if (MetaModel::IsValidAttCode('Person', 'org_id'))
{
$oContact->Set('org_id', $iOrgId);
}
$oContact->Set('email', 'my.email@foo.org');
@@ -456,17 +481,20 @@ class UserRightsProfile extends UserRightsAddOnAPI
}
}
$oUser = new UserLocal();
$oUser->Set('login', $sAdminUser);
$oUser->Set('password', $sAdminPwd);
if (MetaModel::IsValidAttCode('UserLocal', 'contactid') && ($iContactId != 0)) {
if (MetaModel::IsValidAttCode('UserLocal', 'contactid') && ($iContactId != 0))
{
$oUser->Set('contactid', $iContactId);
}
$oUser->Set('language', $sLanguage); // Language was chosen during the installation
// Add this user to the very specific 'admin' profile
$oAdminProfile = MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", ['name' => ADMIN_PROFILE_NAME], true /*all data*/);
if (is_object($oAdminProfile)) {
$oAdminProfile = MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", array('name' => ADMIN_PROFILE_NAME), true /*all data*/);
if (is_object($oAdminProfile))
{
$oUserProfile = new URP_UserProfile();
$oUserProfile->Set('profileid', $oAdminProfile->GetKey());
$oUserProfile->Set('reason', 'By definition, the administrator must have the administrator profile');
@@ -481,11 +509,11 @@ class UserRightsProfile extends UserRightsAddOnAPI
{
}
protected $m_aUserOrgs = []; // userid -> array of orgid
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 = [];
protected $m_aObjectActionGrants = array();
/**
* Read and cache organizations allowed to the given user
@@ -500,25 +528,31 @@ class UserRightsProfile extends UserRightsAddOnAPI
public function GetUserOrgs($oUser, $sClass)
{
$iUser = $oUser->GetKey();
if (!array_key_exists($iUser, $this->m_aUserOrgs)) {
$this->m_aUserOrgs[$iUser] = [];
if (!array_key_exists($iUser, $this->m_aUserOrgs))
{
$this->m_aUserOrgs[$iUser] = array();
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass('Organization');
if ($sHierarchicalKeyCode !== false) {
if ($sHierarchicalKeyCode !== false)
{
$sUserOrgQuery = 'SELECT UserOrg, Org FROM Organization AS Org JOIN Organization AS Root ON Org.'.$sHierarchicalKeyCode.' BELOW Root.id JOIN URP_UserOrg AS UserOrg ON UserOrg.allowed_org_id = Root.id WHERE UserOrg.userid = :userid';
$oUserOrgSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sUserOrgQuery), [], ['userid' => $iUser]);
while ($aRow = $oUserOrgSet->FetchAssoc()) {
$oUserOrgSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sUserOrgQuery), array(), array('userid' => $iUser));
while ($aRow = $oUserOrgSet->FetchAssoc())
{
$oOrg = $aRow['Org'];
$this->m_aUserOrgs[$iUser][] = $oOrg->GetKey();
}
} else {
}
else
{
$oSearch = new DBObjectSearch('URP_UserOrg');
$oSearch->AllowAllData();
$oCondition = new BinaryExpression(new FieldExpression('userid'), '=', new VariableExpression('userid'));
$oSearch->AddConditionExpression($oCondition);
$oUserOrgSet = new DBObjectSet($oSearch, [], ['userid' => $iUser]);
while ($oUserOrg = $oUserOrgSet->Fetch()) {
$oUserOrgSet = new DBObjectSet($oSearch, array(), array('userid' => $iUser));
while ($oUserOrg = $oUserOrgSet->Fetch())
{
$this->m_aUserOrgs[$iUser][] = $oUserOrg->Get('allowed_org_id');
}
}
@@ -529,19 +563,21 @@ class UserRightsProfile extends UserRightsAddOnAPI
public function ResetCache()
{
// Loaded by Load cache
$this->m_aUserOrgs = [];
$this->m_aUserOrgs = array();
// Cache
$this->m_aObjectActionGrants = [];
$this->m_aObjectActionGrants = array();
$this->m_aAdministrators = null;
}
public function LoadCache()
{
static $bSharedObjectInitialized = false;
if (!$bSharedObjectInitialized) {
if (!$bSharedObjectInitialized)
{
$bSharedObjectInitialized = true;
if (self::HasSharing()) {
if (self::HasSharing())
{
SharedObject::InitSharedClassProperties();
}
}
@@ -579,40 +615,45 @@ class UserRightsProfile extends UserRightsAddOnAPI
*/
public function ListProfiles($oUser)
{
$aRet = [];
$aRet = array();
$oSearch = new DBObjectSearch('URP_UserProfile');
$oSearch->AllowAllData();
$oSearch->NoContextParameters();
$oSearch->Addcondition('userid', $oUser->GetKey(), '=');
$oProfiles = new DBObjectSet($oSearch);
while ($oUserProfile = $oProfiles->Fetch()) {
while ($oUserProfile = $oProfiles->Fetch())
{
$aRet[$oUserProfile->Get('profileid')] = $oUserProfile->Get('profileid_friendlyname');
}
return $aRet;
}
public function GetSelectFilter($oUser, $sClass, $aSettings = [])
public function GetSelectFilter($oUser, $sClass, $aSettings = array())
{
$this->LoadCache();
// Let us pass an administrator for bypassing the grant matrix check in order to test this method without the need to set up a complex profile
// In the nominal case Administrators never end up here (since they completely bypass GetSelectFilter)
if (!static::IsAdministrator($oUser) && (MetaModel::HasCategory($sClass, 'silo') || MetaModel::HasCategory($sClass, 'bizmodel'))) {
if (!static::IsAdministrator($oUser) && (MetaModel::HasCategory($sClass, 'silo') || MetaModel::HasCategory($sClass, 'bizmodel')))
{
// N°4354 - Categories 'silo' and 'bizmodel' do check the grant matrix. Whereas 'filter' always allows to read (but the result can be filtered)
$aObjectPermissions = $this->GetUserActionGrant($oUser, $sClass, UR_ACTION_READ);
if ($aObjectPermissions['permission'] == UR_ALLOWED_NO) {
if ($aObjectPermissions['permission'] == UR_ALLOWED_NO)
{
return false;
}
}
$oFilter = true;
$aConditions = [];
$aConditions = array();
// Determine if this class is part of a silo and build the filter for it
$sAttCode = self::GetOwnerOrganizationAttCode($sClass);
if (!is_null($sAttCode)) {
if (!is_null($sAttCode))
{
$aUserOrgs = $this->GetUserOrgs($oUser, $sClass);
if (count($aUserOrgs) > 0) {
if (count($aUserOrgs) > 0)
{
$oFilter = $this->MakeSelectFilter($sClass, $aUserOrgs, $aSettings, $sAttCode);
}
// else: No org means 'any org'
@@ -621,15 +662,20 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Specific conditions to hide, for non-administrators, the Administrator Users, the Administrator Profile and related links
// Note: when logged as an administrator, GetSelectFilter is completely bypassed.
if ($this->AdministratorsAreHidden()) {
if ($sClass == 'URP_Profiles') {
if ($this->AdministratorsAreHidden())
{
if ($sClass == 'URP_Profiles')
{
$oExpression = new FieldExpression('id', $sClass);
$oScalarExpr = new ScalarExpression(1);
$aConditions[] = new BinaryExpression($oExpression, '!=', $oScalarExpr);
} elseif (($sClass == 'URP_UserProfile') || ($sClass == 'User') || (is_subclass_of($sClass, 'User'))) {
}
else if (($sClass == 'URP_UserProfile') || ($sClass == 'User') || (is_subclass_of($sClass, 'User')))
{
$aAdministrators = $this->GetAdministrators();
if (count($aAdministrators) > 0) {
if (count($aAdministrators) > 0)
{
$sAttCode = ($sClass == 'URP_UserProfile') ? 'userid' : 'id';
$oExpression = new FieldExpression($sAttCode, $sClass);
$oListExpr = ListExpression::FromScalars($aAdministrators);
@@ -639,14 +685,17 @@ class UserRightsProfile extends UserRightsAddOnAPI
}
// Handling of the added conditions
if (count($aConditions) > 0) {
if ($oFilter === true) {
if (count($aConditions) > 0)
{
if($oFilter === true)
{
// No 'silo' filter, let's build a clean one
$oFilter = new DBObjectSearch($sClass);
}
// Add the conditions to the filter
foreach ($aConditions as $oCondition) {
foreach($aConditions as $oCondition)
{
$oFilter->AddConditionExpression($oCondition);
}
}
@@ -661,9 +710,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
*/
private function GetAdministrators()
{
if ($this->m_aAdministrators === null) {
if ($this->m_aAdministrators === null)
{
// Find all administrators
$this->m_aAdministrators = [];
$this->m_aAdministrators = array();
$oAdministratorsFilter = new DBObjectSearch('User');
$oLnkFilter = new DBObjectSearch('URP_UserProfile');
$oExpression = new FieldExpression('profileid', 'URP_UserProfile');
@@ -673,8 +723,9 @@ class UserRightsProfile extends UserRightsAddOnAPI
$oAdministratorsFilter->AddCondition_ReferencedBy($oLnkFilter, 'userid');
$oAdministratorsFilter->AllowAllData(true); // Mandatory to prevent infinite recursion !!
$oSet = new DBObjectSet($oAdministratorsFilter);
$oSet->OptimizeColumnLoad(['User' => ['login']]);
while ($oUser = $oSet->Fetch()) {
$oSet->OptimizeColumnLoad(array('User' => array('login')));
while($oUser = $oSet->Fetch())
{
$this->m_aAdministrators[] = $oUser->GetKey();
}
}
@@ -690,6 +741,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
return ((bool)MetaModel::GetConfig()->Get('security.hide_administrators'));
}
// This verb has been made public to allow the development of an accurate feedback for the current configuration
public function GetProfileActionGrant($iProfile, $sClass, $sAction)
{
@@ -706,29 +758,33 @@ class UserRightsProfile extends UserRightsAddOnAPI
// load and cache permissions for the current user on the given class
//
$iUser = $oUser->GetKey();
if (isset($this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode])) {
if (isset($this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode])){
$aTest = $this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode];
if (is_array($aTest)) {
return $aTest;
}
if (is_array($aTest)) return $aTest;
}
$sAction = self::$m_aActionCodes[$iActionCode];
$bStatus = null;
// Cache user's profiles
if (false === array_key_exists($iUser, $this->aUsersProfilesList)) {
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
// Cache user's profiles
if(false === array_key_exists($iUser, $this->aUsersProfilesList)){
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
}
// Call the API of UserRights because it caches the list for us
foreach ($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile) {
foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
{
$bGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
if (!is_null($bGrant)) {
if ($bGrant) {
if (is_null($bStatus)) {
if (!is_null($bGrant))
{
if ($bGrant)
{
if (is_null($bStatus))
{
$bStatus = true;
}
} else {
}
else
{
$bStatus = false;
}
}
@@ -736,9 +792,9 @@ class UserRightsProfile extends UserRightsAddOnAPI
$iPermission = $bStatus ? UR_ALLOWED_YES : UR_ALLOWED_NO;
$aRes = [
$aRes = array(
'permission' => $iPermission,
];
);
$this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode] = $aRes;
return $aRes;
}
@@ -753,46 +809,64 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Note: In most cases the object set is ignored because it was interesting to optimize for huge data sets
// and acceptable to consider only the root class of the object set
if ($iPermission != UR_ALLOWED_YES) {
if ($iPermission != UR_ALLOWED_YES)
{
// It is already NO for everyone... that's the final word!
} elseif ($iActionCode == UR_ACTION_READ) {
}
elseif ($iActionCode == UR_ACTION_READ)
{
// We are protected by GetSelectFilter: the object set contains objects allowed or shared for reading
} elseif ($iActionCode == UR_ACTION_BULK_READ) {
}
elseif ($iActionCode == UR_ACTION_BULK_READ)
{
// We are protected by GetSelectFilter: the object set contains objects allowed or shared for reading
} elseif ($oInstanceSet) {
}
elseif ($oInstanceSet)
{
// We are protected by GetSelectFilter: the object set contains objects allowed or shared for reading
// We have to answer NO for objects shared for reading purposes
if (self::HasSharing() && SharedObject::GetSharedClassProperties($sClass)) {
// This class is shared, GetSelectFilter may allow some objects for read only
// But currently we are checking whether the objects might be written...
// Let's exclude the objects based on the relevant criteria
if (self::HasSharing())
{
$aClassProps = SharedObject::GetSharedClassProperties($sClass);
if ($aClassProps)
{
// This class is shared, GetSelectFilter may allow some objects for read only
// But currently we are checking wether the objects might be written...
// Let's exclude the objects based on the relevant criteria
// Use $oInstanceSet only if sClass is the main class
if (!is_a($oInstanceSet->GetClass(), $sClass, true)) {
/** @var \DBObjectSet $oInstanceSet */
throw new CoreException(__FUNCTION__.': Expecting object set to be of class '.$sClass.' but it is of class '.$oInstanceSet->GetClass(), ['OQL_Query' => $oInstanceSet->GetFilter()->ToOQL(), 'classes' => $oInstanceSet->GetSelectedClasses()]);
}
$sOrgAttCode = self::GetOwnerOrganizationAttCode($sClass);
if (!is_null($sOrgAttCode)) {
$aUserOrgs = $this->GetUserOrgs($oUser, $sClass);
if (!is_null($aUserOrgs) && count($aUserOrgs) > 0) {
$iCountNO = 0;
$iCountYES = 0;
$oInstanceSet->Rewind();
while ($oObject = $oInstanceSet->Fetch()) {
$iOrg = $oObject->Get($sOrgAttCode);
if (in_array($iOrg, $aUserOrgs)) {
$iCountYES++;
} else {
$iCountNO++;
$sOrgAttCode = self::GetOwnerOrganizationAttCode($sClass);
if (!is_null($sOrgAttCode))
{
$aUserOrgs = $this->GetUserOrgs($oUser, $sClass);
if (!is_null($aUserOrgs) && count($aUserOrgs) > 0)
{
$iCountNO = 0;
$iCountYES = 0;
$oInstanceSet->Rewind();
while($oObject = $oInstanceSet->Fetch())
{
$iOrg = $oObject->Get($sOrgAttCode);
if (in_array($iOrg, $aUserOrgs))
{
$iCountYES++;
}
else
{
$iCountNO++;
}
}
if ($iCountNO == 0)
{
$iPermission = UR_ALLOWED_YES;
}
elseif ($iCountYES == 0)
{
$iPermission = UR_ALLOWED_NO;
}
else
{
$iPermission = UR_ALLOWED_DEPENDS;
}
}
if ($iCountNO == 0) {
$iPermission = UR_ALLOWED_YES;
} elseif ($iCountYES == 0) {
$iPermission = UR_ALLOWED_NO;
} else {
$iPermission = UR_ALLOWED_DEPENDS;
}
}
}
@@ -823,8 +897,8 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Note: this code is VERY close to the code of IsActionAllowed()
$iUser = $oUser->GetKey();
// Cache user's profiles
if (false === array_key_exists($iUser, $this->aUsersProfilesList)) {
// Cache user's profiles
if(false === array_key_exists($iUser, $this->aUsersProfilesList)){
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
}
@@ -832,14 +906,20 @@ class UserRightsProfile extends UserRightsAddOnAPI
// and acceptable to consider only the root class of the object set
$bStatus = null;
// Call the API of UserRights because it caches the list for us
foreach ($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile) {
foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
{
$bGrant = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
if (!is_null($bGrant)) {
if ($bGrant) {
if (is_null($bStatus)) {
if (!is_null($bGrant))
{
if ($bGrant)
{
if (is_null($bStatus))
{
$bStatus = true;
}
} else {
}
else
{
$bStatus = false;
}
}
@@ -863,16 +943,22 @@ class UserRightsProfile extends UserRightsAddOnAPI
{
$sAttCode = null;
$aCallSpec = [$sClass, 'MapContextParam'];
if (($sClass == 'Organization') || is_subclass_of($sClass, 'Organization')) {
$aCallSpec = array($sClass, 'MapContextParam');
if (($sClass == 'Organization') || is_subclass_of($sClass, 'Organization'))
{
$sAttCode = 'id';
} elseif (is_callable($aCallSpec)) {
}
elseif (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, 'org_id'); // Returns null when there is no mapping for this parameter
if (!MetaModel::IsValidAttCode($sClass, $sAttCode)) {
if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
{
// Skip silently. The data model checker will tell you something about this...
$sAttCode = null;
}
} elseif (MetaModel::IsValidAttCode($sClass, 'org_id')) {
}
elseif(MetaModel::IsValidAttCode($sClass, 'org_id'))
{
$sAttCode = 'org_id';
}
@@ -885,11 +971,15 @@ class UserRightsProfile extends UserRightsAddOnAPI
protected static function HasSharing()
{
static $bHasSharing;
if (!isset($bHasSharing)) {
if (!isset($bHasSharing))
{
$bHasSharing = class_exists('SharedObject');
}
return $bHasSharing;
}
}
UserRights::SelectModule('UserRightsProfile');
?>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,4 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -26,5 +25,5 @@ require_once('approot.inc.php');
require_once('application/startup.inc.php');
return function (array $context) {
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -49,7 +48,8 @@ class DBSearchHelper
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
}
} catch (Exception $e) {
}
catch (Exception $e) {
// If filtering fails just ignore it
}
}
@@ -57,4 +57,4 @@ class DBSearchHelper
}
}
}
}
}

View File

@@ -1,5 +1,4 @@
<?php
/**
* @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
@@ -17,9 +16,10 @@ use Combodo\iTop\Application\WebPage\AjaxPage;
*/
class ajax_page extends AjaxPage
{
public function __construct($s_title)
function __construct($s_title)
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('ajax_page is deprecated. Please use AjaxPage instead');
parent::__construct($s_title);
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0

View File

@@ -1,10 +1,9 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// 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.
@@ -17,6 +16,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Class ApplicationContext
*
@@ -47,16 +47,16 @@ interface iDBObjectURLMaker
/**
* Direct end-users to the standard iTop application: UI.php
*/
*/
class iTopStandardURLMaker implements iDBObjectURLMaker
{
/**
* @param string $sClass
* @param string $iId
*
* @return string
* @throws \Exception
*/
/**
* @param string $sClass
* @param string $iId
*
* @return string
* @throws \Exception
*/
public static function MakeObjectURL($sClass, $iId)
{
$sPage = DBObject::ComputeStandardUIPage($sClass);
@@ -68,16 +68,16 @@ class iTopStandardURLMaker implements iDBObjectURLMaker
/**
* Direct end-users to the standard Portal application
*/
*/
class PortalURLMaker implements iDBObjectURLMaker
{
/**
* @param string $sClass
* @param string $iId
*
* @return string
* @throws \Exception
*/
/**
* @param string $sClass
* @param string $iId
*
* @return string
* @throws \Exception
*/
public static function MakeObjectURL($sClass, $iId)
{
$sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot();
@@ -86,6 +86,7 @@ class PortalURLMaker implements iDBObjectURLMaker
}
}
/**
* Helper class to store and manipulate the parameters that make the application's context
*
@@ -98,117 +99,112 @@ class PortalURLMaker implements iDBObjectURLMaker
*/
class ApplicationContext
{
public static $m_sUrlMakerClass = null;
protected static $m_aPluginProperties = null;
protected static $aDefaultValues; // Cache shared among all instances
public static $m_sUrlMakerClass = null;
protected static $m_aPluginProperties = null;
protected static $aDefaultValues; // Cache shared among all instances
protected $aNames;
protected $aValues;
/**
* ApplicationContext constructor.
*
* @param bool $bReadContext
*
* @throws \Exception
*/
/**
* ApplicationContext constructor.
*
* @param bool $bReadContext
*
* @throws \Exception
*/
public function __construct($bReadContext = true)
{
$this->aNames = [
'org_id', 'menu',
];
if ($bReadContext) {
$this->ReadContext();
$this->aNames = array(
'org_id', 'menu'
);
if ($bReadContext)
{
$this->ReadContext();
}
}
/**
* Read the context directly in the PHP parameters (either POST or GET)
* return nothing
*
* @throws \Exception
*/
/**
* Read the context directly in the PHP parameters (either POST or GET)
* return nothing
*
* @throws \Exception
*/
protected function ReadContext()
{
if (!isset(self::$aDefaultValues)) {
self::$aDefaultValues = [];
$aContext = utils::ReadParam('c', [], false, 'context_param');
foreach ($this->aNames as $sName) {
if (!isset(self::$aDefaultValues))
{
self::$aDefaultValues = array();
$aContext = utils::ReadParam('c', array(), false, 'context_param');
foreach($this->aNames as $sName)
{
$sValue = isset($aContext[$sName]) ? $aContext[$sName] : '';
// TO DO: check if some of the context parameters are mandatory (or have default values)
if (!empty($sValue)) {
if (!empty($sValue))
{
self::$aDefaultValues[$sName] = $sValue;
}
// Hmm, there must be a better (more generic) way to handle the case below:
// When there is only one possible (allowed) organization, the context must be
// fixed to this org unless there is only one organization in the system then
// no filter is applied
if ($sName == 'org_id') {
if (MetaModel::IsValidClass('Organization')) {
if ($sName == 'org_id')
{
if (MetaModel::IsValidClass('Organization'))
{
$oSearchFilter = new DBObjectSearch('Organization');
$oSet = new CMDBObjectSet($oSearchFilter);
$iCount = $oSet->CountWithLimit(2);
if ($iCount > 1) {
if ($iCount > 1)
{
$oSearchFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', true);
$oSet = new CMDBObjectSet($oSearchFilter);
$iCount = $oSet->CountWithLimit(2);
if ($iCount == 1) {
if ($iCount == 1)
{
// Only one possible value for org_id, set it in the context
$oOrg = $oSet->Fetch();
self::$aDefaultValues[$sName] = $oOrg->GetKey();
}
}
}
}
}
}
}
$this->aValues = self::$aDefaultValues;
}
/**
* Returns the current value for the given parameter
*
* @param string $sParamName Name of the parameter to read
* @param string $defaultValue
*
* @return mixed The value for this parameter
*/
/**
* Returns the current value for the given parameter
*
* @param string $sParamName Name of the parameter to read
* @param string $defaultValue
*
* @return mixed The value for this parameter
*/
public function GetCurrentValue($sParamName, $defaultValue = '')
{
if (isset($this->aValues[$sParamName])) {
if (isset($this->aValues[$sParamName]))
{
return $this->aValues[$sParamName];
}
return $defaultValue;
}
/**
* Returns the context as string with the format name1=value1&name2=value2....
* @return string The context as a string to be appended to an href property
*
*/
public function GetForLink(bool $bWithLeadingAmpersand = false)
public function GetForLink()
{
// If there are no parameters, return an empty string
if (empty($this->aValues)) {
return '';
}
// Build the query string with ampersand separated parameters
$aParams = [];
foreach ($this->aValues as $sName => $sValue) {
$aParams = array();
foreach($this->aValues as $sName => $sValue)
{
$aParams[] = "c[$sName]".'='.urlencode($sValue);
}
$sReturnValue = implode('&', $aParams);
// add the leading ampersand if requested
if ($bWithLeadingAmpersand) {
$sReturnValue = '&'.$sReturnValue;
}
return $sReturnValue;
return implode("&", $aParams);
}
/**
* @since 3.0.0 N°2534 - dashboard: bug with autorefresh that deactivates filtering on organisation
* Returns the params as c[menu]:..., c[org_id]:....
@@ -267,13 +263,14 @@ class ApplicationContext
*/
public function GetAsHash()
{
$aReturn = [];
foreach ($this->aValues as $sName => $sValue) {
$aReturn = array();
foreach($this->aValues as $sName => $sValue)
{
$aReturn["c[$sName]"] = $sValue;
}
return $aReturn;
}
/**
* Returns an array of the context parameters NAMEs
* @return array The list of context parameters
@@ -286,10 +283,11 @@ class ApplicationContext
* Removes the specified parameter from the context, for example when the same parameter
* is already a search parameter
* @param string $sParamName Name of the parameter to remove
*/
*/
public function Reset($sParamName)
{
if (isset($this->aValues[$sParamName])) {
if (isset($this->aValues[$sParamName]))
{
unset($this->aValues[$sParamName]);
}
}
@@ -305,22 +303,27 @@ class ApplicationContext
public function InitObjectFromContext(DBObject &$oObj)
{
$sClass = get_class($oObj);
foreach ($this->GetNames() as $key) {
$aCallSpec = [$sClass, 'MapContextParam'];
if (is_callable($aCallSpec)) {
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
foreach($this->GetNames() as $key)
{
$aCallSpec = array($sClass, 'MapContextParam');
if (is_callable($aCallSpec))
{
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
if (MetaModel::IsValidAttCode($sClass, $sAttCode)) {
if (MetaModel::IsValidAttCode($sClass, $sAttCode))
{
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef->IsWritable()) {
if ($oAttDef->IsWritable())
{
$value = $this->GetCurrentValue($key, null);
if (!is_null($value)) {
if (!is_null($value))
{
$oObj->Set($sAttCode, $value);
}
}
}
}
}
}
}
/**
@@ -344,10 +347,14 @@ class ApplicationContext
*/
public static function GetUrlMakerClass()
{
if (is_null(self::$m_sUrlMakerClass)) {
if (Session::IsSet('UrlMakerClass')) {
if (is_null(self::$m_sUrlMakerClass))
{
if (Session::IsSet('UrlMakerClass'))
{
self::$m_sUrlMakerClass = Session::Get('UrlMakerClass');
} else {
}
else
{
self::$m_sUrlMakerClass = 'iTopStandardURLMaker';
}
}
@@ -365,23 +372,23 @@ class ApplicationContext
* @return string the name of the class
* @throws \Exception
*/
public static function MakeObjectUrl($sObjClass, $sObjKey, $sUrlMakerClass = null, $bWithNavigationContext = true)
{
$oAppContext = new ApplicationContext();
public static function MakeObjectUrl($sObjClass, $sObjKey, $sUrlMakerClass = null, $bWithNavigationContext = true)
{
$oAppContext = new ApplicationContext();
if (is_null($sUrlMakerClass)) {
$sUrlMakerClass = self::GetUrlMakerClass();
}
$sUrl = call_user_func([$sUrlMakerClass, 'MakeObjectUrl'], $sObjClass, $sObjKey);
if (utils::StrLen($sUrl) > 0) {
if ($bWithNavigationContext) {
return $sUrl.$oAppContext->GetForLink(true);
} else {
return $sUrl;
}
} else {
return '';
}
if (is_null($sUrlMakerClass)) {
$sUrlMakerClass = self::GetUrlMakerClass();
}
$sUrl = call_user_func(array($sUrlMakerClass, 'MakeObjectUrl'), $sObjClass, $sObjKey);
if (utils::StrLen($sUrl) > 0) {
if ($bWithNavigationContext) {
return $sUrl."&".$oAppContext->GetForLink();
} else {
return $sUrl;
}
} else {
return '';
}
}
/**
@@ -390,10 +397,13 @@ class ApplicationContext
*/
protected static function LoadPluginProperties()
{
if (Session::IsSet('PluginProperties')) {
if (Session::IsSet('PluginProperties'))
{
self::$m_aPluginProperties = Session::Get('PluginProperties');
} else {
self::$m_aPluginProperties = [];
}
else
{
self::$m_aPluginProperties = array();
}
}
@@ -406,9 +416,7 @@ class ApplicationContext
*/
public static function SetPluginProperty($sPluginClass, $sProperty, $value)
{
if (is_null(self::$m_aPluginProperties)) {
self::LoadPluginProperties();
}
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
self::$m_aPluginProperties[$sPluginClass][$sProperty] = $value;
Session::Set(['PluginProperties', $sPluginClass, $sProperty], $value);
@@ -421,14 +429,15 @@ class ApplicationContext
*/
public static function GetPluginProperties($sPluginClass)
{
if (is_null(self::$m_aPluginProperties)) {
self::LoadPluginProperties();
}
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
if (array_key_exists($sPluginClass, self::$m_aPluginProperties)) {
if (array_key_exists($sPluginClass, self::$m_aPluginProperties))
{
return self::$m_aPluginProperties[$sPluginClass];
} else {
return [];
}
else
{
return array();
}
}

View File

@@ -524,7 +524,7 @@ abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
*/
public function EnumUsedAttributes($oObject)
{
return [];
return array();
}
/**
@@ -548,7 +548,7 @@ abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
*/
public function EnumAllowedActions(DBObjectSet $oSet)
{
return [];
return array();
}
}
@@ -686,7 +686,7 @@ abstract class AbstractApplicationObjectExtension implements iApplicationObjectE
*/
public function OnCheckToWrite($oObject)
{
return [];
return array();
}
/**
@@ -694,7 +694,7 @@ abstract class AbstractApplicationObjectExtension implements iApplicationObjectE
*/
public function OnCheckToDelete($oObject)
{
return [];
return array();
}
/**
@@ -739,21 +739,21 @@ interface iPopupMenuExtension
* $param is a DBObjectSet containing the list of objects
* @api
*/
public const MENU_OBJLIST_ACTIONS = 1;
const MENU_OBJLIST_ACTIONS = 1;
/**
* Insert an item into the Toolkit menu of a list
*
* $param is a DBObjectSet containing the list of objects
* @api
*/
public const MENU_OBJLIST_TOOLKIT = 2;
const MENU_OBJLIST_TOOLKIT = 2;
/**
* Insert an item into the Actions menu on an object details page
*
* $param is a DBObject instance: the object currently displayed
* @api
*/
public const MENU_OBJDETAILS_ACTIONS = 3;
const MENU_OBJDETAILS_ACTIONS = 3;
/**
* Insert an item into the Dashboard menu
*
@@ -763,14 +763,14 @@ interface iPopupMenuExtension
* $param is a Dashboard instance: the dashboard currently displayed
* @api
*/
public const MENU_DASHBOARD_ACTIONS = 4;
const MENU_DASHBOARD_ACTIONS = 4;
/**
* Insert an item into the User menu (upper right corner)
*
* $param is null
* @api
*/
public const MENU_USER_ACTIONS = 5;
const MENU_USER_ACTIONS = 5;
/**
* Insert an item into the Action menu on an object item in an objects list in the portal
*
@@ -778,7 +778,7 @@ interface iPopupMenuExtension
* the current line)
* @api
*/
public const PORTAL_OBJLISTITEM_ACTIONS = 7;
const PORTAL_OBJLISTITEM_ACTIONS = 7;
/**
* Insert an item into the Action menu on an object details page in the portal
*
@@ -786,7 +786,7 @@ interface iPopupMenuExtension
* currently displayed)
* @api
*/
public const PORTAL_OBJDETAILS_ACTIONS = 8;
const PORTAL_OBJDETAILS_ACTIONS = 8;
/**
* Insert an item into the Actions menu of a list in the portal
@@ -796,7 +796,7 @@ interface iPopupMenuExtension
*
* @todo
*/
public const PORTAL_OBJLIST_ACTIONS = 6;
const PORTAL_OBJLIST_ACTIONS = 6;
/**
* Insert an item into the user menu of the portal
* Note: This is not implemented yet !
@@ -805,7 +805,7 @@ interface iPopupMenuExtension
*
* @todo
*/
public const PORTAL_USER_ACTIONS = 9;
const PORTAL_USER_ACTIONS = 9;
/**
* Insert an item into the navigation menu of the portal
* Note: This is not implemented yet !
@@ -814,7 +814,7 @@ interface iPopupMenuExtension
*
* @todo
*/
public const PORTAL_MENU_ACTIONS = 10;
const PORTAL_MENU_ACTIONS = 10;
/**
* Get the list of items to be added to a menu.
@@ -864,7 +864,7 @@ abstract class ApplicationPopupMenuItem
$this->sLabel = $sLabel;
$this->sTooltip = '';
$this->sIconClass = '';
$this->aCssClasses = [];
$this->aCssClasses = array();
}
/**
@@ -920,6 +920,7 @@ abstract class ApplicationPopupMenuItem
$this->aCssClasses[] = $sCssClass;
}
/**
* @param $sTooltip
*
@@ -975,7 +976,7 @@ abstract class ApplicationPopupMenuItem
/** @ignore */
public function GetLinkedScripts()
{
return [];
return array();
}
}
@@ -1014,13 +1015,13 @@ class URLPopupMenuItem extends ApplicationPopupMenuItem
/** @ignore */
public function GetMenuItem()
{
return ['label' => $this->GetLabel(),
return array('label' => $this->GetLabel(),
'url' => $this->GetUrl(),
'target' => $this-> GetTarget(),
'css_classes' => $this->aCssClasses,
'icon_class' => $this->sIconClass,
'tooltip' => $this->sTooltip,
];
'tooltip' => $this->sTooltip
);
}
/** @ignore */
@@ -1064,7 +1065,7 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
* ans $sTarget will be ignored
* @param array $aIncludeJSFiles An array of file URLs to be included (once) to provide some JS libraries for the page.
*/
public function __construct($sUID, $sLabel, $sJSCode, $aIncludeJSFiles = [])
public function __construct($sUID, $sLabel, $sJSCode, $aIncludeJSFiles = array())
{
parent::__construct($sUID, $sLabel);
$this->sJsCode = $sJSCode;
@@ -1076,14 +1077,14 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
public function GetMenuItem()
{
// Note: the semicolumn is a must here!
return [
return array(
'label' => $this->GetLabel(),
'onclick' => $this->GetJsCode().'; return false;',
'url' => $this->GetUrl(),
'css_classes' => $this->GetCssClasses(),
'icon_class' => $this->sIconClass,
'tooltip' => $this->sTooltip,
];
'tooltip' => $this->sTooltip
);
}
/** @ignore */
@@ -1115,7 +1116,7 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
*/
class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
{
public static $idx = 0;
static $idx = 0;
/**
* Constructor
@@ -1129,7 +1130,7 @@ class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
/** @ignore */
public function GetMenuItem()
{
return ['label' => '<hr class="menu-separator">', 'url' => '', 'css_classes' => $this->aCssClasses];
return array('label' => '<hr class="menu-separator">', 'url' => '', 'css_classes' => $this->aCssClasses);
}
}
@@ -1142,6 +1143,7 @@ class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
*/
class URLButtonItem extends URLPopupMenuItem
{
}
/**
@@ -1153,6 +1155,7 @@ class URLButtonItem extends URLPopupMenuItem
*/
class JSButtonItem extends JSPopupMenuItem
{
}
/**
@@ -1501,9 +1504,9 @@ interface iBackofficeDictEntriesPrefixesExtension
*/
interface iPortalUIExtension
{
public const ENUM_PORTAL_EXT_UI_BODY = 'Body';
public const ENUM_PORTAL_EXT_UI_NAVIGATION_MENU = 'NavigationMenu';
public const ENUM_PORTAL_EXT_UI_MAIN_CONTENT = 'MainContent';
const ENUM_PORTAL_EXT_UI_BODY = 'Body';
const ENUM_PORTAL_EXT_UI_NAVIGATION_MENU = 'NavigationMenu';
const ENUM_PORTAL_EXT_UI_MAIN_CONTENT = 'MainContent';
/**
* Returns an array of CSS file urls
@@ -1590,7 +1593,7 @@ abstract class AbstractPortalUIExtension implements iPortalUIExtension
*/
public function GetCSSFiles(Container $oContainer)
{
return [];
return array();
}
/**
@@ -1606,7 +1609,7 @@ abstract class AbstractPortalUIExtension implements iPortalUIExtension
*/
public function GetJSFiles(Container $oContainer)
{
return [];
return array();
}
/**
@@ -1708,11 +1711,6 @@ interface iRestServiceProvider
public function ExecOperation($sVersion, $sVerb, $aParams);
}
interface iRestInputSanitizer
{
public function SanitizeJsonInput(string $sJsonInput): string;
}
/**
* Minimal REST response structure. Derive this structure to add response data and error codes.
*
@@ -1726,62 +1724,62 @@ class RestResult
* Result: no issue has been encountered
* @api
*/
public const OK = 0;
const OK = 0;
/**
* Result: missing/wrong credentials or the user does not have enough rights to perform the requested operation
* @api
*/
public const UNAUTHORIZED = 1;
const UNAUTHORIZED = 1;
/**
* Result: the parameter 'version' is missing
* @api
*/
public const MISSING_VERSION = 2;
const MISSING_VERSION = 2;
/**
* Result: the parameter 'json_data' is missing
* @api
*/
public const MISSING_JSON = 3;
const MISSING_JSON = 3;
/**
* Result: the input structure is not a valid JSON string
* @api
*/
public const INVALID_JSON = 4;
const INVALID_JSON = 4;
/**
* Result: the parameter 'auth_user' is missing, authentication aborted
* @api
*/
public const MISSING_AUTH_USER = 5;
const MISSING_AUTH_USER = 5;
/**
* Result: the parameter 'auth_pwd' is missing, authentication aborted
* @api
*/
public const MISSING_AUTH_PWD = 6;
const MISSING_AUTH_PWD = 6;
/**
* Result: no operation is available for the specified version
* @api
*/
public const UNSUPPORTED_VERSION = 10;
const UNSUPPORTED_VERSION = 10;
/**
* Result: the requested operation is not valid for the specified version
* @api
*/
public const UNKNOWN_OPERATION = 11;
const UNKNOWN_OPERATION = 11;
/**
* Result: the requested operation cannot be performed because it can cause data (integrity) loss
* @api
*/
public const UNSAFE = 12;
const UNSAFE = 12;
/**
* Result: the request page number is not valid. It must be an integer greater than 0
* @api
*/
public const INVALID_PAGE = 13;
const INVALID_PAGE = 13;
/**
* Result: the operation could not be performed, see the message for troubleshooting
* @api
*/
public const INTERNAL_ERROR = 100;
const INTERNAL_ERROR = 100;
/**
* Default constructor - ok!
@@ -1804,14 +1802,6 @@ class RestResult
* @api
*/
public $message;
/**
* Sanitize the content of this result to hide sensitive information
*/
public function SanitizeContent()
{
// The default implementation does nothing
}
}
/**
@@ -1851,13 +1841,17 @@ class RestUtils
*/
public static function GetMandatoryParam($oData, $sParamName)
{
if (isset($oData->$sParamName)) {
if (isset($oData->$sParamName))
{
return $oData->$sParamName;
} else {
}
else
{
throw new Exception("Missing parameter '$sParamName'");
}
}
/**
* Read an optional parameter from a Rest/Json structure.
*
@@ -1872,13 +1866,17 @@ class RestUtils
*/
public static function GetOptionalParam($oData, $sParamName, $default)
{
if (isset($oData->$sParamName)) {
if (isset($oData->$sParamName))
{
return $oData->$sParamName;
} else {
}
else
{
return $default;
}
}
/**
* Read a class from a Rest/Json structure.
*
@@ -1892,13 +1890,15 @@ class RestUtils
public static function GetClass($oData, $sParamName)
{
$sClass = self::GetMandatoryParam($oData, $sParamName);
if (!MetaModel::IsValidClass($sClass)) {
if (!MetaModel::IsValidClass($sClass))
{
throw new Exception("$sParamName: '$sClass' is not a valid class'");
}
return $sClass;
}
/**
* Read a list of attribute codes from a Rest/Json structure.
*
@@ -1914,21 +1914,31 @@ class RestUtils
public static function GetFieldList($sClass, $oData, $sParamName)
{
$sFields = self::GetOptionalParam($oData, $sParamName, '*');
$aShowFields = [];
if ($sFields == '*') {
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
$aShowFields = array();
if ($sFields == '*')
{
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
$aShowFields[$sClass][] = $sAttCode;
}
} elseif ($sFields == '*+') {
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sRefClass) {
foreach (MetaModel::ListAttributeDefs($sRefClass) as $sAttCode => $oAttDef) {
}
elseif ($sFields == '*+')
{
foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sRefClass)
{
foreach (MetaModel::ListAttributeDefs($sRefClass) as $sAttCode => $oAttDef)
{
$aShowFields[$sRefClass][] = $sAttCode;
}
}
} else {
foreach (explode(',', $sFields) as $sAttCode) {
}
else
{
foreach (explode(',', $sFields) as $sAttCode)
{
$sAttCode = trim($sAttCode);
if (($sAttCode != 'id') && (!MetaModel::IsValidAttCode($sClass, $sAttCode))) {
if (($sAttCode != 'id') && (!MetaModel::IsValidAttCode($sClass, $sAttCode)))
{
throw new Exception("$sParamName: invalid attribute code '$sAttCode'");
}
$aShowFields[$sClass][] = $sAttCode;
@@ -1951,30 +1961,38 @@ class RestUtils
*/
protected static function FindObjectFromCriteria($sClass, $oCriteria)
{
$aCriteriaReport = [];
if (isset($oCriteria->finalclass)) {
if (!MetaModel::IsValidClass($oCriteria->finalclass)) {
$aCriteriaReport = array();
if (isset($oCriteria->finalclass))
{
if (!MetaModel::IsValidClass($oCriteria->finalclass))
{
throw new Exception("finalclass: Unknown class '".$oCriteria->finalclass."'");
}
if (!MetaModel::IsParentClass($sClass, $oCriteria->finalclass)) {
if (!MetaModel::IsParentClass($sClass, $oCriteria->finalclass))
{
throw new Exception("finalclass: '".$oCriteria->finalclass."' is not a child class of '$sClass'");
}
$sClass = $oCriteria->finalclass;
}
$oSearch = new DBObjectSearch($sClass);
foreach ($oCriteria as $sAttCode => $value) {
foreach ($oCriteria as $sAttCode => $value)
{
$realValue = static::MakeValue($sClass, $sAttCode, $value);
$oSearch->AddCondition($sAttCode, $realValue, '=');
if (is_object($value) || is_array($value)) {
if (is_object($value) || is_array($value))
{
$value = json_encode($value);
}
$aCriteriaReport[] = "$sAttCode: $value ($realValue)";
}
$oSet = new DBObjectSet($oSearch);
$iCount = $oSet->Count();
if ($iCount == 0) {
if ($iCount == 0)
{
throw new Exception("No item found with criteria: ".implode(', ', $aCriteriaReport));
} elseif ($iCount > 1) {
}
elseif ($iCount > 1)
{
throw new Exception("Several items found ($iCount) with criteria: ".implode(', ', $aCriteriaReport));
}
$res = $oSet->Fetch();
@@ -1982,6 +2000,7 @@ class RestUtils
return $res;
}
/**
* Find an object from a polymorph search specification (Rest/Json)
*
@@ -1997,29 +2016,43 @@ class RestUtils
*/
public static function FindObjectFromKey($sClass, $key, $bAllowNullValue = false)
{
if (is_object($key)) {
if (is_object($key))
{
$res = static::FindObjectFromCriteria($sClass, $key);
} elseif (is_numeric($key)) {
if ($bAllowNullValue && ($key == 0)) {
}
elseif (is_numeric($key))
{
if ($bAllowNullValue && ($key == 0))
{
$res = null;
} else {
}
else
{
$res = MetaModel::GetObject($sClass, $key, false);
if (is_null($res)) {
if (is_null($res))
{
throw new Exception("Invalid object $sClass::$key");
}
}
} elseif (is_string($key)) {
}
elseif (is_string($key))
{
// OQL
$oSearch = DBObjectSearch::FromOQL($key);
$oSet = new DBObjectSet($oSearch);
$iCount = $oSet->Count();
if ($iCount == 0) {
if ($iCount == 0)
{
throw new Exception("No item found for query: $key");
} elseif ($iCount > 1) {
}
elseif ($iCount > 1)
{
throw new Exception("Several items found ($iCount) for query: $key");
}
$res = $oSet->Fetch();
} else {
}
else
{
throw new Exception("Wrong format for key");
}
@@ -2040,37 +2073,47 @@ class RestUtils
*/
public static function GetObjectSetFromKey($sClass, $key, $iLimit = 0, $iOffset = 0)
{
if (is_object($key)) {
if (isset($key->finalclass)) {
if (is_object($key))
{
if (isset($key->finalclass))
{
$sClass = $key->finalclass;
if (!MetaModel::IsValidClass($sClass)) {
if (!MetaModel::IsValidClass($sClass))
{
throw new Exception("finalclass: Unknown class '$sClass'");
}
}
$oSearch = new DBObjectSearch($sClass);
foreach ($key as $sAttCode => $value) {
foreach ($key as $sAttCode => $value)
{
$realValue = static::MakeValue($sClass, $sAttCode, $value);
$oSearch->AddCondition($sAttCode, $realValue, '=');
}
} elseif (is_numeric($key)) {
}
elseif (is_numeric($key))
{
$oSearch = new DBObjectSearch($sClass);
$oSearch->AddCondition('id', $key);
} elseif (is_string($key)) {
}
elseif (is_string($key))
{
// OQL
try {
$oSearch = DBObjectSearch::FromOQL($key);
} catch (Exception $e) {
throw new CoreOqlException('Query failed to execute', [
'query' => $key,
'exception_class' => get_class($e),
'exception_message' => $e->getMessage(),
]);
}
} else {
try {
$oSearch = DBObjectSearch::FromOQL($key);
} catch (Exception $e) {
throw new CoreOqlException('Query failed to execute', [
'query' => $key,
'exception_class' => get_class($e),
'exception_message' => $e->getMessage(),
]);
}
}
else
{
throw new Exception("Wrong format for key");
}
$oObjectSet = new DBObjectSet($oSearch, [], [], null, $iLimit, $iOffset);
$oObjectSet = new DBObjectSet($oSearch, array(), array(), null, $iLimit, $iOffset);
return $oObjectSet;
}
@@ -2088,38 +2131,53 @@ class RestUtils
*/
public static function MakeValue($sClass, $sAttCode, $value)
{
try {
if (!MetaModel::IsValidAttCode($sClass, $sAttCode)) {
try
{
if (!MetaModel::IsValidAttCode($sClass, $sAttCode))
{
throw new Exception("Unknown attribute");
}
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef instanceof AttributeExternalKey) {
if ($oAttDef instanceof AttributeExternalKey)
{
$oExtKeyObject = static::FindObjectFromKey($oAttDef->GetTargetClass(), $value, true /* allow null */);
$value = ($oExtKeyObject != null) ? $oExtKeyObject->GetKey() : 0;
} elseif ($oAttDef instanceof AttributeLinkedSet) {
if (!is_array($value)) {
}
elseif ($oAttDef instanceof AttributeLinkedSet)
{
if (!is_array($value))
{
throw new Exception("A link set must be defined by an array of objects");
}
$sLnkClass = $oAttDef->GetLinkedClass();
$aLinks = [];
foreach ($value as $oValues) {
$aLinks = array();
foreach ($value as $oValues)
{
$oLnk = static::MakeObjectFromFields($sLnkClass, $oValues);
// Fix for N°1939
if (($oAttDef instanceof AttributeLinkedSetIndirect) && ($oLnk->Get($oAttDef->GetExtKeyToRemote()) == 0)) {
if (($oAttDef instanceof AttributeLinkedSetIndirect) && ($oLnk->Get($oAttDef->GetExtKeyToRemote()) == 0))
{
continue;
}
$aLinks[] = $oLnk;
}
$value = DBObjectSet::FromArray($sLnkClass, $aLinks);
} elseif ($oAttDef instanceof AttributeTagSet) {
if (!is_array($value)) {
}
elseif ($oAttDef instanceof AttributeTagSet)
{
if (!is_array($value))
{
throw new Exception("A tag set must be defined by an array of tag codes");
}
$value = $oAttDef->FromJSONToValue($value);
} else {
}
else
{
$value = $oAttDef->FromJSONToValue($value);
}
} catch (Exception $e) {
}
catch (Exception $e)
{
throw new Exception("$sAttCode: ".$e->getMessage(), $e->getCode());
}
@@ -2139,11 +2197,15 @@ class RestUtils
public static function MakeObjectFromFields($sClass, $aFields)
{
$oObject = MetaModel::NewObject($sClass);
foreach ($aFields as $sAttCode => $value) {
foreach ($aFields as $sAttCode => $value)
{
$realValue = static::MakeValue($sClass, $sAttCode, $value);
try {
try
{
$oObject->Set($sAttCode, $realValue);
} catch (Exception $e) {
}
catch (Exception $e)
{
throw new Exception("$sAttCode: ".$e->getMessage(), $e->getCode());
}
}
@@ -2164,11 +2226,15 @@ class RestUtils
public static function UpdateObjectFromFields($oObject, $aFields)
{
$sClass = get_class($oObject);
foreach ($aFields as $sAttCode => $value) {
foreach ($aFields as $sAttCode => $value)
{
$realValue = static::MakeValue($sClass, $sAttCode, $value);
try {
try
{
$oObject->Set($sAttCode, $realValue);
} catch (Exception $e) {
}
catch (Exception $e)
{
throw new Exception("$sAttCode: ".$e->getMessage(), $e->getCode());
}
}
@@ -2177,6 +2243,7 @@ class RestUtils
}
}
/**
* Helpers for modules extensibility, with discover performed by the MetaModel.
*
@@ -2199,21 +2266,21 @@ interface iModuleExtension
*/
interface iKPILoggerExtension
{
/**
* Init the statistics collected
*
* @return void
*/
public function InitStats();
/**
* 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);
/**
* Add a new KPI to the stats
*
* @param \Combodo\iTop\Core\Kpi\KpiLogData $oKpiLogData
*
* @return mixed
*/
public function LogOperation($oKpiLogData);
}
/**
@@ -2231,6 +2298,7 @@ interface iBackupExtraFilesExtension
public function GetExtraFilesRelPaths(): array;
}
/**
* Interface to provide messages to be displayed in the "Welcome Popup"
*
@@ -2291,7 +2359,7 @@ abstract class AbstractWelcomePopupExtension implements iWelcomePopupExtension
{
return [];
}
/**
* @inheritDoc
*/
@@ -2300,4 +2368,4 @@ abstract class AbstractWelcomePopupExtension implements iWelcomePopupExtension
// No need to process the acknowledgment notice by default
return;
}
}
}

View File

@@ -1,10 +1,9 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// 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.
@@ -17,6 +16,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* This class manages the audit "categories". Each category defines a set of objects
* to check and is linked to a set of rules that determine the valid or invalid objects
@@ -32,36 +32,34 @@ class AuditCategory extends cmdbAbstractObject
{
public static function Init()
{
$aParams =
[
$aParams = array
(
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => ['name'],
"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'),
];
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("name", ["description" => "Short name for this category", "allowed_values" => null, "sql" => "name", "default_value" => "", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("description", ["allowed_values" => null, "sql" => "description", "default_value" => "", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeOQL("definition_set", ["allowed_values" => null, "sql" => "definition_set", "default_value" => "", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeLinkedSet("rules_list", ["linked_class" => "AuditRule", "ext_key_to_me" => "category_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => [], "edit_mode" => LINKSET_EDITMODE_INPLACE, "tracking_level" => LINKSET_TRACKING_ALL]));
MetaModel::Init_AddAttribute(new AttributeInteger("ok_error_tolerance", ["allowed_values" => null, "sql" => "ok_error_tolerance", "default_value" => 5, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeInteger("warning_error_tolerance", ["allowed_values" => null, "sql" => "warning_error_tolerance", "default_value" => 25, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect(
"domains_list",
["linked_class" => "lnkAuditCategoryToAuditDomain", "ext_key_to_me" => "category_id", "ext_key_to_remote" => "domain_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => [], "display_style" => 'property']
));
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())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeOQL("definition_set", array("allowed_values"=>null, "sql"=>"definition_set", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSet("rules_list", array("linked_class"=>"AuditRule", "ext_key_to_me"=>"category_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array(), "edit_mode" => LINKSET_EDITMODE_INPLACE, "tracking_level" => LINKSET_TRACKING_ALL)));
MetaModel::Init_AddAttribute(new AttributeInteger("ok_error_tolerance", array("allowed_values"=>null, "sql"=>"ok_error_tolerance", "default_value"=>5, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("warning_error_tolerance", array("allowed_values" => null, "sql" => "warning_error_tolerance", "default_value" => 25, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("domains_list",
array("linked_class" => "lnkAuditCategoryToAuditDomain", "ext_key_to_me" => "category_id", "ext_key_to_remote" => "domain_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array(), "display_style" => 'property')));
// Display lists
MetaModel::Init_SetZListItems('details', ['name', 'description', 'definition_set', 'ok_error_tolerance', 'warning_error_tolerance', 'rules_list', 'domains_list']); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', ['description', ]); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', array('name', 'description', 'definition_set', 'ok_error_tolerance', 'warning_error_tolerance', 'rules_list', 'domains_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description', )); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', ['description', 'definition_set']); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', ['name', 'description']); // Criteria of the default search form
MetaModel::Init_SetZListItems('standard_search', array('description', 'definition_set')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
}
/**
@@ -76,9 +74,9 @@ class AuditCategory extends cmdbAbstractObject
public function GetReportColor($iTotal, $iErrors)
{
$sResult = 'red';
if (($iTotal == 0) || ($iErrors / $iTotal) <= ($this->Get('ok_error_tolerance') / 100)) {
if ( ($iTotal == 0) || ($iErrors / $iTotal) <= ($this->Get('ok_error_tolerance') / 100) ) {
$sResult = 'green';
} elseif (($iErrors / $iTotal) <= ($this->Get('warning_error_tolerance') / 100)) {
} else if (($iErrors / $iTotal) <= ($this->Get('warning_error_tolerance') / 100)) {
$sResult = 'orange';
}
@@ -95,3 +93,4 @@ class AuditCategory extends cmdbAbstractObject
return $aShortcutActions;
}
}
?>

View File

@@ -1,5 +1,4 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -17,6 +16,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* This class manages the audit "categories". Each category defines a set of objects
* to check and is linked to a set of rules that determine the valid or invalid objects
@@ -33,34 +33,32 @@ class AuditDomain extends cmdbAbstractObject
{
public static function Init()
{
$aParams =
[
$aParams = array
(
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"complementary_name_attcode" => ['description'],
"complementary_name_attcode" => array('description'),
"state_attcode" => "",
"reconc_keys" => ['name'],
"reconc_keys" => array('name'),
"db_table" => "priv_auditdomain",
"db_key_field" => "id",
"db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-album.svg'),
];
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("name", ["description" => "Short name for this category", "allowed_values" => null, "sql" => "name", "default_value" => "", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("description", ["allowed_values" => null, "sql" => "description", "default_value" => "", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeImage("icon", ["is_null_allowed" => true, "depends_on" => [], "display_max_width" => 96, "display_max_height" => 96, "storage_max_width" => 256, "storage_max_height" => 256, "default_image" => null, "always_load_in_tables" => false]));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect(
"categories_list",
["linked_class" => "lnkAuditCategoryToAuditDomain", "ext_key_to_me" => "domain_id", "ext_key_to_remote" => "category_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => []]
));
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())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => "", "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeImage("icon", array("is_null_allowed" => true, "depends_on" => array(), "display_max_width" => 96, "display_max_height" => 96, "storage_max_width" => 256, "storage_max_height" => 256, "default_image" => null, "always_load_in_tables" => false)));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("categories_list",
array("linked_class" => "lnkAuditCategoryToAuditDomain", "ext_key_to_me" => "domain_id", "ext_key_to_remote" => "category_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', ['name', 'description', 'icon', 'categories_list']); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', ['description',]); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', array('name', 'description', 'icon', 'categories_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description',)); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', ['description']); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', ['name', 'description']); // Criteria of the default search form
MetaModel::Init_SetZListItems('standard_search', array('description')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
}
public static function GetShortcutActions($sFinalClass)
@@ -86,39 +84,40 @@ class lnkAuditCategoryToAuditDomain extends cmdbAbstractObject
*/
public static function Init()
{
$aParams =
[
$aParams = array
(
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => ['category_id', 'domain_id'],
"reconc_keys" => array('category_id', 'domain_id'),
"db_table" => "priv_link_audit_category_domain",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => true,
'uniqueness_rules' => [
'no_duplicate' => [
'attributes' => [
'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", ["targetclass" => "AuditCategory", "jointype" => '', "allowed_values" => null, "sql" => "category_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalField("category_name", ["allowed_values" => null, "extkey_attcode" => 'category_id', "target_attcode" => "name"]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("domain_id", ["targetclass" => "AuditDomain", "jointype" => '', "allowed_values" => null, "sql" => "domain_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalField("domain_name", ["allowed_values" => null, "extkey_attcode" => 'domain_id', "target_attcode" => "name"]));
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())));
MetaModel::Init_AddAttribute(new AttributeExternalField("category_name", array("allowed_values" => null, "extkey_attcode" => 'category_id', "target_attcode" => "name")));
MetaModel::Init_AddAttribute(new AttributeExternalKey("domain_id", array("targetclass" => "AuditDomain", "jointype" => '', "allowed_values" => null, "sql" => "domain_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("domain_name", array("allowed_values" => null, "extkey_attcode" => 'domain_id', "target_attcode" => "name")));
// Display lists
MetaModel::Init_SetZListItems('details', ['category_id', 'domain_id']);
MetaModel::Init_SetZListItems('list', ['category_id', 'domain_id']);
MetaModel::Init_SetZListItems('details', array('category_id', 'domain_id'));
MetaModel::Init_SetZListItems('list', array('category_id', 'domain_id'));
// Search criteria
MetaModel::Init_SetZListItems('standard_search', ['category_id', 'domain_id']);
MetaModel::Init_SetZListItems('standard_search', array('category_id', 'domain_id'));
}
}

View File

@@ -1,10 +1,9 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// 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.
@@ -17,6 +16,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* This class manages the audit "rule" linked to a given audit category.
* Each rule is based on an OQL expression that returns either the "good" objects
@@ -33,34 +33,35 @@ class AuditRule extends cmdbAbstractObject
{
public static function Init()
{
$aParams =
[
$aParams = array
(
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => ['name'],
"reconc_keys" => array('name'),
"db_table" => "priv_auditrule",
"db_key_field" => "id",
"db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit.svg'),
];
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("name", ["allowed_values" => null, "sql" => "name", "default_value" => "", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("description", ["allowed_values" => null, "sql" => "description", "default_value" => "", "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeOQL("query", ["allowed_values" => null, "sql" => "query", "default_value" => "", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeEnum("valid_flag", ["allowed_values" => new ValueSetEnum('true,false'), "sql" => "valid_flag", "default_value" => "true", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", ["allowed_values" => null, "sql" => "category_id", "targetclass" => "AuditCategory", "is_null_allowed" => false, "on_target_delete" => DEL_MANUAL, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalField("category_name", ["allowed_values" => null, "extkey_attcode" => 'category_id', "target_attcode" => "name"]));
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values" => null, "sql" => "name", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => "", "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeOQL("query", array("allowed_values" => null, "sql" => "query", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("valid_flag", array("allowed_values" => new ValueSetEnum('true,false'), "sql" => "valid_flag", "default_value" => "true", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", array("allowed_values" => null, "sql" => "category_id", "targetclass" => "AuditCategory", "is_null_allowed" => false, "on_target_delete" => DEL_MANUAL, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("category_name", array("allowed_values" => null, "extkey_attcode" => 'category_id', "target_attcode" => "name")));
// Display lists
MetaModel::Init_SetZListItems('details', ['category_id', 'name', 'description', 'query', 'valid_flag']); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', ['category_id', 'description', 'valid_flag']); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', array('category_id', 'name', 'description', 'query', 'valid_flag')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('category_id', 'description', 'valid_flag')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', ['category_id', 'name', 'description', 'valid_flag', 'query']); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', ['name', 'description', 'category_id']); // Criteria of the advanced search form
MetaModel::Init_SetZListItems('standard_search', array('category_id', 'name', 'description', 'valid_flag', 'query')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'category_id')); // Criteria of the advanced search form
}
public static function GetShortcutActions($sFinalClass)
{
$aShortcutActions = parent::GetShortcutActions($sFinalClass);
@@ -71,3 +72,4 @@ class AuditRule extends cmdbAbstractObject
return $aShortcutActions;
}
}
?>

View File

@@ -1,9 +1,8 @@
<?php
/**
* @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-2024 Combodo SAS
*/
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/CaptureWebPage.php, now loadable using autoloader');
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/CaptureWebPage.php, now loadable using autoloader');

View File

@@ -1,9 +1,8 @@
<?php
/**
* @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-2024 Combodo SAS
*/
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/CLIPage.php, now loadable using autoloader');
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/CLIPage.php, now loadable using autoloader');

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,4 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -33,8 +32,7 @@ class CompileCSSService
{
}
public function CompileCSSFromSASS($sSassContent, $aImportPaths = [], $aVariables = [])
{
public function CompileCSSFromSASS($sSassContent, $aImportPaths = [], $aVariables = []){
return utils::CompileCSSFromSASS($sSassContent, $aImportPaths, $aVariables);
}
}
}

View File

@@ -1,9 +1,8 @@
<?php
/**
* @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-2024 Combodo SAS
*/
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/CSVPage.php, now loadable using autoloader');
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/CSVPage.php, now loadable using autoloader');

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -54,7 +53,7 @@ abstract class Dashboard
$this->sLayoutClass = 'DashboardLayoutOneCol';
$this->bAutoReload = false;
$this->iAutoReloadSec = MetaModel::GetConfig()->GetStandardReloadInterval();
$this->aCells = [];
$this->aCells = array();
$this->oDOMNode = null;
$this->sId = $sId;
}
@@ -66,8 +65,8 @@ abstract class Dashboard
*/
public function FromXml($sXml)
{
$this->aCells = []; // reset the content of the dashboard
set_error_handler(['Dashboard', 'ErrorHandler']);
$this->aCells = array(); // reset the content of the dashboard
set_error_handler(array('Dashboard', 'ErrorHandler'));
$oDoc = new DOMDocument();
$oDoc->loadXML($sXml);
restore_error_handler();
@@ -80,69 +79,87 @@ abstract class Dashboard
public function FromDOMDocument(DOMDocument $oDoc)
{
$this->oDOMNode = $oDoc->getElementsByTagName('dashboard')->item(0);
if ($oLayoutNode = $this->oDOMNode->getElementsByTagName('layout')->item(0)) {
if ($oLayoutNode = $this->oDOMNode->getElementsByTagName('layout')->item(0))
{
$this->sLayoutClass = $oLayoutNode->textContent;
} else {
}
else
{
$this->sLayoutClass = 'DashboardLayoutOneCol';
}
if ($oTitleNode = $this->oDOMNode->getElementsByTagName('title')->item(0)) {
if ($oTitleNode = $this->oDOMNode->getElementsByTagName('title')->item(0))
{
$this->sTitle = $oTitleNode->textContent;
} else {
}
else
{
$this->sTitle = '';
}
$this->bAutoReload = false;
$this->iAutoReloadSec = MetaModel::GetConfig()->GetStandardReloadInterval();
if ($oAutoReloadNode = $this->oDOMNode->getElementsByTagName('auto_reload')->item(0)) {
if ($oAutoReloadEnabled = $oAutoReloadNode->getElementsByTagName('enabled')->item(0)) {
if ($oAutoReloadNode = $this->oDOMNode->getElementsByTagName('auto_reload')->item(0))
{
if ($oAutoReloadEnabled = $oAutoReloadNode->getElementsByTagName('enabled')->item(0))
{
$this->bAutoReload = ($oAutoReloadEnabled->textContent == 'true');
}
if ($oAutoReloadInterval = $oAutoReloadNode->getElementsByTagName('interval')->item(0)) {
if ($oAutoReloadInterval = $oAutoReloadNode->getElementsByTagName('interval')->item(0))
{
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int)$oAutoReloadInterval->textContent);
}
}
if ($oCellsNode = $this->oDOMNode->getElementsByTagName('cells')->item(0)) {
if ($oCellsNode = $this->oDOMNode->getElementsByTagName('cells')->item(0))
{
$oCellsList = $oCellsNode->getElementsByTagName('cell');
$aCellOrder = [];
$aCellOrder = array();
$iCellRank = 0;
/** @var \DOMElement $oCellNode */
foreach ($oCellsList as $oCellNode) {
foreach($oCellsList as $oCellNode)
{
$oCellRank = $oCellNode->getElementsByTagName('rank')->item(0);
if ($oCellRank) {
if ($oCellRank)
{
$iCellRank = (float)$oCellRank->textContent;
}
$oDashletsNode = $oCellNode->getElementsByTagName('dashlets')->item(0);
{
$oDashletList = $oDashletsNode->getElementsByTagName('dashlet');
$iRank = 0;
$aDashletOrder = [];
$aDashletOrder = array();
/** @var \DOMElement $oDomNode */
foreach ($oDashletList as $oDomNode) {
foreach($oDashletList as $oDomNode)
{
$oRank = $oDomNode->getElementsByTagName('rank')->item(0);
if ($oRank) {
if ($oRank)
{
$iRank = (float)$oRank->textContent;
}
$oNewDashlet = $this->InitDashletFromDOMNode($oDomNode);
$aDashletOrder[] = ['rank' => $iRank, 'dashlet' => $oNewDashlet];
$aDashletOrder[] = array('rank' => $iRank, 'dashlet' => $oNewDashlet);
}
usort($aDashletOrder, [get_class($this), 'SortOnRank']);
$aDashletList = [];
foreach ($aDashletOrder as $aItem) {
usort($aDashletOrder, array(get_class($this), 'SortOnRank'));
$aDashletList = array();
foreach($aDashletOrder as $aItem)
{
$aDashletList[] = $aItem['dashlet'];
}
$aCellOrder[] = ['rank' => $iCellRank, 'dashlets' => $aDashletList];
$aCellOrder[] = array('rank' => $iCellRank, 'dashlets' => $aDashletList);
}
}
usort($aCellOrder, [get_class($this), 'SortOnRank']);
foreach ($aCellOrder as $aItem) {
usort($aCellOrder, array(get_class($this), 'SortOnRank'));
foreach($aCellOrder as $aItem)
{
$this->aCells[] = $aItem['dashlets'];
}
} else {
$this->aCells = [];
}
else
{
$this->aCells = array();
}
}
@@ -152,20 +169,20 @@ abstract class Dashboard
* @return mixed
*/
protected function InitDashletFromDOMNode($oDomNode)
{
$sId = $oDomNode->getAttribute('id');
{
$sId = $oDomNode->getAttribute('id');
$sDashletType = $oDomNode->getAttribute('xsi:type');
$sDashletType = $oDomNode->getAttribute('xsi:type');
// Test if dashlet can be instantiated, otherwise (uninstalled, broken, ...) we display a placeholder
$sClass = static::GetDashletClassFromType($sDashletType);
/** @var \Dashlet $oNewDashlet */
$oNewDashlet = new $sClass($this->oMetaModel, $sId);
$oNewDashlet->SetDashletType($sDashletType);
$oNewDashlet->FromDOMNode($oDomNode);
// Test if dashlet can be instantiated, otherwise (uninstalled, broken, ...) we display a placeholder
$sClass = static::GetDashletClassFromType($sDashletType);
/** @var \Dashlet $oNewDashlet */
$oNewDashlet = new $sClass($this->oMetaModel, $sId);
$oNewDashlet->SetDashletType($sDashletType);
$oNewDashlet->FromDOMNode($oDomNode);
return $oNewDashlet;
}
return $oNewDashlet;
}
/**
* @param array $aItem1
@@ -191,9 +208,12 @@ abstract class Dashboard
*/
public static function ErrorHandler($errno, $errstr, $errfile, $errline)
{
if ($errno == E_WARNING && (substr_count($errstr, "DOMDocument::loadXML()") > 0)) {
if ($errno == E_WARNING && (substr_count($errstr,"DOMDocument::loadXML()")>0))
{
throw new DOMException($errstr);
} else {
}
else
{
return false;
}
}
@@ -211,7 +231,7 @@ abstract class Dashboard
$oMainNode = $oDoc->createElement('dashboard');
$oMainNode->setAttribute('xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance");
$oDoc->appendChild($oMainNode);
$this->ToDOMNode($oMainNode);
$sXml = $oDoc->saveXML();
@@ -241,21 +261,23 @@ abstract class Dashboard
$oCellsNode = $oDoc->createElement('cells');
$oDefinition->appendChild($oCellsNode);
$iCellRank = 0;
foreach ($this->aCells as $aCell) {
foreach ($this->aCells as $aCell)
{
$oCellNode = $oDoc->createElement('cell');
$oCellNode->setAttribute('id', $iCellRank);
$oCellsNode->appendChild($oCellNode);
$oCellRank = $oDoc->createElement('rank', $iCellRank);
$oCellNode->appendChild($oCellRank);
$iCellRank++;
$iDashletRank = 0;
$oDashletsNode = $oDoc->createElement('dashlets');
$oCellNode->appendChild($oDashletsNode);
/** @var \Dashlet $oDashlet */
foreach ($aCell as $oDashlet) {
foreach ($aCell as $oDashlet)
{
$oNode = $oDoc->createElement('dashlet');
$oDashletsNode->appendChild($oNode);
$oNode->setAttribute('id', $oDashlet->GetID());
@@ -277,15 +299,18 @@ abstract class Dashboard
$this->sTitle = $aParams['title'];
$this->bAutoReload = $aParams['auto_reload'] == 'true';
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int) $aParams['auto_reload_sec']);
foreach ($aParams['cells'] as $aCell) {
$aCellDashlets = [];
foreach ($aCell as $aDashletParams) {
foreach($aParams['cells'] as $aCell)
{
$aCellDashlets = array();
foreach($aCell as $aDashletParams)
{
$sDashletClass = $aDashletParams['dashlet_class'];
$sId = $aDashletParams['dashlet_id'];
/** @var \Dashlet $oNewDashlet */
$oNewDashlet = new $sDashletClass($this->oMetaModel, $sId);
if (isset($aDashletParams['dashlet_type'])) {
if (isset($aDashletParams['dashlet_type']))
{
$oNewDashlet->SetDashletType($aDashletParams['dashlet_type']);
}
$oForm = $oNewDashlet->GetForm();
@@ -297,12 +322,12 @@ abstract class Dashboard
}
$this->aCells[] = $aCellDashlets;
}
}
public function Save()
{
}
/**
@@ -395,7 +420,7 @@ abstract class Dashboard
{
$sId = $this->GetNewDashletId();
$oDashlet->SetId($sId);
$this->aCells[] = [$oDashlet];
$this->aCells[] = array($oDashlet);
}
/**
@@ -405,7 +430,7 @@ abstract class Dashboard
* @throws \ReflectionException
* @throws \Exception
*/
public function RenderProperties($oPage, $aExtraParams = [])
public function RenderProperties($oPage, $aExtraParams = array())
{
// menu to pick a layout and edit other properties of the dashboard
$oPage->add('<div class="ui-widget-content ui-corner-all ibo-dashboard-editor--properties"><div class="ui-widget-header ui-corner-all ibo-dashboard-editor--properties-title">'.Dict::S('UI:DashboardEdit:Properties').'</div>');
@@ -417,7 +442,7 @@ abstract class Dashboard
if (is_subclass_of($sLayoutClass, 'DashboardLayout')) {
$oReflection = new ReflectionClass($sLayoutClass);
if (!$oReflection->isAbstract()) {
$aCallSpec = [$sLayoutClass, 'GetInfo'];
$aCallSpec = array($sLayoutClass, 'GetInfo');
$aInfo = call_user_func($aCallSpec);
$sChecked = ($this->sLayoutClass == $sLayoutClass) ? 'checked' : '';
$oPage->add('<input type="radio" name="layout_class" '.$sChecked.' value="'.$sLayoutClass.'" id="layout_'.$sLayoutClass.'"><label for="layout_'.$sLayoutClass.'"><img src="'.$sUrl.$aInfo['icon'].'" class="ibo-dashboard--properties--icon" data-role="ibo-dashboard--properties--icon"/></label>'); // title="" on either the img or the label does nothing !
@@ -441,6 +466,7 @@ abstract class Dashboard
$oField->SetBoundaries(MetaModel::GetConfig()->Get('min_reload_interval'), null); // no upper limit
$oForm->AddField($oField);
$this->SetFormParams($oForm, $aExtraParams);
$oForm->RenderAsPropertySheet($oPage, false, '.itop-dashboard');
@@ -448,7 +474,7 @@ abstract class Dashboard
$sRateTitle = addslashes(Dict::Format('UI:DashboardEdit:AutoReloadSec+', MetaModel::GetConfig()->Get('min_reload_interval')));
$oPage->add_ready_script(
<<<EOF
<<<EOF
// Note: the title gets deleted by the validation mechanism
$("#attr_auto_reload_sec").attr('data-tooltip-content', '$sRateTitle');
CombodoTooltip.InitTooltipFromMarkup($("#attr_auto_reload_sec"));
@@ -496,9 +522,11 @@ EOF
*
* @return \Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardLayout
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = [], $bCanEdit = true)
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
{
$aExtraParams['dashboard_div_id'] = utils::Sanitize($aExtraParams['dashboard_div_id'] ?? null, $this->GetId(), utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
if (!array_key_exists('dashboard_div_id', $aExtraParams)) {
$aExtraParams['dashboard_div_id'] = utils::Sanitize($this->GetId(), '', 'element_identifier');
}
/** @var \DashboardLayoutMultiCol $oLayout */
$oLayout = new $this->sLayoutClass();
@@ -525,8 +553,7 @@ EOF
$oToolbar->AddHtml($sHtml);
} else {
$oPage->add_script(
<<<JS
$oPage->add_script(<<<JS
$(".ibo-top-bar--toolbar-dashboard-title").html("$sTitleForHTML").attr("title", $('<div>').html("$sTitleForHTML").text());
JS
);
@@ -570,7 +597,7 @@ JS
* @param WebPage $oPage
* @param array $aExtraParams
*/
public function RenderDashletsProperties(WebPage $oPage, $aExtraParams = [])
public function RenderDashletsProperties(WebPage $oPage, $aExtraParams = array())
{
// Toolbox/palette to edit the properties of each dashlet
$oPage->add('<div class="ui-widget-content ui-corner-all ibo-dashlet--properties"><div class="ui-widget-header ui-corner-all ibo-dashlet--properties--title">'.Dict::S('UI:DashboardEdit:DashletProperties').'</div>');
@@ -579,10 +606,13 @@ JS
$oLayout = new $this->sLayoutClass();
$oPage->add('<div id="dashlet_properties">');
foreach ($this->aCells as $iCellIdx => $aCell) {
foreach($this->aCells as $iCellIdx => $aCell)
{
/** @var \Dashlet $oDashlet */
foreach ($aCell as $oDashlet) {
if ($oDashlet->IsVisible()) {
foreach($aCell as $oDashlet)
{
if ($oDashlet->IsVisible())
{
$oPage->add('<div class="dashlet_properties" id="dashlet_properties_'.$oDashlet->GetID().'" style="display:none">');
$oForm = $oDashlet->GetForm();
$this->SetFormParams($oForm, $aExtraParams);
@@ -604,17 +634,18 @@ JS
*/
protected function GetAvailableDashlets()
{
$aDashlets = [];
$aDashlets = array();
foreach (get_declared_classes() as $sDashletClass) {
foreach( get_declared_classes() as $sDashletClass)
{
// DashletUnknown is not among the selection as it is just a fallback for dashlets that can't instantiated.
if (is_subclass_of($sDashletClass, 'Dashlet') && !in_array($sDashletClass, ['DashletUnknown', 'DashletProxy'])) {
if (is_subclass_of($sDashletClass, 'Dashlet') && !in_array($sDashletClass, array('DashletUnknown', 'DashletProxy'))) {
$oReflection = new ReflectionClass($sDashletClass);
if (!$oReflection->isAbstract()) {
$aCallSpec = [$sDashletClass, 'IsVisible'];
$aCallSpec = array($sDashletClass, 'IsVisible');
$bVisible = call_user_func($aCallSpec);
if ($bVisible) {
$aCallSpec = [$sDashletClass, 'GetInfo'];
$aCallSpec = array($sDashletClass, 'GetInfo');
$aInfo = call_user_func($aCallSpec);
$aDashlets[$sDashletClass] = $aInfo;
}
@@ -631,9 +662,11 @@ JS
protected function GetNewDashletId()
{
$iNewId = 0;
foreach ($this->aCells as $aDashlets) {
foreach($this->aCells as $aDashlets)
{
/** @var \Dashlet $oDashlet */
foreach ($aDashlets as $oDashlet) {
foreach($aDashlets as $oDashlet)
{
$iNewId = max($iNewId, (int)$oDashlet->GetID());
}
}
@@ -650,15 +683,15 @@ JS
*
* @return void
*/
abstract protected function PrepareDashletForRendering(Dashlet $oDashlet, $aCoordinates, $aExtraParams = []);
abstract protected function PrepareDashletForRendering(Dashlet $oDashlet, $aCoordinates, $aExtraParams = array());
/**
* @param \DesignerForm $oForm
* @param array $aExtraParams
*
* @return mixed
*/
abstract protected function SetFormParams($oForm, $aExtraParams = []);
/**
* @param \DesignerForm $oForm
* @param array $aExtraParams
*
* @return mixed
*/
abstract protected function SetFormParams($oForm, $aExtraParams = array());
/**
* @param string $sType
@@ -668,7 +701,8 @@ JS
*/
public static function GetDashletClassFromType($sType, $oFactory = null)
{
if (is_subclass_of($sType, 'Dashlet')) {
if (is_subclass_of($sType, 'Dashlet'))
{
return $sType;
}
return 'DashletUnknown';
@@ -691,12 +725,14 @@ JS
*/
public static function GetDashletUniqueId($bIsCustomized, $sDashboardDivId, $iRow, $iCol, $sDashletOrigId)
{
if (strpos($sDashletOrigId, '_ID_row') !== false) {
if(strpos($sDashletOrigId, '_ID_row') !== false)
{
return $sDashletOrigId;
}
$sDashletId = $sDashboardDivId."_ID_row".$iRow."_col".$iCol."_".$sDashletOrigId;
if ($bIsCustomized) {
if ($bIsCustomized)
{
$sDashletId = 'CUSTOM_'.$sDashletId;
}
@@ -748,9 +784,9 @@ class RuntimeDashboard extends Dashboard
* @inheritDoc
* @throws \Exception
*/
protected function SetFormParams($oForm, $aExtraParams = [])
protected function SetFormParams($oForm, $aExtraParams = array())
{
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', ['operation' => 'update_dashlet_property', 'extra_params' => $aExtraParams]);
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property', 'extra_params' => $aExtraParams));
}
/**
@@ -766,11 +802,14 @@ class RuntimeDashboard extends Dashboard
$oUDSearch->AddCondition('menu_code', $this->sId, '=');
$oUDSet = new DBObjectSet($oUDSearch);
$bIsNew = false;
if ($oUDSet->Count() > 0) {
if ($oUDSet->Count() > 0)
{
// Assuming there is at most one couple {user, menu}!
$oUserDashboard = $oUDSet->Fetch();
$oUserDashboard->Set('contents', $sXml);
} else {
}
else
{
// No such customized dashboard for the current user, let's create a new record
$oUserDashboard = new UserDashboard();
$oUserDashboard->Set('user_id', UserRights::GetUserId());
@@ -801,7 +840,8 @@ class RuntimeDashboard extends Dashboard
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$oUDSearch->AddCondition('menu_code', $this->sId, '=');
$oUDSet = new DBObjectSet($oUDSearch);
if ($oUDSet->Count() > 0) {
if ($oUDSet->Count() > 0)
{
// Assuming there is at most one couple {user, menu}!
$oUserDashboard = $oUDSet->Fetch();
utils::PushArchiveMode(false);
@@ -845,11 +885,14 @@ class RuntimeDashboard extends Dashboard
} else {
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
}
} else {
}
else
{
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
}
if ($sDashboardDefinition !== false) {
if ($sDashboardDefinition !== false)
{
$oDashboard = new RuntimeDashboard($sDashBoardId);
$oDashboard->FromXml($sDashboardDefinition);
$oDashboard->SetCustomFlag($bCustomized);
@@ -896,6 +939,7 @@ class RuntimeDashboard extends Dashboard
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
}
if ($sDashboardDefinition !== false) {
$oDashboard = new RuntimeDashboard($sDashBoardId);
$oDashboard->FromXml($sDashboardDefinition);
@@ -912,11 +956,11 @@ class RuntimeDashboard extends Dashboard
* @inheritDoc
* @throws \Exception
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = [], $bCanEdit = true)
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
{
if (!isset($aExtraParams['query_params']) && isset($aExtraParams['this->class'])) {
$oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']);
$aRenderParams = ['query_params' => $oObj->ToArgsForQuery()];
$aRenderParams = array('query_params' => $oObj->ToArgsForQuery());
} else {
$aRenderParams = $aExtraParams;
}
@@ -926,7 +970,7 @@ class RuntimeDashboard extends Dashboard
if (isset($aExtraParams['query_params']['this->object()'])) {
/** @var \DBObject $oObj */
$oObj = $aExtraParams['query_params']['this->object()'];
$aAjaxParams = ['this->class' => get_class($oObj), 'this->id' => $oObj->GetKey()];
$aAjaxParams = array('this->class' => get_class($oObj), 'this->id' => $oObj->GetKey());
if (isset($aExtraParams['from_dashboard_page'])) {
$aAjaxParams['from_dashboard_page'] = $aExtraParams['from_dashboard_page'];
}
@@ -959,7 +1003,9 @@ class RuntimeDashboard extends Dashboard
}
JS
);
} else {
}
else
{
$oPage->add_script(
<<<EOF
if (typeof(AutoReloadDashboardId$sDivId) !== 'undefined')
@@ -988,7 +1034,7 @@ EOF
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
protected function RenderSelector(WebPage $oPage, DashboardLayoutUIBlock $oDashboard, $aAjaxParams = [])
protected function RenderSelector(WebPage $oPage, DashboardLayoutUIBlock $oDashboard, $aAjaxParams = array())
{
if (!$this->HasCustomDashboard()) {
return;
@@ -1006,7 +1052,7 @@ EOF
$sSelectorHtml .= '</div>';
$sFile = addslashes($this->GetDefinitionFile());
$sReloadURL = json_encode($this->GetReloadURL());
$sReloadURL = $this->GetReloadURL();
$bFromDashboardPage = isset($aAjaxParams['from_dashboard_page']) ? isset($aAjaxParams['from_dashboard_page']) : false;
if ($bFromDashboardPage) {
@@ -1048,7 +1094,8 @@ JS
*/
protected function HasCustomDashboard()
{
try {
try
{
// Search for an eventual user defined dashboard
$oUDSearch = new DBObjectSearch('UserDashboard');
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
@@ -1056,7 +1103,9 @@ JS
$oUDSet = new DBObjectSet($oUDSearch);
return ($oUDSet->Count() > 0);
} catch (Exception $e) {
}
catch (Exception $e)
{
return false;
}
}
@@ -1092,23 +1141,22 @@ JS
->AddCSSClass('ibo-action-button');
$oToolbar->AddSubBlock($oActionButton);
$aActions = [];
$aActions = array();
$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)");
$aActions[$oEdit->GetUID()] = $oEdit->GetMenuItem();
$oRevert = new JSPopupMenuItem(
'UI:Dashboard:RevertConfirm',
Dict::S('UI:Dashboard:DeleteCustom'),
"if (confirm('".addslashes(Dict::S('UI:Dashboard:RevertConfirm'))."')) return RevertDashboard('{$this->sId}', $sJSExtraParams); else return false"
);
$oRevert = new JSPopupMenuItem('UI:Dashboard:RevertConfirm', Dict::S('UI:Dashboard:DeleteCustom'),
"if (confirm('".addslashes(Dict::S('UI:Dashboard:RevertConfirm'))."')) return RevertDashboard('{$this->sId}', $sJSExtraParams); else return false");
$aActions[$oRevert->GetUID()] = $oRevert->GetMenuItem();
} else {
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:CreateCustom'), "return EditDashboard('{$this->sId}', '$sFile', $sJSExtraParams)");
$aActions[$oEdit->GetUID()] = $oEdit->GetMenuItem();
}
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_DASHBOARD_ACTIONS, $this, $aActions);
$oActionsMenu = $oPage->GetPopoverMenu($sPopoverMenuId, $aActions)
@@ -1118,7 +1166,7 @@ JS
$oToolbar->AddSubBlock($oActionButton)
->AddSubBlock($oActionsMenu);
$sReloadURL = json_encode($this->GetReloadURL());
$sReloadURL = $this->GetReloadURL();
$oPage->add_script(
<<<EOF
function EditDashboard(sId, sDashboardFile, aExtraParams)
@@ -1148,12 +1196,12 @@ EOF
/**
* @inheritDoc
*/
public function RenderProperties($oPage, $aExtraParams = [])
public function RenderProperties($oPage, $aExtraParams = array())
{
parent::RenderProperties($oPage, $aExtraParams);
$oPage->add_ready_script(
<<<EOF
<<<EOF
$('#select_layout input').on('click', function() {
var sLayoutClass = $(this).val();
$('.itop-dashboard').runtimedashboard('option', {layout_class: sLayoutClass});
@@ -1180,6 +1228,7 @@ EOF
);
}
/**
* @param WebPage $oPage
*
@@ -1190,11 +1239,11 @@ EOF
* @throws \ReflectionException
* @throws \Exception
*/
public function RenderEditor($oPage, $aExtraParams = [])
public function RenderEditor($oPage, $aExtraParams = array())
{
if (isset($aExtraParams['this->class'])) {
$oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']);
$aRenderParams = ['query_params' => $oObj->ToArgsForQuery()];
$aRenderParams = array('query_params' => $oObj->ToArgsForQuery());
} else {
$aRenderParams = $aExtraParams;
}
@@ -1216,22 +1265,23 @@ EOF
$sDialogTitle = Dict::S('UI:DashboardEdit:Title');
$sOkButtonLabel = Dict::S('UI:Button:Save');
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$sId = json_encode($this->sId);
$sLayoutClass = json_encode($this->sLayoutClass);
$sId = utils::HtmlEntities($this->sId);
$sLayoutClass = utils::HtmlEntities($this->sLayoutClass);
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
$sAutoReloadSec = (string) $this->iAutoReloadSec;
$sTitle = json_encode($this->sTitle);
$sFile = json_encode($this->GetDefinitionFile());
$sTitle = utils::HtmlEntities($this->sTitle);
$sFile = utils::HtmlEntities($this->GetDefinitionFile());
$sFileForJS = json_encode($this->GetDefinitionFile());
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
$sReloadURL = json_encode($this->GetReloadURL());
$sReloadURL = $this->GetReloadURL();
$sExitConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
$sCancelConfirmationMessage = addslashes(Dict::S('UI:CancelConfirmationMessage'));
$sAutoApplyConfirmationMessage = addslashes(Dict::S('UI:AutoApplyConfirmationMessage'));
$oPage->add_ready_script(
<<<JS
<<<JS
window.bLeavingOnUserAction = false;
$('#dashboard_editor').dialog({
@@ -1278,15 +1328,15 @@ $('#dashboard_editor').dialog({
});
$('#dashboard_editor .ui-layout-center').runtimedashboard({
dashboard_id: $sId,
layout_class: $sLayoutClass,
title: $sTitle,
dashboard_id: '$sId',
layout_class: '$sLayoutClass',
title: '$sTitle',
auto_reload: $sAutoReload,
auto_reload_sec: $sAutoReloadSec,
submit_to: '$sUrl',
submit_parameters: {operation: 'save_dashboard', file: $sFile, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
submit_parameters: {operation: 'save_dashboard', file: {$sFileForJS}, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
render_to: '$sUrl',
render_parameters: {operation: 'render_dashboard', file: $sFile, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
render_parameters: {operation: 'render_dashboard', file: {$sFileForJS}, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
new_dashlet_parameters: {operation: 'new_dashlet'}
});
@@ -1339,89 +1389,104 @@ JS
$sContextMenuId = $oAppContext->GetCurrentValue('menu', null);
$oForm = new DesignerForm();
// Get the list of all 'dashboard' menus in which we can insert a dashlet
$aAllMenus = ApplicationMenu::ReflectionMenuNodes();
$sRootMenuId = ApplicationMenu::GetRootMenuId($sContextMenuId);
$aAllowedDashboards = [];
$aAllowedDashboards = array();
$sDefaultDashboard = null;
// Store the parent menus for acces check
$aParentMenus = [];
foreach ($aAllMenus as $idx => $aMenu) {
/** @var MenuNode $oMenu */
$oMenu = $aMenu['node'];
if (count(ApplicationMenu::GetChildren($oMenu->GetIndex())) > 0) {
$aParentMenus[$oMenu->GetMenuId()] = $aMenu;
}
}
$aParentMenus = array();
foreach($aAllMenus as $idx => $aMenu)
{
/** @var MenuNode $oMenu */
$oMenu = $aMenu['node'];
if (count(ApplicationMenu::GetChildren($oMenu->GetIndex())) > 0)
{
$aParentMenus[$oMenu->GetMenuId()] = $aMenu;
}
}
foreach ($aAllMenus as $idx => $aMenu) {
foreach($aAllMenus as $idx => $aMenu)
{
$oMenu = $aMenu['node'];
if ($oMenu instanceof DashboardMenuNode) {
// Get the root parent for access check
$sParentId = $aMenu['parent'];
$aParentMenu = $aParentMenus[$sParentId];
while (isset($aParentMenus[$aParentMenu['parent']])) {
// grand parent exists
$sParentId = $aParentMenu['parent'];
$aParentMenu = $aParentMenus[$sParentId];
}
/** @var \MenuNode $oParentMenu */
$oParentMenu = $aParentMenu['node'];
if ($oMenu->IsEnabled() && $oParentMenu->IsEnabled()) {
$sMenuLabel = $oMenu->GetTitle();
$sParentLabel = Dict::S('Menu:'.$sParentId);
if ($sParentLabel != $sMenuLabel) {
$aAllowedDashboards[$oMenu->GetMenuId()] = $sParentLabel.' - '.$sMenuLabel;
} else {
$aAllowedDashboards[$oMenu->GetMenuId()] = $sMenuLabel;
}
if (empty($sDefaultDashboard) && ($sRootMenuId == ApplicationMenu::GetRootMenuId($oMenu->GetMenuId()))) {
$sDefaultDashboard = $oMenu->GetMenuId();
}
}
}
if ($oMenu instanceof DashboardMenuNode)
{
// Get the root parent for access check
$sParentId = $aMenu['parent'];
$aParentMenu = $aParentMenus[$sParentId];
while (isset($aParentMenus[$aParentMenu['parent']]))
{
// grand parent exists
$sParentId = $aParentMenu['parent'];
$aParentMenu = $aParentMenus[$sParentId];
}
/** @var \MenuNode $oParentMenu */
$oParentMenu = $aParentMenu['node'];
if ($oMenu->IsEnabled() && $oParentMenu->IsEnabled())
{
$sMenuLabel = $oMenu->GetTitle();
$sParentLabel = Dict::S('Menu:'.$sParentId);
if ($sParentLabel != $sMenuLabel)
{
$aAllowedDashboards[$oMenu->GetMenuId()] = $sParentLabel.' - '.$sMenuLabel;
}
else
{
$aAllowedDashboards[$oMenu->GetMenuId()] = $sMenuLabel;
}
if (empty($sDefaultDashboard) && ($sRootMenuId == ApplicationMenu::GetRootMenuId($oMenu->GetMenuId())))
{
$sDefaultDashboard = $oMenu->GetMenuId();
}
}
}
}
asort($aAllowedDashboards);
$oField = new DesignerComboField('menu_id', Dict::S('UI:DashletCreation:Dashboard'), $sDefaultDashboard);
$oField->SetAllowedValues($aAllowedDashboards);
$oField->SetMandatory(true);
$oForm->AddField($oField);
// Get the list of possible dashlets that support a creation from
// an OQL
$aDashlets = [];
foreach (get_declared_classes() as $sDashletClass) {
if (is_subclass_of($sDashletClass, 'Dashlet')) {
$aDashlets = array();
foreach(get_declared_classes() as $sDashletClass)
{
if (is_subclass_of($sDashletClass, 'Dashlet'))
{
$oReflection = new ReflectionClass($sDashletClass);
if (!$oReflection->isAbstract()) {
$aCallSpec = [$sDashletClass, 'CanCreateFromOQL'];
if (!$oReflection->isAbstract())
{
$aCallSpec = array($sDashletClass, 'CanCreateFromOQL');
$bShorcutMode = call_user_func($aCallSpec);
if ($bShorcutMode) {
$aCallSpec = [$sDashletClass, 'GetInfo'];
if ($bShorcutMode)
{
$aCallSpec = array($sDashletClass, 'GetInfo');
$aInfo = call_user_func($aCallSpec);
$aDashlets[$sDashletClass] = ['label' => $aInfo['label'], 'class' => $sDashletClass, 'icon' => $aInfo['icon']];
$aDashlets[$sDashletClass] = array('label' => $aInfo['label'], 'class' => $sDashletClass, 'icon' => $aInfo['icon']);
}
}
}
}
$oSelectorField = new DesignerFormSelectorField('dashlet_class', Dict::S('UI:DashletCreation:DashletType'), '');
$oForm->AddField($oSelectorField);
foreach ($aDashlets as $sDashletClass => $aDashletInfo) {
foreach($aDashlets as $sDashletClass => $aDashletInfo)
{
$oSubForm = new DesignerForm();
$oMetaModel = new ModelReflectionRuntime();
/** @var \Dashlet $oDashlet */
$oDashlet = new $sDashletClass($oMetaModel, 0);
$oDashlet->GetPropertiesFieldsFromOQL($oSubForm, $sOQL);
$oSelectorField->AddSubForm($oSubForm, $aDashletInfo['label'], $aDashletInfo['class']);
}
$oField = new DesignerBooleanField('open_editor', Dict::S('UI:DashletCreation:EditNow'), true);
$oForm->AddField($oField);
return $oForm;
}
@@ -1440,11 +1505,11 @@ JS
$oForm->Render($oPage);
$oPage->add('</div>');
$sDialogTitle = Dict::S('UI:DashletCreation:Title');
$sOkButtonLabel = Dict::S('UI:Button:Ok');
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$oPage->add_ready_script(
<<<JS
$('#dashlet_creation_dlg').dialog({
@@ -1542,7 +1607,7 @@ JS
/**
* @inheritDoc
*/
protected function PrepareDashletForRendering(Dashlet $oDashlet, $aCoordinates, $aExtraParams = [])
protected function PrepareDashletForRendering(Dashlet $oDashlet, $aCoordinates, $aExtraParams = array())
{
$sDashletIdOrig = $oDashlet->GetID();
$sDashboardSanitizedId = $this->GetSanitizedId();
@@ -1569,27 +1634,31 @@ JS
private function UpdateDashletUserPrefs(Dashlet $oDashlet, $sDashletIdOrig, array $aExtraParams)
{
$bIsDashletWithListPref = ($oDashlet instanceof DashletObjectList);
if (!$bIsDashletWithListPref) {
if (!$bIsDashletWithListPref)
{
return;
}
/** @var \DashletObjectList $oDashlet */
$bDashletIdInNewFormat = ($sDashletIdOrig === $oDashlet->GetID());
if ($bDashletIdInNewFormat) {
if ($bDashletIdInNewFormat)
{
return;
}
$sNewPrefKey = $this->GetDashletObjectListAppUserPreferencesPrefix($oDashlet, $aExtraParams, $oDashlet->GetID());
$sPrefValueForNewKey = appUserPreferences::GetPref($sNewPrefKey, null);
$bHasPrefInNewFormat = ($sPrefValueForNewKey !== null);
if ($bHasPrefInNewFormat) {
if ($bHasPrefInNewFormat)
{
return;
}
$sOldPrefKey = $this->GetDashletObjectListAppUserPreferencesPrefix($oDashlet, $aExtraParams, $sDashletIdOrig);
$sPrefValueForOldKey = appUserPreferences::GetPref($sOldPrefKey, null);
$bHasPrefInOldFormat = ($sPrefValueForOldKey !== null);
if (!$bHasPrefInOldFormat) {
if (!$bHasPrefInOldFormat)
{
return;
}
@@ -1608,7 +1677,7 @@ JS
private function GetDashletObjectListAppUserPreferencesPrefix(DashletObjectList $oDashlet, $aExtraParams, $sDashletId)
{
$sDataTableId = Dashlet::APPUSERPREFERENCES_PREFIX.$sDashletId;
$aClassAliases = [];
$aClassAliases = array();
try {
$oFilter = $oDashlet->GetDBSearch($aExtraParams);
$aClassAliases = $oFilter->GetSelectedClasses();

View File

@@ -1,10 +1,9 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// 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.
@@ -39,21 +38,21 @@ abstract class DashboardLayout
* @since 2.7.0
*/
abstract public function GetDashletCoordinates($iCellIdx);
public static function GetInfo()
{
return [
return array(
'label' => '',
'icon' => '',
'description' => '',
];
);
}
}
abstract class DashboardLayoutMultiCol extends DashboardLayout
{
protected $iNbCols;
public function __construct()
{
$this->iNbCols = 1;
@@ -64,38 +63,47 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
$aKeys = array_reverse(array_keys($aDashlets));
$idx = 0;
$bNoVisibleFound = true;
while ($idx < count($aKeys) && $bNoVisibleFound) {
while($idx < count($aKeys) && $bNoVisibleFound)
{
/** @var \Dashlet $oDashlet */
$oDashlet = $aDashlets[$aKeys[$idx]];
if ($oDashlet::IsVisible()) {
if ($oDashlet::IsVisible())
{
$bNoVisibleFound = false;
} else {
}
else
{
unset($aDashlets[$aKeys[$idx]]);
}
$idx++;
}
return $aDashlets;
}
protected function TrimCellsArray($aCells)
{
foreach ($aCells as $key => $aDashlets) {
foreach($aCells as $key => $aDashlets)
{
$aCells[$key] = $this->TrimCell($aDashlets);
}
$aKeys = array_reverse(array_keys($aCells));
$idx = 0;
$bNoVisibleFound = true;
while ($idx < count($aKeys) && $bNoVisibleFound) {
while($idx < count($aKeys) && $bNoVisibleFound)
{
$aDashlets = $aCells[$aKeys[$idx]];
if (count($aDashlets) > 0) {
if (count($aDashlets) > 0)
{
$bNoVisibleFound = false;
} else {
}
else
{
unset($aCells[$aKeys[$idx]]);
}
$idx++;
}
return $aCells;
return $aCells;
}
/**
@@ -104,7 +112,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
* @param bool $bEditMode
* @param array $aExtraParams
*/
public function Render($oPage, $aCells, $bEditMode = false, $aExtraParams = [])
public function Render($oPage, $aCells, $bEditMode = false, $aExtraParams = array())
{
// Trim the list of cells to remove the invisible/empty ones at the end of the array
$aCells = $this->TrimCellsArray($aCells);
@@ -149,7 +157,8 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
$oPage->add_script("function updateDashboard".$aExtraParams['dashboard_div_id']."(){".$sJSReload."}");
if ($bEditMode) { // Add one row for extensibility
if ($bEditMode) // Add one row for extensibility
{
$oDashboardRow = new DashboardRow();
$oDashboardLayout->AddDashboardRow($oDashboardRow);
@@ -171,7 +180,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
$iColNumber = (int) $iCellIdx % $this->iNbCols;
$iRowNumber = (int) floor($iCellIdx / $this->iNbCols);
return [$iColNumber, $iRowNumber];
return array($iColNumber, $iRowNumber);
}
}
@@ -182,13 +191,13 @@ class DashboardLayoutOneCol extends DashboardLayoutMultiCol
parent::__construct();
$this->iNbCols = 1;
}
public static function GetInfo()
static public function GetInfo()
{
return [
return array(
'label' => 'One Column',
'icon' => 'images/layout_1col.png',
'description' => '',
];
);
}
}
@@ -199,13 +208,13 @@ class DashboardLayoutTwoCols extends DashboardLayoutMultiCol
parent::__construct();
$this->iNbCols = 2;
}
public static function GetInfo()
static public function GetInfo()
{
return [
return array(
'label' => 'Two Columns',
'icon' => 'images/layout_2col.png',
'description' => '',
];
);
}
}
@@ -216,12 +225,12 @@ class DashboardLayoutThreeCols extends DashboardLayoutMultiCol
parent::__construct();
$this->iNbCols = 3;
}
public static function GetInfo()
static public function GetInfo()
{
return [
return array(
'label' => 'Two Columns',
'icon' => 'images/layout_3col.png',
'description' => '',
];
);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.3">
<classes>
<class id="AbstractResource" _delta="define">
<parent>cmdbAbstractObject</parent>
@@ -45,7 +45,7 @@
<properties>
<comment>/** Acknowledge welcome popup messages */</comment>
<abstract>false</abstract>
<category></category>
<category/>
<key_type>autoincrement</key_type>
<db_table>priv_welcome_popup_acknowledge</db_table>
</properties>
@@ -238,10 +238,6 @@ The object can be modified.]]></description>
<description>Creation flag</description>
<type>boolean</type>
</event_datum>
<event_datum id="stimulus_applied">
<description>Life cycle stimulus applied (null if not within a transition)</description>
<type>string</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
@@ -267,10 +263,6 @@ Call $this->AddCheckWarning($sWarningMessage) to display a warning.
<description>Creation flag</description>
<type>boolean</type>
</event_datum>
<event_datum id="stimulus_applied">
<description>Life cycle stimulus applied (null if not within a transition)</description>
<type>string</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
@@ -298,10 +290,6 @@ The modifications can be propagated to other objects.]]></description>
<description><![CDATA[For updates, the list of changes done during this operation]]></description>
<type>array</type>
</event_datum>
<event_datum id="stimulus_applied">
<description>Life cycle stimulus applied (null if not within a transition)</description>
<type>string</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
@@ -432,14 +420,6 @@ The only action allowed is to deny transitions with $this->DenyTransition($sTran
<description>The object inserted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="is_new">
<description>Creation flag</description>
<type>boolean</type>
</event_datum>
<event_datum id="stimulus_applied">
<description>Life cycle stimulus applied (null if not within a transition)</description>
<type>string</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>

View File

@@ -80,31 +80,42 @@ class DataTable
// Identified tables can have their own specific settings
$oCustomSettings = DataTableSettings::GetTableSettings($this->aClassAliases, $this->sTableId);
if ($oCustomSettings != null) {
if ($oCustomSettings != null)
{
// Custom settings overload the default ones
$this->bUseCustomSettings = true;
if ($this->oDefaultSettings->iDefaultPageSize == 0) {
if ($this->oDefaultSettings->iDefaultPageSize == 0)
{
$oCustomSettings->iDefaultPageSize = 0;
}
} else {
}
else
{
$oCustomSettings = $oSettings;
}
if ($oCustomSettings->iDefaultPageSize > 0) {
if ($oCustomSettings->iDefaultPageSize > 0)
{
$this->oSet->SetLimit($oCustomSettings->iDefaultPageSize);
}
$this->oSet->SetOrderBy($oCustomSettings->GetSortOrder());
// Load only the requested columns
$aColumnsToLoad = [];
foreach ($oCustomSettings->aColumns as $sAlias => $aColumnsInfo) {
foreach ($aColumnsInfo as $sAttCode => $aData) {
if ($sAttCode != '_key_') {
if ($aData['checked']) {
$aColumnsToLoad = array();
foreach($oCustomSettings->aColumns as $sAlias => $aColumnsInfo)
{
foreach($aColumnsInfo as $sAttCode => $aData)
{
if ($sAttCode != '_key_')
{
if ($aData['checked'])
{
$aColumnsToLoad[$sAlias][] = $sAttCode;
} else {
// See if this column is a must to load
}
else
{
// See if this column is a must to load
$sClass = $this->aClassAliases[$sAlias];
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef->AlwaysLoadInTables()) {
@@ -116,15 +127,18 @@ class DataTable
}
$this->oSet->OptimizeColumnLoad($aColumnsToLoad);
$bToolkitMenu = true;
if (isset($aExtraParams['toolkit_menu'])) {
if (isset($aExtraParams['toolkit_menu']))
{
$bToolkitMenu = (bool) $aExtraParams['toolkit_menu'];
}
if (UserRights::IsPortalUser()) {
if (UserRights::IsPortalUser())
{
// Portal users have a limited access to data, for now they can only see what's configured for them
$bToolkitMenu = false;
}
return $this->GetAsHTML($oPage, $oCustomSettings->iDefaultPageSize, $oCustomSettings->iDefaultPageSize, 0, $oCustomSettings->aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams);
}
@@ -153,10 +167,10 @@ class DataTable
if ($bActionsMenu) {
$sActionsMenu = $this->GetActionsMenu($oPage, $aExtraParams);
}
// if ($bToolkitMenu)
// {
// $sToolkitMenu = $this->GetToolkitMenu($oPage, $aExtraParams);
// }
// if ($bToolkitMenu)
// {
// $sToolkitMenu = $this->GetToolkitMenu($oPage, $aExtraParams);
// }
$sDataTable = $this->GetHTMLTable($oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
$sConfigDlg = $this->GetTableConfigDlg($oPage, $aColumns, $bViewLink, $iDefaultPageSize);
@@ -174,7 +188,7 @@ class DataTable
$aExtraParams['show_obsolete_data'] = $this->bShowObsoleteData;
$aOptions = [
$aOptions = array(
'sPersistentId' => '',
'sFilter' => $this->oSet->GetFilter()->serialize(),
'oColumns' => $aColumns,
@@ -188,11 +202,12 @@ class DataTable
'sTableId' => $this->sTableId,
'oExtraParams' => $aExtraParams,
'sRenderUrl' => utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php',
'oRenderParameters' => ['str' => ''], // Forces JSON to encode this as a object...
'oDefaultSettings' => ['str' => ''], // Forces JSON to encode this as a object...
'oLabels' => ['moveup' => Dict::S('UI:Button:MoveUp'), 'movedown' => Dict::S('UI:Button:MoveDown')],
];
if ($this->oDefaultSettings != null) {
'oRenderParameters' => array('str' => ''), // Forces JSON to encode this as a object...
'oDefaultSettings' => array('str' => ''), // Forces JSON to encode this as a object...
'oLabels' => array('moveup' => Dict::S('UI:Button:MoveUp'), 'movedown' => Dict::S('UI:Button:MoveDown')),
);
if($this->oDefaultSettings != null)
{
$aOptions['oDefaultSettings'] = $this->GetAsHash($this->oDefaultSettings);
}
$sJSOptions = json_encode($aOptions);
@@ -207,14 +222,16 @@ class DataTable
*/
public function GetAsHTMLTableRows(WebPage $oPage, $iPageSize, $aColumns, $sSelectMode, $bViewLink, $aExtraParams)
{
if ($iPageSize < 1) {
if ($iPageSize < 1)
{
$iPageSize = -1; // convention: no pagination
}
$aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
$aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
$sHtml = '';
foreach ($aValues as $aRow) {
foreach($aValues as $aRow)
{
$sHtml .= $oPage->GetTableRow($aRow, $aAttribs);
}
return $sHtml;
@@ -228,12 +245,15 @@ class DataTable
*/
protected function GetObjectCount(WebPage $oPage, $sSelectMode)
{
if (($sSelectMode == 'single') || ($sSelectMode == 'multiple')) {
if (($sSelectMode == 'single') || ($sSelectMode == 'multiple'))
{
$sHtml = '<div class="pagination_objcount">'.Dict::Format('UI:Pagination:HeaderSelection', '<span id="total">'.$this->iNbObjects.'</span>', '<span class="selectedCount">0</span>').'</div>';
} else {
}
else
{
$sHtml = '<div class="pagination_objcount">'.Dict::Format('UI:Pagination:HeaderNoSelection', '<span id="total">'.$this->iNbObjects.'</span>').'</div>';
}
return $sHtml;
return $sHtml;
}
/**
@@ -247,19 +267,26 @@ class DataTable
protected function GetPager(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex)
{
$sHtml = '';
if ($iPageSize < 1) { // Display all
if ($iPageSize < 1) // Display all
{
$sPagerStyle = 'style="display:none"'; // no limit: display the full table, so hide the "pager" UI
// WARNING: mPDF does not take the "display" style into account
// when applied to a <td> or a <table> tag, so make sure you apply this to a div
} else {
// WARNING: mPDF does not take the "display" style into account
// when applied to a <td> or a <table> tag, so make sure you apply this to a div
}
else
{
$sPagerStyle = '';
}
$sCombo = '<select class="pagesize">';
if ($iPageSize < 1) {
if($iPageSize < 1)
{
$sCombo .= "<option selected=\"selected\" value=\"-1\">".Dict::S('UI:Pagination:All')."</option>";
} else {
for ($iPage = 1; $iPage < 5; $iPage++) {
}
else
{
for($iPage = 1; $iPage < 5; $iPage++)
{
$iNbItems = $iPage * $iDefaultPageSize;
$sSelected = ($iNbItems == $iPageSize) ? 'selected="selected"' : '';
$sCombo .= "<option $sSelected value=\"$iNbItems\">$iNbItems</option>";
@@ -268,25 +295,31 @@ class DataTable
}
$sCombo .= '</select>';
$sPages = Dict::S('UI:Pagination:PagesLabel');
$sPageSizeCombo = Dict::Format('UI:Pagination:PageSize', $sCombo);
$iNbPages = ($iPageSize < 1) ? 1 : ceil($this->iNbObjects / $iPageSize);
if ($iNbPages == 1) {
if ($iNbPages == 1)
{
// No need to display the pager
$sPagerStyle = 'style="display:none"';
}
$aPagesToDisplay = [];
for ($idx = 0; $idx <= min(4, $iNbPages - 1); $idx++) {
if ($idx == 0) {
$aPagesToDisplay = array();
for($idx = 0; $idx <= min(4, $iNbPages-1); $idx++)
{
if ($idx == 0)
{
$aPagesToDisplay[$idx] = '<span page="0" class="curr_page">1</span>';
} else {
$aPagesToDisplay[$idx] = "<span id=\"gotopage_$idx\" class=\"gotopage\" page=\"$idx\">".(1 + $idx)."</span>";
}
else
{
$aPagesToDisplay[$idx] = "<span id=\"gotopage_$idx\" class=\"gotopage\" page=\"$idx\">".(1+$idx)."</span>";
}
}
$iLastPageIdx = $iNbPages - 1;
if (!isset($aPagesToDisplay[$iLastPageIdx])) {
if (!isset($aPagesToDisplay[$iLastPageIdx]))
{
unset($aPagesToDisplay[$idx - 1]); // remove the last page added to make room for the very last page
$aPagesToDisplay[$iLastPageIdx] = "<span id=\"gotopage_$iLastPageIdx\" class=\"gotopage\" page=\"$iLastPageIdx\">... $iNbPages</span>";
}
@@ -353,19 +386,22 @@ EOF;
*/
protected function GetToolkitMenu(WebPage $oPage, $aExtraParams)
{
if (!$oPage->IsPrintableVersion()) {
if (!$oPage->IsPrintableVersion())
{
$sMenuTitle = Dict::S('UI:ConfigureThisList');
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li aria-label="'.Dict::S('UI:Menu:Toolkit').'"><i class="fas fa-tools"></i><i class="fas fa-caret-down"></i><ul>';
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
$aActions = [
$aActions = array(
$oMenuItem1->GetUID() => $oMenuItem1->GetMenuItem(),
];
);
$this->oSet->Rewind();
utils::GetPopupMenuItems($oPage, iPopupMenuExtension::MENU_OBJLIST_TOOLKIT, $this->oSet, $aActions, $this->sTableId, $this->iListId);
$this->oSet->Rewind();
$sHtml .= $oPage->RenderPopupMenuItems($aActions);
} else {
}
else
{
$sHtml = '';
}
return $sHtml;
@@ -386,10 +422,10 @@ EOF;
$sChecked = ($this->bUseCustomSettings) ? '' : 'checked';
$sHtml .= "<p><input id=\"dtbl_dlg_settings_{$this->iListId}\" type=\"radio\" name=\"settings\" $sChecked value=\"defaults\"><label for=\"dtbl_dlg_settings_{$this->iListId}\">&nbsp;".Dict::S('UI:UseDefaultSettings').'</label></p>';
$sHtml .= "<fieldset>";
$sChecked = ($this->bUseCustomSettings) ? 'checked' : '';
$sChecked = ($this->bUseCustomSettings) ? 'checked': '';
$sHtml .= "<legend class=\"transparent\"><input id=\"dtbl_dlg_specific_{$this->iListId}\" type=\"radio\" class=\"specific_settings\" name=\"settings\" $sChecked value=\"specific\"><label for=\"dtbl_dlg_specific_{$this->iListId}\">&nbsp;".Dict::S('UI:UseSpecificSettings')."</label></legend>";
$sHtml .= Dict::S('UI:ColumnsAndSortOrder').'<br/><ul class="sortable_field_list" id="sfl_'.$this->iListId.'"></ul>';
$sHtml .= '<p>'.Dict::Format('UI:Display_X_ItemsPerPage', '<input type="text" size="4" name="page_size" value="'.$iDefaultPageSize.'">').'</p>';
$sHtml .= "</fieldset>";
$sHtml .= "<fieldset>";
@@ -408,7 +444,7 @@ EOF;
$sHtml .= '</td></tr></table>';
$sHtml .= "</form>";
$sHtml .= "</div>";
$sDlgTitle = addslashes(Dict::S('UI:ListConfigurationTitle'));
$oPage->add_ready_script("$('#datatable_dlg_{$this->iListId}').dialog({autoOpen: false, title: '$sDlgTitle', width: 500, close: function() { $('#{$this->sDatatableContainerId}').datatable('onDlgCancel'); } });");
@@ -422,7 +458,7 @@ EOF;
*/
public function GetAsHash($oSetting)
{
$aSettings = ['iDefaultPageSize' => $oSetting->iDefaultPageSize, 'oColumns' => $oSetting->aColumns];
$aSettings = array('iDefaultPageSize' => $oSetting->iDefaultPageSize, 'oColumns' => $oSetting->aColumns);
return $aSettings;
}
@@ -438,46 +474,55 @@ EOF;
*/
protected function GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink)
{
$aAttribs = [];
if ($sSelectMode == 'multiple') {
$aAttribs['form::select'] = [
$aAttribs = array();
if ($sSelectMode == 'multiple')
{
$aAttribs['form::select'] = array(
'label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->iListId}:not(:disabled)', this.checked);\" class=\"checkAll\"></input>",
'description' => Dict::S('UI:SelectAllToggle+'),
'metadata' => [],
];
} elseif ($sSelectMode == 'single') {
$aAttribs['form::select'] = ['label' => '', 'description' => '', 'metadata' => []];
'metadata' => array(),
);
}
else if ($sSelectMode == 'single')
{
$aAttribs['form::select'] = array('label' => '', 'description' => '', 'metadata' => array());
}
foreach ($this->aClassAliases as $sAlias => $sClassName) {
foreach ($aColumns[$sAlias] as $sAttCode => $aData) {
if ($aData['checked']) {
if ($sAttCode == '_key_') {
foreach($this->aClassAliases as $sAlias => $sClassName)
{
foreach($aColumns[$sAlias] as $sAttCode => $aData)
{
if ($aData['checked'])
{
if ($sAttCode == '_key_')
{
$sAttLabel = MetaModel::GetName($sClassName);
$aAttribs['key_'.$sAlias] = [
$aAttribs['key_'.$sAlias] = array(
'label' => $sAttLabel,
'description' => '',
'metadata' => [
'metadata' => array(
'object_class' => $sClassName,
'attribute_label' => $sAttLabel,
],
];
} else {
),
);
}
else
{
$oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode);
$sAttDefClass = get_class($oAttDef);
$sAttLabel = MetaModel::GetLabel($sClassName, $sAttCode);
$aAttribs[$sAttCode.'_'.$sAlias] = [
$aAttribs[$sAttCode.'_'.$sAlias] = array(
'label' => $sAttLabel,
'description' => $oAttDef->GetOrderByHint(),
'metadata' => [
'metadata' => array(
'object_class' => $sClassName,
'attribute_code' => $sAttCode,
'attribute_type' => $sAttDefClass,
'attribute_label' => $sAttLabel,
],
];
),
);
}
}
}
@@ -485,6 +530,7 @@ EOF;
return $aAttribs;
}
/**
* @param $aColumns
* @param $sSelectMode
@@ -503,74 +549,104 @@ EOF;
protected function GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
{
$bLocalize = true;
if (isset($aExtraParams['localize_values'])) {
if (isset($aExtraParams['localize_values']))
{
$bLocalize = (bool) $aExtraParams['localize_values'];
}
$aValues = [];
$aAttDefsCache = [];
$aValues = array();
$aAttDefsCache = array();
$this->oSet->Seek(0);
$iMaxObjects = $iPageSize;
while (($aObjects = $this->oSet->FetchAssoc()) && ($iMaxObjects != 0)) {
while (($aObjects = $this->oSet->FetchAssoc()) && ($iMaxObjects != 0))
{
$bFirstObject = true;
$aRow = [];
foreach ($this->aClassAliases as $sAlias => $sClassName) {
if (is_object($aObjects[$sAlias])) {
$aRow = array();
foreach($this->aClassAliases as $sAlias => $sClassName)
{
if (is_object($aObjects[$sAlias]))
{
$sHilightClass = MetaModel::GetHilightClass($sClassName, $aObjects[$sAlias]);
if ($sHilightClass != '') {
$aRow['@class'] = $sHilightClass;
if ($sHilightClass != '')
{
$aRow['@class'] = $sHilightClass;
}
if ((($sSelectMode == 'single') || ($sSelectMode == 'multiple')) && $bFirstObject) {
if (array_key_exists('selection_enabled', $aExtraParams) && isset($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()])) {
if ((($sSelectMode == 'single') || ($sSelectMode == 'multiple')) && $bFirstObject)
{
if (array_key_exists('selection_enabled', $aExtraParams) && isset($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]))
{
$sDisabled = ($aExtraParams['selection_enabled'][$aObjects[$sAlias]->GetKey()]) ? '' : ' disabled="disabled"';
} else {
}
else
{
$sDisabled = '';
}
if ($sSelectMode == 'single') {
if ($sSelectMode == 'single')
{
$aRow['form::select'] = "<input type=\"radio\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
} else {
}
else
{
$aRow['form::select'] = "<input type=\"checkbox\" $sDisabled class=\"selectList{$this->iListId}\" name=\"selectObject[]\" value=\"".$aObjects[$sAlias]->GetKey()."\"></input>";
}
}
foreach ($aColumns[$sAlias] as $sAttCode => $aData) {
if ($aData['checked']) {
if ($sAttCode == '_key_') {
$aRow['key_'.$sAlias] = [
foreach($aColumns[$sAlias] as $sAttCode => $aData)
{
if ($aData['checked'])
{
if ($sAttCode == '_key_')
{
$aRow['key_'.$sAlias] = array(
'value_raw' => $aObjects[$sAlias]->GetKey(),
'value_html' => $aObjects[$sAlias]->GetHyperLink(),
];
} else {
);
}
else
{
// Prepare att. def. classes cache to avoid retrieving AttDef for each row
if (!isset($aAttDefsCache[$sClassName][$sAttCode])) {
if(!isset($aAttDefsCache[$sClassName][$sAttCode]))
{
$aAttDefClassesCache[$sClassName][$sAttCode] = get_class(MetaModel::GetAttributeDef($sClassName, $sAttCode));
}
// Only retrieve raw (stored) value for simple fields
$bExcludeRawValue = false;
foreach (cmdbAbstractObject::GetAttDefClassesToExcludeFromMarkupMetadataRawValue() as $sAttDefClassToExclude) {
if (is_a($aAttDefClassesCache[$sClassName][$sAttCode], $sAttDefClassToExclude, true)) {
foreach (cmdbAbstractObject::GetAttDefClassesToExcludeFromMarkupMetadataRawValue() as $sAttDefClassToExclude)
{
if (is_a($aAttDefClassesCache[$sClassName][$sAttCode], $sAttDefClassToExclude, true))
{
$bExcludeRawValue = true;
break;
}
}
if ($bExcludeRawValue) {
if($bExcludeRawValue)
{
$aRow[$sAttCode.'_'.$sAlias] = $aObjects[$sAlias]->GetAsHTML($sAttCode, $bLocalize);
} else {
$aRow[$sAttCode.'_'.$sAlias] = [
}
else
{
$aRow[$sAttCode.'_'.$sAlias] = array(
'value_raw' => $aObjects[$sAlias]->Get($sAttCode),
'value_html' => $aObjects[$sAlias]->GetAsHTML($sAttCode, $bLocalize),
];
);
}
}
}
}
} else {
foreach ($aColumns[$sAlias] as $sAttCode => $aData) {
if ($aData['checked']) {
if ($sAttCode == '_key_') {
}
else
{
foreach($aColumns[$sAlias] as $sAttCode => $aData)
{
if ($aData['checked'])
{
if ($sAttCode == '_key_')
{
$aRow['key_'.$sAlias] = '';
} else {
}
else
{
$aRow[$sAttCode.'_'.$sAlias] = '';
}
}
@@ -605,7 +681,8 @@ EOF;
public function GetHTMLTable(WebPage $oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
{
$iNbPages = ($iPageSize < 1) ? 1 : ceil($this->iNbObjects / $iPageSize);
if ($iPageSize < 1) {
if ($iPageSize < 1)
{
$iPageSize = -1; // convention: no pagination
}
$aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
@@ -614,7 +691,8 @@ EOF;
$sHtml = '<table class="listContainer object-list">';
foreach ($this->oSet->GetFilter()->GetInternalParams() as $sName => $sValue) {
foreach($this->oSet->GetFilter()->GetInternalParams() as $sName => $sValue)
{
$aExtraParams['query_params'][$sName] = $sValue;
}
$aExtraParams['show_obsolete_data'] = $this->bShowObsoleteData;
@@ -629,16 +707,20 @@ EOF;
$sExtraParams = addslashes(str_replace('"', "'", json_encode(array_merge($aExtraParams, $aArgs)))); // JSON encode, change the style of the quotes and escape them
$sSelectModeJS = '';
$sHeaders = '';
if (($sSelectMode == 'single') || ($sSelectMode == 'multiple')) {
if (($sSelectMode == 'single') || ($sSelectMode == 'multiple'))
{
$sSelectModeJS = $sSelectMode;
$sHeaders = 'headers: { 0: {sorter: false}},';
}
$sDisplayKey = ($bViewLink) ? 'true' : 'false';
// Protect against duplicate elements in the Zlist
$aUniqueOrderedList = [];
foreach ($this->aClassAliases as $sAlias => $sClassName) {
foreach ($aColumns[$sAlias] as $sAttCode => $aData) {
if ($aData['checked']) {
$aUniqueOrderedList = array();
foreach($this->aClassAliases as $sAlias => $sClassName)
{
foreach($aColumns[$sAlias] as $sAttCode => $aData)
{
if ($aData['checked'])
{
$aUniqueOrderedList[$sAttCode] = true;
}
}
@@ -650,32 +732,41 @@ EOF;
$this->oSet->ApplyParameters();
// Display the actual sort order of the table
$aRealSortOrder = $this->oSet->GetRealSortOrder();
$aDefaultSort = [];
$aDefaultSort = array();
$iColOffset = 0;
if (($sSelectMode == 'single') || ($sSelectMode == 'multiple')) {
if (($sSelectMode == 'single') || ($sSelectMode == 'multiple'))
{
$iColOffset += 1;
}
if ($bViewLink) {
// $iColOffset += 1;
if ($bViewLink)
{
// $iColOffset += 1;
}
foreach ($aRealSortOrder as $sColCode => $bAscending) {
foreach($aRealSortOrder as $sColCode => $bAscending)
{
$iPos = array_search($sColCode, $aUniqueOrderedList);
if ($iPos !== false) {
$aDefaultSort[] = "[".($iColOffset + $iPos).",".($bAscending ? '0' : '1')."]";
} elseif (($iPos = array_search(preg_replace('/_friendlyname$/', '', $sColCode), $aUniqueOrderedList)) !== false) {
if ($iPos !== false)
{
$aDefaultSort[] = "[".($iColOffset+$iPos).",".($bAscending ? '0' : '1')."]";
}
else if (($iPos = array_search(preg_replace('/_friendlyname$/', '', $sColCode), $aUniqueOrderedList)) !== false)
{
// if sorted on the friendly name of an external key, then consider it sorted on the column that shows the links
$aDefaultSort[] = "[".($iColOffset + $iPos).",".($bAscending ? '0' : '1')."]";
} elseif ($sColCode == 'friendlyname' && $bViewLink) {
$aDefaultSort[] = "[".($iColOffset+$iPos).",".($bAscending ? '0' : '1')."]";
}
else if($sColCode == 'friendlyname' && $bViewLink)
{
$aDefaultSort[] = "[".($iColOffset).",".($bAscending ? '0' : '1')."]";
}
}
$sFakeSortList = '';
if (count($aDefaultSort) > 0) {
if (count($aDefaultSort) > 0)
{
$sFakeSortList = '['.implode(',', $aDefaultSort).']';
}
$sOQL = addslashes($this->oSet->GetFilter()->serialize());
$oPage->add_ready_script(
<<<JS
<<<JS
var oTable = $('#{$this->sDatatableContainerId} table.listResults');
oTable.tableHover();
oTable
@@ -693,8 +784,9 @@ oTable
class_aliases: $sJSClassAliases $sCssCount
});
JS
);
if ($sFakeSortList != '') {
);
if ($sFakeSortList != '')
{
$oPage->add_ready_script("oTable.trigger(\"fakesorton\", [$sFakeSortList]);");
}
return $sHtml;
@@ -711,9 +803,12 @@ JS
$iPageIndex = 0;
$sHtml = $this->GetPager($oPage, $iPageSize, $iDefaultPageSize, $iPageIndex);
$oPage->add_ready_script("$('#pager{$this->iListId}').html('".json_encode($sHtml)."');");
if ($iDefaultPageSize < 1) {
if ($iDefaultPageSize < 1)
{
$oPage->add_ready_script("$('#pager{$this->iListId}').parent().hide()");
} else {
}
else
{
$oPage->add_ready_script("$('#pager{$this->iListId}').parent().show()");
}
}
@@ -770,15 +865,16 @@ class PrintableDataTable extends DataTable
public function GetHTMLTable(WebPage $oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
{
$iNbPages = ($iPageSize < 1) ? 1 : ceil($this->iNbObjects / $iPageSize);
if ($iPageSize < 1) {
if ($iPageSize < 1)
{
$iPageSize = -1; // convention: no pagination
}
$aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
$aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
$sHtml = $oPage->GetTable($aAttribs, $aValues);
return $sHtml;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,8 @@
<?php
/**
* @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-2024 Combodo SAS
*/
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/ErrorPage.php, now loadable using autoloader');
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/ErrorPage.php, now loadable using autoloader');

View File

@@ -19,67 +19,73 @@ class ExcelExporter
protected $iPosition;
protected $sOutputFilePath;
protected $bAdvancedMode;
public function __construct($sToken = null)
{
$this->aStatistics = [
$this->aStatistics = array(
'objects_count' => 0,
'total_duration' => 0,
'data_retrieval_duration' => 0,
'excel_build_duration' => 0,
'excel_write_duration' => 0,
'peak_memory_usage' => 0,
];
'peak_memory_usage' => 0,
);
$this->fStartTime = microtime(true);
$this->oSearch = null;
$this->sState = 'new';
$this->aObjectsIDs = [];
$this->aObjectsIDs = array();
$this->iPosition = 0;
$this->aAuthorizedClasses = null;
$this->aTableHeaders = null;
$this->sOutputFilePath = null;
$this->bAdvancedMode = false;
$this->CheckDataDir();
if ($sToken == null) {
if ($sToken == null)
{
$this->sToken = $this->GetNewToken();
} else {
}
else
{
$this->sToken = $sToken;
$this->ReloadState();
}
}
public function __destruct()
{
if (($this->sState != 'done') && ($this->sState != 'error') && ($this->sToken != null)) {
if (($this->sState != 'done') && ($this->sState != 'error') && ($this->sToken != null))
{
// Operation in progress, save the state
$this->SaveState();
} else {
}
else
{
// Operation completed, cleanup the temp files
@unlink($this->GetStateFile());
@unlink($this->GetDataFile());
}
self::CleanupOldFiles();
self::CleanupOldFiles();
}
public function SetChunkSize($iChunkSize)
{
$this->iChunkSize = $iChunkSize;
$this->iChunkSize = $iChunkSize;
}
public function SetOutputFilePath($sDestFilePath)
{
$this->sOutputFilePath = $sDestFilePath;
}
public function SetAdvancedMode($bAdvanced)
{
$this->bAdvancedMode = $bAdvanced;
}
public function SaveState()
{
$aState = [
$aState = array(
'state' => $this->sState,
'statistics' => $this->aStatistics,
'filter' => $this->oSearch->serialize(),
@@ -88,28 +94,31 @@ class ExcelExporter
'object_ids' => $this->aObjectsIDs,
'output_file_path' => $this->sOutputFilePath,
'advanced_mode' => $this->bAdvancedMode,
];
);
file_put_contents($this->GetStateFile(), json_encode($aState));
return $this->sToken;
}
public function ReloadState()
{
if ($this->sToken == null) {
if ($this->sToken == null)
{
throw new Exception('ExcelExporter not initialized with a token, cannot reload state');
}
if (!file_exists($this->GetStateFile())) {
if (!file_exists($this->GetStateFile()))
{
throw new Exception("ExcelExporter: missing status file '".$this->GetStateFile()."', cannot reload state.");
}
$sJson = file_get_contents($this->GetStateFile());
$aState = json_decode($sJson, true);
if ($aState === null) {
if ($aState === null)
{
throw new Exception("ExcelExporter:corrupted status file '".$this->GetStateFile()."', not a JSON, cannot reload state.");
}
$this->sState = $aState['state'];
$this->aStatistics = $aState['statistics'];
$this->oSearch = DBObjectSearch::unserialize($aState['filter']);
@@ -119,197 +128,223 @@ class ExcelExporter
$this->sOutputFilePath = $aState['output_file_path'];
$this->bAdvancedMode = $aState['advanced_mode'];
}
public function SetObjectList($oSearch)
{
$this->oSearch = $oSearch;
}
public function Run()
{
$sCode = 'error';
$iPercentage = 100;
$sMessage = Dict::Format('ExcelExporter:ErrorUnexpected_State', $this->sState);
$fTime = microtime(true);
try {
switch ($this->sState) {
try
{
switch($this->sState)
{
case 'new':
$oIDSet = new DBObjectSet($this->oSearch);
$oIDSet->OptimizeColumnLoad(['id']);
$this->aObjectsIDs = [];
while ($oObj = $oIDSet->Fetch()) {
$this->aObjectsIDs[] = $oObj->GetKey();
}
$sCode = 'retrieving-data';
$iPercentage = 5;
$sMessage = Dict::S('ExcelExporter:RetrievingData');
$this->iPosition = 0;
$this->aStatistics['objects_count'] = count($this->aObjectsIDs);
$this->aStatistics['data_retrieval_duration'] += microtime(true) - $fTime;
// The first line of the file is the "headers" specifying the label and the type of each column
$this->GetFieldsList($oIDSet, $this->bAdvancedMode);
$sRow = json_encode($this->aTableHeaders);
$hFile = @fopen($this->GetDataFile(), 'ab');
if ($hFile === false) {
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for writing.');
}
fwrite($hFile, $sRow."\n");
fclose($hFile);
// Next state
$this->sState = 'retrieving-data';
break;
$oIDSet = new DBObjectSet($this->oSearch);
$oIDSet->OptimizeColumnLoad(array('id'));
$this->aObjectsIDs = array();
while($oObj = $oIDSet->Fetch())
{
$this->aObjectsIDs[] = $oObj->GetKey();
}
$sCode = 'retrieving-data';
$iPercentage = 5;
$sMessage = Dict::S('ExcelExporter:RetrievingData');
$this->iPosition = 0;
$this->aStatistics['objects_count'] = count($this->aObjectsIDs);
$this->aStatistics['data_retrieval_duration'] += microtime(true) - $fTime;
// The first line of the file is the "headers" specifying the label and the type of each column
$this->GetFieldsList($oIDSet, $this->bAdvancedMode);
$sRow = json_encode($this->aTableHeaders);
$hFile = @fopen($this->GetDataFile(), 'ab');
if ($hFile === false)
{
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for writing.');
}
fwrite($hFile, $sRow."\n");
fclose($hFile);
// Next state
$this->sState = 'retrieving-data';
break;
case 'retrieving-data':
$oCurrentSearch = clone $this->oSearch;
$aIDs = array_slice($this->aObjectsIDs, $this->iPosition, $this->iChunkSize);
$oCurrentSearch->AddCondition('id', $aIDs, 'IN');
$hFile = @fopen($this->GetDataFile(), 'ab');
if ($hFile === false) {
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for writing.');
}
$oSet = new DBObjectSet($oCurrentSearch);
$this->GetFieldsList($oSet, $this->bAdvancedMode);
while ($aObjects = $oSet->FetchAssoc()) {
$aRow = [];
foreach ($this->aAuthorizedClasses as $sAlias => $sClassName) {
$oObj = $aObjects[$sAlias];
if ($this->bAdvancedMode) {
$aRow[] = $oObj->GetKey();
}
foreach ($this->aFieldsList[$sAlias] as $sAttCodeEx => $oAttDef) {
$value = $oObj->Get($sAttCodeEx);
if ($value instanceof ormCaseLog) {
// Extract the case log as text and remove the "===" which make Excel think that the cell contains a formula the next time you edit it!
$sExcelVal = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $value->GetText()));
} else {
$sExcelVal = $oAttDef->GetEditValue($value, $oObj);
}
$aRow[] = $sExcelVal;
}
$oCurrentSearch = clone $this->oSearch;
$aIDs = array_slice($this->aObjectsIDs, $this->iPosition, $this->iChunkSize);
$oCurrentSearch->AddCondition('id', $aIDs, 'IN');
$hFile = @fopen($this->GetDataFile(), 'ab');
if ($hFile === false)
{
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for writing.');
}
$oSet = new DBObjectSet($oCurrentSearch);
$this->GetFieldsList($oSet, $this->bAdvancedMode);
while($aObjects = $oSet->FetchAssoc())
{
$aRow = array();
foreach($this->aAuthorizedClasses as $sAlias => $sClassName)
{
$oObj = $aObjects[$sAlias];
if ($this->bAdvancedMode)
{
$aRow[] = $oObj->GetKey();
}
foreach($this->aFieldsList[$sAlias] as $sAttCodeEx => $oAttDef)
{
$value = $oObj->Get($sAttCodeEx);
if ($value instanceOf ormCaseLog)
{
// Extract the case log as text and remove the "===" which make Excel think that the cell contains a formula the next time you edit it!
$sExcelVal = trim(preg_replace('/========== ([^=]+) ============/', '********** $1 ************', $value->GetText()));
}
else
{
$sExcelVal = $oAttDef->GetEditValue($value, $oObj);
}
$aRow[] = $sExcelVal;
}
$sRow = json_encode($aRow);
fwrite($hFile, $sRow."\n");
}
fclose($hFile);
if (($this->iPosition + $this->iChunkSize) > count($this->aObjectsIDs)) {
// Next state
$this->sState = 'building-excel';
$sCode = 'building-excel';
$iPercentage = 80;
$sMessage = Dict::S('ExcelExporter:BuildingExcelFile');
} else {
$sCode = 'retrieving-data';
$this->iPosition += $this->iChunkSize;
$iPercentage = 5 + round(75 * ($this->iPosition / count($this->aObjectsIDs)));
$sMessage = Dict::S('ExcelExporter:RetrievingData');
}
break;
case 'building-excel':
$hFile = @fopen($this->GetDataFile(), 'rb');
if ($hFile === false) {
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for reading.');
}
$sHeaders = fgets($hFile);
$aHeaders = json_decode($sHeaders, true);
$aData = [];
while ($sLine = fgets($hFile)) {
$aRow = json_decode($sLine);
$aData[] = $aRow;
}
fclose($hFile);
@unlink($this->GetDataFile());
$fStartExcel = microtime(true);
$writer = new XLSXWriter();
$writer->setAuthor(UserRights::GetUserFriendlyName());
$writer->writeSheet($aData, 'Sheet1', $aHeaders);
$fExcelTime = microtime(true) - $fStartExcel;
$this->aStatistics['excel_build_duration'] = $fExcelTime;
$fTime = microtime(true);
$writer->writeToFile($this->GetExcelFilePath());
$fExcelSaveTime = microtime(true) - $fTime;
$this->aStatistics['excel_write_duration'] = $fExcelSaveTime;
$sRow = json_encode($aRow);
fwrite($hFile, $sRow."\n");
}
fclose($hFile);
if (($this->iPosition + $this->iChunkSize) > count($this->aObjectsIDs))
{
// Next state
$this->sState = 'done';
$sCode = 'done';
$iPercentage = 100;
$sMessage = Dict::S('ExcelExporter:Done');
break;
$this->sState = 'building-excel';
$sCode = 'building-excel';
$iPercentage = 80;
$sMessage = Dict::S('ExcelExporter:BuildingExcelFile');
}
else
{
$sCode = 'retrieving-data';
$this->iPosition += $this->iChunkSize;
$iPercentage = 5 + round(75 * ($this->iPosition / count($this->aObjectsIDs)));
$sMessage = Dict::S('ExcelExporter:RetrievingData');
}
break;
case 'building-excel':
$hFile = @fopen($this->GetDataFile(), 'rb');
if ($hFile === false)
{
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for reading.');
}
$sHeaders = fgets($hFile);
$aHeaders = json_decode($sHeaders, true);
$aData = array();
while($sLine = fgets($hFile))
{
$aRow = json_decode($sLine);
$aData[] = $aRow;
}
fclose($hFile);
@unlink($this->GetDataFile());
$fStartExcel = microtime(true);
$writer = new XLSXWriter();
$writer->setAuthor(UserRights::GetUserFriendlyName());
$writer->writeSheet($aData,'Sheet1', $aHeaders);
$fExcelTime = microtime(true) - $fStartExcel;
$this->aStatistics['excel_build_duration'] = $fExcelTime;
$fTime = microtime(true);
$writer->writeToFile($this->GetExcelFilePath());
$fExcelSaveTime = microtime(true) - $fTime;
$this->aStatistics['excel_write_duration'] = $fExcelSaveTime;
// Next state
$this->sState = 'done';
$sCode = 'done';
$iPercentage = 100;
$sMessage = Dict::S('ExcelExporter:Done');
break;
case 'done':
$this->sState = 'done';
$sCode = 'done';
$iPercentage = 100;
$sMessage = Dict::S('ExcelExporter:Done');
break;
$this->sState = 'done';
$sCode = 'done';
$iPercentage = 100;
$sMessage = Dict::S('ExcelExporter:Done');
break;
}
} catch (Exception $e) {
}
catch(Exception $e)
{
$sCode = 'error';
$sMessage = $e->getMessage();
}
$this->aStatistics['total_duration'] += microtime(true) - $fTime;
$peak_memory = memory_get_peak_usage(true);
if ($peak_memory > $this->aStatistics['peak_memory_usage']) {
if ($peak_memory > $this->aStatistics['peak_memory_usage'])
{
$this->aStatistics['peak_memory_usage'] = $peak_memory;
}
return [
return array(
'code' => $sCode,
'message' => $sMessage,
'percentage' => $iPercentage,
];
);
}
public function GetExcelFilePath()
{
if ($this->sOutputFilePath == null) {
return APPROOT.'data/bulk_export/'.$this->sToken.'.xlsx';
} else {
if ($this->sOutputFilePath == null)
{
return utils::GetDataPath().'bulk_export/'.$this->sToken.'.xlsx';
}
else
{
return $this->sOutputFilePath;
}
}
public static function GetExcelFileFromToken($sToken)
{
return @file_get_contents(APPROOT.'data/bulk_export/'.$sToken.'.xlsx');
return @file_get_contents(utils::GetDataPath().'bulk_export/'.$sToken.'.xlsx');
}
public static function CleanupFromToken($sToken)
{
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.status');
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.data');
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.xlsx');
@unlink(utils::GetDataPath().'bulk_export/'.$sToken.'.status');
@unlink(utils::GetDataPath().'bulk_export/'.$sToken.'.data');
@unlink(utils::GetDataPath().'bulk_export/'.$sToken.'.xlsx');
}
public function Cleanup()
{
self::CleanupFromToken($this->sToken);
}
/**
* Delete all files in the data/bulk_export directory which are older than 1 day
* unless a different delay is configured.
*/
public static function CleanupOldFiles()
{
$aFiles = glob(APPROOT.'data/bulk_export/*.*');
$aFiles = glob(utils::GetDataPath().'bulk_export/*.*');
$iDelay = MetaModel::GetConfig()->Get('xlsx_exporter_cleanup_old_files_delay');
if ($iDelay > 0) {
foreach ($aFiles as $sFile) {
if($iDelay > 0)
{
foreach($aFiles as $sFile)
{
$iModificationTime = filemtime($sFile);
if ($iModificationTime < (time() - $iDelay)) {
if($iModificationTime < (time() - $iDelay))
{
// Temporary files older than one day are deleted
//echo "Supposed to delete: '".$sFile." (Unix Modification Time: $iModificationTime)'\n";
@unlink($sFile);
@@ -317,155 +352,189 @@ class ExcelExporter
}
}
}
public function DisplayStatistics(Page $oPage)
{
$aStats = [
$aStats = array(
'Number of objects exported' => $this->aStatistics['objects_count'],
'Total export duration' => sprintf('%.3f s', $this->aStatistics['total_duration']),
'Data retrieval duration' => sprintf('%.3f s', $this->aStatistics['data_retrieval_duration']),
'Excel build duration' => sprintf('%.3f s', $this->aStatistics['excel_build_duration']),
'Excel write duration' => sprintf('%.3f s', $this->aStatistics['excel_write_duration']),
'Peak memory usage' => self::HumanDisplay($this->aStatistics['peak_memory_usage']),
];
if ($oPage instanceof CLIPage) {
);
if ($oPage instanceof CLIPage)
{
$oPage->add($this->GetStatistics('text'));
} else {
}
else
{
$oPage->add($this->GetStatistics('html'));
}
}
public function GetStatistics($sFormat = 'html')
{
$sStats = '';
$aStats = [
$aStats = array(
'Number of objects exported' => $this->aStatistics['objects_count'],
'Total export duration' => sprintf('%.3f s', $this->aStatistics['total_duration']),
'Data retrieval duration' => sprintf('%.3f s', $this->aStatistics['data_retrieval_duration']),
'Excel build duration' => sprintf('%.3f s', $this->aStatistics['excel_build_duration']),
'Excel write duration' => sprintf('%.3f s', $this->aStatistics['excel_write_duration']),
'Peak memory usage' => self::HumanDisplay($this->aStatistics['peak_memory_usage']),
];
if ($sFormat == 'text') {
foreach ($aStats as $sLabel => $sValue) {
);
if ($sFormat == 'text')
{
foreach($aStats as $sLabel => $sValue)
{
$sStats .= "+------------------------------+----------+\n";
$sStats .= sprintf("|%-30s|%10s|\n", $sLabel, $sValue);
}
$sStats .= "+------------------------------+----------+";
} else {
}
else
{
$sStats .= '<table><tbody>';
foreach ($aStats as $sLabel => $sValue) {
foreach($aStats as $sLabel => $sValue)
{
$sStats .= "<tr><td>$sLabel</td><td>$sValue</td></tr>";
}
$sStats .= '</tbody></table>';
}
return $sStats;
}
public static function HumanDisplay($iSize)
{
$aUnits = ['B','KB','MB','GB','TB','PB'];
return @round($iSize / pow(1024, ($i = floor(log($iSize, 1024)))), 2).' '.$aUnits[$i];
$aUnits = array('B','KB','MB','GB','TB','PB');
return @round($iSize/pow(1024,($i=floor(log($iSize,1024)))),2).' '.$aUnits[$i];
}
protected function CheckDataDir()
{
if (!is_dir(APPROOT."data/bulk_export")) {
@mkdir(APPROOT."data/bulk_export", 0777, true /* recursive */);
if(!is_dir(utils::GetDataPath()."bulk_export"))
{
@mkdir(utils::GetDataPath()."bulk_export", 0777, true /* recursive */);
clearstatcache();
}
if (!is_writable(APPROOT."data/bulk_export")) {
throw new Exception('Data directory "'.APPROOT.'data/bulk_export" could not be written.');
if (!is_writable(utils::GetDataPath()."bulk_export"))
{
throw new Exception('Data directory "'.utils::GetDataPath().'bulk_export" could not be written.');
}
}
protected function GetStateFile($sToken = null)
{
if ($sToken == null) {
if ($sToken == null)
{
$sToken = $this->sToken;
}
return APPROOT."data/bulk_export/$sToken.status";
return utils::GetDataPath()."bulk_export/$sToken.status";
}
protected function GetDataFile()
{
return APPROOT.'data/bulk_export/'.$this->sToken.'.data';
return utils::GetDataPath().'bulk_export/'.$this->sToken.'.data';
}
protected function GetNewToken()
{
$iNum = rand();
do {
do
{
$iNum++;
$sToken = sprintf("%08x", $iNum);
$sFileName = $this->GetStateFile($sToken);
$hFile = @fopen($sFileName, 'x');
} while ($hFile === false);
}
while($hFile === false);
fclose($hFile);
return $sToken;
}
protected function GetFieldsList($oSet, $bFieldsAdvanced = false, $bLocalize = true, $aFields = null)
{
$this->aFieldsList = [];
$this->aFieldsList = array();
$oAppContext = new ApplicationContext();
$aClasses = $oSet->GetFilter()->GetSelectedClasses();
$this->aAuthorizedClasses = [];
foreach ($aClasses as $sAlias => $sClassName) {
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO) {
$this->aAuthorizedClasses = array();
foreach($aClasses as $sAlias => $sClassName)
{
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO)
{
$this->aAuthorizedClasses[$sAlias] = $sClassName;
}
}
$aAttribs = [];
$this->aTableHeaders = [];
foreach ($this->aAuthorizedClasses as $sAlias => $sClassName) {
$aList[$sAlias] = [];
foreach (MetaModel::ListAttributeDefs($sClassName) as $sAttCode => $oAttDef) {
if (is_null($aFields) || (count($aFields) == 0)) {
$aAttribs = array();
$this->aTableHeaders = array();
foreach($this->aAuthorizedClasses as $sAlias => $sClassName)
{
$aList[$sAlias] = array();
foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode => $oAttDef)
{
if (is_null($aFields) || (count($aFields) == 0))
{
// Standard list of attributes (no link sets)
if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField())) {
if ($oAttDef->IsScalar() && ($oAttDef->IsWritable() || $oAttDef->IsExternalField()))
{
$sAttCodeEx = $oAttDef->IsExternalField() ? $oAttDef->GetKeyAttCode().'->'.$oAttDef->GetExtAttCode() : $sAttCode;
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE)) {
if ($bFieldsAdvanced) {
if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
{
if ($bFieldsAdvanced)
{
$aList[$sAlias][$sAttCodeEx] = $oAttDef;
if ($oAttDef->IsExternalKey(EXTKEY_RELATIVE)) {
$sRemoteClass = $oAttDef->GetTargetClass();
foreach (MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode) {
if ($oAttDef->IsExternalKey(EXTKEY_RELATIVE))
{
$sRemoteClass = $oAttDef->GetTargetClass();
foreach(MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode)
{
$this->aFieldsList[$sAlias][$sAttCode.'->'.$sRemoteAttCode] = MetaModel::GetAttributeDef($sRemoteClass, $sRemoteAttCode);
}
}
}
}
} else {
}
else
{
// Any other attribute
$this->aFieldsList[$sAlias][$sAttCodeEx] = $oAttDef;
}
}
} else {
}
else
{
// User defined list of attributes
if (in_array($sAttCode, $aFields) || in_array($sAlias.'.'.$sAttCode, $aFields)) {
if (in_array($sAttCode, $aFields) || in_array($sAlias.'.'.$sAttCode, $aFields))
{
$this->aFieldsList[$sAlias][$sAttCode] = $oAttDef;
}
}
}
if ($bFieldsAdvanced) {
if ($bFieldsAdvanced)
{
$this->aTableHeaders['id'] = '0';
}
foreach ($this->aFieldsList[$sAlias] as $sAttCodeEx => $oAttDef) {
foreach($this->aFieldsList[$sAlias] as $sAttCodeEx => $oAttDef)
{
$sLabel = $bLocalize ? MetaModel::GetLabel($sClassName, $sAttCodeEx, isset($aParams['showMandatoryFields'])) : $sAttCodeEx;
if ($oAttDef instanceof AttributeDateTime) {
if($oAttDef instanceof AttributeDateTime)
{
$this->aTableHeaders[$sLabel] = 'datetime';
} else {
}
else
{
$this->aTableHeaders[$sLabel] = 'string';
}
}
}
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -7,4 +6,4 @@
class BulkChangeException extends CoreException
{
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -7,4 +6,4 @@
class CSVParserException extends CoreException
{
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -7,4 +6,4 @@
class ConfigException extends CoreException
{
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -64,20 +63,21 @@ class CoreCannotSaveObjectException extends CoreException
public function getTextMessage()
{
$sTitle = Dict::S('UI:Error:SaveFailed');
$sContent = $sTitle;
$sContent = utils::HtmlEntities($sTitle);
if (count($this->aIssues) == 1) {
$sIssue = reset($this->aIssues);
$sContent .= $sIssue;
$sContent .= utils::HtmlEntities($sIssue);
} else {
foreach ($this->aIssues as $sError) {
$sContent .= " ".$sError.", ";
$sContent .= " ".utils::HtmlEntities($sError).", ";
}
}
return $sContent;
}
public function getIssues()
{
return $this->aIssues;

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -36,10 +35,10 @@ class CoreException extends Exception
}
if (count($this->m_aContextData) > 0) {
$sMessage .= ": ";
$aContextItems = [];
$aContextItems = array();
foreach ($this->m_aContextData as $sKey => $value) {
if (is_array($value)) {
$aPairs = [];
$aPairs = array();
foreach ($value as $key => $val) {
if (is_array($val)) {
$aPairs[] = $key.'=>('.implode(', ', $val).')';
@@ -68,7 +67,7 @@ class CoreException extends Exception
public function getHtmlDesc($sHighlightHtmlBegin = '<b>', $sHighlightHtmlEnd = '</b>')
{
return utils::EscapeHtml($this->getMessage());
return $this->getMessage();
}
/**
@@ -110,4 +109,4 @@ class CoreException extends Exception
{
return $this->m_aContextData;
}
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -10,4 +9,5 @@
*/
class CorePortalInvalidActionRuleException extends CoreException
{
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -15,4 +14,4 @@ class CoreTemplateException extends CoreException
$sMessage = "Twig Exception when rendering '$sTemplatePath' : ".$oTwigException->getMessage();
parent::__construct($sMessage, null, '', $oTwigException);
}
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -7,4 +6,4 @@
class DeleteException extends CoreException
{
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0

View File

@@ -12,13 +12,13 @@ class InvalidExternalKeyValueException extends CoreUnexpectedValue
public function __construct($oObject, $sAttCode, $aContextData = null, $oPrevious = null)
{
$aContextData[self::ENUM_PARAMS_OBJECT] = get_class($oObject).'::'.$oObject->GetKey();
$aContextData[self::ENUM_PARAMS_OBJECT] = get_class($oObject) . '::' . $oObject->GetKey();
$aContextData[self::ENUM_PARAMS_ATTCODE] = $sAttCode;
$aContextData[self::ENUM_PARAMS_ATTVALUE] = $oObject->Get($sAttCode);
$oCurrentUser = UserRights::GetUserObject();
if (false === is_null($oCurrentUser)) {
$aContextData[self::ENUM_PARAMS_USER] = get_class($oCurrentUser).'::'.$oCurrentUser->GetKey();
$aContextData[self::ENUM_PARAMS_USER] = get_class($oCurrentUser) . '::' . $oCurrentUser->GetKey();
}
parent::__construct('Attribute pointing to an object that is either non existing or not readable by the current user', $aContextData, '', $oPrevious);

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -12,4 +11,4 @@
*/
class InvalidPasswordAttributeOneWayPassword extends CoreException
{
}
}

View File

@@ -1,5 +1,4 @@
<?php
/**
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -11,4 +10,4 @@ use Exception;
class PageNotFoundException extends Exception
{
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -7,4 +6,4 @@
class SynchroExceptionNotStarted extends CoreException
{
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -7,4 +6,4 @@
class UserRightException extends CoreException
{
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -7,4 +6,4 @@
class DictException extends CoreException
{
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -9,9 +8,9 @@ class DictExceptionMissingString extends DictException
{
public function __construct($sLanguageCode, $sStringCode)
{
$aContext = [];
$aContext = array();
$aContext['language_code'] = $sLanguageCode;
$aContext['string_code'] = $sStringCode;
parent::__construct('Missing localized string', $aContext);
}
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -9,8 +8,8 @@ class DictExceptionUnknownLanguage extends DictException
{
public function __construct($sLanguageCode)
{
$aContext = [];
$aContext = array();
$aContext['language_code'] = $sLanguageCode;
parent::__construct('Unknown localization language', $aContext);
}
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -7,4 +6,5 @@
class iTopXmlException extends CoreException
{
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -21,7 +20,7 @@ class MySQLException extends CoreException
$aContext['mysql_errno'] = $oException->getCode();
$this->code = $oException->getCode();
$aContext['mysql_error'] = $oException->getMessage();
} elseif ($oMysqli != null) {
} else if ($oMysqli != null) {
$aContext['mysql_errno'] = $oMysqli->errno;
$this->code = $oMysqli->errno;
$aContext['mysql_error'] = $oMysqli->error;
@@ -37,4 +36,4 @@ class MySQLException extends CoreException
error_reporting(0);
}
}
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -20,14 +19,14 @@ class MySQLHasGoneAwayException extends MySQLException
*/
public static function getErrorCodes()
{
return [
return array(
2006,
2013,
];
);
}
public function __construct($sIssue, $aContext)
{
parent::__construct($sIssue, $aContext, null);
}
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -10,4 +9,5 @@
*/
class MySQLNoTransactionException extends MySQLException
{
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -12,4 +11,5 @@
*/
class MySQLQueryHasNoResultException extends MySQLException
{
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -10,4 +9,5 @@
*/
class MySQLTransactionNotClosedException extends MySQLException
{
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -13,4 +12,4 @@
*/
class ProcessException extends CoreException
{
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -14,4 +13,4 @@
*/
class ProcessFatalException extends CoreException
{
}
}

View File

@@ -1,5 +1,4 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -10,4 +9,4 @@
*/
class ProcessInvalidConfigException extends ProcessException
{
}
}

View File

@@ -1,5 +1,4 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -23,8 +22,8 @@
* @author Olivier DAIN <olivier.dain@combodo.com>
* @since 3.0.0 N°3588
*/
class FindStylesheetObject
{
class FindStylesheetObject{
//file URIs
private $aStylesheetFileURIs;
@@ -65,7 +64,7 @@ class FindStylesheetObject
return $this->aStylesheetFileURIs;
}
public function GetLastModified(): int
public function GetLastModified() : int
{
return $this->iLastModified;
}
@@ -93,8 +92,7 @@ class FindStylesheetObject
$this->sLastStyleSheetPath = $sStylesheetFilePath;
}
public function AlreadyFetched(string $sStylesheetFilePath): bool
{
public function AlreadyFetched(string $sStylesheetFilePath) : bool {
return in_array($sStylesheetFilePath, $this->aAllStylesheetFilePaths);
}
@@ -113,4 +111,4 @@ class FindStylesheetObject
{
$this->sLastStyleSheetPath = "";
}
}
}

Some files were not shown because too many files have changed in this diff Show More