Compare commits

..

249 Commits

Author SHA1 Message Date
jf-cbd
043f695a4a Contribution guidelines proposition 2026-04-16 18:48:53 +02:00
Benjamin DALSASS
87dd003a6d N°8638 - Adapt mysqldump calls to follow iTop SSL configuration
- fix unitary test
2026-04-14 08:54:54 +02:00
Benjamin Dalsass
7201bef8db N°8638 - Adapt mysqldump calls to follow iTop SSL configuration (#883) 2026-04-14 08:08:17 +02:00
Benjamin Dalsass
af01ff9e62 N°9121 - CSV Import : The advanced mode option no longer works as bef… (#863) 2026-04-14 08:02:00 +02:00
Molkobain
ab1290dfd0 N°8766 - Fix user's login displayed in backoffice log entry instead of user's contact friendlyname when user is disabled 2026-04-13 21:14:03 +02:00
Stephen Abello
68d14c4de6 N°9468 - Fix double scroll down bars in input set (#876) 2026-04-13 16:28:56 +02:00
Benjamin Dalsass
a96e1c286d N°9379 PHP unserialize encapsulation (#878) 2026-04-13 16:04:00 +02:00
Stephen Abello
b799be3cb7 N°9177 - Blockquote in HTML field are unreadable in darkmoon (again) 2026-04-13 10:24:55 +02:00
jf-cbd
d000d93b19 N°8766 - Fix wrong author on portal log (#880) 2026-04-13 10:10:40 +02:00
Stephen Abello
9f25635a64 N°9177 - Blockquote in HTML field are unreadable in darkmoon 2026-04-09 15:19:15 +02:00
Stephen Abello
6bd34dc73e N°4460 - Fix date and date time picker in Darkmoon 2026-04-08 14:59:36 +02:00
Stephen Abello
9dc3c56689 N°9448 - Fix external auth variable value (#871) 2026-04-08 14:19:24 +02:00
Stephen Abello
effd35c3e6 N°8758 - Fix mandatory caselog in transition requiring double confirmation (#868) 2026-04-08 14:19:09 +02:00
lenaick.moreira
f1735767c3 💚 Fix CI 2026-04-08 10:19:31 +02:00
lenaick.moreira
00735f0c54 N°9483 - The search suggestion message is not displaying correctly 2026-04-08 09:48:15 +02:00
lenaick.moreira
882390e8d6 N°9043 - Fix automatic search on direct linkset block 2026-04-08 09:48:15 +02:00
Lenaick
5d0da47f21 N°8178 - Respect "high_cardinality_classes" parameter on search operation (#870) 2026-04-08 09:47:46 +02:00
lenaick.moreira
4eadff7f3b ♻️ Refactor BODY_DATA_GUI_TYPE constants to public visibility (N°9101) 2026-04-08 09:38:40 +02:00
Molkobain
f66ce1c956 Fix cURL calls to iTop within Docker dev. environment with PHP version autodetection (#869) 2026-04-07 19:06:30 +02:00
jf-cbd
802f9f3e08 N°8576 - [RequestTemplate] Slowness during the selection of drop-down list field (#872) 2026-04-07 17:06:21 +02:00
lenaick.moreira
5c5d98bb78 Enable the retrieval of all data for event notifications in the activity panel 2026-04-01 16:12:36 +02:00
Stephen Abello
a08a9b43f3 N°8737 - Fix search criteria dropdown vertical overflow (#867)
* N°8737 - Fix search criteria dropdown vertical overflow

* Update css/backoffice/components/_search-form.scss
2026-04-01 14:57:10 +02:00
jf-cbd
abd85ff4db Merge remote-tracking branch 'origin/support/3.2' into support/3.2 2026-04-01 10:37:59 +02:00
jf-cbd
81f328b26e N°8543 - rename security parameter with new convention 2026-04-01 10:37:46 +02:00
lenaick.moreira
9a2c8f10bf Fix CliResetSessionTest by allowing empty POST fields to be defined in the curl options 2026-04-01 10:35:59 +02:00
Lenaick
3cdadf3c6e Add tests for lock acquisition functionality (#865) 2026-03-31 17:01:22 +02:00
jf-cbd
a6295f1b14 Duplicate try-catch to avoid finally statement in case of uncorrrecth auth 2026-03-31 16:39:05 +02:00
Stephen Abello
e467ca83cf N°8532 - Apply filters on all DBSearch classes (#848)
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>
2026-03-31 15:41:28 +02:00
Stephen Abello
7791585387 N°9231 - Make OrmDocument apply same safety to attachments and regular documents (#860) 2026-03-30 15:25:52 +02:00
Molkobain
3406ca79de N°9361 - Update PHPDoc 2026-03-30 13:49:30 +02:00
Stephen Abello
91ad01055e N°5228 - Allow themes variable imports to be overloaded by variable entries (#858) 2026-03-27 15:46:15 +01:00
Lenaick
804cdffe42 N°8234 - Fix permission checks to conditionally allow display of unauthorized objects (#859) 2026-03-27 15:09:31 +01:00
v-dumas
5f4affc896 N°9057 - Fix tests broken due to ModuleInstallation given grant_by_profile (2) 2026-03-26 17:56:52 +01:00
v-dumas
042fee2360 N°9057 - Fix tests broken due to ModuleInstallation given grant_by_profile 2026-03-26 17:37:18 +01:00
Vincent Dumas
7f8ec25977 N°9057 - Enable SuperUser to execute collectors (#799) 2026-03-26 12:13:45 +01:00
Lenaick
41f8437c23 N°8234 - Allow display of unauthorized objects in notifications and event queries (#853)
* N°8234 - Allow display of unauthorized objects in notifications and event queries

* Refactor EventNotificationNewsroom class usage in iTopNewsroomController
2026-03-26 11:44:06 +01:00
Anne-Catherine
df8b25d4b4 N°9223 - Portal - AttributeExternalKey or AttributeEnum are not displayed after adding a link. (#802) 2026-03-26 10:34:41 +01:00
Lenaick
511dabe2b0 N°8834 - Updated the PHP version to 8.4 for the CI in the commit 2026-03-25 17:43:39 +01:00
Lenaick
0c517f254c N°9101 - Improve HTML markup for end-to-end tests automation (#856) 2026-03-25 17:20:28 +01:00
Stephen Abello
c56c7a1f9d Fix CI by fixing code style 2026-03-25 10:25:37 +01:00
Stephen Abello
fb2f0f1447 N°9328 - Add scssphp compatibility with PHP 8.4 (#851) 2026-03-25 09:58:05 +01:00
Lenaick
b3223eb9b6 N°8606 - Check user permissions in search operation of ajax.render.php (#836) 2026-03-24 08:52:22 +01:00
Benjamin DALSASS
458a996c29 N°8612 - force authentication for inline image endpoints
- ajax.render dict route needs to be reached without login authentication
2026-03-23 15:50:47 +01:00
Anne-Catherine
c61b21559c N°8692 - Notification - placeholder attributesubitem (#778) 2026-03-23 15:33:50 +01:00
jf-cbd
ed33238750 Merge remote-tracking branch 'origin/support/3.2' into support/3.2 2026-03-20 16:31:44 +01:00
jf-cbd
272678b8cd N°9361 - Indicate to itop admin concerned by 8543 that changes could be required in extension 2026-03-20 16:30:53 +01:00
Benjamin Dalsass
170014e8f0 N°9232 - Information Disclosure (#850) 2026-03-20 14:35:05 +01:00
Molkobain
006f666089 N°8554 - Fix impossible installation of portal new look via iTop Hub (#846) 2026-03-18 19:59:42 +01:00
Stephen Abello
2a16143e53 N°9229 - Modernize search foreign keys code with built in JS tools (#847)
* N°9229 - Modernize search foreign keys code with built in JS tools

* N°9229 - Allow modals to have button id specified

* N°9229 - Remove the modal instead of only destroying it

* N°9229 - Remove dead code

* Update js/searchformforeignkeys.js

* Add robustness to modals button id
2026-03-18 15:23:52 +01:00
jf-cbd
eabbe2f00b N°8543 - best practices 2026-03-18 10:09:25 +01:00
Molkobain
58790bc352 N°8597 - Fix special characters being escaped for BrowseBrick items in "Tree" mode (#845)
* N°8597 - Fix special characters being escaped for BrowseBrick items in "Tree" mode

* N°8597 - Fix forgotten use case for "Tree" mode for intermediate levels
2026-03-17 15:02:20 +01:00
Lenaick
28db230697 N°9233 - Check user access before acquiring lock on object (#844) 2026-03-16 17:07:26 +01:00
jf-cbd
4fe61cbdc7 N°8543 - Add checks on exec.php (#835) 2026-03-16 17:06:37 +01:00
Benjamin Dalsass
e2994b645b N°8612 inline images to base64 (#826) 2026-03-16 08:36:37 +01:00
lenaick.moreira
9fca81cc32 N°9366 - Update PHPUnit to be compatible with PHP 8.4 2026-03-10 10:59:54 +01:00
lenaick.moreira
9792358aea Remove debug OQL filter in HTML comments of the universal search page 2026-03-09 17:06:00 +01:00
lenaick.moreira
7bfa14a874 N°9235 - Sanitize oql_clause query parameter in universal search page 2026-03-09 17:06:00 +01:00
Lenaick
9236449b21 N°9238 - Sanitize data_source_id query parameter in synchro_import script (#831) 2026-03-09 09:02:17 +01:00
Benjamin Dalsass
ab8e7bd15e N°9236 - tag admin fix (#832) 2026-03-09 08:30:39 +01:00
lenaick.moreira
307c308eb0 Fix unit test testPhpMinVersionConsistency 2026-03-05 16:15:18 +01:00
Lenaick
61e5536b50 N°9234 - Sanitize query expression parameter in suggested OQL on run query page (#829) 2026-03-05 16:02:30 +01:00
Lenaick
104dd1970f N°9230 - Sanitize dashboard_id parameter in "revert_dashboard" operation of AJAX render function (#828) 2026-03-05 15:55:28 +01:00
Stephen Abello
929b8b9eca Fix CI by fixing code style 2026-03-05 15:28:27 +01:00
Stephen Abello
3b8e079cf1 N°6977 - Sanitize Excel formulas in exports (#818)
* N°6977 - Sanitize Excel formulas in export in the backoffice
---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2026-03-05 15:07:27 +01:00
Lenaick
fc967c06ce N°8834 - Add compatibility with PHP 8.4 (#819)
* N°8834 - Add compatibility with PHP 8.4

* Rollback of scssphp/scssphp version upgrade due to compilation error
2026-02-26 10:36:32 +01:00
Eric Espie
d4821b7edc remove log level config 2026-02-26 10:19:34 +01:00
Molkobain
62e09f1224 N°8604 N°8605 - Add authentication to combodo-db-tools binaries (#817)
* N°8604 N°8605 - Add autoloader and dedicated classes for binaries utils

* N°8605 - Harden security

* N°8604 - Harden security

* N°8604 N°8605 - Fixes from code review

* N°8604 N°8605 - Improve robustness whether module is in datamodels/2.x or env-xxx folder
2026-02-26 09:29:20 +01:00
Eric Espié
f82389d156 N°8632 - Various fixes (#814)
* N°8632 - Check existence of parameter file within iTop

* N°8632 - block parameter file from request

* log on error

* PHP CS fixer

* N°8632 - param files must be outside iTop

* PHP CS fixer

* Update webservices/export.php

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update tests/php-unit-tests/unitary-tests/application/utilsTest.php

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

*  Fix CI

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-25 10:42:04 +01:00
Molkobain
f558093f5d N°8796 - Add PHP-CS-Fixer cache file to .gitignore file 2026-02-24 21:10:06 +01:00
Eric Espie
5201a1ed3b PHP CS fixer 2026-02-24 16:38:21 +01:00
odain
29920bfeb7 cleanup: log level in setUp + remove ls cli 2026-02-24 11:26:48 +01:00
Stephen Abello
aede5ea7b8 Fix CI by updating files code style 2026-02-23 16:25:08 +01:00
Stephen Abello
da6c443a35 Fix CI by updating files code style 2026-02-23 16:08:20 +01:00
Stephen Abello
9c39efd9af N°8549 - Update inline images secret (#815) 2026-02-23 15:42:21 +01:00
jf-cbd
d5f2303ed2 N°8673 - Avoid server overload when user access news page with a lot of unread news (#816) 2026-02-23 15:35:31 +01:00
Eric Espié
48e584503e N°8782 - Union queries allow all data (#807)
* 8782 - Union queries allow all data

* N°8782 - Union queries allow all data
2026-02-23 15:10:09 +01:00
Benjamin DALSASS
454a1b26eb N°8603 N°8601 - Remove unnecessary quotes in reload url injection 2026-02-23 10:22:10 +01:00
Benjamin Dalsass
330539abd2 N°8601 N°8603 dashboards issues (#813) 2026-02-23 06:51:40 +01:00
Stephen Abello
5357a0c060 Fix code style 2026-02-21 14:26:21 +01:00
lenaick.moreira
fc22cce037 N°8854 - Replace trigger_error() usage with E_USER_ERROR 2026-02-20 10:07:30 +01:00
lenaick.moreira
34c8a57814 N°8851 - Explicit nullable in functions parameters 2026-02-20 10:07:30 +01:00
Stephen Abello
2247691e58 Fix CI by updating files code style 2026-02-19 16:59:25 +01:00
Stephen Abello
076d49abc2 Dump autoloader 2026-02-19 16:15:58 +01:00
Stephen Abello
9fd0ffd84e N°8545 - Standardize return message from password reset (#812)
* N°8545 - Standardize return message from password reset

* N°8545 - Change log severity depending on the error source

* Add copyrights

* Update application/loginwebpage.class.inc.php

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update application/loginwebpage.class.inc.php

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Avoid using dictionary entries in logs

* Update application/loginwebpage.class.inc.php

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-19 16:12:34 +01:00
Benjamin Dalsass
d2f67dcb3c N°8910 - Upgrade Symfony packages 2026-02-19 09:18:56 +01:00
jf-cbd
d5706fcbef 🧑‍💻 Add auto-assign for Combodo members 2026-02-18 14:39:25 +01:00
Benjamin Dalsass
807f2a88bc N°8933 - Change password default length 2026-02-18 14:36:38 +01:00
Stephen Abello
9d3311e623 Fix CI by updating new dictionary files code style again 2026-02-18 12:18:15 +01:00
Stephen Abello
9bf2cb7e1d Fix CI by updating new dictionary files code style 2026-02-18 11:50:38 +01:00
Stephen Abello
54909520e9 N°8544 - Update comparison method (#808) 2026-02-18 11:30:34 +01:00
Stephen Abello
d124f8ee58 N°8541 - Replace login logo title with an alt customizable through dictionary entry (#805) 2026-02-18 09:53:57 +01:00
Molkobain
3b62597092 Update PHP version to 8.3 and DB version to MariaDB latest for CI jobs on commit 2026-02-17 20:39:58 +01:00
Molkobain
c0a2771d4e N°8681 - Fix PHP code styles 2026-02-16 16:46:00 +01:00
Molkobain
6bd5a7b634 N°8681 - PHP 8.1: Fix deprecated notice for null value passed to preg_match_all() (#803)
* N°8681 - PHP 8.1: Fix deprecated notice for null value passed to preg_match_all()

* N°8681 - Add unit tests

* N°8681 - Add unit tests
2026-02-16 16:18:04 +01:00
lenaick.moreira
82b7ef86c7 N°8796 - Refactor PHP CS Fixer configuration for improved readability
* `'indentation_type'` already included in @PSR12 rule set
2026-02-16 15:35:46 +01:00
Eric Espie
4f878536a8 Fix CI 2026-02-05 16:53:02 +01:00
Eric Espie
d937ec0350 Fix CI 2026-02-05 16:24:27 +01:00
Eric Espie
985db46960 N°9193 - Start the KPI logs at the beginning of the http request 2026-02-05 14:57:54 +01:00
v-dumas
01adaadfad N°8492 - Missing accent for 'Categorie' 2026-02-02 14:56:53 +01:00
v-dumas
643752f8e7 N°8378 - Missing rights on incident for SuperUser 2026-01-23 16:24:19 +01:00
v-dumas
0e0c09c420 N°9027 - Add right on WorkOrder transition to SuperUser 2026-01-23 15:55:59 +01:00
Stephen Abello
f34373be6d N°7909 - Missing spacing between fields when columns collapse 2026-01-16 15:30:32 +01:00
odain
4dba47798c ci: fix broken test due to POST CURL file not sent 2025-12-19 18:35:42 +01:00
odain
49a7f3118d ci: fix Array to String Conversion in CallUrl with POST array of array 2025-12-19 16:12:53 +01:00
odain
09afcb229c ci: fix callUrl with posted params 2025-12-19 11:11:03 +01:00
odain
3df4ddc696 ci: fix code style 2025-12-19 10:39:15 +01:00
odain
1fe401c102 ci: rename CallItopUrl by CallUrl + cleanup 2025-12-19 10:01:37 +01:00
odain-cbd
6cb08ba361 Add few APIs to ease tests (#788)
* add few APIs to ease tests

* code style

* log testname via IssueLog only through ItopDataTestCase

* code style

* ci: phpunit fix fatal error
2025-12-18 13:31:48 +01:00
Molkobain
f9db405343 N°6644 - PHP Static Analysis: Update PHPStan to v2.1 2025-12-17 22:21:43 +01:00
Molkobain
4f1f144c51 N°6644 - PHP Static Analysis: Update configuration to:
- Ignore compiled folders other than "env-production"
 - Ignore Lempar.php as its content isn't valid PHP and it won't be included in the baseline
2025-12-17 22:04:49 +01:00
Molkobain
ef8ade5d20 📝 Update unit tests documentation 2025-12-17 18:36:18 +01:00
Molkobain
0b242d872a N°6644 - Tests: Restore proper CI branch, unit tests run and fix a typo 2025-12-17 11:06:36 +01:00
Molkobain
d9261b8342 N°6644 - Tests: Add static analysis for PHP (#536) 2025-12-17 10:45:53 +01:00
odain
4187f552a9 Merge branch 'support/3.2.1' into support/3.2 2025-11-18 15:36:32 +01:00
odain
7f7ce0837e Merge branch 'designer-3.2.1' into support/3.2.1 2025-11-18 15:35:40 +01:00
odain
fd10887849 N°8306 - ci fixes
php code style
2025-11-16 08:15:03 +01:00
odain
7df09541ac N°8306 - ci fixes 2025-11-16 08:11:34 +01:00
odain
6d5660ca67 fix code style 2025-11-15 21:28:38 +01:00
odain
0013b8cfee Merge branch 'designer-3.2.1' into support/3.2 2025-11-15 21:27:37 +01:00
odain
7fb0ae48f9 Fix deprecated warning
<b>Deprecated</b>:  Return type of Combodo\iTop\DesignDocument::loadXML(string $source, int $options = 0) should either be compatible with DOMDocument::loadXML(string $source, int $options = 0): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in <b>/var/www/html/iTop/core/designdocument.class.inc.php</b> on line <b>73</b><br />
2025-11-15 21:26:36 +01:00
odain
e89a8edae0 N°8306 - fix MFEException use 2025-11-15 21:19:01 +01:00
odain
5b7a5e14a3 code style fix 2025-11-14 10:41:47 +01:00
odain
2b1ecf15b4 Merge branch 'support/3.2.1' into support/3.2 2025-11-14 10:39:04 +01:00
odain
f6366057c9 Merge branch 'designer-3.2.1' into support/3.2.1 2025-11-14 10:18:46 +01:00
v-dumas
67018b9b17 N°8768 - Fix PHP CS in dictionnaries 2025-11-14 09:40:17 +01:00
v-dumas
8597dea398 N°8768 - Fix PHP CS in dictionnaries 2025-11-14 09:02:54 +01:00
v-dumas
a6972af266 N°8768 - Missing lnkActionNotificationToContact translation 2025-11-13 17:36:18 +01:00
Fabrice VINCENT
066b27c982 fix testing MySQLBinDir (#770) 2025-11-13 16:37:50 +01:00
v-dumas
283de1ef7c N°8768 - Fix code style & duplicated dico entries 2025-11-13 16:27:10 +01:00
Vincent Dumas
8b00016115 N°8768 - Fix deletion of Person notified (#779)
* N°8768 - Fix deletion of Person notified
2025-11-13 14:53:23 +01:00
odain
b5c79e1d75 N°8912 - Cannot work on a licence with legacy extensions including modules without explicit version 2025-11-13 11:13:33 +01:00
Stephen Abello
80d4e65a81 N°8006 - Make CKEditor dropdowns visible when colliding with an input in the backoffice 2025-11-10 10:53:38 +01:00
odain
4a9a85458d N°8796 - fix ci jo regression due to code style formatting 2025-11-07 16:04:23 +01:00
odain
890a2568c8 N°8796 - Add PHP code style validation in iTop and extensions - format whole code base 2025-11-07 15:39:53 +01:00
odain-cbd
12f23113f5 N°8796 - Add PHP code style validation in iTop and extensions (#757)
* poc php-cs-fixer

* PSR2 for now

* PSR12 + array short

* move php-cs-fixer stuff in tests/php-code-style

* add README

* fix php-cs-fixer move into php-code-style

* illustrate code reformatting on webservices folder

* phpstan: change composer php requirements

* indent with tabs + inception style applied everywhere even php-cs-fixer itself

* use tabs for code style indentation

* format concat with spaces

* finalize included/excluded folders to formatting tool

* add trailing_comma_in_multiline option

* adapt composer.json requirements

* Revert changes on webservices folder
2025-11-07 15:12:03 +01:00
Anne-Cath
26f21ee6eb N°4250 - Problem with unencryption when the attribute is empty
N°4058 - Setup failed when added an encrypted field due to default value NULL non SODIUM compatible
2025-11-04 15:25:53 +01:00
Stephen Abello
adfa800063 N°7939 - Improve activity panel tab togglers when there's more than 2 log attributes (#766) 2025-10-30 16:38:38 +01:00
Stephen Abello
5df936c587 N°7982 - Allow select drop-down height to be customized through SCSS variable 2025-10-28 15:24:30 +01:00
Stephen Abello
8056a63e82 N°8748 - Implement horizontal scroll for LongText attributes 2025-10-28 09:34:44 +01:00
Stephen Abello
4f845c63cf N°8715 - Missing name for SetUIBlock when initialized in twig 2025-10-27 15:57:11 +01:00
Stephen Abello
6226ac8746 N°5228 - Make darkmoon and accessibility themes colors overloadable 2025-10-16 11:04:06 +02:00
Stephen Abello
e661e0bdbb N°8524 - Make grant matrix display correctly in darkmoon and make it accessible for color vision impaired 2025-10-16 10:36:53 +02:00
Stephen Abello
1d52665012 N°4460 - Make input error messages a bit more red in darkmoon 2025-10-15 16:33:32 +02:00
Stephen Abello
4aa0c19183 N°4460 - Add css rule to fix poor contrast in tagset add action when using darkmoon 2025-10-15 16:27:29 +02:00
Stephen Abello
809d4cd665 N°4460 - Add css rule to fix poor contrast in tagset placeholder when using darkmoon 2025-10-15 16:04:06 +02:00
Stephen Abello
0c3bc7f35b N°8392 - Fix poor contrast in autocompletes when using darkmoon 2025-10-15 15:49:16 +02:00
Anne-Cath
778c16da86 N°8663 - DoCheckToWrite fonction implode() 2025-09-29 14:33:52 +02:00
Eric Espie
5045ec4afa N°7722 - Fatal error on xml injection if xml entry "redefine" doesn't exist 2025-09-25 09:43:19 +02:00
Eric Espie
981d5c6263 Allow KPIs for REST api 2025-09-22 16:43:24 +02:00
Stephen Abello
4ed21dc21a N°7920 - Replace Symfony sendmail transport with PHP mail() transport 2025-09-22 16:24:58 +02:00
Stephen Abello
1f4a2f0f56 N°8579 - Fix calls to Twig "spaceless" deprecated filter 2025-09-18 10:35:02 +02:00
Stephen Abello
bf7a756714 Add symfony/mailer/Test to denied dirs 2025-09-17 16:25:22 +02:00
Stephen Abello
428d2c6356 N°7920 - laminas-mail is an abandoned package, replace it with symfony/mailer (#742)
* N°7920 - laminas-mail is an abandoned package, replace it with symfony/mailer

* Fix composer following merge
2025-09-17 16:04:20 +02:00
Stephen Abello
8982f7e0e3 Dump autoloader 2025-09-15 14:16:03 +02:00
Benjamin Dalsass
bb8a09d8e2 N°8637 - Alerts from dependabot, vulnerable libraries
* Update twig/twig from 3.16.0 to 3.21.1

* Update tecnickcom/tcpdf from 6.7.5 to 6.10.0

* Correct font folder case failing on linux server

* Suppress documentation generator from project in favor of the online version

* Update symfony/http-foundation from 6.4.2 to 6.4.14
Update symfony/runtime from 6.4.0 to 6.4.24
2025-09-09 16:15:21 +02:00
Stephen Abello
901f8f2a7b N°8691 - Backup modal icon spins around the text 2025-09-09 14:24:18 +02:00
Stephen Abello
ba6fff801b N°8497 - Fix typo in French dictionaries 2025-09-08 14:25:54 +02:00
Stephen Abello
50098bad73 N°5200 - Activity panel entries' relative date isn't translated 2025-09-08 14:03:43 +02:00
jf-cbd
65c9145b10 N°8570 - Allow @ as part of url - add test 2025-09-04 17:04:21 +02:00
Eric Espie
88e0f17164 N°8306 - Wrong line number error if XML is over 65535 lines 2025-08-26 11:56:44 +02:00
Stephen Abello
437296eab9 N°8634 - Newsroom providers are hardly visible if there's more than 2 2025-08-20 16:00:14 +02:00
Stephen Abello
7cf6f65265 Fix typo thanks to @jbostoen 2025-08-06 09:04:36 +02:00
Björn Rudner
491f55c7bd 🐛 Allow @ as part of url (#729) 2025-08-01 15:27:37 +02:00
Stephen Abello
7b44ec23a1 Fix return type in PHPDoc thanks to @jbostoen 2025-07-28 16:14:13 +02:00
BenGrenoble
f00b8d529c N°8286 - Be able to add additional data in iTop session files via custom classes
Change to fit convention.
2025-07-24 10:59:48 +02:00
BenGrenoble
af44ffd0ad N°8286 - Be able to add additional data in iTop session files via custom classes
Experimental warning
2025-07-24 10:28:15 +02:00
Benjamin Dalsass
c63e01e27b N°8552 - Aggregate brick, Search Brick and new look issue 2025-07-23 16:36:01 +02:00
Eric Espie
de54676b9f N°7938 - Cant' create OnInsert/OnUpdate/Afterxxx methods from Designer with iTop 3.2 2025-07-21 16:53:13 +02:00
Benjamin Dalsass
1438006861 N°8419 - Improve Setup process by avoiding the update of the attachment table when contact_id is already set 2025-07-16 16:41:50 +02:00
Eric Espie
e5129c9618 N°7938 - tooltip error style 2025-07-16 15:22:10 +02:00
Eric Espie
d0f816109b N°7938 - Cant' create OnInsert/OnUpdate/Afterxxx methods from Designer with iTop 3.2 2025-07-16 13:22:50 +02:00
Stephen Abello
700470dd29 N°8219 - Allow to reset user cache 2025-07-11 10:28:24 +02:00
Eric Espie
05642cdf84 N°8306 - Wrong line number error if XML is over 65535 lines 2025-07-10 16:53:36 +02:00
bdalsass
497fc4978a N°8340 - CKEditor is slow with large texts 2025-07-10 10:20:23 +02:00
v-dumas
6826ab509d N°6925 - Add a dedicated dict entry for "View the OQL query" 2025-07-09 17:10:07 +02:00
bdalsass
41194ce9ab N°8365 - Portal: impact analysis and silo, Ticket cannot be created due to CI not visible 2025-07-09 07:38:07 +02:00
Anne-Catherine
0e5bece5b6 N°8347 - Bad display of special characters on the portal CheckToWrite message (#712) 2025-07-08 11:16:11 +02:00
jf-cbd
e987ea72bc Fix broken URI 2025-07-07 16:01:57 +02:00
bdalsass
c8371f1c19 N°8395 - php8.1 compatibility in class extended Exception 2025-07-07 14:03:29 +02:00
Eric Espie
031305cfbf Add stack trace to SQL errors
Reload object on modify error to display the real unmodified object
2025-07-07 13:44:45 +02:00
bdalsass
740f83f43e N°8395 - php8.1 compatibility in class extended Exception 2025-07-03 13:39:39 +02:00
bdalsass
14791bf6b4 N°8148 - CAS problem when sending a link ending in &
- ApplicationContextTest mocking service
2025-07-01 15:57:45 +02:00
Benjamin Dalsass
062d543b26 N°8148 - CAS problem when sending a link ending in & (#722)
N°8148 - CAS problem when sending a link ending in &
2025-06-30 14:19:33 +02:00
bdalsass
8cece0f0fd N°8395 - php8.1 compatibility in class extended Exception 2025-06-30 10:01:00 +02:00
odain
471422b274 N°8306 : enhance MFEException constructor and remove boilerplate throw method 2025-06-20 08:08:42 +02:00
odain
5573a222c8 N°8286 - fix call to CompleteSessionData in case of no proper session content 2025-06-19 11:32:23 +02:00
odain
18871566d2 N°8286 - fix call to CompleteSessionData in case of no proper session content 2025-06-19 11:29:48 +02:00
odain
b565b700a9 Merge branch 'feature/7398-sessiontracker-extensibility' into support/3.2 2025-06-19 10:39:35 +02:00
odain
3846087dc1 N°7398 - regenerate autoload 2025-06-19 10:39:09 +02:00
odain
4f96246cbe N°7398-log level with invalid json 2025-06-19 10:38:01 +02:00
odain
ddd3dcdaf2 N°8286 - Be able to add additional data in iTop session files via custom classes 2025-06-19 10:38:01 +02:00
odain
82ad9d4317 N°7398 - be able to add custom data in session files generated by SessionHandler 2025-06-19 10:38:00 +02:00
odain
83eb2b81e3 N°8306 : fix ModelFactoryEx class not found 2025-06-18 16:25:53 +02:00
odain
86d2a3424d N°8306 - Wrong line number error if XML is over 65535 lines 2025-06-18 15:45:39 +02:00
Anne-Cath
6dc48ad36d N°8351 - [Export Impact Analysis] Field to display for the CSV export - Add the friendlyname to each list 2025-06-17 10:22:53 +02:00
Björn Rudner
84476a869f 🎨 remove outdated or duplicated elemets (#726) 2025-06-16 16:25:22 +02:00
jf-cbd
684f829581 Merge commit from fork 2025-06-16 15:06:45 +02:00
Eric Espie
cd1c6f5ec5 N°8404 - Error when upgrading composant version on Designer (Migration status on licence) 2025-06-16 08:59:43 +02:00
Björn Rudner
d1e6334629 N°8456 - 🐛 add missing element attributes (#725) 2025-06-13 16:50:56 +02:00
Eric Espie
047ba8c6c7 Better KPI log performances 2025-06-13 15:31:30 +02:00
v-dumas
f609010d7f Fix typo in SynchroLog class name 2025-06-11 17:49:28 +02:00
v-dumas
9330b5ec9a N°8377 - Fix confusing duplicated stimulus label on Change 2025-06-11 17:42:04 +02:00
v-dumas
18b85d4080 N°8377 - Missing rights on change ITIL for SuperUser 2025-06-11 17:41:01 +02:00
v-dumas
7214e5b43a Fix typo in SynchroLog class name 2025-06-11 17:14:15 +02:00
v-dumas
3689a83953 N°2583 - Control rights per class (bulk read on business, bulk write on others) 2025-06-11 17:13:25 +02:00
v-dumas
a715ce1292 Fix typo in SynchroLog class name 2025-06-11 17:11:43 +02:00
v-dumas
17d85fbb3f N°2583 - Advanced mode on CSV import add technical classes and class codes 2025-06-11 16:50:38 +02:00
v-dumas
5e59aff74f N°2583 - Filter Universal Search based on user rights 2025-06-11 16:48:28 +02:00
v-dumas
9e1e81ccc1 N°2583 - Audit, User, Query classes available for universal Search without the "Administrator" profile 2025-06-11 11:17:27 +02:00
v-dumas
c57c8d2af3 N°8406 - Fix FR translations 2025-06-11 10:16:50 +02:00
bdalsass
8a436f9760 N°8251 - Deprecated function when bulk assign
- Deprecated on strlen
2025-06-06 15:26:05 +02:00
bdalsass
2e77713772 N°8399 - Portal: unnecessary screen is displayed during a transition with no field to display 2025-06-06 15:04:21 +02:00
bdalsass
975c554e91 N°8251 - Deprecated function when bulk assign 2025-06-06 09:02:29 +02:00
bdalsass
ac6f642052 N°8232 - CKEditor: Unexpected lines repeatedly added after blocks 2025-06-06 08:54:23 +02:00
bdalsass
cf9ab2a83c N°8436 - CreateBrick fails when used with an abstract class 2025-06-04 16:22:41 +02:00
bdalsass
3ff3dcba54 N°8308 - Broken URL in sending mail 2025-06-02 17:15:51 +02:00
jf-cbd
657fc912bf N°8198 - ModuleInstallation now extends DBObject + better exception message 2025-06-02 16:09:29 +02:00
Anne-Catherine
5ae5221f6f N°6925 - Enable on any search result edition of OQL (#711) 2025-05-28 10:59:50 +02:00
jf-cbd
b15ca2fbc9 N°8260 - Change format of REST logs when they are close to the SQL field size limit 2025-05-27 10:41:05 +02:00
Stephen Abello
cb382eab4e Fix CI following dict behavior change 2025-05-26 16:23:38 +02:00
Stephen Abello
9723cde24c N°7960 - Add a wrapper to vsprintf to handle argument number disparities (#717) 2025-05-26 15:41:25 +02:00
Stephen Abello
7ae49e2cf4 N°8076 - Fix cancel and close buttons in a modal blocking all buttons for the underlying object 2025-05-26 11:25:08 +02:00
v-dumas
cb13a7a5b4 N°2583 - Audit, User, Query classes can be Import with bulk_modify right 2025-05-23 17:37:47 +02:00
bdalsass
9618e47045 N°8201 - [CVE_Request]_Cross-Site-Script Reflected(XSS Reflected at the name="attr_installed" (Low or Medium) 2025-05-23 10:16:22 +02:00
bdalsass
d84506ea9e Merge remote-tracking branch 'origin/support/2.7' into support/3.2
# Conflicts:
#	pages/UI.php
2025-05-23 10:14:50 +02:00
bdalsass
13239c2751 N°8201 - [CVE_Request]_Cross-Site-Script Reflected(XSS Reflected at the name="attr_installed" (Low or Medium) 2025-05-23 10:06:01 +02:00
bdalsass
8b30e36dd1 N°8168 - Stored XSS in portals lnk 2025-05-23 08:52:18 +02:00
bdalsass
80b290ab88 Merge remote-tracking branch 'origin/support/2.7' into support/3.2
# Conflicts:
#	sources/renderer/bootstrap/fieldrenderer/bslinkedsetfieldrenderer.class.inc.php
2025-05-23 08:49:52 +02:00
bdalsass
81b20ee583 N°8168 - Stored XSS in portals lnk 2025-05-23 08:42:56 +02:00
Eric Espie
2ee30692ff Change deprecated calls 2025-05-22 15:02:29 +02:00
odain
9aa13f57d8 N°8413 - Make data synchro work on DBObject 2025-05-21 10:12:11 +02:00
Stephen Abello
a5545b0084 N°8205 - Security hardening 2025-05-20 09:48:00 +02:00
Stephen Abello
d72e861dfe N°8315 - Security hardening 2025-05-19 14:51:12 +02:00
Stephen Abello
92385273ff N°8315 - Security hardening 2025-05-19 14:45:24 +02:00
bdalsass
61c25f85e7 N°8313 - edit dashboard (fix broken test 3.2) 2025-05-19 13:12:02 +02:00
bdalsass
b1cf2ec137 N°8313 - edit dashboard (fix broken test 3.2) 2025-05-16 14:35:39 +02:00
bdalsass
7549ded51d Merge remote-tracking branch 'origin/support/2.7' into support/3.2
# Conflicts:
#	application/dashboard.class.inc.php
#	setup/setuputils.class.inc.php
#	tests/php-unit-tests/unitary-tests/application/utilsTest.php
2025-05-16 14:19:00 +02:00
bdalsass
38683c20b1 N°8313 - edit dashboard (fix broken test) 2025-05-16 14:05:55 +02:00
bdalsass
81791dd253 N°8313 - edit dashboard 2025-05-16 14:05:55 +02:00
bdalsass
e77e0eec9f N°8355 - render_dashboard 2025-05-16 14:05:55 +02:00
jf-cbd
f5ddbbbe0e Rollback typing parameter 2025-05-13 16:59:43 +02:00
jf-cbd
6811a82e1a Merge remote-tracking branch 'origin/support/2.7' into support/3.2
# Conflicts:
#	datamodels/2.x/itop-backup/status.php
#	setup/setuputils.class.inc.php
2025-05-13 16:40:11 +02:00
jf-cbd
960133c0df N°8379 - fix backup issue 2025-05-13 16:27:59 +02:00
jf-cbd
544c4ae888 N°8379 - fix backup issue 2025-05-13 16:05:39 +02:00
odain
f9d9fcc440 N°7398-log level with invalid json 2025-05-07 11:03:13 +02:00
odain
a4a05a2579 N°7398 - be able to add custom data in session files generated by SessionHandler 2025-05-07 11:03:05 +02:00
jbostoen
9ee18c2f36 Add helper method to create an ObjectResult from a DBObject. (#706)
Author: Jeffrey Bostoen <support@jeffreybostoen.be>
2025-04-25 14:46:33 +02:00
jf-cbd
43a10e6944 Revert "N°8259 - Problem with GetMaxSize on AttributeText"
This reverts commit 29c75f626b.
2025-04-22 09:37:54 +02:00
Benjamin Dalsass
f44468b7a1 N°8245 - External key not saved in n-n link in edition (#703)
* N°8245 - External key not saved in n-n link in edition

* Remove the listener from the input element itself, put it on the body with a selector that matches our input

* Revert "N°8245 - External key not saved in n-n link in edition"

This reverts commit a756f9b159.

---------

Co-authored-by: Stephen Abello <stephen.abello@combodo.com>
2025-03-24 14:05:30 +01:00
4413 changed files with 140103 additions and 189815 deletions

View File

@@ -1,101 +0,0 @@
# 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

View File

@@ -1,6 +0,0 @@
#!/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

@@ -1,7 +0,0 @@
#!/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

View File

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

3015
.doc/composer.lock generated

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 983 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 MiB

View File

@@ -1,104 +0,0 @@
# 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"
checkout support/2.7
commit id: "2025-02-25" tag: "2.7.12"
checkout support/3.1
commit id: "2025-02-25 " tag: "3.1.3"
checkout support/3.2
commit id: "2025-02-25 " tag: "3.2.1"
```
To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start).

View File

@@ -1,20 +0,0 @@
<?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

@@ -1,58 +0,0 @@
<?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

@@ -1,136 +0,0 @@
{% 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

@@ -1,31 +0,0 @@
{% 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

@@ -1,95 +0,0 @@
{% 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

@@ -1,49 +0,0 @@
{% 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

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

View File

@@ -1,122 +0,0 @@
{% 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

@@ -1,42 +0,0 @@
{% 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

@@ -1,5 +0,0 @@
# 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

@@ -1,34 +0,0 @@
{% 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

@@ -1,12 +0,0 @@
{% 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

@@ -1,26 +0,0 @@
{% 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

@@ -1,26 +0,0 @@
{% 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

@@ -1,56 +0,0 @@
{% 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

@@ -1,20 +0,0 @@
{% 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

@@ -1,22 +0,0 @@
{% 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

@@ -1,24 +0,0 @@
{% 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

@@ -1,24 +0,0 @@
{% 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

@@ -1,11 +0,0 @@
{% 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

@@ -1,121 +0,0 @@
{% 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

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

View File

@@ -1,51 +0,0 @@
{% 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

@@ -1,49 +0,0 @@
====== 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

@@ -1,27 +0,0 @@
<?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>

41
.github/issue_template.md vendored Normal file
View File

@@ -0,0 +1,41 @@
<!--
Please explain why you're creating this issue :
- Did you find a bug ? Do you want to propose an enhancement ?
- Are you willing to create a PR for the bug fix / enhancement ? If so, we'll indicate in the issue if we're interested in it.
- Then, please describe how to reproduce the issue (if it's a bug) or what's your improvement proposition (if it's an enhancement).
-->
## Base information
| Question | Answer |
|-------------------------------------------|--------------------------|
| Type of issue ? | Bug report / Enhancement |
| Are you willing to create a PR for that ? | Yes / No |
## Reproduction procedure (bug)
<!--
Please explain step by step how to reproduce the issue on a standard iTop Community.
If it requires a custom datamodel, provide the minimal XML delta to reproduce it on a standard iTop Community.
-->
1. On iTop x.y.z <!-- Put complete iTop version (eg. 3.1.0-2) -->
2. With PHP x.y.z <!-- Put complete PHP version (eg. 8.1.24) -->
3. First go there
4. Then do that
5. ...
6. Finally, see that... (what is expected and what is actually happening)
Additional information (if needed)
<!--
- If possible put error messages, logs or screenshots (you can paste image directly in this editor).
-->
## Reproduction procedure (enhancement)
<!--
Please explain what do you want to improve, and what's your proposition to make it better.
Add screenshots if it's related to UI.
If it requires a custom datamodel, provide the minimal XML delta to reproduce it on a standard iTop Community.
-->

View File

@@ -1,83 +1,69 @@
<!--
IMPORTANT: Before creating your PR, please create an issue first to know if Combodo is interested in your contribution (not needed for translations PR).
Since we may refuse a PR, it's preferable to create an issue first, to avoid spending time coding something that won't be accepted.
IMPORTANT: Please follow the guidelines within this PR template before submitting it, it will greatly help us process your PR. 🙏
Once you've done it, and we confirmed we're interested in it, please follow the guidelines within this PR template before submitting it, it will greatly help us process your PR. 🙏
Any PRs not following the guidelines or with missing information will not be considered.
-->
## Base information
| Question | Answer
|---------------------------------------------------------------|--------
| Related to a SourceForge thead / Another PR / Combodo ticket? | <!-- Put the URL -->
| Type of change? | Bug fix / Enhancement / Translations
## Symptom (bug) / Objective (enhancement)
<!--
If it's a bug
- Explain the symptom in details
- If possible put error messages, logs or screenshots (you can paste image directly in this editor).
If it's an enhancement
- Describe what is blocking you, what is the objective with as much details as possible.
- Add screenshots if it's related to UI.
-->
| Question | Answer |
|---------------------------------------------------------------------------------|--------------------------------------|
| Related to a SourceForge thread / Another PR / A GitHub Issue / Combodo ticket? | <!-- Put the URL --> |
| Type of change? | Bug fix / Enhancement / Translations |
## Reproduction procedure (bug)
<!--
Remove this section only if it's NOT a bug.
Otherwise, explain step by step how to reproduce the issue on a standard iTop Community.
Please explain step by step how to reproduce the issue on a standard iTop Community.
If it requires a custom datamodel, provide the minimal XML delta to reproduce it on a standard iTop Community.
-->
1. On iTop x.y.z <!-- Put complete iTop version (eg. 3.1.0-2) -->
2. With PHP x.y.z <!-- Put complete PHP version (eg. 8.1.24) -->
2. First go there
2. Then do that
3. ...
4. Finally, see that...
3. First go there
4. Then do that
5. ...
6. Finally, see that... (what is expected and what is actually happening)
Additional information (if needed)
<!--
- If possible put error messages, logs or screenshots (you can paste image directly in this editor).
-->
## Reproduction procedure (enhancement)
<!--
Please explain how we can reproduce the feature/behavior you want to improve, and what's your proposition to make it better.
Add screenshots if it's related to UI.
If it requires a custom datamodel, provide the minimal XML delta to reproduce it on a standard iTop Community.
-->
## Cause (bug)
<!--
Remove this section only if it's NOT a bug.
Otherwise, explain what is the cause of the issue (where in the code and why)
-->
## Proposed solution (bug and enhancement)
<!--
Explain in details how you are proposing to solve this:
- What did you do in the code and why
- If you changed something in the UI, put before / after screenshots (you can paste image directly in this editor)
-->
## Checklist before requesting a review
<!--
Don't remove these lines, check them once done.
-->
- [ ] I have performed a self-review of my code
- [ ] I have tested all changes I made on an iTop instance
- [ ] I have added a unit test, otherwise I have explained why I couldn't
- [ ] Is the PR clear and detailed enough so anyone can understand digging in the code?
## Checklist of things to do before PR is ready to merge
<!--
Things that needs to be done in the PR before it can be considered as ready to be merged
Examples:
- Changes requested in the review
- Unit test to add
- Dictionary entries to translate
- ...
-->
- [ ] ...
- [ ] ...
- [ ] ...
- [ ] Is the PR clear and detailed enough so anyone can understand without digging in the code?

View File

@@ -26,13 +26,23 @@ jobs:
fi
- name: Add internal tag if member
- name: Add internal tag if member of the organization
if: env.is_member == 'true'
run: |
curl -X POST -H "Authorization: token ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/Combodo/iTop/issues/${{ github.event.pull_request.number }}/labels \
-d '{"labels":["internal"]}'
- name: Set PR author as assignee if member of the organization
if: env.is_member == 'true'
run: |
curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}" \
https://api.github.com/repos/Combodo/iTop/issues/${{ github.event.pull_request.number }}/assignees \
-d '{"assignees":["${{ github.event.pull_request.user.login }}"]}'
env:
is_member: ${{ env.is_member }}
@@ -40,4 +50,4 @@ jobs:
uses: actions/add-to-project@v1.0.2
with:
project-url: ${{ env.project_url }}
github-token: ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}
github-token: ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}

3
.gitignore vendored
View File

@@ -58,6 +58,9 @@ tests/*/vendor/*
/tests/php-unit-tests/phpunit.xml
/tests/php-unit-tests/postbuild_integration.xml
# PHP CS Fixer: Cache file
/.php-cs-fixer.cache
# Jetbrains
/.idea/**

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,34 +25,12 @@
if (count($argv) === 1) {
echo "⚠ You must pass the base tag/sha1 as parameter\n";
echo '⚠ You must pass the base tag/sha1 as parameter';
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);
@@ -95,5 +73,5 @@ echo "\n";
echo "# Logs line without bug referenced\n";
echo "sha1;subject\n";
foreach ($aLogLineNoBug as $sLogLine) {
echo ReplaceGitmojis($sLogLine)."\n";
}
echo "$sLogLine\n";
}

View File

@@ -1,589 +0,0 @@
{
"$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);
$oFileXml->loadXML($sFileContent, LIBXML_BIGLINES);
$oFileItopFormat = new iTopDesignFormat($oFileXml);

View File

@@ -4,30 +4,29 @@ You want to contribute to iTop? Many thanks to you! 🎉 👍
Here are some guidelines that will help us integrate your work!
## Contributions
### Subjects
You are welcome to create pull requests on any of those subjects:
* 🐛 bug fix
* 🌐 translation / i18n / l10n
If you want to implement a **new feature**, please [create a corresponding ticket](https://sourceforge.net/p/itop/tickets/new/) for review.
If you ever want to begin implementation, do so in a fork, and add a link to the corresponding commits in the ticket.
If you want to implement a **new feature**, please [create a corresponding issue](https://github.com/Combodo/iTop/issues) for review.
We should review within two weeks, and get back to you to indicate if we're interested in your proposal or not.
f you ever want to begin implementation, do so in a fork, and add a link to the corresponding commits in the ticket.
For all **security related subjects**, please see our [security policy](SECURITY.md).
All **datamodel modification** should be done in an extension. Beware that such change would
impact all existing customers, and could prevent them from
upgrading!
Combodo has a long experience of datamodel changes: they are very disruptive!
All **datamodel modification** should be done in an extension. Beware that such change would
impact all existing customers, and could prevent them from upgrading!
Combodo has a long experience of datamodel changes: they are very disruptive!
This is why we avoid them in iTop core, especially the changes on existing objects/fields.
If you have an idea you're sure would benefit to all of iTop users, you may
[create a corresponding ticket](https://sourceforge.net/p/itop/tickets/new/) to submit it, but be warned that there are lots of good
If you have an idea you're sure would benefit to all of iTop users, you may
[create a corresponding ticket](https://sourceforge.net/p/itop/tickets/new/) to submit it, but be warned that there are lots of good
reasons to refuse such changes.
### 📄 License and copyright
iTop is distributed under the AGPL-3.0 license (see the [license.txt] file).
The iTop repository is divided in three parts: iTop (mainly PHP/JS/XML sources and dictionaries), images, and third-party libraries.
@@ -37,48 +36,33 @@ Anyhow, you are encouraged to signal your contribution by the mean of `@author`
If you want to use another license or keep the code ownership (copyright), you may [create an extension][wiki new ext].
[license.txt]: https://github.com/Combodo/iTop/blob/develop/license.txt
[wiki new ext]: https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Astart#by_writing_your_own_extension
[wiki new ext]: https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Astart#by_writing_your_own_extension
## 🔀 iTop branch model
When we first start with Git, we were using the [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) branch model. As
there was some confusions about branches to use for current developed release and previous maintained release, and also because we were
using just a very few of the GitFlow commands, we decided to add just a little modification to this branch model : since april 2020
we don't have a `master` branch anymore.
there was some confusions about branches to use for current developed release and previous maintained release, and also because we were
using just a very few of the GitFlow commands, we decided to add just a little modification to this branch model : since April 2020
we don't have a `master` branch anymore.
Here are the branches we use and their meaning :
Here are the branches we use and their meaning :
- `develop`: ongoing development version
- `release/*`: if present, that means we are working on a alpha/beta/rc version for shipping
- `support/*`: maintenance branches for older versions
For example, if no version is currently prepared for shipping we could have:
- `develop` containing future 3.1.0 version
- `support/3.0`: 3.0.x maintenance version
- `support/2.7`: 2.7.x maintenance version
- `support/2.6`: 2.6.x maintenance version
- `develop` containing future 3.3.0 version
- `support/3.2`: 3.2.x maintenance version
In this example, when 3.1.0-beta is shipped that will become:
And when 3.3.0 final will be out:
- `develop`: future 3.2.0 version
- `release/3.1.0`: 3.1.0-beta
- `support/3.0`: 3.0.x maintenance version
- `support/2.7`: 2.7.x maintenance version
- `support/2.6`: 2.6.x maintenance version
And when 3.1.0 final will be out:
- `develop`: future 3.2.0 version
- `support/3.1`: 3.1.x maintenance version (will host developments for 3.1.1)
- `support/3.0`: 3.0.x maintenance version
- `support/2.7`: 2.7.x maintenance version
- `support/2.6`: 2.6.x maintenance version
- `develop`: future 3.4.0 version
- `support/3.2`: 3.2.x maintenance version (will host developments for 3.2.4)
Also note that we have a "micro-version" concept : each of those versions have a very small amount of modifications. They are made from
`support/*` branches as well. For example 2.6.2-1 and 2.6.2-2 were made from the `support/2.6.2` branch.
`support/*` branches as well. For example 2.6.2-1 and 2.6.2-2 were made from the `support/2.6.2` branch.
## Coding
@@ -92,11 +76,93 @@ A [dedicated page](https://www.itophub.io/wiki/page?id=latest%3Acustomization%3A
2. Create a branch in this fork, based on the develop branch
3. Code !
Do create a dedicated branch for each modification you want to propose : if you don't it will be very hard to merge back your work !
Do create a dedicated branch for each modification you want to propose : if you don't, it will be very hard to merge back your work !
Most of the time you should based your developments on the develop branch.
Most of the time you should base your developments on the develop branch.
That may be different if you want to fix a bug, please use develop anyway and ask in your PR if rebase is possible.
### 🎨 PHP styleguide# Contributing to iTop
You want to contribute to iTop? Many thanks to you! 🎉 👍
Here are some guidelines that will help us integrate your work!
### Subjects
You are welcome to create pull requests on any of those subjects:
* 🐛 bug fix
* 🌐 translation / i18n / l10n
If you want to implement a **new feature**, please [create a corresponding issue](https://github.com/Combodo/iTop/issues) for review.
We should review within two weeks, and get back to you to indicate if we're interested in your proposal or not.
f you ever want to begin implementation, do so in a fork, and add a link to the corresponding commits in the ticket.
For all **security related subjects**, please see our [security policy](SECURITY.md).
All **datamodel modification** should be done in an extension. Beware that such change would
impact all existing customers, and could prevent them from upgrading!
Combodo has a long experience of datamodel changes: they are very disruptive!
This is why we avoid them in iTop core, especially the changes on existing objects/fields.
If you have an idea you're sure would benefit to all of iTop users, you may
[create a corresponding ticket](https://sourceforge.net/p/itop/tickets/new/) to submit it, but be warned that there are lots of good
reasons to refuse such changes.
### 📄 License and copyright
iTop is distributed under the AGPL-3.0 license (see the [license.txt] file).
The iTop repository is divided in three parts: iTop (mainly PHP/JS/XML sources and dictionaries), images, and third-party libraries.
Combodo has the copyright on most of the source files in the iTop part of the repository: please do not modify the existing file copyrights.
Anyhow, you are encouraged to signal your contribution by the mean of `@author` annotations.
If you want to use another license or keep the code ownership (copyright), you may [create an extension][wiki new ext].
[license.txt]: https://github.com/Combodo/iTop/blob/develop/license.txt
[wiki new ext]: https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Astart#by_writing_your_own_extension
## 🔀 iTop branch model
When we first start with Git, we were using the [GitFlow](https://nvie.com/posts/a-successful-git-branching-model/) branch model. As
there was some confusions about branches to use for current developed release and previous maintained release, and also because we were
using just a very few of the GitFlow commands, we decided to add just a little modification to this branch model : since April 2020
we don't have a `master` branch anymore.
Here are the branches we use and their meaning :
- `develop`: ongoing development version
- `support/*`: maintenance branches for older versions
For example, if no version is currently prepared for shipping we could have:
- `develop` containing future 3.3.0 version
- `support/3.2`: 3.2.x maintenance version
And when 3.3.0 final will be out:
- `develop`: future 3.4.0 version
- `support/3.2`: 3.2.x maintenance version (will host developments for 3.2.4)
Also note that we have a "micro-version" concept : each of those versions have a very small amount of modifications. They are made from
`support/*` branches as well. For example 2.6.2-1 and 2.6.2-2 were made from the `support/2.6.2` branch.
## Coding
### 🌐 Translations
A [dedicated page](https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Atranslation) is available in the official wiki.
### Where to start ?
1. Create a fork from our repository (see [Working with forks - GitHub Help](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/working-with-forks))
2. Create a branch in this fork, based on the develop branch
3. Code !
Do create a dedicated branch for each modification you want to propose : if you don't, it will be very hard to merge back your work !
Most of the time you should base your developments on the develop branch.
That may be different if you want to fix a bug, please use develop anyway and ask in your PR if rebase is possible.
### 🎨 PHP styleguide
@@ -106,7 +172,7 @@ Please follow [our guidelines](https://www.itophub.io/wiki/page?id=latest%3Acust
Please create tests that covers as much as possible the code you're submitting.
Our tests are located in the `test/` directory, containing a PHPUnit config file : `phpunit.xml.dist`.
Our tests are located in the `tests/` directory, containing a PHPUnit config file : `phpunit.xml.dist`.
### Git Commit Messages
@@ -138,14 +204,14 @@ When your code is working, please:
* Rebase your branch on our repo last commit,
* Create a pull request. _Detailed procedure to work on fork and create PR is available [in GitHub help pages](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)_.
* Pull request description: mind to add all the information useful to understand why you're suggesting this modification and anything necessary to dive into your work. Especially:
- Bugfixes: exact steps to reproduce the bug (given/when/then), description of the bug cause and what solution is implemented
- Enhancements: use cases, implementation details if needed
* Mind to check the "[Allow edits from maintainers](https://docs.github.com/en/github-ae@latest/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork)" option ! (note that if you are working with an org fork, this option [won't be available](https://github.com/orgs/community/discussions/5634))
- Bugfixes: exact steps to reproduce the bug (given/when/then), description of the bug cause and what solution is implemented
- Enhancements: use cases, implementation details if needed
* Mind to check the "[Allow edits from maintainers](https://docs.github.com/en/github-ae@latest/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork)" option ! (note that if you are working with an org fork, this
option [won't be available](https://github.com/orgs/community/discussions/5634))
## 🙏 We are thankful
We are thankful for all your contributions to the iTop universe! As a thank you gift, we will send stickers to every iTop (& extensions) contributors!
We are thankful for all your contributions to the iTop universe! As a thank-you gift, we will send stickers to every iTop (& extensions) contributors!
We have one sticker per contribution type. You might get multiple stickers with one contribution though :)
@@ -157,8 +223,4 @@ We have one sticker per contribution type. You might get multiple stickers with
* Graduated: Follow a Combodo's iTop training
* Ambassador: Outstanding community contributors
* Beta tester: Test and give feedback on beta releases
* Extension developer: Develop and publish an extension
Here is the design of each stickers for year 2024:
![iTop stickers 2024](.doc/contributing-guide/2024.contributing-stickers-side-by-side.png)
* Extension developer: Develop and publish an extension

View File

@@ -1,5 +1,9 @@
<p align="center"><a href="https://www.combodo.com/itop-193" target="_blank">
<img src="https://www.combodo.com/logos/logo-itop-baseline.svg" width=350>
<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>
</a></p>
@@ -76,9 +80,7 @@ We would like to give a special thank you 🤗 to the people from the community
### Names
- Al Hallak, Amr (a.k.a [@v4yne1](https://github.com/v4yne1))
- Alves, David
- Audon, Florian
- Beck, Pedro
- Beer, Christian (a.k.a [@ChristianBeer](https://www.github.com/ChristianBeer))
- Bilger, Jean-François
@@ -90,26 +92,19 @@ We would like to give a special thank you 🤗 to the people from the community
- Colantoni, Maria Laura
- Couronné, Guy
- Dejin, Bie (a.k.a [@bdejin](https://github.com/bdejin))
- Delicado, Elodie
- Dvořák, Lukáš
- Goethals, Stefan
- Giuva, Vincenzo Katriel (a.k.a [@DarkNight97boss](https://github.com/DarkNight97boss))
- Gumble, David
- Heloir, Arthur
- Janssens, Jelle (a.k.a [@janssensjelle](https://github.com/janssensjelle))
- Ji, Leeb (冀利斌) (a.k.a [@chileeb](https://github.com/chileeb))
- Kaltefleiter, Lars (a.k.a [@larhip](https://www.github.com/larhip))
- Khamit, Shamil
- Kincel, Martin
- Konečný, Kamil
- Kunin, Vladimir
- Lassiter, Denis (a.k.a [@delassiter](https://github.com/delassiter))
- Lassiter, Dennis
- Lazcano, Federico
- Lucas, Jonathan
- Malik, Remie
- Mantel, Ina
- Martin, Pierre (a.k.a [@Worty](https://github.com/worty-syn))
- Melchiorre, Romain
- Mindêllo de Andrade, Lucas (a.k.a [@rokam](https://www.github.com/rokam))
- Mozart de Oliveira, Eduardo (a.k.a [@eduardomozart](https://github.com/eduardomozart))
- Raenker, Martin
@@ -121,12 +116,10 @@ We would like to give a special thank you 🤗 to the people from the community
- Seki, Shoji
- Shilov, Vladimir
- Stetina, Pavel (a.k.a [@Stetinac](https://github.com/Stetinac))
- Stukalov, Ilya (a.k.a [@ilya](https://www.github.com/ilya-stukalov))
- Stukalov, Ilya (a.k.a [@ilya](https://www.github.com/ilya)-stukalov)
- Tarjányi, Csaba (a.k.a [@tacsaby](https://github.com/tacsaby))
- Toraya, Chairat (a.k.a [@Kyokito1412](https://github.com/Kyokito1412))
- Tulio, Marco
- Turrubiates, Miguel
- Višnjić, Aldin (a.k.a[@viliald](https://github.com/viliald))
- Vlk, Karel (a.k.a [@vlk-charles](https://www.github.com/vlk-charles))
### Aliases
@@ -148,6 +141,4 @@ We would like to give a special thank you 🤗 to the people from the community
- [ITOMIG](https://www.itomig.de/)
- [Pimkie](https://www.pimkie.com/)
- [Super-Visions](https://www.super-visions.com/)
- [Defence Tech Cyber Security - Malware Lab](https://github.com/DefenceTechSecurity)
- Orange Cyberdefense
- MipihSIB

View File

@@ -1,9 +1,10 @@
<?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.
@@ -23,30 +24,29 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
class UserRightsMatrixClassGrant extends DBObject
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"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", 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 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("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())));
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" => []]));
}
}
@@ -54,25 +54,25 @@ class UserRightsMatrixClassStimulusGrant extends DBObject
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"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", 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 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("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())));
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" => []]));
}
}
@@ -80,42 +80,39 @@ class UserRightsMatrixAttributeGrant extends DBObject
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"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", 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 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 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())));
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" => []]));
}
}
class UserRightsMatrix extends UserRightsAddOnAPI
{
static public $m_aActionCodes = array(
public static $m_aActionCodes = [
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')
@@ -149,8 +146,7 @@ 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;
@@ -158,23 +154,16 @@ class UserRightsMatrix extends UserRightsAddOnAPI
protected function SetupUser($iUserId, $bNewUser = false)
{
foreach(array('bizmodel', 'application', 'gui', 'core/cmdb') as $sCategory)
{
foreach (MetaModel::GetClasses($sCategory) as $sClass)
{
foreach (self::$m_aActionCodes as $iActionCode => $sAction)
{
if ($bNewUser)
{
foreach (['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);
@@ -184,19 +173,14 @@ 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);
@@ -206,21 +190,15 @@ 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 (array('read', 'modify') as $sAction)
{
if ($bAddCell) {
foreach (['read', 'modify'] as $sAction) {
// Create a new entry
$oMyAttGrant = MetaModel::NewObject("UserRightsMatrixAttributeGrant");
$oMyAttGrant->Set("userid", $iUserId);
@@ -261,14 +239,13 @@ class UserRightsMatrix extends UserRightsAddOnAPI
*/
}
public function Init()
{
// Could be loaded in a shared memory (?)
return true;
}
public function GetSelectFilter($oUser, $sClass, $aSettings = array())
public function GetSelectFilter($oUser, $sClass, $aSettings = [])
{
$oNullFilter = new DBObjectSearch($sClass);
return $oNullFilter;
@@ -276,21 +253,18 @@ 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;
@@ -304,21 +278,18 @@ 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;
@@ -333,14 +304,12 @@ 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;
@@ -358,5 +327,3 @@ class UserRightsMatrix extends UserRightsAddOnAPI
}
UserRights::SelectModule('UserRightsMatrix');
?>

View File

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

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -29,56 +30,52 @@ class UserRightsBaseClassGUI extends cmdbAbstractObject
}
}
class URP_Profiles extends UserRightsBaseClassGUI
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "addon/userrights,grant_by_profile,filter",
"key_type" => "autoincrement",
"name_attcode" => "name",
"complementary_name_attcode" => array('description'),
"complementary_name_attcode" => ['description'],
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"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", 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 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 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())));
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" => []]));
// Display lists
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
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
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('name','description')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array ('name','description'));
MetaModel::Init_SetZListItems('standard_search', ['name','description']); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', ['name','description']);
}
protected static $m_aCacheProfiles = null;
public static function DoCreateProfile($sName, $sDescription)
{
if (is_null(self::$m_aCacheProfiles))
{
self::$m_aCacheProfiles = array();
if (is_null(self::$m_aCacheProfiles)) {
self::$m_aCacheProfiles = [];
$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");
@@ -89,27 +86,21 @@ class URP_Profiles extends UserRightsBaseClassGUI
return $iId;
}
function GetGrantAsHtml($oUserRights, $sClass, $sAction)
public function GetGrantAsHtml($oUserRights, $sClass, $sAction)
{
$bGrant = $oUserRights->GetProfileActionGrant($this->GetKey(), $sClass, $sAction);
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>';
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>';
}
}
function DoShowGrantSumary($oPage)
public 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;
@@ -118,21 +109,18 @@ class URP_Profiles extends UserRightsBaseClassGUI
// Note: for sure, we assume that the instance is derived from UserRightsProfile
$oUserRights = UserRights::GetModuleInstance();
$aDisplayData = array();
foreach (MetaModel::GetClasses('bizmodel,grant_by_profile') as $sClass)
{
$aStimuli = array();
foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus)
{
$aDisplayData = [];
foreach (MetaModel::GetClasses('bizmodel,grant_by_profile') as $sClass) {
$aStimuli = [];
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[] = array(
$aDisplayData[] = [
'class' => MetaModel::GetName($sClass),
'read' => $this->GetGrantAsHtml($oUserRights, $sClass, 'r'),
'bulkread' => $this->GetGrantAsHtml($oUserRights, $sClass, 'br'),
@@ -141,22 +129,22 @@ class URP_Profiles extends UserRightsBaseClassGUI
'delete' => $this->GetGrantAsHtml($oUserRights, $sClass, 'd'),
'bulkdelete' => $this->GetGrantAsHtml($oUserRights, $sClass, 'bd'),
'stimuli' => $sStimuli,
);
];
}
$aDisplayConfig = array();
$aDisplayConfig['class'] = array('label' => Dict::S('UI:UserManagement:Class'), 'description' => Dict::S('UI:UserManagement:Class+'));
$aDisplayConfig['read'] = array('label' => Dict::S('UI:UserManagement:Action:Read'), 'description' => Dict::S('UI:UserManagement:Action:Read+'));
$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+'));
$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+')];
$oPage->table($aDisplayConfig, $aDisplayData);
}
function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
public function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
{
parent::DisplayBareRelations($oPage, $bEditMode);
@@ -166,10 +154,9 @@ class URP_Profiles extends UserRightsBaseClassGUI
public static function GetReadOnlyAttributes()
{
return array('name', 'description');
return ['name', 'description'];
}
// returns an array of id => array of column => php value(so-called "real value")
public static function GetPredefinedObjects()
{
@@ -181,15 +168,13 @@ 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();
}
}
@@ -202,11 +187,10 @@ 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 = array(), $sTargetState = '')
public function GetAttributeFlags($sAttCode, &$aReasons = [], $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;
}
@@ -214,52 +198,52 @@ class URP_Profiles extends UserRightsBaseClassGUI
}
}
class URP_UserProfile extends UserRightsBaseClassGUI
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "addon/userrights,grant_by_profile,filter",
"key_type" => "autoincrement",
"name_attcode" => array("userlogin", "profile"),
"name_attcode" => ["userlogin", "profile"],
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"db_table" => "priv_urp_userprofile",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => true, /** @since 3.1.0 N°6482 */
'uniqueness_rules' => array(
'no_duplicate' => array(
'attributes' => array(
'uniqueness_rules' => [
'no_duplicate' => [
'attributes' => [
0 => 'userid',
1 => 'profileid',
),
],
'filter' => '',
'disabled' => false,
'is_blocking' => true,
),
),
);
],
],
];
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
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("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("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 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 AttributeString("reason", array("allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("reason", ["allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
// Display lists
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
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
// Search criteria
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
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
}
public function CheckToDelete(&$oDeletionPlan)
@@ -267,15 +251,14 @@ 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, array('deletion not allowed in demo mode.'), true);
$oDeletionPlan->SetDeletionIssues($this, ['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);
@@ -292,15 +275,14 @@ 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, array('deletion not allowed in demo mode.'), true);
$oDeletionPlan->SetDeletionIssues($this, ['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);
@@ -336,29 +318,26 @@ 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'));
}
}
@@ -369,33 +348,33 @@ class URP_UserOrg extends UserRightsBaseClassGUI
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "addon/userrights,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => array("userlogin", "allowed_org_name"),
"name_attcode" => ["userlogin", "allowed_org_name"],
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"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", 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("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("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 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 AttributeString("reason", array("allowed_values"=>null, "sql"=>"reason", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("reason", ["allowed_values" => null, "sql" => "reason", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
// Display lists
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
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
// Search criteria
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
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
}
protected function OnInsert()
@@ -418,40 +397,37 @@ 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
{
static public $m_aActionCodes = array(
public static $m_aActionCodes = [
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
@@ -472,8 +448,7 @@ 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');
@@ -481,20 +456,17 @@ 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", array('name' => ADMIN_PROFILE_NAME), true /*all data*/);
if (is_object($oAdminProfile))
{
$oAdminProfile = MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", ['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');
@@ -509,11 +481,11 @@ class UserRightsProfile extends UserRightsAddOnAPI
{
}
protected $m_aUserOrgs = array(); // userid -> array of orgid
protected $m_aUserOrgs = []; // userid -> array of orgid
protected $m_aAdministrators = null; // [user id]
// Built on demand, could be optimized if necessary (doing a query for each attribute that needs to be read)
protected $m_aObjectActionGrants = array();
protected $m_aObjectActionGrants = [];
/**
* Read and cache organizations allowed to the given user
@@ -528,31 +500,25 @@ class UserRightsProfile extends UserRightsAddOnAPI
public function GetUserOrgs($oUser, $sClass)
{
$iUser = $oUser->GetKey();
if (!array_key_exists($iUser, $this->m_aUserOrgs))
{
$this->m_aUserOrgs[$iUser] = array();
if (!array_key_exists($iUser, $this->m_aUserOrgs)) {
$this->m_aUserOrgs[$iUser] = [];
$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), array(), array('userid' => $iUser));
while ($aRow = $oUserOrgSet->FetchAssoc())
{
$oUserOrgSet = new DBObjectSet(DBObjectSearch::FromOQL_AllData($sUserOrgQuery), [], ['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, array(), array('userid' => $iUser));
while ($oUserOrg = $oUserOrgSet->Fetch())
{
$oUserOrgSet = new DBObjectSet($oSearch, [], ['userid' => $iUser]);
while ($oUserOrg = $oUserOrgSet->Fetch()) {
$this->m_aUserOrgs[$iUser][] = $oUserOrg->Get('allowed_org_id');
}
}
@@ -563,21 +529,19 @@ class UserRightsProfile extends UserRightsAddOnAPI
public function ResetCache()
{
// Loaded by Load cache
$this->m_aUserOrgs = array();
$this->m_aUserOrgs = [];
// Cache
$this->m_aObjectActionGrants = array();
$this->m_aObjectActionGrants = [];
$this->m_aAdministrators = null;
}
public function LoadCache()
{
static $bSharedObjectInitialized = false;
if (!$bSharedObjectInitialized)
{
if (!$bSharedObjectInitialized) {
$bSharedObjectInitialized = true;
if (self::HasSharing())
{
if (self::HasSharing()) {
SharedObject::InitSharedClassProperties();
}
}
@@ -615,45 +579,40 @@ class UserRightsProfile extends UserRightsAddOnAPI
*/
public function ListProfiles($oUser)
{
$aRet = array();
$aRet = [];
$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 = array())
public function GetSelectFilter($oUser, $sClass, $aSettings = [])
{
$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 = array();
$aConditions = [];
// 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'
@@ -662,20 +621,15 @@ 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);
}
else if (($sClass == 'URP_UserProfile') || ($sClass == 'User') || (is_subclass_of($sClass, 'User')))
{
} elseif (($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);
@@ -685,17 +639,14 @@ 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);
}
}
@@ -710,10 +661,9 @@ class UserRightsProfile extends UserRightsAddOnAPI
*/
private function GetAdministrators()
{
if ($this->m_aAdministrators === null)
{
if ($this->m_aAdministrators === null) {
// Find all administrators
$this->m_aAdministrators = array();
$this->m_aAdministrators = [];
$oAdministratorsFilter = new DBObjectSearch('User');
$oLnkFilter = new DBObjectSearch('URP_UserProfile');
$oExpression = new FieldExpression('profileid', 'URP_UserProfile');
@@ -723,9 +673,8 @@ class UserRightsProfile extends UserRightsAddOnAPI
$oAdministratorsFilter->AddCondition_ReferencedBy($oLnkFilter, 'userid');
$oAdministratorsFilter->AllowAllData(true); // Mandatory to prevent infinite recursion !!
$oSet = new DBObjectSet($oAdministratorsFilter);
$oSet->OptimizeColumnLoad(array('User' => array('login')));
while($oUser = $oSet->Fetch())
{
$oSet->OptimizeColumnLoad(['User' => ['login']]);
while ($oUser = $oSet->Fetch()) {
$this->m_aAdministrators[] = $oUser->GetKey();
}
}
@@ -741,7 +690,6 @@ 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)
{
@@ -758,33 +706,29 @@ 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;
}
}
@@ -792,9 +736,9 @@ class UserRightsProfile extends UserRightsAddOnAPI
$iPermission = $bStatus ? UR_ALLOWED_YES : UR_ALLOWED_NO;
$aRes = array(
$aRes = [
'permission' => $iPermission,
);
];
$this->m_aObjectActionGrants[$iUser][$sClass][$iActionCode] = $aRes;
return $aRes;
}
@@ -809,20 +753,13 @@ 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)) {
@@ -886,8 +823,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);
}
@@ -895,20 +832,14 @@ 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;
}
}
@@ -932,22 +863,16 @@ class UserRightsProfile extends UserRightsAddOnAPI
{
$sAttCode = null;
$aCallSpec = array($sClass, 'MapContextParam');
if (($sClass == 'Organization') || is_subclass_of($sClass, 'Organization'))
{
$aCallSpec = [$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';
}
@@ -960,14 +885,11 @@ 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,4 +1,5 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -25,5 +26,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,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -48,8 +49,7 @@ 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,4 +1,5 @@
<?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
@@ -16,10 +17,9 @@ use Combodo\iTop\Application\WebPage\AjaxPage;
*/
class ajax_page extends AjaxPage
{
function __construct($s_title)
public function __construct($s_title)
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('ajax_page is deprecated. Please use AjaxPage instead');
parent::__construct($s_title);
}
}

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -11,7 +12,6 @@
require_once(APPROOT.'/application/applicationcontext.class.inc.php');
require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
require_once(APPROOT.'/application/displayblock.class.inc.php');
require_once(APPROOT.'/application/audit.AuditFilterField.class.inc.php');
require_once(APPROOT.'/application/audit.category.class.inc.php');
require_once(APPROOT.'/application/audit.domain.class.inc.php');
require_once(APPROOT.'/application/audit.rule.class.inc.php');

View File

@@ -1,9 +1,10 @@
<?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.
@@ -16,7 +17,6 @@
// 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,7 +86,6 @@ class PortalURLMaker implements iDBObjectURLMaker
}
}
/**
* Helper class to store and manipulate the parameters that make the application's context
*
@@ -99,112 +98,117 @@ 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 = array(
'org_id', 'menu'
);
if ($bReadContext)
{
$this->ReadContext();
$this->aNames = [
'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 = array();
$aContext = utils::ReadParam('c', array(), false, 'context_param');
foreach($this->aNames as $sName)
{
if (!isset(self::$aDefaultValues)) {
self::$aDefaultValues = [];
$aContext = utils::ReadParam('c', [], 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()
public function GetForLink(bool $bWithLeadingAmpersand = false)
{
$aParams = array();
foreach($this->aValues as $sName => $sValue)
{
// 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[] = "c[$sName]".'='.urlencode($sValue);
}
return implode("&", $aParams);
$sReturnValue = implode('&', $aParams);
// add the leading ampersand if requested
if ($bWithLeadingAmpersand) {
$sReturnValue = '&'.$sReturnValue;
}
return $sReturnValue;
}
/**
* @since 3.0.0 N°2534 - dashboard: bug with autorefresh that deactivates filtering on organisation
* Returns the params as c[menu]:..., c[org_id]:....
@@ -263,14 +267,13 @@ class ApplicationContext
*/
public function GetAsHash()
{
$aReturn = array();
foreach($this->aValues as $sName => $sValue)
{
$aReturn = [];
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
@@ -283,11 +286,10 @@ 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]);
}
}
@@ -303,27 +305,22 @@ class ApplicationContext
public function InitObjectFromContext(DBObject &$oObj)
{
$sClass = get_class($oObj);
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
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
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);
}
}
}
}
}
}
}
/**
@@ -347,14 +344,10 @@ 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';
}
}
@@ -372,23 +365,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(array($sUrlMakerClass, 'MakeObjectUrl'), $sObjClass, $sObjKey);
if (utils::StrLen($sUrl) > 0) {
if ($bWithNavigationContext) {
return $sUrl."&".$oAppContext->GetForLink();
} else {
return $sUrl;
}
} else {
return '';
}
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 '';
}
}
/**
@@ -397,13 +390,10 @@ class ApplicationContext
*/
protected static function LoadPluginProperties()
{
if (Session::IsSet('PluginProperties'))
{
if (Session::IsSet('PluginProperties')) {
self::$m_aPluginProperties = Session::Get('PluginProperties');
}
else
{
self::$m_aPluginProperties = array();
} else {
self::$m_aPluginProperties = [];
}
}
@@ -416,7 +406,9 @@ 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);
@@ -429,15 +421,14 @@ class ApplicationContext
*/
public static function GetPluginProperties($sPluginClass)
{
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
if (array_key_exists($sPluginClass, self::$m_aPluginProperties))
{
return self::$m_aPluginProperties[$sPluginClass];
if (is_null(self::$m_aPluginProperties)) {
self::LoadPluginProperties();
}
else
{
return array();
if (array_key_exists($sPluginClass, self::$m_aPluginProperties)) {
return self::$m_aPluginProperties[$sPluginClass];
} else {
return [];
}
}

View File

@@ -524,7 +524,7 @@ abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
*/
public function EnumUsedAttributes($oObject)
{
return array();
return [];
}
/**
@@ -548,7 +548,7 @@ abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
*/
public function EnumAllowedActions(DBObjectSet $oSet)
{
return array();
return [];
}
}
@@ -686,7 +686,7 @@ abstract class AbstractApplicationObjectExtension implements iApplicationObjectE
*/
public function OnCheckToWrite($oObject)
{
return array();
return [];
}
/**
@@ -694,7 +694,7 @@ abstract class AbstractApplicationObjectExtension implements iApplicationObjectE
*/
public function OnCheckToDelete($oObject)
{
return array();
return [];
}
/**
@@ -739,21 +739,21 @@ interface iPopupMenuExtension
* $param is a DBObjectSet containing the list of objects
* @api
*/
const MENU_OBJLIST_ACTIONS = 1;
public 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
*/
const MENU_OBJLIST_TOOLKIT = 2;
public 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
*/
const MENU_OBJDETAILS_ACTIONS = 3;
public 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
*/
const MENU_DASHBOARD_ACTIONS = 4;
public const MENU_DASHBOARD_ACTIONS = 4;
/**
* Insert an item into the User menu (upper right corner)
*
* $param is null
* @api
*/
const MENU_USER_ACTIONS = 5;
public 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
*/
const PORTAL_OBJLISTITEM_ACTIONS = 7;
public 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
*/
const PORTAL_OBJDETAILS_ACTIONS = 8;
public 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
*/
const PORTAL_OBJLIST_ACTIONS = 6;
public 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
*/
const PORTAL_USER_ACTIONS = 9;
public 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
*/
const PORTAL_MENU_ACTIONS = 10;
public 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 = array();
$this->aCssClasses = [];
}
/**
@@ -920,7 +920,6 @@ abstract class ApplicationPopupMenuItem
$this->aCssClasses[] = $sCssClass;
}
/**
* @param $sTooltip
*
@@ -976,7 +975,7 @@ abstract class ApplicationPopupMenuItem
/** @ignore */
public function GetLinkedScripts()
{
return array();
return [];
}
}
@@ -1015,13 +1014,13 @@ class URLPopupMenuItem extends ApplicationPopupMenuItem
/** @ignore */
public function GetMenuItem()
{
return array('label' => $this->GetLabel(),
return ['label' => $this->GetLabel(),
'url' => $this->GetUrl(),
'target' => $this-> GetTarget(),
'css_classes' => $this->aCssClasses,
'icon_class' => $this->sIconClass,
'tooltip' => $this->sTooltip
);
'tooltip' => $this->sTooltip,
];
}
/** @ignore */
@@ -1065,7 +1064,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 = array())
public function __construct($sUID, $sLabel, $sJSCode, $aIncludeJSFiles = [])
{
parent::__construct($sUID, $sLabel);
$this->sJsCode = $sJSCode;
@@ -1077,14 +1076,14 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
public function GetMenuItem()
{
// Note: the semicolumn is a must here!
return array(
return [
'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 */
@@ -1116,7 +1115,7 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
*/
class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
{
static $idx = 0;
public static $idx = 0;
/**
* Constructor
@@ -1130,7 +1129,7 @@ class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
/** @ignore */
public function GetMenuItem()
{
return array('label' => '<hr class="menu-separator">', 'url' => '', 'css_classes' => $this->aCssClasses);
return ['label' => '<hr class="menu-separator">', 'url' => '', 'css_classes' => $this->aCssClasses];
}
}
@@ -1143,7 +1142,6 @@ class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
*/
class URLButtonItem extends URLPopupMenuItem
{
}
/**
@@ -1155,7 +1153,6 @@ class URLButtonItem extends URLPopupMenuItem
*/
class JSButtonItem extends JSPopupMenuItem
{
}
/**
@@ -1504,9 +1501,9 @@ interface iBackofficeDictEntriesPrefixesExtension
*/
interface iPortalUIExtension
{
const ENUM_PORTAL_EXT_UI_BODY = 'Body';
const ENUM_PORTAL_EXT_UI_NAVIGATION_MENU = 'NavigationMenu';
const ENUM_PORTAL_EXT_UI_MAIN_CONTENT = 'MainContent';
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';
/**
* Returns an array of CSS file urls
@@ -1593,7 +1590,7 @@ abstract class AbstractPortalUIExtension implements iPortalUIExtension
*/
public function GetCSSFiles(Container $oContainer)
{
return array();
return [];
}
/**
@@ -1609,7 +1606,7 @@ abstract class AbstractPortalUIExtension implements iPortalUIExtension
*/
public function GetJSFiles(Container $oContainer)
{
return array();
return [];
}
/**
@@ -1711,11 +1708,6 @@ interface iRestServiceProvider
public function ExecOperation($sVersion, $sVerb, $aParams);
}
/**
* A REST service provider implementing this interface will have its input JSON data sanitized for logging purposes
* @since 2.7.13, 3.2.1-1
* @see \iRestServiceProvider
*/
interface iRestInputSanitizer
{
public function SanitizeJsonInput(string $sJsonInput): string;
@@ -1734,62 +1726,62 @@ class RestResult
* Result: no issue has been encountered
* @api
*/
const OK = 0;
public const OK = 0;
/**
* Result: missing/wrong credentials or the user does not have enough rights to perform the requested operation
* @api
*/
const UNAUTHORIZED = 1;
public const UNAUTHORIZED = 1;
/**
* Result: the parameter 'version' is missing
* @api
*/
const MISSING_VERSION = 2;
public const MISSING_VERSION = 2;
/**
* Result: the parameter 'json_data' is missing
* @api
*/
const MISSING_JSON = 3;
public const MISSING_JSON = 3;
/**
* Result: the input structure is not a valid JSON string
* @api
*/
const INVALID_JSON = 4;
public const INVALID_JSON = 4;
/**
* Result: the parameter 'auth_user' is missing, authentication aborted
* @api
*/
const MISSING_AUTH_USER = 5;
public const MISSING_AUTH_USER = 5;
/**
* Result: the parameter 'auth_pwd' is missing, authentication aborted
* @api
*/
const MISSING_AUTH_PWD = 6;
public const MISSING_AUTH_PWD = 6;
/**
* Result: no operation is available for the specified version
* @api
*/
const UNSUPPORTED_VERSION = 10;
public const UNSUPPORTED_VERSION = 10;
/**
* Result: the requested operation is not valid for the specified version
* @api
*/
const UNKNOWN_OPERATION = 11;
public const UNKNOWN_OPERATION = 11;
/**
* Result: the requested operation cannot be performed because it can cause data (integrity) loss
* @api
*/
const UNSAFE = 12;
public const UNSAFE = 12;
/**
* Result: the request page number is not valid. It must be an integer greater than 0
* @api
*/
const INVALID_PAGE = 13;
public const INVALID_PAGE = 13;
/**
* Result: the operation could not be performed, see the message for troubleshooting
* @api
*/
const INTERNAL_ERROR = 100;
public const INTERNAL_ERROR = 100;
/**
* Default constructor - ok!
@@ -1812,7 +1804,7 @@ class RestResult
* @api
*/
public $message;
/**
* Sanitize the content of this result to hide sensitive information
*/
@@ -1859,17 +1851,13 @@ 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.
*
@@ -1884,17 +1872,13 @@ 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.
*
@@ -1908,15 +1892,13 @@ 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.
*
@@ -1932,31 +1914,21 @@ class RestUtils
public static function GetFieldList($sClass, $oData, $sParamName)
{
$sFields = self::GetOptionalParam($oData, $sParamName, '*');
$aShowFields = array();
if ($sFields == '*')
{
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
$aShowFields = [];
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;
@@ -1979,38 +1951,30 @@ class RestUtils
*/
protected static function FindObjectFromCriteria($sClass, $oCriteria)
{
$aCriteriaReport = array();
if (isset($oCriteria->finalclass))
{
if (!MetaModel::IsValidClass($oCriteria->finalclass))
{
$aCriteriaReport = [];
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();
@@ -2018,7 +1982,6 @@ class RestUtils
return $res;
}
/**
* Find an object from a polymorph search specification (Rest/Json)
*
@@ -2034,43 +1997,29 @@ 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");
}
@@ -2091,47 +2040,37 @@ 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, array(), array(), null, $iLimit, $iOffset);
$oObjectSet = new DBObjectSet($oSearch, [], [], null, $iLimit, $iOffset);
return $oObjectSet;
}
@@ -2149,53 +2088,38 @@ 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 = array();
foreach ($value as $oValues)
{
$aLinks = [];
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());
}
@@ -2215,15 +2139,11 @@ 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());
}
}
@@ -2244,15 +2164,11 @@ 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());
}
}
@@ -2261,7 +2177,6 @@ class RestUtils
}
}
/**
* Helpers for modules extensibility, with discover performed by the MetaModel.
*
@@ -2284,21 +2199,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);
}
/**
@@ -2316,7 +2231,6 @@ interface iBackupExtraFilesExtension
public function GetExtraFilesRelPaths(): array;
}
/**
* Interface to provide messages to be displayed in the "Welcome Popup"
*
@@ -2377,7 +2291,7 @@ abstract class AbstractWelcomePopupExtension implements iWelcomePopupExtension
{
return [];
}
/**
* @inheritDoc
*/
@@ -2386,4 +2300,4 @@ abstract class AbstractWelcomePopupExtension implements iWelcomePopupExtension
// No need to process the acknowledgment notice by default
return;
}
}
}

View File

@@ -1,319 +0,0 @@
<?php
/*
* @copyright Copyright (C) 2010-2025 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
* @since 3.3.0
*/
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
use Combodo\iTop\Application\UI\Base\Component\Html\HtmlFactory;
use Combodo\iTop\Application\UI\Base\Component\Input\Select\SelectOptionUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Input\Select\SelectUIBlockFactory;
use Combodo\iTop\Application\UI\Base\UIBlock;
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
/**
* This class allow to define placeholder to be used in the audit 'rule' and audit 'category' OQL queries.
*
* @copyright Copyright (C) 2010-2025 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
class AuditFilterField extends cmdbAbstractObject
{
public static function Init()
{
$aParams = array
(
'category' => 'application,grant_by_profile',
'key_type' => 'autoincrement',
'name_attcode' => 'placeholder',
'state_attcode' => '',
'reconc_keys' => array('placeholder'),
'db_table' => 'priv_auditfilterfield',
'db_key_field' => 'id',
'db_finalclass_field' => '',
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-filtre.svg'),
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("placeholder", array("allowed_values" => null, "sql" => "placeholder", "default_value" => "", "is_null_allowed" => false, "depends_on" => array(), "validation_pattern"=>'^\w+$')));
MetaModel::Init_AddAttribute(new AttributeString("label", array("allowed_values" => null, "sql" => "label", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("type", array("allowed_values"=>new ValueSetEnum("select_oql,select_values,number,date" , true), "display_style"=>'list', "sql"=>'type', "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array(), "always_load_in_tables"=>false)));
MetaModel::Init_AddAttribute(new AttributeOQL("oql", array("allowed_values" => null, "sql" => "oql", "default_value" => "", "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("values", array("allowed_values" => null, "sql" => "values", "default_value" => "", "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("auditdomain_id", array("targetclass" => "AuditDomain", "jointype" => null, "allowed_values" => null, "sql" => "auditdomain_id", "is_null_allowed" => false, "on_target_delete" => DEL_SILENT, "depends_on" => array(), )));
MetaModel::Init_AddAttribute(new AttributeExternalField("auditdomain_name", array("allowed_values" => null, "extkey_attcode" => 'auditdomain_id', "target_attcode" => 'name', "always_load_in_tables" => false)));
// Display lists
MetaModel::Init_SetZListItems('details', array('label', 'placeholder', 'type', 'oql', 'values')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('label', 'placeholder', 'type', 'oql','values')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('label', 'placeholder','type')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('label', 'placeholder')); // Criteria of the advanced search form
}
/**
* @param WebPage $oPage
* @return void
* @throws ConfigException
* @throws CoreException
*/
public static function DisplayListOfFields(WebPage $oPage): void
{
$sOQL = 'SELECT AuditFilterField';
$oSearch = DBObjectSearch::FromOQL($sOQL);
$oAuditFilterSet = new DBObjectSet($oSearch, array(), array());
if ($oAuditFilterSet->Count()>0) {
$sHtml = '<ul style="list-style: unset; padding-left: 20px;">';
while ($oAuditFilter = $oAuditFilterSet->Fetch()) {
$sHtml .= '<li><i>:' . $oAuditFilter->Get('placeholder') . '</i> for ' . $oAuditFilter->Get('label') . '</li>';
}
$sHtml .= '</ul>';
$oInfoBlock = AlertUIBlockFactory::MakeForInformation('In OQL query, you can use this placeholders:', '')
->AddSubBlock(new Html($sHtml))
->SetOpenedByDefault(false);
$oPage->AddUiBlock($oInfoBlock);
}
}
protected function RegisterEventListeners()
{
parent::RegisterEventListeners();
// listenerId = CheckUsersUpdate
$this->RegisterCRUDListener('EVENT_DB_CHECK_TO_WRITE', 'CheckPlaceholderName', 1, 'itop-structure');
$this->RegisterCRUDListener('EVENT_DB_CHECK_TO_WRITE', 'CheckMandatoryFields', 2, 'itop-structure');
}
public function CheckPlaceholderName(Combodo\iTop\Service\Events\EventData $oEventData)
{
$aChanges = $this->ListChanges();
if (array_key_exists('placeholder', $aChanges)) {
$sPlaceholder = $this->Get('placeholder');
if (str_starts_with('this->', $sPlaceholder)) {
$this->AddCheckIssue(Dict::S('Class:AuditFilterField/Attribute:placeholder:Error:StartWith','this'));
}
if (str_starts_with('current_user', $sPlaceholder)) {
$this->AddCheckIssue(Dict::S('Class:AuditFilterField/Attribute:placeholder:Error:StartWith','current_user'));
}
if (str_starts_with('current_contact', $sPlaceholder)) {
$this->AddCheckIssue(Dict::S('Class:AuditFilterField/Attribute:placeholder:Error:StartWith','current_contact'));
}
if (in_array($sPlaceholder, ['current_user', 'current_contact']) ) {
$this->AddCheckIssue(Dict::S('Class:AuditFilterField/Attribute:placeholder:Error:ReservedWord', $sPlaceholder));
}
}
}
public function CheckMandatoryFields(Combodo\iTop\Service\Events\EventData $oEventData)
{
switch ($this->Get('type')) {
case 'select_oql':
if (utils::IsNullOrEmptyString($this->Get('oql'))) {
$this->AddCheckIssue(Dict::S('Class:AuditFilterField/Attribute:oql:Error:Empty'));
}
break;
case 'select_values':
if (utils::IsNullOrEmptyString($this->Get('values'))) {
$this->AddCheckIssue(Dict::S('Class:AuditFilterField/Attribute:values:Error:Empty'));
}
break;
// case 'number':
// case 'date':
// break;
}
}
public function GetFieldBlock(WebPage $oPage, string $sCurrentValue = ''): UIBlock
{
switch ($this->Get('type')) {
case 'select_oql':
$sFieldName = $this->Get('placeholder');
$sOql = $this->Get('oql');
$sLabel = $this->Get('label');
$oSearch = DBObjectSearch::FromOQL($sOql);
$oAllowedValues = new DBObjectSet($oSearch);
$oAllowedValues->SetShowObsoleteData(utils::ShowObsoleteData());
$iMaxComboLength = MetaModel::GetConfig()->Get('max_combo_length');
$bIsAutocomplete = $oAllowedValues->CountExceeds($iMaxComboLength);
$sWrapperCssClass = $bIsAutocomplete ? 'field_input_extkey ibo-input-wrapper ibo-input-select-wrapper--with-buttons ibo-input-select-autocomplete-wrapper' : 'ibo-input-select-wrapper';
$sHTMLValue = "<div class=\"field_input_zone $sWrapperCssClass\">";
// We just need to compare the number of entries with MaxComboLength, so no need to get the real count.
if (!$bIsAutocomplete) {
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
$sHelpText = '';
$aOptions = [];
$aOptions['value'] = "";
$aOptions['label'] = Dict::S('UI:SelectOne');
$oAllowedValues->Rewind();
$sClassAllowed = $oAllowedValues->GetClass();
$bAddingValue = false;
$aFieldsToLoad = [];
$aComplementAttributeSpec = MetaModel::GetNameSpec($oAllowedValues->GetClass(), FriendlyNameType::COMPLEMENTARY);
$sFormatAdditionalField = $aComplementAttributeSpec[0];
$aAdditionalField = $aComplementAttributeSpec[1];
if (count($aAdditionalField) > 0) {
$bAddingValue = true;
$aFieldsToLoad[$sClassAllowed] = $aAdditionalField;
}
$sObjectImageAttCode = MetaModel::GetImageAttributeCode($sClassAllowed);
if (!empty($sObjectImageAttCode)) {
$aFieldsToLoad[$sClassAllowed][] = $sObjectImageAttCode;
}
$aFieldsToLoad[$sClassAllowed][] = 'friendlyname';
$oAllowedValues->OptimizeColumnLoad($aFieldsToLoad);
$oSelect = SelectUIBlockFactory::MakeForSelect($sFieldName, $sFieldName);
$oSelect->AddCSSClass('ibo-input-field-wrapper');
while ($oChoiceItem = $oAllowedValues->Fetch()) {
$sOptionName = utils::HtmlEntityDecode($oChoiceItem->GetName());
if ($bAddingValue) {
$aArguments = [];
foreach ($aAdditionalField as $sAdditionalField) {
array_push($aArguments, $oAllowedValues->Get($sAdditionalField));
}
$sOptionName .= '<br><i>' . utils::HtmlEntities(vsprintf($sFormatAdditionalField, $aArguments)) . '</i>';;
}
if (!empty($sObjectImageAttCode)) {
// Try to retrieve image for contact
/** @var \ormDocument $oImage */
$oImage = $oAllowedValues->Get($sObjectImageAttCode);
if (!$oImage->IsEmpty()) {
$sPicturepictureUrl = $oImage->GetDisplayURL($sClassAllowed, $oChoiceItem->GetKey(), $sObjectImageAttCode);
$sOptionName .= ' <span class="ibo-input-select--autocomplete-item-image" style="background-image: url(' . $sPicturepictureUrl . ');"></span>';
} else {
$sInitials = utils::FormatInitialsForMedallion(utils::ToAcronym($oChoiceItem->Get('friendlyname')));
$sOptionName .= ' <span class="ibo-input-select--autocomplete-item-image" ">' . $sInitials . '</span>';
}
}
$oOption = SelectOptionUIBlockFactory::MakeForSelectOption($oChoiceItem->GetKey(), $sOptionName, ($sCurrentValue == $oChoiceItem->GetKey()));
$oSelect->AddOption($oOption);
}
$sInputType = CmdbAbstractObject::ENUM_INPUT_TYPE_DROPDOWN_DECORATED;
$sJsonOptions = str_replace("'", "\'", str_replace('\\', '\\\\', json_encode($aOptions)));
$oPage->add_ready_script(
<<<JS
let select$sFieldName = $('#$sFieldName').selectize({
plugins:['custom_itop', 'selectize-plugin-a11y'],
});
JS
);
return $oSelect;
} else {
// Too many choices, use an autocomplete
// Check that the given value is allowed
$oSearch = $oAllowedValues->GetFilter();
$oSearch->AddCondition('id', $sCurrentValue);
$oSet = new DBObjectSet($oSearch);
$sClass = $oSet->GetClass();
if ($oSet->Count() == 0) {
$sCurrentValue = null;
}
if (is_null($sCurrentValue) || ($sCurrentValue == 0)) // Null values are displayed as ''
{
$sDisplayValue = '';
} else {
$sDisplayValue = MetaModel::GetObject($sClass, $sCurrentValue)->GetName();
}
$iMinChars = MetaModel::GetConfig()->Get('min_autocomplete_chars'); //@@@ $this->oAttDef->GetMinAutoCompleteChars();
// the input for the auto-complete
$sInputType = CmdbAbstractObject::ENUM_INPUT_TYPE_AUTOCOMPLETE;
$sHTMLValue .= "<input class=\"field_autocomplete ibo-input ibo-input-select ibo-input-select-autocomplete\" type=\"text\" id=\"label_$sFieldName\" value=\"$sDisplayValue\" placeholder='...'/>";
// another hidden input to store & pass the object's Id
$sHTMLValue .= "<input type=\"hidden\" id=\"$sFieldName\" name=\"{$sFieldName}\" value=\"" . utils::HtmlEntities($sCurrentValue) . "\" />\n";
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
$oPage->add_ready_script(
<<<EOF
oACWidget_{$sFieldName} = new ExtKeyWidget('$sFieldName', '$sClass', '$sOql', '$sLabel', false, null, '{$sFieldName}', true, false);
oACWidget_{$sFieldName}.emptyHtml = "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>$sMessage</p></div>";
oACWidget_{$sFieldName}.AddAutocomplete($iMinChars, '');
if ($('#ac_dlg_{$sFieldName}').length == 0)
{
$('body').append('<div id="ac_dlg_{$sFieldName}"></div>');
}
EOF
);
$sHTMLValue .= "<div class=\"ibo-input-select--action-buttons\">";
$sHTMLValue .= "<a href=\"#\" class=\"ibo-input-select--action-button ibo-input-select--action-button--clear ibo-is-hidden\" id=\"mini_clear_{$sFieldName}\" onClick=\"$('#$sFieldName').val('');$('#label_$sFieldName').val(''); $('#label_$sFieldName').data('selected_value', '');\" data-tooltip-content='" . Dict::S('UI:Button:Clear') . "'><i class=\"fas fa-times\"></i></a>";
$sHTMLValue .= "<a href=\"#\" class=\"ibo-input-select--action-button ibo-input-select--action-button--search\" id=\"mini_search_{$sFieldName}\" onClick=\"oACWidget_{$sFieldName}.Search();\" data-tooltip-content='" . Dict::S('UI:Button:Search') . "'><i class=\"fas fa-search\"></i></a>";
if (MetaModel::IsHierarchicalClass($sClass) !== false) {
$sHTMLValue .= "<a href=\"#\" class=\"ibo-input-select--action-button ibo-input-select--action-button--hierarchy\" id=\"mini_tree_{$sFieldName}\" onClick=\"oACWidget_{$sFieldName}.HKDisplay();\" data-tooltip-content='" . Dict::S('UI:Button:SearchInHierarchy') . "'><i class=\"fas fa-sitemap\"></i></a>";
$oPage->add_ready_script(
<<<JS
if ($('#ac_tree_{$sFieldName}').length == 0)
{
$('body').append('<div id="ac_tree_{$sFieldName}"></div>');
}
JS
);
}
}
$sHTMLValue .= "</div>";
$sHTMLValue .= "</div>";
return new Html($sHTMLValue);
break;
case 'select_values':
$aListValues = explode(',',$this->Get('values'));
$oSelect = SelectUIBlockFactory::MakeForSelect($this->Get('placeholder'), $this->Get('placeholder'));
$oSelect->AddCSSClass('ibo-input-field-wrapper');
foreach($aListValues as $sValue) {
$oSelect->AddOption(SelectOptionUIBlockFactory::MakeForSelectOption($sValue, $sValue, ($sCurrentValue == $sValue)));
}
return $oSelect;
break;
case 'number':
$oInput = HtmlFactory::MakeRaw('<input type="number" id="'.$this->Get('placeholder').'" name="'.$this->Get('placeholder').'" value="'.$sCurrentValue.'"/>');
return $oInput;
break;
case 'date':
$sFieldName = $this->Get('placeholder');
$sDateFormatDatePicker = AttributeDate::GetFormat()->ToDatePicker();
$oInput = HtmlFactory::MakeRaw('<input class="date date-pick ibo-input ibo-input-date" type="text" id="'.$sFieldName.'" name="'.$sFieldName.'" value="'.$sCurrentValue.'"/>');
$oPage->add_ready_script(<<<EOF
$('#$sFieldName').datepicker({
showOn: 'button',
buttonImage: '../images/calendar.png',
buttonImageOnly: true,
dateFormat: '$sDateFormatDatePicker',
constrainInput: false,
changeMonth: true,
changeYear: true,
});
EOF);
return $oInput;
break;
default:
return new Html('Not implemented yet');
}
}
}

View File

@@ -1,9 +1,10 @@
<?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.
@@ -16,7 +17,6 @@
// 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,34 +32,36 @@ class AuditCategory extends cmdbAbstractObject
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"reconc_keys" => ['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", 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')));
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']
));
// Display lists
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
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
// Search criteria
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
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
}
/**
@@ -74,9 +76,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';
} else if (($iErrors / $iTotal) <= ($this->Get('warning_error_tolerance') / 100)) {
} elseif (($iErrors / $iTotal) <= ($this->Get('warning_error_tolerance') / 100)) {
$sResult = 'orange';
}
@@ -92,13 +94,4 @@ class AuditCategory extends cmdbAbstractObject
return $aShortcutActions;
}
public function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{
AuditFilterField::DisplayListOfFields($oPage);
$aFieldsMap = parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);;
return $aFieldsMap;
}
}
}

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -16,7 +17,6 @@
// 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
@@ -25,7 +25,6 @@
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
require_once(APPROOT.'/application/audit.AuditFilterField.class.inc.php');
/**
* @since 3.1.0
@@ -34,32 +33,34 @@ class AuditDomain extends cmdbAbstractObject
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"complementary_name_attcode" => array('description'),
"complementary_name_attcode" => ['description'],
"state_attcode" => "",
"reconc_keys" => array('name'),
"reconc_keys" => ['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", 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())));
MetaModel::Init_AddAttribute(new AttributeLinkedSet("filterfield_list", array("linked_class"=>"AuditFilterField", "ext_key_to_me"=>"auditdomain_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>5,"edit_mode"=>LINKSET_EDITMODE_INPLACE, "depends_on"=>array(), "tracking_level"=>LINKSET_TRACKING_ALL)));
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" => []]
));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'icon', 'categories_list', 'filterfield_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description',)); // Attributes to be displayed for a list
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
// Search criteria
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
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
}
public static function GetShortcutActions($sFinalClass)
@@ -71,36 +72,6 @@ class AuditDomain extends cmdbAbstractObject
return $aShortcutActions;
}
public function GetDependentFields():array
{
$aListFields=[];
foreach ($this->Get('categories_list') as $oLnkToCategory) {
$aMatches = [];
$oCategory = MetaModel::GetObject('AuditCategory', $oLnkToCategory->Get('category_id'));
if (preg_match_all('/:\w+/', $oCategory->Get('definition_set'), $aMatches)) {
foreach ($aMatches as $aMatchesList) {
foreach ($aMatchesList as $sPlaceholder) {
if (!in_array(substr($sPlaceholder, 1), $aListFields)) {
$aListFields[] = substr($sPlaceholder, 1);
}
}
}
}
foreach ($oCategory->Get('rules_list') as $oRule) {
$aMatches = [];
if (preg_match_all('/:\w+/', $oRule->Get('query'), $aMatches)) {
foreach ($aMatches as $aMatchesList) {
foreach ($aMatchesList as $sPlaceholder) {
if (!in_array(substr($sPlaceholder, 1), $aListFields)) {
$aListFields[] = substr($sPlaceholder, 1);
}
}
}
}
}
}
return $aListFields;
}
}
@@ -115,40 +86,39 @@ class lnkAuditCategoryToAuditDomain extends cmdbAbstractObject
*/
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array('category_id', 'domain_id'),
"reconc_keys" => ['category_id', 'domain_id'],
"db_table" => "priv_link_audit_category_domain",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => true,
'uniqueness_rules' => array(
'no_duplicate' => array(
'attributes' => array(
'uniqueness_rules' => [
'no_duplicate' => [
'attributes' => [
0 => 'category_id',
1 => 'domain_id',
),
],
'filter' => '',
'disabled' => false,
'is_blocking' => true,
),
),
);
],
],
];
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", array("targetclass" => "AuditCategory", "jointype" => '', "allowed_values" => null, "sql" => "category_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
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")));
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"]));
// Display lists
MetaModel::Init_SetZListItems('details', array('category_id', 'domain_id'));
MetaModel::Init_SetZListItems('list', array('category_id', 'domain_id'));
MetaModel::Init_SetZListItems('details', ['category_id', 'domain_id']);
MetaModel::Init_SetZListItems('list', ['category_id', 'domain_id']);
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('category_id', 'domain_id'));
MetaModel::Init_SetZListItems('standard_search', ['category_id', 'domain_id']);
}
}

View File

@@ -1,9 +1,10 @@
<?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.
@@ -16,7 +17,6 @@
// 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
@@ -27,44 +27,40 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
require_once(APPROOT.'/application/audit.category.class.inc.php');
class AuditRule extends cmdbAbstractObject
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"reconc_keys" => ['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", 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")));
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"]));
// Display lists
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
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
// Search criteria
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
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
}
public static function GetShortcutActions($sFinalClass)
{
$aShortcutActions = parent::GetShortcutActions($sFinalClass);
@@ -74,11 +70,4 @@ class AuditRule extends cmdbAbstractObject
return $aShortcutActions;
}
public function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{
AuditFilterField::DisplayListOfFields($oPage);
$aFieldsMap = parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);;
return $aFieldsMap;
}
}
}

View File

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

View File

@@ -1,8 +1,9 @@
<?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,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -53,7 +54,7 @@ abstract class Dashboard
$this->sLayoutClass = 'DashboardLayoutOneCol';
$this->bAutoReload = false;
$this->iAutoReloadSec = MetaModel::GetConfig()->GetStandardReloadInterval();
$this->aCells = array();
$this->aCells = [];
$this->oDOMNode = null;
$this->sId = $sId;
}
@@ -65,8 +66,8 @@ abstract class Dashboard
*/
public function FromXml($sXml)
{
$this->aCells = array(); // reset the content of the dashboard
set_error_handler(array('Dashboard', 'ErrorHandler'));
$this->aCells = []; // reset the content of the dashboard
set_error_handler(['Dashboard', 'ErrorHandler']);
$oDoc = new DOMDocument();
$oDoc->loadXML($sXml);
restore_error_handler();
@@ -79,87 +80,69 @@ 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 = array();
$aCellOrder = [];
$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 = array();
$aDashletOrder = [];
/** @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[] = array('rank' => $iRank, 'dashlet' => $oNewDashlet);
$aDashletOrder[] = ['rank' => $iRank, 'dashlet' => $oNewDashlet];
}
usort($aDashletOrder, array(get_class($this), 'SortOnRank'));
$aDashletList = array();
foreach($aDashletOrder as $aItem)
{
usort($aDashletOrder, [get_class($this), 'SortOnRank']);
$aDashletList = [];
foreach ($aDashletOrder as $aItem) {
$aDashletList[] = $aItem['dashlet'];
}
$aCellOrder[] = array('rank' => $iCellRank, 'dashlets' => $aDashletList);
$aCellOrder[] = ['rank' => $iCellRank, 'dashlets' => $aDashletList];
}
}
usort($aCellOrder, array(get_class($this), 'SortOnRank'));
foreach($aCellOrder as $aItem)
{
usort($aCellOrder, [get_class($this), 'SortOnRank']);
foreach ($aCellOrder as $aItem) {
$this->aCells[] = $aItem['dashlets'];
}
}
else
{
$this->aCells = array();
} else {
$this->aCells = [];
}
}
@@ -169,20 +152,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
@@ -208,12 +191,9 @@ 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;
}
}
@@ -231,7 +211,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();
@@ -261,23 +241,21 @@ 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());
@@ -296,16 +274,13 @@ abstract class Dashboard
public function FromParams($aParams)
{
$this->sLayoutClass = $aParams['layout_class'];
if (!is_subclass_of($this->sLayoutClass,DashboardLayout::class)) {
throw new InvalidParameterException('Invalid parameter layout_class "'.$aParams['layout_class'].'"');
}
$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 = array();
foreach($aCell as $aDashletParams) {
foreach ($aParams['cells'] as $aCell) {
$aCellDashlets = [];
foreach ($aCell as $aDashletParams) {
$sDashletClass = $aDashletParams['dashlet_class'];
$sId = $aDashletParams['dashlet_id'];
/** @var \Dashlet $oNewDashlet */
@@ -322,12 +297,12 @@ abstract class Dashboard
}
$this->aCells[] = $aCellDashlets;
}
}
public function Save()
{
}
/**
@@ -420,7 +395,7 @@ abstract class Dashboard
{
$sId = $this->GetNewDashletId();
$oDashlet->SetId($sId);
$this->aCells[] = array($oDashlet);
$this->aCells[] = [$oDashlet];
}
/**
@@ -430,7 +405,7 @@ abstract class Dashboard
* @throws \ReflectionException
* @throws \Exception
*/
public function RenderProperties($oPage, $aExtraParams = array())
public function RenderProperties($oPage, $aExtraParams = [])
{
// 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>');
@@ -442,7 +417,7 @@ abstract class Dashboard
if (is_subclass_of($sLayoutClass, 'DashboardLayout')) {
$oReflection = new ReflectionClass($sLayoutClass);
if (!$oReflection->isAbstract()) {
$aCallSpec = array($sLayoutClass, 'GetInfo');
$aCallSpec = [$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 !
@@ -466,7 +441,6 @@ 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');
@@ -474,7 +448,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"));
@@ -522,11 +496,9 @@ EOF
*
* @return \Combodo\iTop\Application\UI\Base\Layout\Dashboard\DashboardLayout
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
public function Render($oPage, $bEditMode = false, $aExtraParams = [], $bCanEdit = true)
{
if (!array_key_exists('dashboard_div_id', $aExtraParams)) {
$aExtraParams['dashboard_div_id'] = utils::Sanitize($this->GetId(), '', 'element_identifier');
}
$aExtraParams['dashboard_div_id'] = utils::Sanitize($aExtraParams['dashboard_div_id'] ?? null, $this->GetId(), utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
/** @var \DashboardLayoutMultiCol $oLayout */
$oLayout = new $this->sLayoutClass();
@@ -553,7 +525,8 @@ 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
);
@@ -597,7 +570,7 @@ JS
* @param WebPage $oPage
* @param array $aExtraParams
*/
public function RenderDashletsProperties(WebPage $oPage, $aExtraParams = array())
public function RenderDashletsProperties(WebPage $oPage, $aExtraParams = [])
{
// 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>');
@@ -606,13 +579,10 @@ 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);
@@ -634,18 +604,17 @@ JS
*/
protected function GetAvailableDashlets()
{
$aDashlets = array();
$aDashlets = [];
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, array('DashletUnknown', 'DashletProxy'))) {
if (is_subclass_of($sDashletClass, 'Dashlet') && !in_array($sDashletClass, ['DashletUnknown', 'DashletProxy'])) {
$oReflection = new ReflectionClass($sDashletClass);
if (!$oReflection->isAbstract()) {
$aCallSpec = array($sDashletClass, 'IsVisible');
$aCallSpec = [$sDashletClass, 'IsVisible'];
$bVisible = call_user_func($aCallSpec);
if ($bVisible) {
$aCallSpec = array($sDashletClass, 'GetInfo');
$aCallSpec = [$sDashletClass, 'GetInfo'];
$aInfo = call_user_func($aCallSpec);
$aDashlets[$sDashletClass] = $aInfo;
}
@@ -662,11 +631,9 @@ 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());
}
}
@@ -683,15 +650,15 @@ JS
*
* @return void
*/
abstract protected function PrepareDashletForRendering(Dashlet $oDashlet, $aCoordinates, $aExtraParams = array());
abstract protected function PrepareDashletForRendering(Dashlet $oDashlet, $aCoordinates, $aExtraParams = []);
/**
* @param \DesignerForm $oForm
* @param array $aExtraParams
*
* @return mixed
*/
abstract protected function SetFormParams($oForm, $aExtraParams = array());
/**
* @param \DesignerForm $oForm
* @param array $aExtraParams
*
* @return mixed
*/
abstract protected function SetFormParams($oForm, $aExtraParams = []);
/**
* @param string $sType
@@ -701,8 +668,7 @@ JS
*/
public static function GetDashletClassFromType($sType, $oFactory = null)
{
if (is_subclass_of($sType, 'Dashlet'))
{
if (is_subclass_of($sType, 'Dashlet')) {
return $sType;
}
return 'DashletUnknown';
@@ -725,14 +691,12 @@ 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;
}
@@ -784,9 +748,9 @@ class RuntimeDashboard extends Dashboard
* @inheritDoc
* @throws \Exception
*/
protected function SetFormParams($oForm, $aExtraParams = array())
protected function SetFormParams($oForm, $aExtraParams = [])
{
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', array('operation' => 'update_dashlet_property', 'extra_params' => $aExtraParams));
$oForm->SetSubmitParams(utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php', ['operation' => 'update_dashlet_property', 'extra_params' => $aExtraParams]);
}
/**
@@ -802,14 +766,11 @@ 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());
@@ -840,8 +801,7 @@ 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);
@@ -885,14 +845,11 @@ 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);
@@ -939,7 +896,6 @@ class RuntimeDashboard extends Dashboard
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
}
if ($sDashboardDefinition !== false) {
$oDashboard = new RuntimeDashboard($sDashBoardId);
$oDashboard->FromXml($sDashboardDefinition);
@@ -956,11 +912,11 @@ class RuntimeDashboard extends Dashboard
* @inheritDoc
* @throws \Exception
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
public function Render($oPage, $bEditMode = false, $aExtraParams = [], $bCanEdit = true)
{
if (!isset($aExtraParams['query_params']) && isset($aExtraParams['this->class'])) {
$oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']);
$aRenderParams = array('query_params' => $oObj->ToArgsForQuery());
$aRenderParams = ['query_params' => $oObj->ToArgsForQuery()];
} else {
$aRenderParams = $aExtraParams;
}
@@ -970,7 +926,7 @@ class RuntimeDashboard extends Dashboard
if (isset($aExtraParams['query_params']['this->object()'])) {
/** @var \DBObject $oObj */
$oObj = $aExtraParams['query_params']['this->object()'];
$aAjaxParams = array('this->class' => get_class($oObj), 'this->id' => $oObj->GetKey());
$aAjaxParams = ['this->class' => get_class($oObj), 'this->id' => $oObj->GetKey()];
if (isset($aExtraParams['from_dashboard_page'])) {
$aAjaxParams['from_dashboard_page'] = $aExtraParams['from_dashboard_page'];
}
@@ -1003,9 +959,7 @@ class RuntimeDashboard extends Dashboard
}
JS
);
}
else
{
} else {
$oPage->add_script(
<<<EOF
if (typeof(AutoReloadDashboardId$sDivId) !== 'undefined')
@@ -1034,7 +988,7 @@ EOF
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
protected function RenderSelector(WebPage $oPage, DashboardLayoutUIBlock $oDashboard, $aAjaxParams = array())
protected function RenderSelector(WebPage $oPage, DashboardLayoutUIBlock $oDashboard, $aAjaxParams = [])
{
if (!$this->HasCustomDashboard()) {
return;
@@ -1052,7 +1006,7 @@ EOF
$sSelectorHtml .= '</div>';
$sFile = addslashes($this->GetDefinitionFile());
$sReloadURL = $this->GetReloadURL();
$sReloadURL = json_encode($this->GetReloadURL());
$bFromDashboardPage = isset($aAjaxParams['from_dashboard_page']) ? isset($aAjaxParams['from_dashboard_page']) : false;
if ($bFromDashboardPage) {
@@ -1072,7 +1026,7 @@ EOF
var dashboard = $('.ibo-dashboard#$sDivId')
dashboard.block();
$.post(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php',
{ operation: 'toggle_dashboard', dashboard_id: '$sId', file: '$sFile', extra_params: $sExtraParams, reload_url: '$sReloadURL' },
{ operation: 'toggle_dashboard', dashboard_id: '$sId', file: '$sFile', extra_params: $sExtraParams, reload_url: $sReloadURL },
function(data) {
dashboard.html(data);
dashboard.unblock();
@@ -1094,8 +1048,7 @@ JS
*/
protected function HasCustomDashboard()
{
try
{
try {
// Search for an eventual user defined dashboard
$oUDSearch = new DBObjectSearch('UserDashboard');
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
@@ -1103,9 +1056,7 @@ JS
$oUDSet = new DBObjectSet($oUDSearch);
return ($oUDSet->Count() > 0);
}
catch (Exception $e)
{
} catch (Exception $e) {
return false;
}
}
@@ -1141,22 +1092,23 @@ JS
->AddCSSClass('ibo-action-button');
$oToolbar->AddSubBlock($oActionButton);
$aActions = array();
$aActions = [];
$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)
@@ -1166,7 +1118,7 @@ JS
$oToolbar->AddSubBlock($oActionButton)
->AddSubBlock($oActionsMenu);
$sReloadURL = $this->GetReloadURL();
$sReloadURL = json_encode($this->GetReloadURL());
$oPage->add_script(
<<<EOF
function EditDashboard(sId, sDashboardFile, aExtraParams)
@@ -1196,12 +1148,12 @@ EOF
/**
* @inheritDoc
*/
public function RenderProperties($oPage, $aExtraParams = array())
public function RenderProperties($oPage, $aExtraParams = [])
{
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});
@@ -1228,7 +1180,6 @@ EOF
);
}
/**
* @param WebPage $oPage
*
@@ -1239,11 +1190,11 @@ EOF
* @throws \ReflectionException
* @throws \Exception
*/
public function RenderEditor($oPage, $aExtraParams = array())
public function RenderEditor($oPage, $aExtraParams = [])
{
if (isset($aExtraParams['this->class'])) {
$oObj = MetaModel::GetObject($aExtraParams['this->class'], $aExtraParams['this->id']);
$aRenderParams = array('query_params' => $oObj->ToArgsForQuery());
$aRenderParams = ['query_params' => $oObj->ToArgsForQuery()];
} else {
$aRenderParams = $aExtraParams;
}
@@ -1265,7 +1216,7 @@ 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);
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
@@ -1273,14 +1224,14 @@ EOF
$sTitle = json_encode($this->sTitle);
$sFile = json_encode($this->GetDefinitionFile());
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
$sReloadURL = $this->GetReloadURL();
$sReloadURL = json_encode($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({
@@ -1388,104 +1339,89 @@ 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 = array();
$aAllowedDashboards = [];
$sDefaultDashboard = null;
// Store the parent menus for acces check
$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)
{
$aParentMenus = [];
foreach ($aAllMenus as $idx => $aMenu) {
/** @var MenuNode $oMenu */
$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 (count(ApplicationMenu::GetChildren($oMenu->GetIndex())) > 0) {
$aParentMenus[$oMenu->GetMenuId()] = $aMenu;
}
}
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 = array();
foreach(get_declared_classes() as $sDashletClass)
{
if (is_subclass_of($sDashletClass, 'Dashlet'))
{
$oReflection = new ReflectionClass($sDashletClass);
if (!$oReflection->isAbstract())
{
$aCallSpec = array($sDashletClass, 'CanCreateFromOQL');
$bShorcutMode = call_user_func($aCallSpec);
if ($bShorcutMode)
{
$aCallSpec = array($sDashletClass, 'GetInfo');
$aInfo = call_user_func($aCallSpec);
$aDashlets[$sDashletClass] = array('label' => $aInfo['label'], 'class' => $sDashletClass, 'icon' => $aInfo['icon']);
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();
}
}
}
}
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')) {
$oReflection = new ReflectionClass($sDashletClass);
if (!$oReflection->isAbstract()) {
$aCallSpec = [$sDashletClass, 'CanCreateFromOQL'];
$bShorcutMode = call_user_func($aCallSpec);
if ($bShorcutMode) {
$aCallSpec = [$sDashletClass, 'GetInfo'];
$aInfo = call_user_func($aCallSpec);
$aDashlets[$sDashletClass] = ['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;
}
@@ -1504,11 +1440,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({
@@ -1606,7 +1542,7 @@ JS
/**
* @inheritDoc
*/
protected function PrepareDashletForRendering(Dashlet $oDashlet, $aCoordinates, $aExtraParams = array())
protected function PrepareDashletForRendering(Dashlet $oDashlet, $aCoordinates, $aExtraParams = [])
{
$sDashletIdOrig = $oDashlet->GetID();
$sDashboardSanitizedId = $this->GetSanitizedId();
@@ -1633,31 +1569,27 @@ 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;
}
@@ -1676,7 +1608,7 @@ JS
private function GetDashletObjectListAppUserPreferencesPrefix(DashletObjectList $oDashlet, $aExtraParams, $sDashletId)
{
$sDataTableId = Dashlet::APPUSERPREFERENCES_PREFIX.$sDashletId;
$aClassAliases = array();
$aClassAliases = [];
try {
$oFilter = $oDashlet->GetDBSearch($aExtraParams);
$aClassAliases = $oFilter->GetSelectedClasses();

View File

@@ -1,9 +1,10 @@
<?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.
@@ -38,21 +39,21 @@ abstract class DashboardLayout
* @since 2.7.0
*/
abstract public function GetDashletCoordinates($iCellIdx);
public static function GetInfo()
{
return array(
return [
'label' => '',
'icon' => '',
'description' => '',
);
];
}
}
abstract class DashboardLayoutMultiCol extends DashboardLayout
{
protected $iNbCols;
public function __construct()
{
$this->iNbCols = 1;
@@ -63,47 +64,38 @@ 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;
}
/**
@@ -112,7 +104,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
* @param bool $bEditMode
* @param array $aExtraParams
*/
public function Render($oPage, $aCells, $bEditMode = false, $aExtraParams = array())
public function Render($oPage, $aCells, $bEditMode = false, $aExtraParams = [])
{
// Trim the list of cells to remove the invisible/empty ones at the end of the array
$aCells = $this->TrimCellsArray($aCells);
@@ -157,8 +149,7 @@ 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);
@@ -180,7 +171,7 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
$iColNumber = (int) $iCellIdx % $this->iNbCols;
$iRowNumber = (int) floor($iCellIdx / $this->iNbCols);
return array($iColNumber, $iRowNumber);
return [$iColNumber, $iRowNumber];
}
}
@@ -191,13 +182,13 @@ class DashboardLayoutOneCol extends DashboardLayoutMultiCol
parent::__construct();
$this->iNbCols = 1;
}
static public function GetInfo()
public static function GetInfo()
{
return array(
return [
'label' => 'One Column',
'icon' => 'images/layout_1col.png',
'description' => '',
);
];
}
}
@@ -208,13 +199,13 @@ class DashboardLayoutTwoCols extends DashboardLayoutMultiCol
parent::__construct();
$this->iNbCols = 2;
}
static public function GetInfo()
public static function GetInfo()
{
return array(
return [
'label' => 'Two Columns',
'icon' => 'images/layout_2col.png',
'description' => '',
);
];
}
}
@@ -225,12 +216,12 @@ class DashboardLayoutThreeCols extends DashboardLayoutMultiCol
parent::__construct();
$this->iNbCols = 3;
}
static public function GetInfo()
public static function GetInfo()
{
return array(
return [
'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.3">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
<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>

View File

@@ -80,42 +80,31 @@ 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 = array();
foreach($oCustomSettings->aColumns as $sAlias => $aColumnsInfo)
{
foreach($aColumnsInfo as $sAttCode => $aData)
{
if ($sAttCode != '_key_')
{
if ($aData['checked'])
{
$aColumnsToLoad = [];
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()) {
@@ -127,18 +116,15 @@ 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);
}
@@ -167,10 +153,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);
@@ -188,7 +174,7 @@ class DataTable
$aExtraParams['show_obsolete_data'] = $this->bShowObsoleteData;
$aOptions = array(
$aOptions = [
'sPersistentId' => '',
'sFilter' => $this->oSet->GetFilter()->serialize(),
'oColumns' => $aColumns,
@@ -202,12 +188,11 @@ class DataTable
'sTableId' => $this->sTableId,
'oExtraParams' => $aExtraParams,
'sRenderUrl' => utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php',
'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)
{
'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) {
$aOptions['oDefaultSettings'] = $this->GetAsHash($this->oDefaultSettings);
}
$sJSOptions = json_encode($aOptions);
@@ -222,16 +207,14 @@ 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;
@@ -245,15 +228,12 @@ 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;
}
/**
@@ -267,26 +247,19 @@ 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>";
@@ -295,31 +268,25 @@ 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 = array();
for($idx = 0; $idx <= min(4, $iNbPages-1); $idx++)
{
if ($idx == 0)
{
$aPagesToDisplay = [];
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>";
}
@@ -386,22 +353,19 @@ 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 = array(
$aActions = [
$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;
@@ -422,10 +386,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>";
@@ -444,7 +408,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'); } });");
@@ -458,7 +422,7 @@ EOF;
*/
public function GetAsHash($oSetting)
{
$aSettings = array('iDefaultPageSize' => $oSetting->iDefaultPageSize, 'oColumns' => $oSetting->aColumns);
$aSettings = ['iDefaultPageSize' => $oSetting->iDefaultPageSize, 'oColumns' => $oSetting->aColumns];
return $aSettings;
}
@@ -474,55 +438,46 @@ EOF;
*/
protected function GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink)
{
$aAttribs = array();
if ($sSelectMode == 'multiple')
{
$aAttribs['form::select'] = array(
$aAttribs = [];
if ($sSelectMode == 'multiple') {
$aAttribs['form::select'] = [
'label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->iListId}:not(:disabled)', this.checked);\" class=\"checkAll\"></input>",
'description' => Dict::S('UI:SelectAllToggle+'),
'metadata' => array(),
);
}
else if ($sSelectMode == 'single')
{
$aAttribs['form::select'] = array('label' => '', 'description' => '', 'metadata' => array());
'metadata' => [],
];
} elseif ($sSelectMode == 'single') {
$aAttribs['form::select'] = ['label' => '', 'description' => '', 'metadata' => []];
}
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] = array(
$aAttribs['key_'.$sAlias] = [
'label' => $sAttLabel,
'description' => '',
'metadata' => array(
'metadata' => [
'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] = array(
$aAttribs[$sAttCode.'_'.$sAlias] = [
'label' => $sAttLabel,
'description' => $oAttDef->GetOrderByHint(),
'metadata' => array(
'metadata' => [
'object_class' => $sClassName,
'attribute_code' => $sAttCode,
'attribute_type' => $sAttDefClass,
'attribute_label' => $sAttLabel,
),
);
],
];
}
}
}
@@ -530,7 +485,6 @@ EOF;
return $aAttribs;
}
/**
* @param $aColumns
* @param $sSelectMode
@@ -549,104 +503,74 @@ 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 = array();
$aAttDefsCache = array();
$aValues = [];
$aAttDefsCache = [];
$this->oSet->Seek(0);
$iMaxObjects = $iPageSize;
while (($aObjects = $this->oSet->FetchAssoc()) && ($iMaxObjects != 0))
{
while (($aObjects = $this->oSet->FetchAssoc()) && ($iMaxObjects != 0)) {
$bFirstObject = true;
$aRow = array();
foreach($this->aClassAliases as $sAlias => $sClassName)
{
if (is_object($aObjects[$sAlias]))
{
$aRow = [];
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] = array(
foreach ($aColumns[$sAlias] as $sAttCode => $aData) {
if ($aData['checked']) {
if ($sAttCode == '_key_') {
$aRow['key_'.$sAlias] = [
'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] = array(
} else {
$aRow[$sAttCode.'_'.$sAlias] = [
'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] = '';
}
}
@@ -681,8 +605,7 @@ 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);
@@ -691,8 +614,7 @@ 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;
@@ -707,20 +629,16 @@ 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 = array();
foreach($this->aClassAliases as $sAlias => $sClassName)
{
foreach($aColumns[$sAlias] as $sAttCode => $aData)
{
if ($aData['checked'])
{
$aUniqueOrderedList = [];
foreach ($this->aClassAliases as $sAlias => $sClassName) {
foreach ($aColumns[$sAlias] as $sAttCode => $aData) {
if ($aData['checked']) {
$aUniqueOrderedList[$sAttCode] = true;
}
}
@@ -732,41 +650,32 @@ EOF;
$this->oSet->ApplyParameters();
// Display the actual sort order of the table
$aRealSortOrder = $this->oSet->GetRealSortOrder();
$aDefaultSort = array();
$aDefaultSort = [];
$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')."]";
}
else if (($iPos = array_search(preg_replace('/_friendlyname$/', '', $sColCode), $aUniqueOrderedList)) !== false)
{
if ($iPos !== false) {
$aDefaultSort[] = "[".($iColOffset + $iPos).",".($bAscending ? '0' : '1')."]";
} elseif (($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')."]";
}
else if($sColCode == 'friendlyname' && $bViewLink)
{
$aDefaultSort[] = "[".($iColOffset + $iPos).",".($bAscending ? '0' : '1')."]";
} elseif ($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
@@ -784,9 +693,8 @@ oTable
class_aliases: $sJSClassAliases $sCssCount
});
JS
);
if ($sFakeSortList != '')
{
);
if ($sFakeSortList != '') {
$oPage->add_ready_script("oTable.trigger(\"fakesorton\", [$sFakeSortList]);");
}
return $sHtml;
@@ -803,12 +711,9 @@ 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()");
}
}
@@ -865,16 +770,15 @@ 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,8 +1,9 @@
<?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,73 +19,67 @@ class ExcelExporter
protected $iPosition;
protected $sOutputFilePath;
protected $bAdvancedMode;
public function __construct($sToken = null)
{
$this->aStatistics = array(
$this->aStatistics = [
'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 = array();
$this->aObjectsIDs = [];
$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 = array(
$aState = [
'state' => $this->sState,
'statistics' => $this->aStatistics,
'filter' => $this->oSearch->serialize(),
@@ -94,31 +88,28 @@ 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']);
@@ -128,223 +119,197 @@ 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(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 = 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;
}
$oIDSet = new DBObjectSet($this->oSearch);
$oIDSet->OptimizeColumnLoad(['id']);
$this->aObjectsIDs = [];
while ($oObj = $oIDSet->Fetch()) {
$this->aObjectsIDs[] = $oObj->GetKey();
}
$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;
$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;
}
}
$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 = 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;
$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;
// 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 array(
return [
'code' => $sCode,
'message' => $sMessage,
'percentage' => $iPercentage,
);
];
}
public function GetExcelFilePath()
{
if ($this->sOutputFilePath == null)
{
return utils::GetDataPath().'bulk_export/'.$this->sToken.'.xlsx';
}
else
{
if ($this->sOutputFilePath == null) {
return APPROOT.'data/bulk_export/'.$this->sToken.'.xlsx';
} else {
return $this->sOutputFilePath;
}
}
public static function GetExcelFileFromToken($sToken)
{
return @file_get_contents(utils::GetDataPath().'bulk_export/'.$sToken.'.xlsx');
return @file_get_contents(APPROOT.'data/bulk_export/'.$sToken.'.xlsx');
}
public static function CleanupFromToken($sToken)
{
@unlink(utils::GetDataPath().'bulk_export/'.$sToken.'.status');
@unlink(utils::GetDataPath().'bulk_export/'.$sToken.'.data');
@unlink(utils::GetDataPath().'bulk_export/'.$sToken.'.xlsx');
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.status');
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.data');
@unlink(APPROOT.'data/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(utils::GetDataPath().'bulk_export/*.*');
$aFiles = glob(APPROOT.'data/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);
@@ -352,189 +317,155 @@ class ExcelExporter
}
}
}
public function DisplayStatistics(Page $oPage)
{
$aStats = array(
$aStats = [
'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 = array(
$aStats = [
'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 = array('B','KB','MB','GB','TB','PB');
return @round($iSize/pow(1024,($i=floor(log($iSize,1024)))),2).' '.$aUnits[$i];
$aUnits = ['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(utils::GetDataPath()."bulk_export"))
{
@mkdir(utils::GetDataPath()."bulk_export", 0777, true /* recursive */);
if (!is_dir(APPROOT."data/bulk_export")) {
@mkdir(APPROOT."data/bulk_export", 0777, true /* recursive */);
clearstatcache();
}
if (!is_writable(utils::GetDataPath()."bulk_export"))
{
throw new Exception('Data directory "'.utils::GetDataPath().'bulk_export" could not be written.');
if (!is_writable(APPROOT."data/bulk_export")) {
throw new Exception('Data directory "'.APPROOT.'data/bulk_export" could not be written.');
}
}
protected function GetStateFile($sToken = null)
{
if ($sToken == null)
{
if ($sToken == null) {
$sToken = $this->sToken;
}
return utils::GetDataPath()."bulk_export/$sToken.status";
return APPROOT."data/bulk_export/$sToken.status";
}
protected function GetDataFile()
{
return utils::GetDataPath().'bulk_export/'.$this->sToken.'.data';
return APPROOT.'data/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 = array();
$this->aFieldsList = [];
$oAppContext = new ApplicationContext();
$aClasses = $oSet->GetFilter()->GetSelectedClasses();
$this->aAuthorizedClasses = array();
foreach($aClasses as $sAlias => $sClassName)
{
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO)
{
$this->aAuthorizedClasses = [];
foreach ($aClasses as $sAlias => $sClassName) {
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $oSet) != UR_ALLOWED_NO) {
$this->aAuthorizedClasses[$sAlias] = $sClassName;
}
}
$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))
{
$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)) {
// 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,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,10 @@
<?php
/**
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ForgotPasswordApplicationException extends Exception
{
}

View File

@@ -0,0 +1,10 @@
<?php
/**
* @copyright Copyright (C) 2010-2026 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ForgotPasswordUserInputException extends Exception
{
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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