Compare commits

..

343 Commits

Author SHA1 Message Date
v-dumas
9683b8d96a N°9499 - Ticket ITIL: remove 'request type' from UserRequest details 2026-04-17 09:51:01 +02:00
Molkobain
a6171c896b Merge remote-tracking branch 'origin/support/3.2' into develop 2026-04-17 09:20:18 +02:00
Benjamin DALSASS
89231976f8 Revert N°8638 - Adapt mysqldump calls to follow iTop SSL configuration 2026-04-16 15:00:01 +02:00
Stephen Abello
4e24ac508d Merge branch 'support/3.2' into develop 2026-04-15 17:01:58 +02:00
Molkobain
e1c804139f 👥 Add Lénaick to the sample data, welcome! 🙌 2026-04-15 16:55:19 +02:00
Stephen Abello
f0a95cbb3d N°9447 - Harmonize title and panel logo rules 2026-04-15 16:49:52 +02:00
Stephen Abello
265acb4a1e Merge branch 'support/3.2' into develop
# Conflicts:
#	css/setup.css
2026-04-15 15:16:01 +02:00
Stephen Abello
30f720b9ad N°9447 - Setup logo is repeating vertically (#884) 2026-04-15 15:13:28 +02:00
Benjamin DALSASS
1992c7e7c1 Merge remote-tracking branch 'origin/support/3.2' into develop 2026-04-14 09:09:23 +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
4dbaaad2b9 N°9379 - PHP unserialze function - security hardening 2026-04-14 08:43:47 +02:00
Benjamin DALSASS
2fe49fbff4 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	core/attributedef.class.inc.php
#	css/backoffice/_shame.scss
#	pages/csvimport.php
2026-04-14 08:41: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
bcf1bb003c Merge remote-tracking branch 'origin/support/3.2' into develop 2026-04-13 10:17:26 +02:00
jf-cbd
d000d93b19 N°8766 - Fix wrong author on portal log (#880) 2026-04-13 10:10:40 +02:00
v-dumas
83a67dff96 N°9138 - restore old fieldset ids to ease migration 2026-04-10 16:53:26 +02:00
Vincent Dumas
79270950ee N°9418 - Add colors on UserRequest request type in Simple Ticket (#862) 2026-04-09 16:58:07 +02:00
Stephen Abello
9f25635a64 N°9177 - Blockquote in HTML field are unreadable in darkmoon 2026-04-09 15:19:15 +02:00
Vincent Dumas
18b91df58d N°8995 - manage End of Support at the Model and Software level (#875)
* N°8995 - CMDB: Manage Groups as TagSet on FunctionalCI
* N°8995 - Set logo as semantic field on NetworkDeviceType
2026-04-09 11:47:32 +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
4afe06d370 Merge branch 'support/3.2' into develop
# Conflicts:
#	core/config.class.inc.php
2026-04-08 14:21:51 +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
587d7fcf72 Merge remote-tracking branch 'origin/support/3.2' into develop 2026-04-08 10:30:35 +02:00
lenaick.moreira
f1735767c3 💚 Fix CI 2026-04-08 10:19:31 +02:00
lenaick.moreira
bfdc8a68e6 Merge remote-tracking branch 'origin/support/3.2' into develop 2026-04-08 09:49:34 +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
684eaac278 Merge remote-tracking branch 'origin/support/3.2' into develop 2026-04-08 09:43:20 +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
4ce8f70382 Merge remote-tracking branch 'origin/support/3.2' into develop 2026-04-07 21:10:09 +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
868eea9b43 Merge remote-tracking branch 'origin/support/3.2' into develop 2026-04-07 17:38:21 +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
v-dumas
70a8bdf427 N°9450 - Add Tape, NAS file System, Fiber Channel Interface in overview dashboard 2026-04-03 09:43:30 +02:00
Vincent Dumas
38cda442e3 N°9138 - Presentation details step2 + move sample data (#866)
N°9471 - Rename in FR Rack as Baie - Switch Baie and Chassis icons
2026-04-02 18:23:11 +02:00
Vincent Dumas
20c8b7c0ce N°9137 logo by network device type (#864)
Add Logo on NetworkDeviceType used as object icon on all NetworkDevice of a given type
Add Brand, OSFamily and NetworkDeviceType icons as dashlet ressources
2026-04-02 17:23:08 +02:00
lenaick.moreira
b58a848cc9 Merge remote-tracking branch 'origin/support/3.2' into develop 2026-04-01 16:25:36 +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
d9433fead5 Merge remote-tracking branch 'origin/support/3.2' into develop 2026-04-01 10:57:23 +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
jf-cbd
1a9e4bd5ad Reformat ormDocumentTest.php 2026-03-31 17:51:06 +02:00
Lenaick
3cdadf3c6e Add tests for lock acquisition functionality (#865) 2026-03-31 17:01:22 +02:00
jf-cbd
36891e441b Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	datamodels/2.x/itop-hub-connector/ajax.php
2026-03-31 16:43:48 +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
ba6b3da238 N°9231 - Fix unit test for 3.3.0 2026-03-31 16:28:19 +02:00
Stephen Abello
2674e9c47f Merge branch 'support/3.2' into develop
# Conflicts:
#	tests/php-unit-tests/unitary-tests/core/ormDocumentTest.php
2026-03-31 15:51:27 +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
Anne-Cath
f7b73717b4 Rollback N°2364 - API : remove old linkedset persistance 2026-03-31 10:37:36 +02:00
v-dumas
0d9b34a879 N°9063 - Add ev_autoresolve on Approved UserRequest in Simple Ticket 2026-03-30 17:13:14 +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
v-dumas
a4a0b3c18c Merge remote-tracking branch 'origin/support/3.2' into develop 2026-03-30 14:31:02 +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
v-dumas
443fa60459 N°8515 - Access rights & relation labels for container classes 2026-03-27 15:19:33 +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
63e40fc0a2 N°9347 - Make the icon of ContainerImages using the one of Software class 2026-03-27 14:17:41 +01:00
v-dumas
176bd51afb N°8515 - Add ContainerImages tab on Software class 2026-03-27 11:35:18 +01:00
v-dumas
e8a1f8c3b6 N°8994 - Classes description fix (FR, EN) 2026-03-27 10:25:35 +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
v-dumas
447e7d01d5 N°9063 - Fix testEnumTransition as ev_autoresolve is no more available in resolve state 2026-03-26 17:02:50 +01:00
lenaick.moreira
d4a9d1e831 N°8834 - Updated the PHP version to 8.4 for the CI in the commit 2026-03-26 15:59:52 +01:00
lenaick.moreira
be3726623f Refactor constructor in AttributeExist to require sOqlPropertyPath parameter
* https://github.com/Combodo/iTop/pull/857#discussion_r2993953031
2026-03-26 15:57:39 +01:00
Lenaick
aae6d324f9 N°8851 - Explicit nullable in functions parameters (#857) 2026-03-26 15:52:57 +01:00
Vincent Dumas
398b47d5d1 N°9356 Improve service classes tooltips (#849) 2026-03-26 15:30:47 +01:00
Vincent Dumas
b85f4a5161 N°9086 - Add uniqueness rules on OSFamily, OSVersion and IOSVersion (#794) 2026-03-26 12:20:07 +01:00
Vincent Dumas
3c17b1bdea N°9063 - Add missing ev_autoresolve transitions on UserRequest and Incident (#798) 2026-03-26 12:14:41 +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
9cc80a8946 Temporarily rollback the PHP version to 8.3 for the CI in the commit 2026-03-25 18:02:20 +01:00
lenaick.moreira
11afd23087 Merge remote-tracking branch 'origin/support/3.2' into develop 2026-03-25 17:56:19 +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
347663f8f7 Merge branch 'support/3.2' into develop
# Conflicts:
#	composer.json
#	composer.lock
#	core/attributedef.class.inc.php
#	sources/Controller/AjaxRenderController.php
2026-03-25 10:30:30 +01:00
Stephen Abello
c56c7a1f9d Fix CI by fixing code style 2026-03-25 10:25:37 +01:00
v-dumas
3d21ff1cd7 Fix SK dico misaligned 2026-03-25 10:20:00 +01:00
Stephen Abello
fb2f0f1447 N°9328 - Add scssphp compatibility with PHP 8.4 (#851) 2026-03-25 09:58:05 +01:00
v-dumas
4847bb563a N°8958 - Fix class name in french 2026-03-24 17:10:50 +01:00
Vincent Dumas
8b664ff644 N°8958 - Fix FR class names, add EN lnk descriptions (#852) 2026-03-24 15:40:10 +01:00
v-dumas
902a05cc84 N°3961 - Service Catalog: fix icons and dico entries 2026-03-24 15:33:36 +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
v-dumas
19d6052460 Merge remote-tracking branch 'origin/develop' into develop 2026-03-23 18:20:36 +01:00
v-dumas
70eaa30e10 N°8515 - Container SummaryCards + containerhosts_list on Server and VM 2026-03-23 18:19:49 +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-Cath
a6de103b4a N°8692 - Notification: Fix AttributeSubitem placeholder behavior - fix for 3.3 2026-03-23 15:45:09 +01:00
Anne-Cath
9328f6d916 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	core/attributedef.class.inc.php
2026-03-23 15:39:25 +01:00
Anne-Catherine
c61b21559c N°8692 - Notification - placeholder attributesubitem (#778) 2026-03-23 15:33:50 +01:00
v-dumas
0d18572fbe N°9357 - Hide in User Portal, Service Subcategory in implementation when in full ITIL 2026-03-23 13:44:52 +01:00
jf-cbd
9db21ab860 Merge remote-tracking branch 'origin/support/3.2' into develop 2026-03-20 16:32:59 +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
3a435eba7d Merge remote-tracking branch 'origin/support/3.2' into develop 2026-03-20 14:37:01 +01:00
Benjamin Dalsass
170014e8f0 N°9232 - Information Disclosure (#850) 2026-03-20 14:35:05 +01:00
Stephen Abello
df05a4688e Merge branch 'support/3.2' into develop
# Conflicts:
#	js/searchformforeignkeys.js
2026-03-19 09:24:10 +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
v-dumas
960990d47d N°8994 - fix duplicate key in EN dico 2026-03-18 17:42:15 +01:00
Vincent Dumas
99d39732a5 N°8994 - Add description on all iTop classes (#789)
* N°8994 - Add description on all iTop classes 

---------

Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>
2026-03-18 17:16:13 +01:00
Vincent Dumas
5e916510fb N°9138 - Add semantic logo on some CMDB classes (#841)
* N°9138 - Align presentation details of CMDB classes
* N°9138 - Align presentation details of Software classes
* N°9138 - fix greptile feedbacks
* N°9347 - Enable icon of some CIs by instance
* N°9347 - Get Software logo as SoftwareInstance icon
* N°9347 - Change Cloud class icon location
* N°9347 - Fix logo dictionary entries missing
* N°9347 - Fix 'impact analysis' spelling
* N°9347 - fix external key labels for PhysicalInterface
2026-03-18 16:26:08 +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
0be4f52b04 N°8543 - best practices 2026-03-18 11:36:13 +01:00
Molkobain
f7d41d3650 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/mode_tree.html.twig
2026-03-18 11:33:29 +01:00
jf-cbd
eabbe2f00b N°8543 - best practices 2026-03-18 10:09:25 +01:00
jf-cbd
52e303cb37 N°8543 - develop branch adaptation to use new method to get module info 2026-03-17 17:22:06 +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
Benjamin DALSASS
0b19333cd7 Dump autoload 2026-03-17 08:08:17 +01:00
Benjamin DALSASS
da09c701cc N°8612 inline images to base64
- AttributeText standalone file modification
2026-03-17 08:07:15 +01:00
Benjamin DALSASS
7c8670b57c Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	core/attributedef.class.inc.php
#	setup/extensionsmap.class.inc.php
#	tests/php-unit-tests/composer.lock
2026-03-17 08:05:10 +01:00
Vincent Dumas
1c1f01aed5 N°9357 - Status mandatory on Service(Subcategory) (#843)
* N°9357 - Status mandatory on Service(Subcategory)

* N°9357 - Fix greptile feedbacks
2026-03-16 17:35:20 +01:00
Vincent Dumas
5d6e2cc9f7 N°3961 Reorganize Service menu (#842)
* N°3961 - Reorganize Service menus

* N°3961 - Dashboard Service Catalog

* N°3961 - dictionaries

* N°3961 - Suppress useless dictionary entries

* N°3961 - Align dictionary entries

* N°3961 - Missing FR translation

* N°3961 - Translation issue

* N°3961 - Remove Rules & Workflow automation menu
2026-03-16 17:14:01 +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
v-dumas
3c5bf8a134 N°9138 - Fix typo in dict entry code ConfigMgmt 2026-03-16 16:51:50 +01:00
Benjamin Dalsass
e2994b645b N°8612 inline images to base64 (#826) 2026-03-16 08:36:37 +01:00
Vincent Dumas
32ddf1c980 Feature/9138 display details cmdb (#839)
* N°9138 - Align presentation details of CMDB classes
2026-03-13 17:19:34 +01:00
jf-cbd
0f7540dec8 Create French counterpart of English dictionaries 2026-03-11 14:28:59 +01:00
Thomas Casteleyn
a36a7cc832 fix: Align FiberChannelInterface and LogicalInterface with PhysicalInterface naming (#784) 2026-03-11 14:10:42 +01:00
v-dumas
3dd1c11d3b N°9304 - Add quick links to access impact analysis by default 2026-03-10 13:28:04 +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
b3613b6c4b Merge remote-tracking branch 'origin/support/3.2' into develop 2026-03-09 08:42:39 +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
884d64a42a Remove hardcoded colors from non theme file and replace hardcoded value with variable 2026-03-05 15:35:49 +01:00
Stephen Abello
44c0a025a8 Merge branch 'support/3.2' into develop
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/templates/bricks/manage/popup-export-excel.html.twig
2026-03-05 15:32:35 +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.moreira
36247ba0ee Fix unit test testPhpMinVersionConsistency 2026-03-02 15:30:30 +01:00
lenaick.moreira
091d99b08f PHP CS Fixer 2026-03-02 15:19:31 +01:00
lenaick.moreira
3c60a80f9b Fix error on CI
* Call to undefined method PhpParser\ConstExprEvaluator::setStaticcallsWhitelist()
2026-03-02 14:55:12 +01:00
lenaick.moreira
ae633111c0 Merge remote-tracking branch 'origin/support/3.2' into develop 2026-03-02 14:50:14 +01:00
Molkobain
52a1d8d626 📝 Update developers.md to fix php static analysis link 2026-03-02 09:55:05 +01:00
Molkobain
3a64e3bb9e 📝 Update README.md to point to a new "Developers" page 2026-03-02 09:52:36 +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
94a36c0066 Merge remote-tracking branch 'origin/support/3.2' into develop 2026-02-26 09:30:14 +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 Espie
57a0b5691f Merge support/3.2 into develop 2026-02-25 10:44:19 +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
9e21976424 Merge remote-tracking branch 'origin/support/3.2' into develop 2026-02-24 21:10:57 +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
Eric Espie
dad39c3ebe Merge support/3.2 into develop 2026-02-24 13:55:55 +01:00
odain
29920bfeb7 cleanup: log level in setUp + remove ls cli 2026-02-24 11:26:48 +01:00
odain
2313ee2bbd cleanup: log level in setUp + remove ls cli 2026-02-24 11:22:24 +01:00
jf-cbd
22b0c431a0 🌐 Add French translations in fr.dict.itop-config-mgmt.php 2026-02-23 18:51:57 +01:00
Thomas Casteleyn
499b3bca88 N°7570 - feat(Typology): Add useful tabs to certain typology classes (#756) 2026-02-23 17:28:56 +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
4853ca444e N°8910 - Upgrade Symfony packages (#811) 2026-02-23 06:54:26 +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
b91e6c384a Merge branch 'support/3.2' into develop 2026-02-19 17:39:47 +01:00
Stephen Abello
2247691e58 Fix CI by updating files code style 2026-02-19 16:59:25 +01:00
Stephen Abello
f014b43761 Merge branch 'support/3.2' into develop 2026-02-19 16:16:56 +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
0f11fd9919 N°8910 - Upgrade Symfony packages
- update composer packages
2026-02-19 09:52:18 +01:00
Benjamin DALSASS
2b828f8a22 Merge branch 'support/3.2' into develop
# Conflicts:
#	composer.lock
#	datamodels/2.x/authent-local/dictionaries/pt_br.dict.authent-local.php
#	dictionaries/fr.dictionary.itop.ui.php
#	dictionaries/it.dictionary.itop.ui.php
#	dictionaries/pt_br.dictionary.itop.ui.php
#	lib/composer/autoload_classmap.php
#	lib/composer/autoload_static.php
#	lib/composer/installed.json
#	lib/composer/installed.php
#	lib/symfony/cache/Traits/RelayProxy.php
#	lib/symfony/form/Exception/AccessException.php
#	lib/symfony/form/Exception/ErrorMappingException.php
#	lib/symfony/form/Exception/StringCastException.php
#	lib/symfony/http-foundation/Request.php
#	lib/symfony/polyfill-intl-icu/composer.json
#	lib/symfony/property-access/LICENSE
#	lib/symfony/security-core/Event/AuthenticationSuccessEvent.php
2026-02-19 09:22:08 +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
Vincent Dumas
0134ead5dd remove installer in itop-container-mgmt 2026-02-18 11:49:40 +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
3fdbcbc0fb N°8681 - Enforce type hint for parameters and return value 2026-02-17 20:55:55 +01:00
Molkobain
a5296e11e1 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	core/attributedef.class.inc.php
2026-02-17 20:54:47 +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
v-dumas
b085147f23 N°8844 - Datamodel Cloud moved in Virtualization 2026-02-17 17:49:55 +01:00
Vincent Dumas
38fccf85e3 N°8515 - Add CMDB datamodel for Docker and Kubernetes (#787)
* N°8515 - Add CMDB datamodel for Docker and Kubernetes
* N°8515 - Add Cloud class under Virtualization

* N°8515 - Add lnkContainerApplicationToImage and more fields on Image
* N°8515 - Move ContainerApplication under SoftwareInstance
* N°8515 - Use structural data instead of explicit load
--------

Co-authored-by: Stephen Abello <stephen.abello@combodo.com>
Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>
2026-02-17 12:20:43 +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
Timothee
f8cf14cbad N°3124 Add deprecation version 2026-02-09 14:17:35 +01:00
Eric Espie
a5dededfb4 Merge remote-tracking branch 'origin/support/3.2' into develop 2026-02-05 17:06:26 +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
1cdcaac3d0 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	sources/Core/Kpi/KpiLogData.php
2026-02-05 15:33:25 +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
Molkobain
3f807a64bb N°9154 - Update Jimmy avatar to reduce size 2026-01-28 11:54:56 +01:00
Molkobain
1d4a155e8f N°9154 - Add Amine, Gaëlle & Lorraine to sample data 2026-01-28 11:53:23 +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
v-dumas
2b6fa8b381 N°9080 - Fix duplicate 'Model' in class labels in EN and FR 2026-01-23 15:07:49 +01:00
v-dumas
7d2dc5e36a N°9076 - Fix typos and cosmetics in FR initial queries 2026-01-23 14:36:38 +01:00
v-dumas
f87df8f28b N°9087 - Add logo on Brand and OS Family, with structural data 2026-01-21 17:13:32 +01:00
Anne-Cath
cc9e64616f N°8786 - configuration: allow conditions on the allowed_login_types field - cleanup 2026-01-16 16:29:15 +01:00
Stephen Abello
f34373be6d N°7909 - Missing spacing between fields when columns collapse 2026-01-16 15:30:32 +01:00
v-dumas
a39234f438 N°4032 - Restore explicit dependency used in filter 2026-01-14 16:06:14 +01:00
v-dumas
ac8937105d N°8873 - Modify Tooltip on 'filter' field of TriggerOnObjectUpdate class 2026-01-12 16:33:36 +01:00
odain
fb6f892244 N°9085 - Cannot install itop without a portal 2026-01-12 15:43:56 +01:00
v-dumas
0a04c83c7b N°9045 - Add a tooltip for Service Family in Service details 2026-01-12 15:20:14 +01:00
v-dumas
cc8252bebf N°9046 - Tooltip mentions that a team is required on a support model 2026-01-12 14:52:57 +01:00
Vincent Dumas
3e879c64a7 N°4032 - On UserRequest, change proposed service subcategories (#786)
* N°4032 - On UserRequest, service subcategory no more limited by request_type
2026-01-08 10:08:52 +01:00
Eric Espie
5c6369b9b8 N°9065 - XML Definition for Dashlet properties 2026-01-07 17:16:50 +01:00
Eric Espie
154fb5c737 N°9066 - Serialization/Unserialization from XML to Forms 2026-01-07 17:09:47 +01:00
Eric Espie
efb1bd765b N°9065 - XML Definition for Dashlet properties 2026-01-07 17:09:20 +01:00
Eric Espie
b39af74d07 Fix CI 2026-01-07 09:04:56 +01:00
Eric Espie
904cd0b518 N°8772 - Move DIService into PSR-11 Service Locator 2025-12-30 14:27:28 +01:00
Benjamin Dalsass
4c1ad0f4f2 N°8772 - Form dependencies manager implementation
- Form SDK implementation
- Basic Forms
- Dynamics Forms
- Basic Blocks + Data Model Block
- Form Compilation
- Turbo integration
2025-12-30 11:42:55 +01:00
v-dumas
3955b4eb22 N°8534 - Prevent ending on Portal 2025-12-22 17:49:47 +01:00
odain
3d46fe6ef1 Merge branch 'support/3.2' into develop 2025-12-19 18:44:55 +01:00
odain
4dba47798c ci: fix broken test due to POST CURL file not sent 2025-12-19 18:35:42 +01:00
Anne-Cath
9480c4053d N°8786 - configuration: allow conditions on the allowed_login_types field - fix tests 2025-12-19 16:37:47 +01:00
odain
9ea197148c Merge branch 'support/3.2' into develop 2025-12-19 16:17:00 +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
bf80b5dca2 Merge branch 'support/3.2' into develop 2025-12-19 15:46:00 +01:00
odain
09afcb229c ci: fix callUrl with posted params 2025-12-19 11:11:03 +01:00
odain
92f843f676 Merge branch 'support/3.2' into develop 2025-12-19 10:39:24 +01:00
odain
3df4ddc696 ci: fix code style 2025-12-19 10:39:15 +01:00
odain
d552727c55 ci: fix code style 2025-12-19 10:38:48 +01:00
odain
1fe401c102 ci: rename CallItopUrl by CallUrl + cleanup 2025-12-19 10:01:37 +01:00
odain
a4b0b6e855 ci: rename CallItopUrl by CallUrl + cleanup 2025-12-19 09:54:43 +01:00
odain
545028d68a Merge branch 'support/3.2' into develop 2025-12-18 13:51:54 +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
b3cd79605d Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2025-12-17 22:22:36 +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
v-dumas
cef4a52081 N°8950 - Fix color of implementation status 2025-12-17 12:21:54 +01:00
v-dumas
dc9fb2d693 N°8950 - Add obsolescence rule on Contract, Service and ServiceSubcategory 2025-12-17 12:21:54 +01:00
v-dumas
e9ffbe5b09 N°8950 - Add colors on 'status' on non-CI classes 2025-12-17 12:21:54 +01:00
v-dumas
9c792a601f N°8951 - Add colors on 'status' field on CIs 2025-12-17 12:21:54 +01:00
Molkobain
cda6c1dfa8 Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2025-12-17 11:09:22 +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
c144c80663 Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2025-12-17 10:56:31 +01:00
Molkobain
d9261b8342 N°6644 - Tests: Add static analysis for PHP (#536) 2025-12-17 10:45:53 +01:00
Anne-Cath
df567fb9fe N°8786 - configuration: allow conditions on the allowed_login_types field 2025-12-09 18:24:44 +01:00
Timothee
028767768c N°8763 Code style fix 2025-12-09 16:04:50 +01:00
Timothee
11ec80830e N°8763 Fix issue with previously installed extensions not installed after upgrade 2025-12-09 15:46:10 +01:00
odain
edf992ef2b N°8724 - reduce log verbosity on ModuleInstallerAPI calls 2025-12-09 10:28:24 +01:00
Anne-Cath
7c8fb1a51d N°8852 - improve display attachment in properties tab 2025-12-09 09:15:51 +01:00
Anne-Cath
aa27b3601b N°8786 - configuration: allow conditions on the allowed_login_types field 2025-12-08 16:47:30 +01:00
Timmy38
73f868ac83 N°8763 Halt setup if non-uninstallable extension is missing (#781)
* N°8763 Halt setup if an installed & non-uninstallable extension is missing from disk
2025-12-04 11:01:31 +01:00
odain
5a2157ba21 N°8724 - refactoring for maintenability 2025-11-27 15:47:25 +01:00
odain
03e25a226e Merge branch 'faf/moduledependency-enhancement' into develop 2025-11-26 19:25:37 +01:00
odain
24048d2b9c N°8724 - Enhance setup feedback in case of module dependency issue (#700)
code style

last test cleanup

review + enhance UI output and display only failed module dependencies

real life test cleanup

review: add more tests + refacto

code review: enhance algo and APIs

review: renaming

enhance test coverage

refactoring

renaming + reorder functions/tests

compute GetDependencyResolutionFeedback in Module class

review2 : renaming things

fix rebase + code formatting

fix code formatting

review changes

refactoring: code cleanup/standardization/remove all prototype stuffs

refactoring: code cleanup/standardization/remove all prototype stuffs

add deps validation to extension ci job

fix ci

fix ci: test broken when dir to scan did not exist like production-modules

fix tests

module dependency validation moved in a core folder + cleanup dedicated unit/integration tests

forget dependency computation optimization seen as too risky + keep only user friendly sort in case of setup error

rebase on develop + split new sort computation apart from modulediscovery

revert to previous legacy order + gather new module computation classes in a dedicated folder

make validation work (dirty way) + cleanup

make setup deterministic: complete dependency order with alphabetical one when 2 module elements are at same position

final deps validation bases on DM and PHP classes

init in beforeclass + read defined classes/interfaces by module

module discovery classes renaming to avoid collision with customer DM definitions

read module file data apart from ModuleDiscovery

cleanup

cleanup

fix inconsistent module dependencies

fix integration check

save tmp work before trying to fetch other wml deps

fix module dependencies

fix DM filename typo

rename ModuleXXX classes by iTopCoreModuleXXX to reduce collisions with extensions

add phpdoc + add more tests

module dependency optimization - refacto + dependency new sort order

module dependency optimization - stop computation when no new dependency is resolved

enhance module dependency computation for optimization and admin feedback
2025-11-26 19:23:26 +01:00
odain
d8121b563a synchro code: fix Deprecated: strlen(): Passing null to parameter 2025-11-21 10:38:26 +01:00
Anne-Cath
f266f5ff36 N°8911 - Attachment visualisation broken 2025-11-20 08:06:00 +01:00
odain
4a6b129eb8 Merge branch 'support/3.2' into develop 2025-11-18 15:36:47 +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
jf-cbd
bc4d50dd0b ✏️ fix a typo 2025-11-17 17:14:15 +01:00
Vincent Dumas
e21a541b70 N°8533 - Impact Analysis, add icons and tooltips in shortcut_actions (#767)
* N°8533 - Impact Analysis, add icons and tooltips in shortcut_actions
2025-11-17 15:55:05 +01:00
v-dumas
ed360edb83 Fix PHP CS 2025-11-17 15:04:19 +01:00
Vincent Dumas
53de040934 N°8534 - Prevent Admin, SuperUser from loose of rights (#774)
* N°8534 - Prevent Admin & SuperUser from suicide
Prevent creation/modification of Administrator, SuperUser, REST User, combined with a Profile denying access to the backoffice
2025-11-17 14:17:18 +01:00
odain
b8345de553 Merge branch 'support/3.2' into develop 2025-11-16 08:15:29 +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
e09987e442 Merge branch 'support/3.2' into develop 2025-11-15 21:29:52 +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
Vincent Dumas
262cc3c206 N°5882 - AuditRule: Add Owner and Process (#775)
* N°5882 - Audit Rule: Add "Owner" and "Correction Process" fields
* Fix PHP CS
2025-11-14 17:04:25 +01:00
odain
b5fe12ca3c Merge branch 'issue/fix-deprecated-aftermerge' into develop 2025-11-14 14:34:22 +01:00
odain
6d279647f1 fix deprecated warnings
fix call to MFException constr
2025-11-14 14:33:52 +01:00
odain
299c468eaa fix code style - remaining itop-portal-base/portal/config/bootstrap.php 2025-11-14 10:54:02 +01:00
odain
7bbcc388ea Merge branch 'support/3.2' into develop 2025-11-14 10:52:27 +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
jf-cbd
41a90b5033 Update datamodel.itop-request-mgmt.xml 2025-11-14 10:11:45 +01:00
v-dumas
67018b9b17 N°8768 - Fix PHP CS in dictionnaries 2025-11-14 09:40:17 +01:00
Vincent Dumas
fa8ecb956e N°8776, N°8623, N°8496, N°8445, N°8378, N°8040 & N°7755 (#769)
* N°8776 - Move Attachemnt above caselog in the UR form in the portal
* N°8623 - French dictionaries, fix missing entries
* N°8496 - Add tooltips on Known Error class
* N°8445 - Color "Priority" on Userrequest & Incident in ITIL
* N°8378 - Missing rights on incident for SuperUser
* N°8040 - FR dico replacing left "Statut" by "Etat"
* N°7755 - Add tto_time_spent and ttr_time_spent on standard datamodel
2025-11-14 09:33:48 +01:00
Vincent Dumas
58eae4dde3 N°7472 - Team Tickets tab replaced by a Dashboard (#776)
* N°640 - No 'Tickets' tab on FunctionalCIs when no Ticket sub-classes exists

* N°7472 - Team Tickets tab replaced by a Dashboard

* N°7472 - Team Tickets tab replaced by a Dashboard (2)

* N°7472 - Team Tickets tab replaced by a Dashboard (3)
2025-11-14 09:08:16 +01:00
Vincent Dumas
d098a5eb87 N°8845 - Set request_type regardless of User rights (#777)
* N°8845 - Set request_type regardless of User rights

* Update datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml

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

---------

Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>
2025-11-14 09:05:54 +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
v-dumas
dd82950eee N°8832 - Fix typo in FR translation 2025-11-10 16:48:37 +01:00
Stephen Abello
2e8b48ce47 Merge branch 'support/3.2' into develop 2025-11-10 10:56:38 +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
b0a792afab N°8796 - Add PHP code style validation in iTop and extensions - format whole code base 2025-11-07 20:39:38 +01:00
odain
7681c157ec Merge branch 'support/3.2' into develop 2025-11-07 20:33:14 +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
Stephen Abello
6cf6e7dd8d Merge branch 'support/3.2' into develop
# Conflicts:
#	core/simplecrypt.class.inc.php
#	tests/php-unit-tests/unitary-tests/core/SympleCryptTest.php
2025-11-06 10:27:03 +01:00
Timmy38
d4183acfde N°8762 - Allow extensions uninstallation at setup 2025-11-05 15:52: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
jf-cbd
c5fb116052 📝 Update CONTRIBUTING.md after Hacktoberfest 2025-11-03 17:46:43 +01:00
odain
bdc8fdd02f N°4720 - fix Deprecation warnings with ormLinkSet->UpdateFromCompleteList API 2025-11-03 08:37:35 +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
Anne-Cath
775ac3df77 N°3815 - Remove "DisplayTemplate" PHP classes - TemplateMenuNode::GetHyperlink() must return '' 2025-10-27 17:26:37 +01:00
Stephen Abello
83927af8ed Merge branch 'support/3.2' into develop
# Conflicts:
#	addons/userrights/userrightsprofile.db.class.inc.php
#	addons/userrights/userrightsprojection.class.inc.php
#	datamodels/2.x/combodo-backoffice-darkmoon-theme/scss/scss-variables.scss
2025-10-27 16:00:08 +01:00
Stephen Abello
f1609168b7 N°8715 - Revert first fix attempt 2025-10-27 15:57:54 +01:00
Stephen Abello
4f845c63cf N°8715 - Missing name for SetUIBlock when initialized in twig 2025-10-27 15:57:11 +01:00
Timothee
d029039d22 N°6759 - Fix shortcut & dictionary entry name 2025-10-27 15:17:08 +01:00
odain
10a7fafe59 N°4720 - EOL restored 2025-10-27 10:25:44 +01:00
odain
700b95e8c6 N°4720 - ease applystimuli support due to API deprecation removals 2025-10-24 15:14:30 +02: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
88e0f17164 N°8306 - Wrong line number error if XML is over 65535 lines 2025-08-26 11:56:44 +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
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
Eric Espie
05642cdf84 N°8306 - Wrong line number error if XML is over 65535 lines 2025-07-10 16:53:36 +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
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
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
Eric Espie
cd1c6f5ec5 N°8404 - Error when upgrading composant version on Designer (Migration status on licence) 2025-06-16 08:59:43 +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
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
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
3514 changed files with 198972 additions and 72531 deletions

9
.doc/developers.md Normal file
View File

@@ -0,0 +1,9 @@
# Developers
## PHP Code Styles
We use `PHP CS Fixer` to ensure code formating consistency across PHP codebase.
You can find the configuration and instructions to run it [here](../tests/php-code-style/README.md).
## PHP Static Analysis
We use `PHPStan` to ensure code quality and to detect potential bugs in our PHP codebase.
You can find the configuration and instructions to run it [here](../tests/php-static-analysis/README.md).

View File

@@ -9,7 +9,7 @@ Any PRs not following the guidelines or with missing information will not be con
## Base information
| Question | Answer
|---------------------------------------------------------------|--------
| Related to a SourceForge thead / Another PR / Combodo ticket? | <!-- Put the URL -->
| Related to a SourceForge thread / Another PR / Combodo ticket? | <!-- Put the URL -->
| Type of change? | Bug fix / Enhancement / Translations

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 }}

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

@@ -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);

16
.phpstorm.meta.php Normal file
View File

@@ -0,0 +1,16 @@
<?php
/*
* @copyright Copyright (C) 2010-2025 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace PHPSTORM_META
{
override(\MetaModel::NewObject(0), map([
'' => '@',
]));
override(\MetaModel::GetObject(0), map([
'' => '@',
]));
}

View File

@@ -5,17 +5,6 @@ You want to contribute to iTop? Many thanks to you! 🎉 👍
Here are some guidelines that will help us integrate your work!
## Contributions
```
_ _ _ _ _ __ _
| | | | __ _ ___| | _| |_ ___ | |__ ___ _ __ / _| ___ ___| |_
| |_| |/ _` |/ __| |/ / __/ _ \| '_ \ / _ \ '__| |_ / _ \/ __| __|
| _ | (_| | (__| <| || (_) | |_) | __/ | | _| __/\__ \ |_
|_| |_|\__,_|\___|_|\_\\__\___/|_.__/ \___|_| |_| \___||___/\__|
```
This repository is part of Hacktoberfest. Contributions are welcome! Feel free to suggest improvements, add translation, fix bugs, or propose new features. Thank you for contributing !
### Subjects

View File

@@ -73,6 +73,9 @@ iTop development is sponsored, led, and supported by [Combodo][0].
[0]: https://www.combodo.com
## Developers
You can find information and instructions about our quality tools and how to run them [here](.doc/developers.md).
## Contributors

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)
{
if ($this->GetRawName() == "Administrator")
public function DoShowGrantSumary($oPage)
{
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,30 @@ 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)))
{
$oSet = new \ormLinkSet(get_class($oUser), 'profile_list', \DBObjectSet::FromScratch(\URP_UserProfile::class));
$oSet->AddItem(MetaModel::NewObject('URP_UserProfile', ['profileid' => $this->GetKey(), 'reason' => 'CheckIfProfileIsAllowed']));
if (!UserRights::IsActionAllowed(get_class($this), $iActionCode, $oSet)) {
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 +352,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,35 +401,32 @@ 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, ...]
@@ -472,8 +452,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,24 +460,19 @@ 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))
{
$oUserProfile = new URP_UserProfile();
$oUserProfile->Set('profileid', $oAdminProfile->GetKey());
$oUserProfile->Set('reason', 'By definition, the administrator must have the administrator profile');
$oSet = DBObjectSet::FromObject($oUserProfile);
$oAdminProfile = MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", ['name' => ADMIN_PROFILE_NAME], true /*all data*/);
if (is_object($oAdminProfile)) {
$oSet = new \ormLinkSet(UserLocal::class, 'profile_list', \DBObjectSet::FromScratch(\URP_UserProfile::class));
$oSet->AddItem(MetaModel::NewObject('URP_UserProfile', ['profileid' => $oAdminProfile->GetKey(), 'reason' => 'CreateAdministrator']));
$oUser->Set('profile_list', $oSet);
}
$iUserId = $oUser->DBInsertNoReload();
@@ -509,11 +483,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 +502,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 +531,20 @@ 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;
$this->aUsersProfilesList = [];
}
public function LoadCache()
{
static $bSharedObjectInitialized = false;
if (!$bSharedObjectInitialized)
{
if (!$bSharedObjectInitialized) {
$bSharedObjectInitialized = true;
if (self::HasSharing())
{
if (self::HasSharing()) {
SharedObject::InitSharedClassProperties();
}
}
@@ -615,45 +582,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 +624,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 +642,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 +664,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 +676,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 +693,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)
{
@@ -760,7 +711,9 @@ class UserRightsProfile extends UserRightsAddOnAPI
$iUser = $oUser->GetKey();
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];
@@ -771,20 +724,14 @@ class UserRightsProfile extends UserRightsAddOnAPI
$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 +739,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 +756,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)) {
@@ -895,20 +835,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 +866,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 +888,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');

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
}
}

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 (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/>
/**
* Class ApplicationContext
*
@@ -86,7 +86,6 @@ class PortalURLMaker implements iDBObjectURLMaker
}
}
/**
* Helper class to store and manipulate the parameters that make the application's context
*
@@ -115,11 +114,10 @@ class ApplicationContext
*/
public function __construct($bReadContext = true)
{
$this->aNames = array(
'org_id', 'menu'
);
if ($bReadContext)
{
$this->aNames = [
'org_id', 'menu',
];
if ($bReadContext) {
$this->ReadContext();
}
@@ -133,36 +131,29 @@ class ApplicationContext
*/
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();
@@ -185,8 +176,7 @@ class ApplicationContext
*/
public function GetCurrentValue($sParamName, $defaultValue = '')
{
if (isset($this->aValues[$sParamName]))
{
if (isset($this->aValues[$sParamName])) {
return $this->aValues[$sParamName];
}
return $defaultValue;
@@ -205,9 +195,8 @@ class ApplicationContext
}
// Build the query string with ampersand separated parameters
$aParams = array();
foreach($this->aValues as $sName => $sValue)
{
$aParams = [];
foreach ($this->aValues as $sName => $sValue) {
$aParams[] = "c[$sName]".'='.urlencode($sValue);
}
$sReturnValue = implode('&', $aParams);
@@ -278,9 +267,8 @@ 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;
@@ -301,8 +289,7 @@ class ApplicationContext
*/
public function Reset($sParamName)
{
if (isset($this->aValues[$sParamName]))
{
if (isset($this->aValues[$sParamName])) {
unset($this->aValues[$sParamName]);
}
}
@@ -318,21 +305,16 @@ class ApplicationContext
public function InitObjectFromContext(DBObject &$oObj)
{
$sClass = get_class($oObj);
foreach($this->GetNames() as $key)
{
$aCallSpec = array($sClass, 'MapContextParam');
if (is_callable($aCallSpec))
{
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);
}
}
@@ -362,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';
}
}
@@ -394,7 +372,7 @@ class ApplicationContext
if (is_null($sUrlMakerClass)) {
$sUrlMakerClass = self::GetUrlMakerClass();
}
$sUrl = call_user_func(array($sUrlMakerClass, 'MakeObjectUrl'), $sObjClass, $sObjKey);
$sUrl = call_user_func([$sUrlMakerClass, 'MakeObjectUrl'], $sObjClass, $sObjKey);
if (utils::StrLen($sUrl) > 0) {
if ($bWithNavigationContext) {
return $sUrl.$oAppContext->GetForLink(true);
@@ -412,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 = [];
}
}
@@ -431,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);
@@ -444,15 +421,14 @@ class ApplicationContext
*/
public static function GetPluginProperties($sPluginClass)
{
if (is_null(self::$m_aPluginProperties)) self::LoadPluginProperties();
if (is_null(self::$m_aPluginProperties)) {
self::LoadPluginProperties();
}
if (array_key_exists($sPluginClass, self::$m_aPluginProperties))
{
if (array_key_exists($sPluginClass, self::$m_aPluginProperties)) {
return self::$m_aPluginProperties[$sPluginClass];
}
else
{
return array();
} else {
return [];
}
}

View File

@@ -74,7 +74,3 @@ require_once(APPROOT.'application/applicationextension/rest/iRestInputSanitizer.
require_once(APPROOT.'application/applicationextension/rest/iRestServiceProvider.php');
require_once(APPROOT.'application/applicationextension/rest/RestResult.php');
require_once(APPROOT.'application/applicationextension/rest/RestUtils.php');

View File

@@ -42,7 +42,7 @@ abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
*/
public function EnumUsedAttributes($oObject)
{
return array();
return [];
}
/**
@@ -66,7 +66,7 @@ abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
*/
public function EnumAllowedActions(DBObjectSet $oSet)
{
return array();
return [];
}
}

View File

@@ -33,7 +33,7 @@ abstract class ApplicationPopupMenuItem
$this->sLabel = $sLabel;
$this->sTooltip = '';
$this->sIconClass = '';
$this->aCssClasses = array();
$this->aCssClasses = [];
}
/**
@@ -89,7 +89,6 @@ abstract class ApplicationPopupMenuItem
$this->aCssClasses[] = $sCssClass;
}
/**
* @param $sTooltip
*
@@ -145,6 +144,6 @@ abstract class ApplicationPopupMenuItem
/** @ignore */
public function GetLinkedScripts()
{
return array();
return [];
}
}

View File

@@ -9,5 +9,4 @@
*/
class JSButtonItem extends JSPopupMenuItem
{
}

View File

@@ -28,7 +28,7 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
* @param array $aIncludeJSFiles An array of file URLs to be included (once) to provide some JS libraries for the page.
* @api
*/
public function __construct($sUID, $sLabel, $sJSCode, $aIncludeJSFiles = array())
public function __construct($sUID, $sLabel, $sJSCode, $aIncludeJSFiles = [])
{
parent::__construct($sUID, $sLabel);
$this->sJsCode = $sJSCode;
@@ -40,14 +40,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 */

View File

@@ -10,7 +10,7 @@
*/
class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
{
static $idx = 0;
public static $idx = 0;
/**
* Constructor
@@ -24,6 +24,6 @@ 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];
}
}

View File

@@ -9,5 +9,4 @@
*/
class URLButtonItem extends URLPopupMenuItem
{
}

View File

@@ -35,13 +35,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 */

View File

@@ -19,21 +19,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
*
@@ -43,14 +43,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
*
@@ -58,7 +58,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
*
@@ -66,7 +66,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
@@ -76,7 +76,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 !
@@ -85,7 +85,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 !
@@ -94,7 +94,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.

View File

@@ -14,7 +14,7 @@ abstract class AbstractPortalUIExtension implements iPortalUIExtension
*/
public function GetCSSFiles(\Symfony\Component\DependencyInjection\Container $oContainer)
{
return array();
return [];
}
/**
@@ -30,7 +30,7 @@ abstract class AbstractPortalUIExtension implements iPortalUIExtension
*/
public function GetJSFiles(\Symfony\Component\DependencyInjection\Container $oContainer)
{
return array();
return [];
}
/**

View File

@@ -11,9 +11,9 @@
*/
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

View File

@@ -13,62 +13,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!

View File

@@ -44,7 +44,6 @@ class RestUtils
}
}
/**
* Read an optional parameter from a Rest/Json structure.
*
@@ -66,7 +65,6 @@ class RestUtils
}
}
/**
* Read a class from a Rest/Json structure.
*
@@ -87,7 +85,6 @@ class RestUtils
return $sClass;
}
/**
* Read a list of attribute codes from a Rest/Json structure.
*
@@ -103,7 +100,7 @@ class RestUtils
public static function GetFieldList($sClass, $oData, $sParamName)
{
$sFields = self::GetOptionalParam($oData, $sParamName, '*');
$aShowFields = array();
$aShowFields = [];
if ($sFields == '*') {
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
$aShowFields[$sClass][] = $sAttCode;
@@ -140,7 +137,7 @@ class RestUtils
*/
protected static function FindObjectFromCriteria($sClass, $oCriteria)
{
$aCriteriaReport = array();
$aCriteriaReport = [];
if (isset($oCriteria->finalclass)) {
if (!MetaModel::IsValidClass($oCriteria->finalclass)) {
throw new Exception("finalclass: Unknown class '".$oCriteria->finalclass."'");
@@ -171,7 +168,6 @@ class RestUtils
return $res;
}
/**
* Find an object from a polymorph search specification (Rest/Json)
*
@@ -260,7 +256,7 @@ class RestUtils
} 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;
}
@@ -291,7 +287,7 @@ class RestUtils
throw new Exception("A link set must be defined by an array of objects");
}
$sLnkClass = $oAttDef->GetLinkedClass();
$aLinks = array();
$aLinks = [];
foreach ($value as $oValues) {
$oLnk = static::MakeObjectFromFields($sLnkClass, $oValues);
// Fix for N°1939

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
@@ -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, "edit_when" => LINKSET_EDITWHEN_ALWAYS, "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
}
/**
@@ -93,4 +95,3 @@ class AuditCategory extends cmdbAbstractObject
return $aShortcutActions;
}
}
?>

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
@@ -33,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 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')); // 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)
@@ -84,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,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 "rule" linked to a given audit category.
* Each rule is based on an OQL expression that returns either the "good" objects
@@ -33,35 +33,35 @@ 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"]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("contact_id", ["allowed_values" => null, "sql" => "contact_id", "targetclass" => "Contact", "is_null_allowed" => true, "on_target_delete" => DEL_MANUAL, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeHTML("process", ["allowed_values" => null, "sql" => "process", "default_value" => "", "is_null_allowed" => true, "depends_on" => []]));
// 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', 'process', 'contact_id']); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', ['category_id', 'description', 'query']); // 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', 'contact_id', 'query']); // Criteria of the advanced search form
}
public static function GetShortcutActions($sFinalClass)
{
$aShortcutActions = parent::GetShortcutActions($sFinalClass);
@@ -72,4 +72,3 @@ class AuditRule extends cmdbAbstractObject
return $aShortcutActions;
}
}
?>

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,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();
@@ -80,86 +81,68 @@ abstract class Dashboard
{
$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 = [];
}
}
@@ -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;
}
}
@@ -263,8 +243,7 @@ abstract class Dashboard
$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);
@@ -276,8 +255,7 @@ abstract class Dashboard
$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());
@@ -304,7 +282,7 @@ abstract class Dashboard
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int) $aParams['auto_reload_sec']);
foreach ($aParams['cells'] as $aCell) {
$aCellDashlets = array();
$aCellDashlets = [];
foreach ($aCell as $aDashletParams) {
$sDashletClass = $aDashletParams['dashlet_class'];
$sId = $aDashletParams['dashlet_id'];
@@ -420,7 +398,7 @@ abstract class Dashboard
{
$sId = $this->GetNewDashletId();
$oDashlet->SetId($sId);
$this->aCells[] = array($oDashlet);
$this->aCells[] = [$oDashlet];
}
/**
@@ -430,7 +408,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 +420,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 +444,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');
@@ -522,7 +499,7 @@ 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)
{
$aExtraParams['dashboard_div_id'] = utils::Sanitize($aExtraParams['dashboard_div_id'] ?? null, $this->GetId(), utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
@@ -551,7 +528,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
);
@@ -595,7 +573,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>');
@@ -604,13 +582,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);
@@ -632,18 +607,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;
}
@@ -660,11 +634,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());
}
}
@@ -681,7 +653,7 @@ JS
*
* @return void
*/
abstract protected function PrepareDashletForRendering(Dashlet $oDashlet, $aCoordinates, $aExtraParams = array());
abstract protected function PrepareDashletForRendering(Dashlet $oDashlet, $aCoordinates, $aExtraParams = []);
/**
* @param \DesignerForm $oForm
@@ -689,7 +661,7 @@ JS
*
* @return mixed
*/
abstract protected function SetFormParams($oForm, $aExtraParams = array());
abstract protected function SetFormParams($oForm, $aExtraParams = []);
/**
* @param string $sType
@@ -699,8 +671,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';
@@ -723,14 +694,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;
}
@@ -782,9 +751,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]);
}
/**
@@ -800,14 +769,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());
@@ -838,8 +804,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);
@@ -883,14 +848,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);
@@ -937,7 +899,6 @@ class RuntimeDashboard extends Dashboard
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
}
if ($sDashboardDefinition !== false) {
$oDashboard = new RuntimeDashboard($sDashBoardId);
$oDashboard->FromXml($sDashboardDefinition);
@@ -954,11 +915,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;
}
@@ -968,7 +929,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'];
}
@@ -1001,9 +962,7 @@ class RuntimeDashboard extends Dashboard
}
JS
);
}
else
{
} else {
$oPage->add_script(
<<<EOF
if (typeof(AutoReloadDashboardId$sDivId) !== 'undefined')
@@ -1032,7 +991,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;
@@ -1070,7 +1029,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();
@@ -1092,8 +1051,7 @@ JS
*/
protected function HasCustomDashboard()
{
try
{
try {
// Search for an eventual user defined dashboard
$oUDSearch = new DBObjectSearch('UserDashboard');
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
@@ -1101,9 +1059,7 @@ JS
$oUDSet = new DBObjectSet($oUDSearch);
return ($oUDSet->Count() > 0);
}
catch (Exception $e)
{
} catch (Exception $e) {
return false;
}
}
@@ -1139,21 +1095,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)
@@ -1193,7 +1151,7 @@ EOF
/**
* @inheritDoc
*/
public function RenderProperties($oPage, $aExtraParams = array())
public function RenderProperties($oPage, $aExtraParams = [])
{
parent::RenderProperties($oPage, $aExtraParams);
@@ -1225,7 +1183,6 @@ EOF
);
}
/**
* @param WebPage $oPage
*
@@ -1236,11 +1193,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;
}
@@ -1389,51 +1346,41 @@ JS
// 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)
{
$aParentMenus = [];
foreach ($aAllMenus as $idx => $aMenu) {
/** @var MenuNode $oMenu */
$oMenu = $aMenu['node'];
if (count(ApplicationMenu::GetChildren($oMenu->GetIndex())) > 0)
{
if (count(ApplicationMenu::GetChildren($oMenu->GetIndex())) > 0) {
$aParentMenus[$oMenu->GetMenuId()] = $aMenu;
}
}
foreach($aAllMenus as $idx => $aMenu)
{
foreach ($aAllMenus as $idx => $aMenu) {
$oMenu = $aMenu['node'];
if ($oMenu instanceof DashboardMenuNode)
{
if ($oMenu instanceof DashboardMenuNode) {
// Get the root parent for access check
$sParentId = $aMenu['parent'];
$aParentMenu = $aParentMenus[$sParentId];
while (isset($aParentMenus[$aParentMenu['parent']]))
{
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())
{
if ($oMenu->IsEnabled() && $oParentMenu->IsEnabled()) {
$sMenuLabel = $oMenu->GetTitle();
$sParentLabel = Dict::S('Menu:'.$sParentId);
if ($sParentLabel != $sMenuLabel)
{
if ($sParentLabel != $sMenuLabel) {
$aAllowedDashboards[$oMenu->GetMenuId()] = $sParentLabel.' - '.$sMenuLabel;
}
else
{
} else {
$aAllowedDashboards[$oMenu->GetMenuId()] = $sMenuLabel;
}
if (empty($sDefaultDashboard) && ($sRootMenuId == ApplicationMenu::GetRootMenuId($oMenu->GetMenuId())))
{
if (empty($sDefaultDashboard) && ($sRootMenuId == ApplicationMenu::GetRootMenuId($oMenu->GetMenuId()))) {
$sDefaultDashboard = $oMenu->GetMenuId();
}
}
@@ -1448,21 +1395,17 @@ JS
// 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'))
{
$aDashlets = [];
foreach (get_declared_classes() as $sDashletClass) {
if (is_subclass_of($sDashletClass, 'Dashlet')) {
$oReflection = new ReflectionClass($sDashletClass);
if (!$oReflection->isAbstract())
{
$aCallSpec = array($sDashletClass, 'CanCreateFromOQL');
if (!$oReflection->isAbstract()) {
$aCallSpec = [$sDashletClass, 'CanCreateFromOQL'];
$bShorcutMode = call_user_func($aCallSpec);
if ($bShorcutMode)
{
$aCallSpec = array($sDashletClass, 'GetInfo');
if ($bShorcutMode) {
$aCallSpec = [$sDashletClass, 'GetInfo'];
$aInfo = call_user_func($aCallSpec);
$aDashlets[$sDashletClass] = array('label' => $aInfo['label'], 'class' => $sDashletClass, 'icon' => $aInfo['icon']);
$aDashlets[$sDashletClass] = ['label' => $aInfo['label'], 'class' => $sDashletClass, 'icon' => $aInfo['icon']];
}
}
}
@@ -1470,8 +1413,7 @@ JS
$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 */
@@ -1603,7 +1545,7 @@ JS
/**
* @inheritDoc
*/
protected function PrepareDashletForRendering(Dashlet $oDashlet, $aCoordinates, $aExtraParams = array())
protected function PrepareDashletForRendering(Dashlet $oDashlet, $aCoordinates, $aExtraParams = [])
{
$sDashletIdOrig = $oDashlet->GetID();
$sDashboardSanitizedId = $this->GetSanitizedId();
@@ -1630,31 +1572,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;
}
@@ -1673,7 +1611,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,4 +1,5 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -41,11 +42,11 @@ abstract class DashboardLayout
public static function GetInfo()
{
return array(
return [
'label' => '',
'icon' => '',
'description' => '',
);
];
}
}
@@ -63,16 +64,12 @@ 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++;
@@ -82,22 +79,17 @@ abstract class DashboardLayoutMultiCol extends DashboardLayout
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++;
@@ -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,10 +1,12 @@
<?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"
xsi:noNamespaceSchemaLocation="https://www.combodo.com/itop-schema/3.3"
version="3.3">
<classes>
<class id="AbstractResource" _delta="define">
<parent>cmdbAbstractObject</parent>
<properties>
<comment>/* Resource access control abstraction. Can be herited by abstract resource access control classes. Generaly controlled using UR_ACTION_MODIFY access right. */</comment>
<comment>/* Resource access control abstraction. Can be herited by abstract resource access control classes. Generally controlled using UR_ACTION_MODIFY access right. */</comment>
<abstract>true</abstract>
</properties>
<presentation/>
@@ -849,5 +851,168 @@ Call $this->AddInitialAttributeFlags($sAttCode, $iFlags) for all the initial att
</methods>
</class>
</classes>
<property_types _delta="define">
<property_type id="Dashlet" xsi:type="Combodo-AbstractPropertyType"/>
<property_type id="DashletGroupBy" xsi:type="Combodo-PropertyType">
<extends>Dashlet</extends>
<definition xsi:type="Combodo-ValueType-PropertyTree">
<label>UI:DashletGroupBy:Title</label>
<nodes>
<node id="title" xsi:type="Combodo-ValueType-Label">
<label>UI:DashletGroupBy:Prop-Title</label>
</node>
<node id="query" xsi:type="Combodo-ValueType-OQL">
<label>UI:DashletGroupBy:Prop-Query</label>
</node>
<node id="group_by" xsi:type="Combodo-ValueType-ClassAttributeGroupBy">
<label>UI:DashletGroupBy:Prop-GroupBy</label>
<class>{{query.selected_class}}</class>
</node>
<node id="style" xsi:type="Combodo-ValueType-Choice"> <!-- Possible de le cacher, etc celui-ci nous met dedans -->
<label>UI:DashletGroupBy:Prop-Style</label>
<values>
<value id="bars">
<label>UI:DashletGroupByBars:Label</label>
</value>
<value id="pie">
<label>UI:DashletGroupByPie:Label</label>
</value>
<value id="table">
<label>UI:DashletGroupByTable:Label</label>
</value>
</values>
</node>
<node id="aggregation_function" xsi:type="Combodo-ValueType-AggregateFunction">
<label>UI:DashletGroupBy:Prop-Function</label>
<class>{{query.selected_class}}</class> <!-- pour savoir si il y a des attributs additionnables -->
</node>
<node id="aggregation_attribute" xsi:type="Combodo-ValueType-ClassAttribute">
<label>UI:DashletGroupBy:Prop-FunctionAttribute</label>
<relevance-condition>{{aggregation_function.value != 'count'}}</relevance-condition>
<class>{{query.selected_class}}</class>
<category>numeric</category>
</node>
<node id="order_by" xsi:type="Combodo-ValueType-ChoiceFromInput">
<label>UI:DashletGroupBy:Prop-OrderField</label>
<values>
<value id="attribute">
<label>{{aggregation_attribute.label}}</label>
</value>
<value id="function">
<label>{{aggregation_function.label}}</label>
</value>
</values>
</node>
<node id="limit" xsi:type="Combodo-ValueType-Integer">
<label>UI:DashletGroupBy:Prop-Limit</label>
<relevance-condition>{{order_by.value = 'function'}}</relevance-condition>
</node>
<node id="order_direction" xsi:type="Combodo-ValueType-Choice">
<label>UI:DashletGroupBy:Prop-OrderDirection</label>
<values>
<value id="asc">
<label>UI:DashletGroupBy:Order:asc</label>
</value>
<value id="desc">
<label>UI:DashletGroupBy:Order:desc</label>
</value>
</values>
</node>
</nodes>
</definition>
</property_type>
<property_type id="DashletBadge" xsi:type="Combodo-PropertyType">
<extends>Dashlet</extends>
<definition xsi:type="Combodo-ValueType-PropertyTree">
<nodes>
<node id="class" xsi:type="Combodo-ValueType-Class">
<label>UI:DashletBadge:Prop-Class</label>
<categories-csv>bizmodel</categories-csv>
</node>
</nodes>
</definition>
</property_type>
<property_type id="DashletHeaderDynamic" xsi:type="Combodo-PropertyType">
<extends>Dashlet</extends>
<definition xsi:type="Combodo-ValueType-PropertyTree">
<label>UI:DashletHeaderDynamic:Title</label>
<nodes>
<node id="title" xsi:type="Combodo-ValueType-Label">
<label>UI:DashletHeaderDynamic:Prop-Title</label>
</node>
<node id="icon" xsi:type="Combodo-ValueType-Icon">
<label>UI:DashletHeaderDynamic:Prop-Icon</label>
</node>
<node id="subtitle" xsi:type="Combodo-ValueType-Label">
<label>UI:DashletHeaderDynamic:Prop-Subtitle</label>
</node>
<node id="query" xsi:type="Combodo-ValueType-OQL">
<label>UI:DashletHeaderDynamic:Prop-Query</label>
</node>
<node id="group_by" xsi:type="Combodo-ValueType-ClassAttribute">
<label>UI:DashletHeaderDynamic:Prop-GroupBy</label>
<class>{{query.selected_class}}</class>
<category>enum</category>
</node>
<node id="values" xsi:type="Combodo-ValueType-CollectionOfValues">
<label>UI:DashletHeaderDynamic:Prop-Values</label>
<xml-format xsi:type="Combodo-XMLFormat-CSV"/>
<value-type xsi:type="Combodo-ValueType-ClassAttributeValue">
<class>{{query.selected_class}}</class>
<attribute>{{group_by.attribute}}</attribute>
</value-type>
</node>
</nodes>
</definition>
</property_type>
<property_type id="DashletHeaderStatic" xsi:type="Combodo-PropertyType">
<extends>Dashlet</extends>
<definition xsi:type="Combodo-ValueType-PropertyTree">
<nodes>
<node id="title" xsi:type="Combodo-ValueType-Label">
<label>UI:DashletHeaderStatic:Prop-Title</label>
</node>
<node id="icon" xsi:type="Combodo-ValueType-Icon">
<label>UI:DashletHeaderStatic:Prop-Icon</label>
</node>
</nodes>
</definition>
</property_type>
<property_type id="DashletObjectList" xsi:type="Combodo-PropertyType">
<extends>Dashlet</extends>
<definition xsi:type="Combodo-ValueType-PropertyTree">
<nodes>
<node id="title" xsi:type="Combodo-ValueType-Label">
<label>UI:DashletObjectList:Prop-Title</label>
</node>
<node id="query" xsi:type="Combodo-ValueType-OQL">
<label>UI:DashletObjectList:Prop-Query</label>
</node>
<node id="menu" xsi:type="Combodo-ValueType-Boolean">
<label>UI:DashletObjectList:Prop-Menu</label>
<on>
<!-- not so cute, but matches exactly 3.2 implementation of boolean fields -->
<label>UI:UserManagement:ActionAllowed:Yes</label>
<value>true</value>
</on>
<off>
<label>UI:UserManagement:ActionAllowed:No</label>
<value>false</value>
</off>
</node>
</nodes>
</definition>
</property_type>
<property_type id="DashletPlainText" xsi:type="Combodo-PropertyType">
<extends>Dashlet</extends>
<definition xsi:type="Combodo-ValueType-PropertyTree">
<nodes>
<node id="text" xsi:type="Combodo-ValueType-Text">
<label>UI:DashletPlainText:Prop-Text</label>
</node>
</nodes>
</definition>
</property_type>
</property_types>
</meta>
</itop_design>

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -104,7 +105,7 @@ class DisplayBlock
*/
public const ENUM_STYLE_CHART_AJAX = 'chart_ajax';
const TAG_BLOCK = 'itopblock';
public const TAG_BLOCK = 'itopblock';
/** @var \DBSearch */
protected $m_oFilter;
protected $m_aConditions; // Conditions added to the filter -> avoid duplicate conditions
@@ -137,20 +138,18 @@ class DisplayBlock
*
* @throws \ApplicationException
*/
public function __construct(DBSearch $oFilter, $sStyle = self::ENUM_STYLE_LIST, $bAsynchronous = false, $aParams = array(), $oSet = null)
public function __construct(DBSearch $oFilter, $sStyle = self::ENUM_STYLE_LIST, $bAsynchronous = false, $aParams = [], $oSet = null)
{
$this->m_oFilter = $oFilter->DeepClone();
$this->m_aConditions = array();
$this->m_aConditions = [];
$this->m_sStyle = $sStyle;
$this->m_bAsynchronous = $bAsynchronous;
$this->m_aParams = $aParams;
$this->m_oSet = $oSet;
if (array_key_exists('show_obsolete_data', $aParams))
{
if (array_key_exists('show_obsolete_data', $aParams)) {
$this->m_bShowObsoleteData = $aParams['show_obsolete_data'];
}
if ($this->m_bShowObsoleteData === null)
{
if ($this->m_bShowObsoleteData === null) {
// User defined
$this->m_bShowObsoleteData = utils::ShowObsoleteData();
}
@@ -412,22 +411,18 @@ class DisplayBlock
* @throws \CoreException
* @throws \Exception
*/
public static function FromObjectSet(DBObjectSet $oSet, $sStyle, $aParams = array())
public static function FromObjectSet(DBObjectSet $oSet, $sStyle, $aParams = [])
{
$oDummyFilter = new DBObjectSearch($oSet->GetClass());
$aKeys = array();
$oSet->OptimizeColumnLoad(array($oSet->GetClassAlias() => array())); // No need to load all the columns just to get the id
while($oObject = $oSet->Fetch())
{
$aKeys = [];
$oSet->OptimizeColumnLoad([$oSet->GetClassAlias() => []]); // No need to load all the columns just to get the id
while ($oObject = $oSet->Fetch()) {
$aKeys[] = $oObject->GetKey();
}
$oSet->Rewind();
if (count($aKeys) > 0)
{
if (count($aKeys) > 0) {
$oDummyFilter->AddCondition('id', $aKeys, 'IN');
}
else
{
} else {
$oDummyFilter->AddCondition('id', 0, '=');
}
$oBlock = new DisplayBlock($oDummyFilter, $sStyle, false, $aParams); // DisplayBlocks built this way are synchronous
@@ -448,7 +443,7 @@ class DisplayBlock
$iStartPos = stripos($sTemplate, '<'.self::TAG_BLOCK.' ', 0);
$iEndPos = stripos($sTemplate, '</'.self::TAG_BLOCK.'>', $iStartPos);
$iEndTag = stripos($sTemplate, '>', $iStartPos);
$aParams = array();
$aParams = [];
if (($iStartPos === false) || ($iEndPos === false)) {
return null;
@@ -456,7 +451,7 @@ class DisplayBlock
$sITopData = substr($sTemplate, 1 + $iEndTag, $iEndPos - $iEndTag - 1);
$sITopTag = substr($sTemplate, $iStartPos + strlen('<'.self::TAG_BLOCK), $iEndTag - $iStartPos - strlen('<'.self::TAG_BLOCK));
$aMatches = array();
$aMatches = [];
$sBlockClass = "DisplayBlock";
$bAsynchronous = false;
$sBlockType = 'list';
@@ -522,25 +517,28 @@ class DisplayBlock
return new $sBlockClass($oFilter, $sBlockType, $bAsynchronous, $aParams);
}
public function DisplayIntoContentBlock(UIContentBlock $oContentBlock, WebPage $oPage, $sId, $aExtraParams = array())
public function DisplayIntoContentBlock(UIContentBlock $oContentBlock, WebPage $oPage, $sId, $aExtraParams = [])
{
$oContentBlock->AddSubBlock($this->GetDisplay($oPage, $sId, $aExtraParams));
}
public function Display(WebPage $oPage, $sId, $aExtraParams = array())
public function Display(WebPage $oPage, $sId, $aExtraParams = [])
{
$oPage->AddUiBlock($this->GetDisplay($oPage, $sId, $aExtraParams));
}
public function GetDisplay(WebPage $oPage, $sId, $aExtraParams = array()): UIContentBlock
public function GetDisplay(WebPage $oPage, $sId, $aExtraParams = []): UIContentBlock
{
$oHtml = new UIContentBlock($sId);
$oHtml->AddCSSClass("display_block");
$aExtraParams = array_merge($aExtraParams, $this->m_aParams);
$aExtraParams['currentId'] = $sId;
$sExtraParams = addslashes(str_replace('"', "'",
json_encode($aExtraParams))); // JSON encode, change the style of the quotes and escape them
$sExtraParams = addslashes(str_replace(
'"',
"'",
json_encode($aExtraParams)
)); // JSON encode, change the style of the quotes and escape them
if (isset($aExtraParams['query_params'])) {
$aQueryParams = $aExtraParams['query_params'];
@@ -549,9 +547,9 @@ class DisplayBlock
$sClass = $aExtraParams['this->class'];
$iKey = $aExtraParams['this->id'];
$oObj = MetaModel::GetObject($sClass, $iKey);
$aQueryParams = array('this->object()' => $oObj);
$aQueryParams = ['this->object()' => $oObj];
} else {
$aQueryParams = array();
$aQueryParams = [];
}
}
@@ -587,8 +585,7 @@ class DisplayBlock
');
}
if ($this->m_sStyle == static::ENUM_STYLE_LIST) // Search form need to extract result list extra data, the simplest way is to expose this configuration
{
if ($this->m_sStyle == static::ENUM_STYLE_LIST) { // Search form need to extract result list extra data, the simplest way is to expose this configuration
$listJsonExtraParams = json_encode(json_encode($aExtraParams));
$oPage->add_ready_script("
$('#$sId').data('sExtraParams', ".$listJsonExtraParams.");
@@ -608,7 +605,7 @@ class DisplayBlock
* @throws \DictExceptionMissingString
* @throws \MySQLException
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
public function RenderContent(WebPage $oPage, $aExtraParams = [])
{
if (!isset($aExtraParams['currentId'])) {
$sId = utils::GetUniqueId(); // Works only if the page is not an Ajax one !
@@ -640,7 +637,7 @@ class DisplayBlock
$this->CheckParams($this->m_sStyle, $aExtraParams);
// Add the extra params into the filter if they make sense for such a filter
$bDoSearch = utils::ReadParam('dosearch', false);
$aQueryParams = array();
$aQueryParams = [];
if (isset($aExtraParams['query_params'])) {
$aQueryParams = $aExtraParams['query_params'];
} else {
@@ -648,7 +645,7 @@ class DisplayBlock
$sClass = $aExtraParams['this->class'];
$iKey = $aExtraParams['this->id'];
$oObj = MetaModel::GetObject($sClass, $iKey);
$aQueryParams = array('this->object()' => $oObj);
$aQueryParams = ['this->object()' => $oObj];
}
}
if ($this->m_oSet == null) {
@@ -658,7 +655,7 @@ class DisplayBlock
$oAppContext = new ApplicationContext();
$sClass = $this->m_oFilter->GetClass();
$aFilterCodes = MetaModel::GetFiltersList($sClass);
$aCallSpec = array($sClass, 'MapContextParam');
$aCallSpec = [$sClass, 'MapContextParam'];
if (is_callable($aCallSpec)) {
foreach ($oAppContext->GetNames() as $sContextParam) {
$sParamCode = call_user_func($aCallSpec, $sContextParam); //Map context parameter to the value/filter code depending on the class
@@ -693,11 +690,9 @@ class DisplayBlock
}
}
if (!is_null($condition))
{
if (!is_null($condition)) {
$sOpCode = null; // default operator
if (is_array($condition))
{
if (is_array($condition)) {
// Multiple values, add them as AND X IN (v1, v2, v3...)
$sOpCode = 'IN';
}
@@ -705,26 +700,22 @@ class DisplayBlock
$this->AddCondition($sFilterCode, $condition, $sOpCode, $bParseSearchString);
}
}
if ($bDoSearch)
{
if ($bDoSearch) {
// Keep the table_id identifying this table if we're performing a search
$sTableId = utils::ReadParam('_table_id_', null, false, utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
if ($sTableId != null)
{
if ($sTableId != null) {
$aExtraParams['table_id'] = $sTableId;
}
}
}
$aOrderBy = array();
if (isset($aExtraParams['order_by']))
{
$aOrderBy = [];
if (isset($aExtraParams['order_by'])) {
// Convert the string describing the order_by parameter into an array
// The syntax is +attCode1,-attCode2
// attCode1 => ascending, attCode2 => descending
$aTemp = explode(',', $aExtraParams['order_by']);
foreach($aTemp as $sTemp)
{
$aMatches = array();
foreach ($aTemp as $sTemp) {
$aMatches = [];
if (preg_match('/^([+-])?(.+)$/', $sTemp, $aMatches)) {
$bAscending = true;
if ($aMatches[1] == '-') {
@@ -735,6 +726,10 @@ class DisplayBlock
}
}
if (!$this->m_oFilter->IsAllDataAllowed() && ($aExtraParams['display_unauthorized_objects'] ?? false) === true) {
$this->m_oFilter->AllowAllData();
}
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, $aOrderBy, $aQueryParams);
}
@@ -801,17 +796,13 @@ class DisplayBlock
$sHtml .= Dict::format('UI:Error:UnsupportedStyleOfBlock', $this->m_sStyle);
}
$bAutoReload = false;
if (isset($aExtraParams['auto_reload']))
{
if ($aExtraParams['auto_reload'] === true)
{
if (isset($aExtraParams['auto_reload'])) {
if ($aExtraParams['auto_reload'] === true) {
// Note: does not work in the switch (case true) because a positive number evaluates to true!!!
$aExtraParams['auto_reload'] = 'standard';
}
switch($aExtraParams['auto_reload'])
{
switch ($aExtraParams['auto_reload']) {
case 'fast':
$bAutoReload = true;
$iReloadInterval = MetaModel::GetConfig()->GetFastReloadInterval() * 1000;
@@ -824,20 +815,16 @@ class DisplayBlock
break;
default:
if (is_numeric($aExtraParams['auto_reload']) && ($aExtraParams['auto_reload'] > 0))
{
if (is_numeric($aExtraParams['auto_reload']) && ($aExtraParams['auto_reload'] > 0)) {
$bAutoReload = true;
$iReloadInterval = max(MetaModel::GetConfig()->Get('min_reload_interval'), $aExtraParams['auto_reload']) * 1000;
}
else
{
} else {
// incorrect config, ignore it
$bAutoReload = false;
}
}
}
if (($bAutoReload) && ($this->m_sStyle != static::ENUM_STYLE_SEARCH)) // Search form do NOT auto-reload
{
if (($bAutoReload) && ($this->m_sStyle != static::ENUM_STYLE_SEARCH)) { // Search form do NOT auto-reload
// Used either for asynchronous or auto_reload
// does a json_encode twice to get a string usable as function parameter
$sFilterBefore = $this->m_oFilter->serialize();
@@ -884,8 +871,7 @@ JS
{
// Workaround to an issue revealed whenever a condition on org_id is applied twice (with a hierarchy of organizations)
// Moreover, it keeps the query as simple as possible
if (isset($this->m_aConditions[$sFilterCode]) && $condition == $this->m_aConditions[$sFilterCode])
{
if (isset($this->m_aConditions[$sFilterCode]) && $condition == $this->m_aConditions[$sFilterCode]) {
// Skip
return;
}
@@ -895,51 +881,40 @@ JS
$bConditionAdded = false;
// If the condition is an external key with a class having a hierarchy, use a "below" criteria
if (MetaModel::IsValidAttCode($sClass, $sFilterCode))
{
if (MetaModel::IsValidAttCode($sClass, $sFilterCode)) {
$oAttDef = MetaModel::GetAttributeDef($sClass, $sFilterCode);
if ($oAttDef->IsExternalKey())
{
if ($oAttDef->IsExternalKey()) {
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($oAttDef->GetTargetClass());
if ($sHierarchicalKeyCode !== false)
{
if ($sHierarchicalKeyCode !== false) {
$oFilter = new DBObjectSearch($oAttDef->GetTargetClass());
if (($sOpCode == 'IN') && is_array($condition))
{
if (($sOpCode == 'IN') && is_array($condition)) {
$oFilter->AddConditionExpression(self::GetConditionIN($oFilter, 'id', $condition));
}
else
{
} else {
$oFilter->AddCondition('id', $condition);
}
$oHKFilter = new DBObjectSearch($oAttDef->GetTargetClass());
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW); // Use the 'below' operator by default
$this->m_oFilter->AddCondition_PointingTo($oHKFilter, $sFilterCode);
$bConditionAdded = true;
}
else if (($sOpCode == 'IN') && is_array($condition))
{
} elseif (($sOpCode == 'IN') && is_array($condition)) {
$this->m_oFilter->AddConditionExpression(self::GetConditionIN($this->m_oFilter, $sFilterCode, $condition));
$bConditionAdded = true;
}
}
else if (($sOpCode == 'IN') && is_array($condition))
{
} elseif (($sOpCode == 'IN') && is_array($condition)) {
$this->m_oFilter->AddConditionExpression(self::GetConditionIN($this->m_oFilter, $sFilterCode, $condition));
$bConditionAdded = true;
}
}
// In all other cases, just add the condition directly
if (!$bConditionAdded)
{
if (!$bConditionAdded) {
$this->m_oFilter->AddCondition($sFilterCode, $condition, null); // Use the default 'loose' operator
}
}
static protected function GetConditionIN($oFilter, $sFilterCode, $condition)
protected static function GetConditionIN($oFilter, $sFilterCode, $condition)
{
$oField = new FieldExpression($sFilterCode, $oFilter->GetClassAlias());
$sListExpr = '('.implode(', ', CMDBSource::Quote($condition)).')';
@@ -972,13 +947,10 @@ JS
protected function MakeGroupByQuery(&$aExtraParams, &$oGroupByExp, &$sGroupByLabel, &$aGroupBy, &$sAggregationFunction, &$sFctVar, &$sAggregationAttr, &$sSql)
{
$sAlias = $this->m_oFilter->GetClassAlias();
if (isset($aExtraParams['group_by_label']))
{
if (isset($aExtraParams['group_by_label'])) {
$oGroupByExp = Expression::FromOQL($aExtraParams['group_by']);
$sGroupByLabel = $aExtraParams['group_by_label'];
}
else
{
} else {
// Backward compatibility: group_by is simply a field id
$oGroupByExp = new FieldExpression($aExtraParams['group_by'], $sAlias);
$sGroupByLabel = MetaModel::GetLabel($this->m_oFilter->GetClass(), $aExtraParams['group_by']);
@@ -986,61 +958,52 @@ JS
// Security filtering
$aFields = $oGroupByExp->ListRequiredFields();
foreach($aFields as $sFieldAlias)
{
$aMatches = array();
if (preg_match('/^([^.]+)\\.([^.]+)$/', $sFieldAlias, $aMatches))
{
foreach ($aFields as $sFieldAlias) {
$aMatches = [];
if (preg_match('/^([^.]+)\\.([^.]+)$/', $sFieldAlias, $aMatches)) {
$sFieldClass = $this->m_oFilter->GetClassName($aMatches[1]);
$oAttDef = MetaModel::GetAttributeDef($sFieldClass, $aMatches[2]);
if ($oAttDef instanceof AttributeOneWayPassword)
{
if ($oAttDef instanceof AttributeOneWayPassword) {
throw new Exception('Grouping on password fields is not supported.');
}
}
}
$aGroupBy = array();
$aGroupBy = [];
$aGroupBy['grouped_by_1'] = $oGroupByExp;
$aQueryParams = array();
if (isset($aExtraParams['query_params']))
{
$aQueryParams = [];
if (isset($aExtraParams['query_params'])) {
$aQueryParams = $aExtraParams['query_params'];
}
$aFunctions = array();
$aFunctions = [];
$sAggregationFunction = 'count';
$sFctVar = '_itop_count_';
$sAggregationAttr = '';
if (isset($aExtraParams['aggregation_function']) && !empty($aExtraParams['aggregation_attribute']))
{
if (isset($aExtraParams['aggregation_function']) && !empty($aExtraParams['aggregation_attribute'])) {
$sAggregationFunction = $aExtraParams['aggregation_function'];
$sAggregationAttr = $aExtraParams['aggregation_attribute'];
$oAttrExpr = Expression::FromOQL('`'.$sAlias.'`.`'.$sAggregationAttr.'`');
$oFctExpr = new FunctionExpression(strtoupper($sAggregationFunction), array($oAttrExpr));
$oFctExpr = new FunctionExpression(strtoupper($sAggregationFunction), [$oAttrExpr]);
$sFctVar = '_itop_'.$sAggregationFunction.'_';
$aFunctions = array($sFctVar => $oFctExpr);
$aFunctions = [$sFctVar => $oFctExpr];
}
if (!empty($sAggregationAttr))
{
if (!empty($sAggregationAttr)) {
$sClass = $this->m_oFilter->GetClass();
$sAggregationAttr = MetaModel::GetLabel($sClass, $sAggregationAttr);
}
$iLimit = 0;
if (isset($aExtraParams['limit']))
{
if (isset($aExtraParams['limit'])) {
$iLimit = intval($aExtraParams['limit']);
}
$aOrderBy = array();
if (isset($aExtraParams['order_direction']) && isset($aExtraParams['order_by']))
{
switch ($aExtraParams['order_by'])
{
$aOrderBy = [];
if (isset($aExtraParams['order_direction']) && isset($aExtraParams['order_by'])) {
switch ($aExtraParams['order_by']) {
case 'attribute':
$aOrderBy = array('grouped_by_1' => ($aExtraParams['order_direction'] === 'asc'));
$aOrderBy = ['grouped_by_1' => ($aExtraParams['order_direction'] === 'asc')];
break;
case 'function':
$aOrderBy = array($sFctVar => ($aExtraParams['order_direction'] === 'asc'));
$aOrderBy = [$sFctVar => ($aExtraParams['order_direction'] === 'asc')];
break;
}
}
@@ -1076,31 +1039,31 @@ JS
$this->AddCondition($sFilterCode, $sContextParamValue);
}
}
$aQueryParams = array();
$aQueryParams = [];
if (isset($aExtraParams['query_params'])) {
$aQueryParams = $aExtraParams['query_params'];
}
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, array(), $aQueryParams);
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, [], $aQueryParams);
$this->m_oSet->SetShowObsoleteData($this->m_bShowObsoleteData);
}
// Summary details
$aCounts = array();
$aStateLabels = array();
$aCounts = [];
$aStateLabels = [];
if (!empty($sStateAttrCode) && !empty($sStatesList)) {
$aStates = explode(',', $sStatesList);
// Generate one count + group by query [#1330]
$sClassAlias = $this->m_oFilter->GetClassAlias();
$oGroupByExpr = Expression::FromOQL($sClassAlias.'.'.$sStateAttrCode);
$aGroupBy = array('group1' => $oGroupByExpr);
$aGroupBy = ['group1' => $oGroupByExpr];
$oGroupBySearch = $this->m_oFilter->DeepClone();
if (isset($this->m_bShowObsoleteData)) {
$oGroupBySearch->SetShowObsoleteData($this->m_bShowObsoleteData);
}
$sCountGroupByQuery = $oGroupBySearch->MakeGroupByQuery($aQueryParams, $aGroupBy, false);
$aCountGroupByResults = CMDBSource::QueryToArray($sCountGroupByQuery);
$aCountsQueryResults = array();
$aCountsQueryResults = [];
foreach ($aCountGroupByResults as $aCountGroupBySingleResult) {
$aCountsQueryResults[$aCountGroupBySingleResult[0]] = $aCountGroupBySingleResult[1];
}
@@ -1114,7 +1077,8 @@ JS
: 0;
if ($aCounts[$sStateValue] == 0) {
$aCounts[$sStateValue] = ['link' => '-', 'label' => $aCounts[$sStateValue]];;
$aCounts[$sStateValue] = ['link' => '-', 'label' => $aCounts[$sStateValue]];
;
} else {
$oSingleGroupByValueFilter = $this->m_oFilter->DeepClone();
$oSingleGroupByValueFilter->AddCondition($sStateAttrCode, $sStateValue, '=');
@@ -1178,7 +1142,8 @@ JS
$('#".$oBlock->GetId()."').html(data);
$('#".$oBlock->GetId()."').unblock();
});
$('#".$oBlock->GetId()."').unblock();");
$('#".$oBlock->GetId()."').unblock();"
);
return $oBlock;
}
@@ -1220,11 +1185,11 @@ JS
$this->AddCondition($sFilterCode, $sContextParamValue);
}
}
$aQueryParams = array();
$aQueryParams = [];
if (isset($aExtraParams['query_params'])) {
$aQueryParams = $aExtraParams['query_params'];
}
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, array(), $aQueryParams);
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, [], $aQueryParams);
$this->m_oSet->SetShowObsoleteData($this->m_bShowObsoleteData);
}
$iCount = $this->m_oSet->Count();
@@ -1241,8 +1206,15 @@ JS
if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY)) {
$sCreateActionUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=new&class='.$sClass.$oAppContext->GetForLink(true);
$sCreateActionLabel = Dict::Format('UI:Button:Create');
$oBlock = DashletFactory::MakeForDashletBadge($sClassIconUrl, $sHyperlink, $iCount, $sClassLabel, $sCreateActionUrl,
$sCreateActionLabel, $aRefreshParams);
$oBlock = DashletFactory::MakeForDashletBadge(
$sClassIconUrl,
$sHyperlink,
$iCount,
$sClassLabel,
$sCreateActionUrl,
$sCreateActionLabel,
$aRefreshParams
);
} else {
$oBlock = DashletFactory::MakeForDashletBadge($sClassIconUrl, $sHyperlink, $iCount, $sClassLabel, null, null, $aRefreshParams);
}
@@ -1272,9 +1244,9 @@ JS
$aRes = CMDBSource::QueryToArray($sSql);
$aGroupBy = array();
$aLabels = array();
$aValues = array();
$aGroupBy = [];
$aLabels = [];
$aValues = [];
$iTotalCount = 0;
foreach ($aRes as $iRow => $aRow) {
$sValue = $aRow['grouped_by_1'];
@@ -1285,7 +1257,7 @@ JS
$iTotalCount += $aRow['_itop_count_'];
}
$aData = array();
$aData = [];
$oAppContext = new ApplicationContext();
$sParams = $oAppContext->GetForLink(true);
foreach ($aGroupBy as $iRow => $iCount) {
@@ -1296,22 +1268,22 @@ JS
if (isset($aExtraParams['query_params'])) {
$aQueryParams = $aExtraParams['query_params'];
} else {
$aQueryParams = array();
$aQueryParams = [];
}
$sFilter = rawurlencode($oSubsetSearch->serialize(false, $aQueryParams));
$aData[] = array(
$aData[] = [
'group' => $aLabels[$iRow],
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1$sParams&filter=$sFilter\">$iCount</a>"
); // TO DO: add the context information
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1$sParams&filter=$sFilter\">$iCount</a>",
]; // TO DO: add the context information
}
$aAttribs = array(
'group' => array('label' => $sGroupByLabel, 'description' => ''),
'value' => array(
$aAttribs = [
'group' => ['label' => $sGroupByLabel, 'description' => ''],
'value' => [
'label' => Dict::S('UI:GroupBy:'.$sAggregationFunction),
'description' => Dict::Format('UI:GroupBy:'.$sAggregationFunction.'+', $sAggregationAttr),
),
);
],
];
$sFormat = isset($aExtraParams['format']) ? $aExtraParams['format'] : 'UI:Pagination:HeaderNoSelection';
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
@@ -1409,10 +1381,12 @@ JS
$oBlock->aExtraParams = $aExtraParams;
$oBlock->sFilter = $this->m_oFilter->ToOQL();
// Check the classes that can be read (i.e authorized) by this user...
foreach ($aClasses as $sAlias => $sClassName) {
if (UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $this->m_oSet) != UR_ALLOWED_NO) {
if (
(UserRights::IsActionAllowed($sClassName, UR_ACTION_READ, $this->m_oSet) !== UR_ALLOWED_NO)
|| ($aExtraParams['display_unauthorized_objects'] ?? false) === true
) {
$aAuthorizedClasses[$sAlias] = $sClassName;
}
}
@@ -1431,7 +1405,7 @@ JS
$sSearchFilter = $this->m_oSet->GetFilter()->serialize();
// Limit the size of the URL (N°1585 - request uri too long)
if (strlen($sSearchFilter) < SERVER_MAX_URL_LENGTH) {
$oBlock->sEventAttachedData = json_encode(array(
$oBlock->sEventAttachedData = json_encode([
'filter' => $sSearchFilter,
'breadcrumb_id' => "ui-search-".$this->m_oSet->GetClass(),
'breadcrumb_label' => MetaModel::GetName($this->m_oSet->GetClass()),
@@ -1439,7 +1413,7 @@ JS
'breadcrumb_instance_id' => MetaModel::GetConfig()->GetItopInstanceid(),
'breadcrumb_icon' => 'fas fa-search',
'breadcrumb_icon_type' => iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES,
));
]);
}
}
@@ -1472,25 +1446,25 @@ JS
$oContentBlock = new UIContentBlock();
$oHtml = new Html();
$oContentBlock->AddSubBlock($oHtml);
$aDisplayAliases = isset($aExtraParams['display_aliases']) ? explode(',', $aExtraParams['display_aliases']) : array();
$aDisplayAliases = isset($aExtraParams['display_aliases']) ? explode(',', $aExtraParams['display_aliases']) : [];
if (!isset($aExtraParams['group_by'])) {
$oHtml->AddHtml('<p>'.Dict::S('UI:Error:MandatoryTemplateParameter_group_by').'</p>');
} else {
$aGroupByFields = array();
$aGroupByFields = [];
$aGroupBy = explode(',', $aExtraParams['group_by']);
foreach ($aGroupBy as $sGroupBy) {
$aMatches = array();
$aMatches = [];
if (preg_match('/^(.+)\.(.+)$/', $sGroupBy, $aMatches) > 0) {
$aGroupByFields[] = array('alias' => $aMatches[1], 'att_code' => $aMatches[2]);
$aGroupByFields[] = ['alias' => $aMatches[1], 'att_code' => $aMatches[2]];
}
}
if (count($aGroupByFields) == 0) {
$oHtml->AddHtml('<p>'.Dict::Format('UI:Error:InvalidGroupByFields', $aExtraParams['group_by']).'</p>');
} else {
$aResults = array();
$aCriteria = array();
$aResults = [];
$aCriteria = [];
while ($aObjects = $this->m_oSet->FetchAssoc()) {
$aKeys = array();
$aKeys = [];
foreach ($aGroupByFields as $aField) {
$sAlias = $aField['alias'];
if (is_null($aObjects[$sAlias])) {
@@ -1507,7 +1481,7 @@ JS
$oHtml->AddHtml("<table>\n");
// Construct a new (parametric) query that will return the content of this block
$oBlockFilter = $this->m_oFilter->DeepClone();
$aExpressions = array();
$aExpressions = [];
$index = 0;
foreach ($aGroupByFields as $aField) {
$aExpressions[] = '`'.$aField['alias'].'`.`'.$aField['att_code'].'` = :param'.$index++;
@@ -1519,7 +1493,7 @@ JS
foreach ($aResults as $sCategory => $aObjects) {
$oHtml->AddHtml("<tr><td><h1>$sCategory</h1></td></tr>\n");
if (count($aDisplayAliases) == 1) {
$aSimpleArray = array();
$aSimpleArray = [];
foreach ($aObjects as $aRow) {
$oObj = $aRow[$aDisplayAliases[0]];
if (!is_null($oObj)) {
@@ -1535,12 +1509,12 @@ JS
$oHtml->AddHtml("</td></tr>\n");
} else {
$index = 0;
$aArgs = array();
$aArgs = [];
foreach ($aGroupByFields as $aField) {
$aArgs['param'.$index] = $aCriteria[$sCategory][$aField['alias'].'.'.$aField['att_code']];
$index++;
}
$oSet = new CMDBObjectSet($oBlockFilter, array(), $aArgs);
$oSet = new CMDBObjectSet($oBlockFilter, [], $aArgs);
if (empty($aExtraParams['currentId'])) {
$iListId = utils::GetUniqueId(); // Works only if not in an Ajax page !!
} else {
@@ -1627,7 +1601,7 @@ JS
{
$sChartType = isset($aExtraParams['chart_type']) ? $aExtraParams['chart_type'] : 'pie';
$sId = utils::ReadParam('id', '');
$aValues = array();
$aValues = [];
$oBlock = null;
$sJSURLs = '';
@@ -1638,21 +1612,18 @@ JS
$this->MakeGroupByQuery($aExtraParams, $oGroupByExp, $sGroupByLabel, $aGroupBy, $sAggregationFunction, $sFctVar, $sAggregationAttr, $sSql);
$aRes = CMDBSource::QueryToArray($sSql);
$iTotalCount = 0;
$aURLs = array();
$aURLs = [];
foreach ($aRes as $iRow => $aRow) {
$sValue = $aRow['grouped_by_1'];
$sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue);
$iTotalCount += $aRow['_itop_count_'];
$aValues[] = array(
$aValues[] = [
'label' => html_entity_decode(strip_tags($sHtmlValue), ENT_QUOTES, 'UTF-8'),
'label_html' => $sHtmlValue,
'value' => (float)$aRow[$sFctVar],
);
];
// Build the search for this subset
$oSubsetSearch = $this->m_oFilter->DeepClone();
@@ -1691,7 +1662,7 @@ JS
$aColumns = [];
$aNames = [];
foreach ($aValues as $idx => $aValue) {
$aColumns[] = array('series_'.$idx, (float)$aValue['value']);
$aColumns[] = ['series_'.$idx, (float)$aValue['value']];
$aNames['series_'.$idx] = $aValue['label'];
}
@@ -1740,12 +1711,12 @@ JS
$oBlock->sDownloadLink = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?expression='.urlencode($this->m_oFilter->ToOQL(true)).'&format=csv&filename='.urlencode($oBlock->sCsvFile);
$oBlock->sLinkToToggle = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search'.$oAppContext->GetForLink(true).'&filter='.rawurlencode($this->m_oFilter->serialize()).'&format=csv';
// Pass the parameters via POST, since expression may be very long
$aParamsToPost = array(
$aParamsToPost = [
'expression' => $this->m_oFilter->ToOQL(true),
'format' => 'csv',
'filename' => $oBlock->sCsvFile,
'charset' => 'UTF-8',
);
];
if ($oBlock->bAdvancedMode) {
$oBlock->sDownloadLink .= '&fields_advanced=1';
$aParamsToPost['fields_advanced'] = 1;
@@ -1804,8 +1775,7 @@ class MenuBlock extends DisplayBlock
$oRouter = Router::GetInstance();
$oRenderBlock = new UIContentBlock();
if ($this->m_sStyle == 'popup') // popup is a synonym of 'list' for backward compatibility
{
if ($this->m_sStyle == 'popup') { // popup is a synonym of 'list' for backward compatibility
$this->m_sStyle = static::ENUM_STYLE_LIST;
}
@@ -1844,7 +1814,6 @@ class MenuBlock extends DisplayBlock
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink(true);
$sFilter = $this->GetFilter()->serialize();
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($sClass);
$sRootUrl = utils::GetAbsoluteUrlAppRoot();
@@ -1922,8 +1891,8 @@ class MenuBlock extends DisplayBlock
// Life cycle actions may be available... if all objects are in the same state
// Group by <state>
$oGroupByExp = new FieldExpression(MetaModel::GetStateAttributeCode($sClass), $this->m_oFilter->GetClassAlias());
$aGroupBy = array('__state__' => $oGroupByExp);
$aQueryParams = array();
$aGroupBy = ['__state__' => $oGroupByExp];
$aQueryParams = [];
if (isset($aExtraParams['query_params'])) {
$aQueryParams = $aExtraParams['query_params'];
}
@@ -1955,10 +1924,10 @@ class MenuBlock extends DisplayBlock
switch ($iActionAllowed) {
case UR_ALLOWED_YES:
case UR_ALLOWED_DEPENDS:
$aTransitionActions[$sStimulusCode] = array(
$aTransitionActions[$sStimulusCode] = [
'label' => $aStimuli[$sStimulusCode]->GetLabel(),
'url' => "{$sRootUrl}pages/UI.php?stimulus=$sStimulusCode&class=$sLifecycleClass&{$sUrlQueryString}",
) + $aActionParams;
] + $aActionParams;
break;
default:
@@ -2020,16 +1989,16 @@ class MenuBlock extends DisplayBlock
// Just one object in the set, possible actions are "new / clone / modify and delete"
if (!isset($aExtraParams['link_attr'])) {
if ($bIsModifyAllowed) {
$aRegularActions['UI:Menu:Modify'] = array(
$aRegularActions['UI:Menu:Modify'] = [
'label' => Dict::S('UI:Menu:Modify'),
'url' => $oRouter->GenerateUrl('object.modify', ['class' => $sClass, 'id' => $id])."{$sContext}#",
) + $aActionParams;
] + $aActionParams;
}
if ($bIsDeleteAllowed) {
$aRegularActions['UI:Menu:Delete'] = array(
$aRegularActions['UI:Menu:Delete'] = [
'label' => Dict::S('UI:Menu:Delete'),
'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$id{$sContext}",
) + $aActionParams;
] + $aActionParams;
}
// Relations...
@@ -2038,16 +2007,16 @@ class MenuBlock extends DisplayBlock
$this->AddMenuSeparator($aRegularActions);
foreach ($aRelations as $sRelationCode => $aRelationInfo) {
if (array_key_exists('down', $aRelationInfo)) {
$aRegularActions[$sRelationCode.'_down'] = array(
$aRegularActions['UI:Menu:'.$sRelationCode.'_down'] = [
'label' => $aRelationInfo['down'],
'url' => "{$sRootUrl}pages/$sUIPage?operation=view_relations&relation=$sRelationCode&direction=down&class=$sClass&id=$id{$sContext}",
) + $aActionParams;
] + $aActionParams;
}
if (array_key_exists('up', $aRelationInfo)) {
$aRegularActions[$sRelationCode.'_up'] = array(
$aRegularActions['UI:Menu:'.$sRelationCode.'_up'] = [
'label' => $aRelationInfo['up'],
'url' => "{$sRootUrl}pages/$sUIPage?operation=view_relations&relation=$sRelationCode&direction=up&class=$sClass&id=$id{$sContext}",
) + $aActionParams;
] + $aActionParams;
}
}
}
@@ -2059,7 +2028,7 @@ class MenuBlock extends DisplayBlock
$bCanKill = false;
$oUser = UserRights::GetUserObject();
$aUserProfiles = array();
$aUserProfiles = [];
if (!is_null($oUser)) {
$oProfileSet = $oUser->Get('profile_list');
while ($oProfile = $oProfileSet->Fetch()) {
@@ -2081,10 +2050,10 @@ class MenuBlock extends DisplayBlock
if ($bCanKill) {
$this->AddMenuSeparator($aRegularActions);
$aRegularActions['concurrent_lock_unlock'] = array(
$aRegularActions['concurrent_lock_unlock'] = [
'label' => Dict::S('UI:Menu:KillConcurrentLock'),
'url' => "{$sRootUrl}pages/$sUIPage?operation=kill_lock&class=$sClass&id=$id{$sContext}",
);
];
}
}
}
@@ -2092,7 +2061,7 @@ class MenuBlock extends DisplayBlock
$this->AddMenuSeparator($aRegularActions);
$this->GetEnumAllowedActions($oSet, function ($sLabel, $data) use (&$aRegularActions, $aActionParams) {
$aRegularActions[$sLabel] = array('label' => $sLabel, 'url' => $data) + $aActionParams;
$aRegularActions[$sLabel] = ['label' => $sLabel, 'url' => $data] + $aActionParams;
});
}
break;
@@ -2289,6 +2258,16 @@ class MenuBlock extends DisplayBlock
$sIconClass = 'fas fa-file-pdf fa-lg';
$sLabel = '';
break;
case 'UI:Menu:impacts_up':
$sIconClass = 'fas fa-sitemap fa-rotate-180';
$sLabel = '';
$aAction['tooltip'] = Dict::S('Relation:impacts/UpStream');
break;
case 'UI:Menu:impacts_down':
$sIconClass = 'fas fa-sitemap';
$sLabel = '';
$aAction['tooltip'] = Dict::S('Relation:impacts/DownStream');
break;
default:
if (isset($aAction['icon_class']) && (strlen($aAction['icon_class']) > 0)) {
@@ -2458,13 +2437,11 @@ class MenuBlock extends DisplayBlock
protected function AddMenuSeparator(&$aActions)
{
$sSeparator = '<hr class="menu-separator"/>';
if (count($aActions) > 0) // Make sure that the separator is not the first item in the menu
{
if (count($aActions) > 0) { // Make sure that the separator is not the first item in the menu
$aKeys = array_keys($aActions);
$sLastKey = array_pop($aKeys);
if ($aActions[$sLastKey]['label'] != $sSeparator) // Make sure there are no 2 consecutive separators
{
$aActions['sep_'.(count($aActions)-1)] = array('label' => $sSeparator, 'url' => '');
if ($aActions[$sLastKey]['label'] != $sSeparator) { // Make sure there are no 2 consecutive separators
$aActions['sep_'.(count($aActions) - 1)] = ['label' => $sSeparator, 'url' => ''];
}
}
}
@@ -2524,10 +2501,10 @@ class MenuBlock extends DisplayBlock
*/
protected function AddBulkDeleteObjectsMenuAction(array &$aActions, string $sClass, string $sFilter, string $sActionIdentifier = 'UI:Menu:BulkDelete', $sActionLabel = 'UI:Menu:BulkDelete')
{
$aActions[$sActionIdentifier] = array(
$aActions[$sActionIdentifier] = [
'label' => Dict::S($sActionLabel),
'url' => $this->PrepareUrlForStandardMenuAction($sClass, "operation=select_for_deletion&filter=".urlencode($sFilter)),
) + $this->GetDefaultParamsForMenuAction();
] + $this->GetDefaultParamsForMenuAction();
}
/**

View File

@@ -22,31 +22,28 @@ class ExcelExporter
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,
);
];
$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();
}
@@ -54,13 +51,10 @@ class ExcelExporter
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());
@@ -85,7 +79,7 @@ class ExcelExporter
public function SaveState()
{
$aState = array(
$aState = [
'state' => $this->sState,
'statistics' => $this->aStatistics,
'filter' => $this->oSearch->serialize(),
@@ -94,7 +88,7 @@ class ExcelExporter
'object_ids' => $this->aObjectsIDs,
'output_file_path' => $this->sOutputFilePath,
'advanced_mode' => $this->bAdvancedMode,
);
];
file_put_contents($this->GetStateFile(), json_encode($aState));
@@ -103,19 +97,16 @@ class ExcelExporter
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.");
}
@@ -141,16 +132,13 @@ class ExcelExporter
$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())
{
$oIDSet->OptimizeColumnLoad(['id']);
$this->aObjectsIDs = [];
while ($oObj = $oIDSet->Fetch()) {
$this->aObjectsIDs[] = $oObj->GetKey();
}
$sCode = 'retrieving-data';
@@ -164,8 +152,7 @@ class ExcelExporter
$this->GetFieldsList($oIDSet, $this->bAdvancedMode);
$sRow = json_encode($this->aTableHeaders);
$hFile = @fopen($this->GetDataFile(), 'ab');
if ($hFile === false)
{
if ($hFile === false) {
throw new Exception('ExcelExporter: Failed to open temporary data file: "'.$this->GetDataFile().'" for writing.');
}
fwrite($hFile, $sRow."\n");
@@ -181,32 +168,24 @@ class ExcelExporter
$oCurrentSearch->AddCondition('id', $aIDs, 'IN');
$hFile = @fopen($this->GetDataFile(), 'ab');
if ($hFile === false)
{
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)
{
while ($aObjects = $oSet->FetchAssoc()) {
$aRow = [];
foreach ($this->aAuthorizedClasses as $sAlias => $sClassName) {
$oObj = $aObjects[$sAlias];
if ($this->bAdvancedMode)
{
if ($this->bAdvancedMode) {
$aRow[] = $oObj->GetKey();
}
foreach($this->aFieldsList[$sAlias] as $sAttCodeEx => $oAttDef)
{
foreach ($this->aFieldsList[$sAlias] as $sAttCodeEx => $oAttDef) {
$value = $oObj->Get($sAttCodeEx);
if ($value instanceOf ormCaseLog)
{
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
{
} else {
$sExcelVal = $oAttDef->GetEditValue($value, $oObj);
}
$aRow[] = $sExcelVal;
@@ -217,16 +196,13 @@ class ExcelExporter
}
fclose($hFile);
if (($this->iPosition + $this->iChunkSize) > count($this->aObjectsIDs))
{
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
{
} else {
$sCode = 'retrieving-data';
$this->iPosition += $this->iChunkSize;
$iPercentage = 5 + round(75 * ($this->iPosition / count($this->aObjectsIDs)));
@@ -236,16 +212,14 @@ class ExcelExporter
case 'building-excel':
$hFile = @fopen($this->GetDataFile(), 'rb');
if ($hFile === false)
{
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))
{
$aData = [];
while ($sLine = fgets($hFile)) {
$aRow = json_decode($sLine);
$aData[] = $aRow;
}
@@ -278,35 +252,29 @@ class ExcelExporter
$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)
{
if ($this->sOutputFilePath == null) {
return utils::GetDataPath().'bulk_export/'.$this->sToken.'.xlsx';
}
else
{
} else {
return $this->sOutputFilePath;
}
}
@@ -337,14 +305,11 @@ class ExcelExporter
$aFiles = glob(utils::GetDataPath().'bulk_export/*.*');
$iDelay = MetaModel::GetConfig()->Get('xlsx_exporter_cleanup_old_files_delay');
if($iDelay > 0)
{
foreach($aFiles as $sFile)
{
if ($iDelay > 0) {
foreach ($aFiles as $sFile) {
$iModificationTime = filemtime($sFile);
if($iModificationTime < (time() - $iDelay))
{
if ($iModificationTime < (time() - $iDelay)) {
// Temporary files older than one day are deleted
//echo "Supposed to delete: '".$sFile." (Unix Modification Time: $iModificationTime)'\n";
@unlink($sFile);
@@ -355,21 +320,18 @@ 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'));
}
}
@@ -377,29 +339,24 @@ class ExcelExporter
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>';
@@ -410,27 +367,24 @@ class ExcelExporter
public static function HumanDisplay($iSize)
{
$aUnits = array('B','KB','MB','GB','TB','PB');
$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"))
{
if (!is_dir(utils::GetDataPath()."bulk_export")) {
@mkdir(utils::GetDataPath()."bulk_export", 0777, true /* recursive */);
clearstatcache();
}
if (!is_writable(utils::GetDataPath()."bulk_export"))
{
if (!is_writable(utils::GetDataPath()."bulk_export")) {
throw new Exception('Data directory "'.utils::GetDataPath().'bulk_export" could not be written.');
}
}
protected function GetStateFile($sToken = null)
{
if ($sToken == null)
{
if ($sToken == null) {
$sToken = $this->sToken;
}
return utils::GetDataPath()."bulk_export/$sToken.status";
@@ -444,14 +398,12 @@ class ExcelExporter
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;
@@ -459,82 +411,61 @@ class ExcelExporter
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();
$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))
{
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))
{
if ($oAttDef->IsExternalKey(EXTKEY_RELATIVE)) {
$sRemoteClass = $oAttDef->GetTargetClass();
foreach(MetaModel::GetReconcKeys($sRemoteClass) as $sRemoteAttCode)
{
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

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
@@ -77,7 +78,6 @@ class CoreCannotSaveObjectException extends CoreException
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).')';

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

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

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

@@ -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

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

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -8,7 +9,7 @@ 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,7 +9,7 @@ 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

View File

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

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 MySQLNoTransactionException extends MySQLException
{
}

View File

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

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 MySQLTransactionNotClosedException extends MySQLException
{
}

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 CoreOqlException extends CoreException
{
}

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 CoreOqlMultipleResultsForbiddenException extends CoreOqlException
{
}

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

View File

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

File diff suppressed because it is too large Load Diff

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/>
/**
* Persistent class InputOutputTask
*
@@ -34,36 +34,37 @@ class InputOutputTask extends cmdbAbstractObject
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "application",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"db_table" => "priv_iotask",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
];
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 AttributeEnum("category", array("allowed_values" => new ValueSetEnum('Input, Ouput'), "sql" => "category", "default_value" => "Input", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("source_type", array("allowed_values" => new ValueSetEnum('File, Database, Web Service'), "sql" => "source_type", "default_value" => "File", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("source_subtype",
array("allowed_values" => new ValueSetEnum('Oracle, MySQL, Postgress, MSSQL, SOAP, HTTP-Get, HTTP-Post, XML/RPC, CSV, XML, Excel'), "sql" => "source_subtype", "default_value" => "CSV", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("source_path", array("allowed_values" => null, "sql" => "source_path", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeClass("objects_class", array("class_category" => "", "more_values" => "", "sql" => "objects_class", "default_value" => null, "is_null_allowed" => true, "depends_on" => array(), "class_exclusion_list" => null)));
MetaModel::Init_AddAttribute(new AttributeEnum("test_mode", array("allowed_values" => new ValueSetEnum('Yes,No'), "sql" => "test_mode", "default_value" => 'No', "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("verbose_mode", array("allowed_values" => new ValueSetEnum('Yes,No'), "sql" => "verbose_mode", "default_value" => 'No', "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("options", array("allowed_values" => new ValueSetEnum('Full, Update Only, Creation Only'), "sql" => "options", "default_value" => 'Full', "is_null_allowed" => true, "depends_on" => array())));
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 AttributeEnum("category", ["allowed_values" => new ValueSetEnum('Input, Ouput'), "sql" => "category", "default_value" => "Input", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeEnum("source_type", ["allowed_values" => new ValueSetEnum('File, Database, Web Service'), "sql" => "source_type", "default_value" => "File", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeEnum(
"source_subtype",
["allowed_values" => new ValueSetEnum('Oracle, MySQL, Postgress, MSSQL, SOAP, HTTP-Get, HTTP-Post, XML/RPC, CSV, XML, Excel'), "sql" => "source_subtype", "default_value" => "CSV", "is_null_allowed" => false, "depends_on" => []]
));
MetaModel::Init_AddAttribute(new AttributeString("source_path", ["allowed_values" => null, "sql" => "source_path", "default_value" => "", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeClass("objects_class", ["class_category" => "", "more_values" => "", "sql" => "objects_class", "default_value" => null, "is_null_allowed" => true, "depends_on" => [], "class_exclusion_list" => null]));
MetaModel::Init_AddAttribute(new AttributeEnum("test_mode", ["allowed_values" => new ValueSetEnum('Yes,No'), "sql" => "test_mode", "default_value" => 'No', "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeEnum("verbose_mode", ["allowed_values" => new ValueSetEnum('Yes,No'), "sql" => "verbose_mode", "default_value" => 'No', "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeEnum("options", ["allowed_values" => new ValueSetEnum('Full, Update Only, Creation Only'), "sql" => "options", "default_value" => 'Full', "is_null_allowed" => true, "depends_on" => []]));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'category', 'objects_class', 'source_type', 'source_subtype', 'source_path', 'options', 'test_mode', 'verbose_mode')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description', 'category', 'objects_class', 'source_type', 'source_subtype', 'options')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', ['name', 'description', 'category', 'objects_class', 'source_type', 'source_subtype', 'source_path', 'options', 'test_mode', 'verbose_mode']); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', ['description', 'category', 'objects_class', 'source_type', 'source_subtype', 'options']); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('name', 'category', 'objects_class', 'source_type', 'source_subtype')); // Criteria of the std search form
MetaModel::Init_SetZListItems('advanced_search', array('name', 'description', 'category', 'objects_class', 'source_type', 'source_subtype')); // Criteria of the advanced search form
MetaModel::Init_SetZListItems('standard_search', ['name', 'category', 'objects_class', 'source_type', 'source_subtype']); // Criteria of the std search form
MetaModel::Init_SetZListItems('advanced_search', ['name', 'description', 'category', 'objects_class', 'source_type', 'source_subtype']); // Criteria of the advanced search form
}
}
?>

View File

@@ -18,23 +18,17 @@ class LoginBasic extends AbstractLoginFSMExtension
*/
public function ListSupportedLoginModes()
{
return array('basic');
return ['basic'];
}
protected function OnModeDetection(&$iErrorCode)
{
if (!Session::IsSet('login_mode'))
{
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION']))
{
if (!Session::IsSet('login_mode')) {
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION'])) {
Session::Set('login_mode', 'basic');
}
elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
{
} elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
Session::Set('login_mode', 'basic');
}
elseif (isset($_SERVER['PHP_AUTH_USER']))
{
} elseif (isset($_SERVER['PHP_AUTH_USER'])) {
Session::Set('login_mode', 'basic');
}
}
@@ -43,22 +37,18 @@ class LoginBasic extends AbstractLoginFSMExtension
protected function OnReadCredentials(&$iErrorCode)
{
if (!Session::IsSet('login_mode') || Session::Get('login_mode') == 'basic')
{
if (!Session::IsSet('login_mode') || Session::Get('login_mode') == 'basic') {
list($sAuthUser) = $this->GetAuthUserAndPassword();
Session::Set('login_temp_auth_user', $sAuthUser);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
protected function OnCheckCredentials(&$iErrorCode)
{
if (Session::Get('login_mode') == 'basic')
{
if (Session::Get('login_mode') == 'basic') {
list($sAuthUser, $sAuthPwd) = $this->GetAuthUserAndPassword();
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal'))
{
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal')) {
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
@@ -69,8 +59,7 @@ class LoginBasic extends AbstractLoginFSMExtension
protected function OnCredentialsOK(&$iErrorCode)
{
if (Session::Get('login_mode') == 'basic')
{
if (Session::Get('login_mode') == 'basic') {
LoginWebPage::OnLoginSuccess(Session::Get('auth_user'), 'internal', Session::Get('login_mode'));
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -78,11 +67,9 @@ class LoginBasic extends AbstractLoginFSMExtension
protected function OnError(&$iErrorCode)
{
if (Session::Get('login_mode') == 'basic')
{
if (Session::Get('login_mode') == 'basic') {
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit === LoginWebPage::EXIT_RETURN)
{
if ($iOnExit === LoginWebPage::EXIT_RETURN) {
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
LoginWebPage::HTTP401Error();
@@ -92,8 +79,7 @@ class LoginBasic extends AbstractLoginFSMExtension
protected function OnConnected(&$iErrorCode)
{
if (Session::Get('login_mode') == 'basic')
{
if (Session::Get('login_mode') == 'basic') {
Session::Set('can_logoff', true);
return LoginWebPage::CheckLoggedUser($iErrorCode);
}
@@ -105,42 +91,33 @@ class LoginBasic extends AbstractLoginFSMExtension
$sAuthUser = '';
$sAuthPwd = null;
$sAuthorization = '';
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION']))
{
if (isset($_SERVER['HTTP_AUTHORIZATION']) && !empty($_SERVER['HTTP_AUTHORIZATION'])) {
$sAuthorization = $_SERVER['HTTP_AUTHORIZATION'];
}
elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
{
} elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && !empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
$sAuthorization = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
}
if (!empty($sAuthorization))
{
if (!empty($sAuthorization)) {
list($sAuthUser, $sAuthPwd) = explode(':', base64_decode(substr($sAuthorization, 6)));
}
else
{
if (isset($_SERVER['PHP_AUTH_USER']))
{
} else {
if (isset($_SERVER['PHP_AUTH_USER'])) {
$sAuthUser = $_SERVER['PHP_AUTH_USER'];
// Unfortunately, the RFC is not clear about the encoding...
// IE and FF supply the user and password encoded in ISO-8859-1 whereas Chrome provides them encoded in UTF-8
// So let's try to guess if it's an UTF-8 string or not... fortunately all encodings share the same ASCII base
if (!LoginWebPage::LooksLikeUTF8($sAuthUser))
{
if (!LoginWebPage::LooksLikeUTF8($sAuthUser)) {
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
// Supposed to be harmless in case of a plain ASCII string...
$sAuthUser = iconv('iso-8859-1', 'utf-8', $sAuthUser);
}
$sAuthPwd = $_SERVER['PHP_AUTH_PW'];
if (!LoginWebPage::LooksLikeUTF8($sAuthPwd))
{
if (!LoginWebPage::LooksLikeUTF8($sAuthPwd)) {
// Does not look like and UTF-8 string, try to convert it from iso-8859-1 to UTF-8
// Supposed to be harmless in case of a plain ASCII string...
$sAuthPwd = iconv('iso-8859-1', 'utf-8', $sAuthPwd);
}
}
}
return array($sAuthUser, $sAuthPwd);
return [$sAuthUser, $sAuthPwd];
}
}

View File

@@ -1,4 +1,5 @@
<?php
/**
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -18,7 +19,7 @@ class LoginDefaultBefore extends AbstractLoginFSMExtension
*/
public function ListSupportedLoginModes()
{
return array('before');
return ['before'];
}
protected function OnStart(&$iErrorCode)
@@ -31,13 +32,10 @@ class LoginDefaultBefore extends AbstractLoginFSMExtension
$aAllowedLoginTypes = MetaModel::GetConfig()->GetAllowedLoginTypes();
$sProposedLoginMode = utils::ReadParam('login_mode', '');
$index = array_search($sProposedLoginMode, $aAllowedLoginTypes);
if ($index !== false)
{
if ($index !== false) {
// Force login mode
Session::Set('login_mode', $sProposedLoginMode);
}
else
{
} else {
Session::Unset('login_mode');
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -49,8 +47,7 @@ class LoginDefaultBefore extends AbstractLoginFSMExtension
$aAllowedLoginTypes = MetaModel::GetConfig()->GetAllowedLoginTypes();
$sProposedLoginMode = utils::ReadParam('login_mode', '');
$index = array_search($sProposedLoginMode, $aAllowedLoginTypes);
if ($index !== false)
{
if ($index !== false) {
// Force login mode
LoginWebPage::SetLoginModeAndReload($sProposedLoginMode);
} else {
@@ -69,8 +66,6 @@ class LoginDefaultBefore extends AbstractLoginFSMExtension
*/
class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExtension
{
/**
* Must be executed after the other login plugins
*
@@ -78,19 +73,16 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
*/
public function ListSupportedLoginModes()
{
return array('after');
return ['after'];
}
protected function OnError(&$iErrorCode)
{
self::ResetLoginSession();
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit === LoginWebPage::EXIT_RETURN)
{
if ($iOnExit === LoginWebPage::EXIT_RETURN) {
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
elseif ($iOnExit == LoginWebPage::EXIT_HTTP_401)
{
} elseif ($iOnExit == LoginWebPage::EXIT_HTTP_401) {
LoginWebPage::HTTP401Error(); // Error, exit
}
// LoginWebPage::EXIT_PROMPT
@@ -99,8 +91,7 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
protected function OnCredentialsOk(&$iErrorCode)
{
if (!Session::IsSet('login_mode'))
{
if (!Session::IsSet('login_mode')) {
// N°6358 - if EXIT_RETURN was asked, send an error
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
@@ -137,10 +128,8 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
private static function ResetLoginSession()
{
LoginWebPage::ResetSession();
foreach (Session::ListVariables() as $sKey)
{
if (utils::StartsWith($sKey, 'login_'))
{
foreach (Session::ListVariables() as $sKey) {
if (utils::StartsWith($sKey, 'login_')) {
Session::Unset($sKey);
}
}

View File

@@ -11,7 +11,6 @@ use Combodo\iTop\Application\Helper\Session;
class LoginExternal extends AbstractLoginFSMExtension
{
/**
* Return the list of supported login modes for this plugin
*
@@ -19,16 +18,14 @@ class LoginExternal extends AbstractLoginFSMExtension
*/
public function ListSupportedLoginModes()
{
return array('external');
return ['external'];
}
protected function OnModeDetection(&$iErrorCode)
{
if (!Session::IsSet('login_mode'))
{
if (!Session::IsSet('login_mode')) {
$sAuthUser = $this->GetAuthUser();
if ($sAuthUser && (strlen($sAuthUser) > 0))
{
if ($sAuthUser && (strlen($sAuthUser) > 0)) {
Session::Set('login_mode', 'external');
}
}
@@ -37,11 +34,9 @@ class LoginExternal extends AbstractLoginFSMExtension
protected function OnCheckCredentials(&$iErrorCode)
{
if (Session::Get('login_mode') == 'external')
{
if (Session::Get('login_mode') == 'external') {
$sAuthUser = $this->GetAuthUser();
if (!UserRights::CheckCredentials($sAuthUser, '', Session::Get('login_mode'), 'external'))
{
if (!UserRights::CheckCredentials($sAuthUser, '', Session::Get('login_mode'), 'external')) {
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
@@ -52,8 +47,7 @@ class LoginExternal extends AbstractLoginFSMExtension
protected function OnCredentialsOK(&$iErrorCode)
{
if (Session::Get('login_mode') == 'external')
{
if (Session::Get('login_mode') == 'external') {
LoginWebPage::OnLoginSuccess(Session::Get('auth_user'), 'external', Session::Get('login_mode'));
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -61,8 +55,7 @@ class LoginExternal extends AbstractLoginFSMExtension
protected function OnConnected(&$iErrorCode)
{
if (Session::Get('login_mode') == 'external')
{
if (Session::Get('login_mode') == 'external') {
Session::Set('can_logoff', false);
return LoginWebPage::CheckLoggedUser($iErrorCode);
}
@@ -71,11 +64,9 @@ class LoginExternal extends AbstractLoginFSMExtension
protected function OnError(&$iErrorCode)
{
if (Session::Get('login_mode') == 'external')
{
if (Session::Get('login_mode') == 'external') {
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit === LoginWebPage::EXIT_RETURN)
{
if ($iOnExit === LoginWebPage::EXIT_RETURN) {
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
LoginWebPage::HTTP401Error();
@@ -84,13 +75,10 @@ class LoginExternal extends AbstractLoginFSMExtension
}
/**
* @return bool
* @return bool|mixed
*/
private function GetAuthUser()
{
$sExtAuthVar = MetaModel::GetConfig()->GetExternalAuthenticationVariable(); // In which variable is the info passed ?
eval('$sAuthUser = isset('.$sExtAuthVar.') ? '.$sExtAuthVar.' : false;'); // Retrieve the value
/** @var string $sAuthUser */
return $sAuthUser; // Retrieve the value
return MetaModel::GetConfig()->GetExternalAuthenticationVariable();
}
}

View File

@@ -23,7 +23,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
*/
public function ListSupportedLoginModes()
{
return array('form');
return ['form'];
}
/**
@@ -34,10 +34,8 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
if (!Session::IsSet('login_mode') || Session::Get('login_mode') == 'form') {
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
if ($this->bForceFormOnError || empty($sAuthUser) || empty($sAuthPwd))
{
if (array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER))
{
if ($this->bForceFormOnError || empty($sAuthUser) || empty($sAuthPwd)) {
if (array_key_exists('HTTP_X_COMBODO_AJAX', $_SERVER)) {
// X-Combodo-Ajax is a special header automatically added to all ajax requests
// Let's reply that we're currently logged-out
header('HTTP/1.0 401 Unauthorized');
@@ -66,12 +64,10 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
*/
protected function OnCheckCredentials(&$iErrorCode)
{
if (Session::Get('login_mode') == 'form')
{
if (Session::Get('login_mode') == 'form') {
$sAuthUser = utils::ReadPostedParam('auth_user', '', 'raw_data');
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal'))
{
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal')) {
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
@@ -85,8 +81,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
*/
protected function OnCredentialsOK(&$iErrorCode)
{
if (Session::Get('login_mode') == 'form')
{
if (Session::Get('login_mode') == 'form') {
// Store 'auth_user' in session for further use
LoginWebPage::OnLoginSuccess(Session::Get('auth_user'), 'internal', Session::Get('login_mode'));
}
@@ -98,8 +93,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
*/
protected function OnError(&$iErrorCode)
{
if (Session::Get('login_mode') == 'form')
{
if (Session::Get('login_mode') == 'form') {
$this->bForceFormOnError = true;
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -110,8 +104,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
*/
protected function OnConnected(&$iErrorCode)
{
if (Session::Get('login_mode') == 'form')
{
if (Session::Get('login_mode') == 'form') {
Session::Set('can_logoff', true);
return LoginWebPage::CheckLoggedUser($iErrorCode);
}
@@ -131,24 +124,23 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
$sAuthPwd = utils::ReadParam('suggest_pwd', '', true, 'raw_data');
$aData = array(
$aData = [
'sAuthUser' => $sAuthUser,
'sAuthPwd' => $sAuthPwd,
);
];
$oLoginContext->AddBlockExtension('login_input', new LoginBlockExtension('extensionblock/loginforminput.html.twig', $aData));
$oLoginContext->AddBlockExtension('login_submit', new LoginBlockExtension('extensionblock/loginformsubmit.html.twig'));
$oLoginContext->AddBlockExtension('login_form_footer', new LoginBlockExtension('extensionblock/loginformfooter.html.twig'));
$bEnableResetPassword = MetaModel::GetConfig()->Get('forgot_password');
$sResetPasswordUrl = MetaModel::GetConfig()->Get('forgot_password.url');
if ($sResetPasswordUrl == '')
{
if ($sResetPasswordUrl == '') {
$sResetPasswordUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?loginop=forgot_pwd';
}
$aData = array(
$aData = [
'bEnableResetPassword' => $bEnableResetPassword,
'sResetPasswordUrl' => $sResetPasswordUrl,
);
];
$oLoginContext->AddBlockExtension('login_links', new LoginBlockExtension('extensionblock/loginformlinks.html.twig', $aData));
return $oLoginContext;

View File

@@ -6,7 +6,6 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Application\Branding;
use Combodo\iTop\Application\TwigBase\Twig\Extension;
use Combodo\iTop\Application\WebPage\NiceWebPage;
@@ -41,11 +40,11 @@ class LoginTwigContext
*/
public function __construct()
{
$this->aBlockExtension = array();
$this->aPostedVars = array();
$this->aBlockExtension = [];
$this->aPostedVars = [];
$this->sTwigLoaderPath = null;
$this->aCSSFiles = array();
$this->aJsFiles = array();
$this->aCSSFiles = [];
$this->aJsFiles = [];
$this->sTwigNameSpace = null;
}
@@ -179,7 +178,7 @@ class LoginBlockExtension
* @param array $aData Data given to the twig template (into the variable {{ aData }})
* @api
*/
public function __construct($sTwig, $aData = array())
public function __construct($sTwig, $aData = [])
{
$this->sTwig = $sTwig;
$this->aData = $aData;
@@ -210,21 +209,18 @@ class LoginTwigRenderer
public function __construct()
{
$this->aLoginPluginList = LoginWebPage::GetLoginPluginList('iLoginUIExtension', false);
$this->aPluginFormData = array();
$aTwigLoaders = array();
$this->aPostedVars = array();
foreach ($this->aLoginPluginList as $oLoginPlugin)
{
$this->aPluginFormData = [];
$aTwigLoaders = [];
$this->aPostedVars = [];
foreach ($this->aLoginPluginList as $oLoginPlugin) {
/** @var \iLoginUIExtension $oLoginPlugin */
$oLoginContext = $oLoginPlugin->GetTwigContext();
if (is_null($oLoginContext))
{
if (is_null($oLoginContext)) {
continue;
}
$this->aPluginFormData[] = $oLoginContext;
$sTwigLoaderPath = $oLoginContext->GetTwigLoaderPath();
if ($sTwigLoaderPath != null)
{
if ($sTwigLoaderPath != null) {
$oExtensionLoader = new FilesystemLoader();
$oExtensionLoader->setPaths($sTwigLoaderPath);
$aTwigLoaders[] = $oExtensionLoader;
@@ -232,8 +228,8 @@ class LoginTwigRenderer
$this->aPostedVars = array_merge($this->aPostedVars, $oLoginContext->GetPostedVars());
}
$oCoreLoader = new FilesystemLoader(array(), APPROOT.'templates');
$aCoreTemplatesPaths = array('pages/login', 'pages/login/password');
$oCoreLoader = new FilesystemLoader([], APPROOT.'templates');
$aCoreTemplatesPaths = ['pages/login', 'pages/login/password'];
// Having this path declared after the plugins let the plugins replace the core templates
$oCoreLoader->setPaths($aCoreTemplatesPaths);
// Having the core templates accessible within a different namespace offer the possibility to extend them while replacing them
@@ -247,23 +243,20 @@ class LoginTwigRenderer
public function GetDefaultVars()
{
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
$sDisplayIcon = Branding::GetLoginLogoAbsoluteUrl();
$aVars = array(
$aVars = [
'sAppRootUrl' => utils::GetAbsoluteUrlAppRoot(),
'aPluginFormData' => $this->GetPluginFormData(),
'sItopVersion' => ITOP_VERSION,
'sVersionShort' => $sVersionShort,
'sIconUrl' => $sIconUrl,
'sDisplayIcon' => $sDisplayIcon,
);
];
return $aVars;
}
public function Render(NiceWebPage $oPage, $sTwigFile, $aVars = array())
public function Render(NiceWebPage $oPage, $sTwigFile, $aVars = [])
{
$oTemplate = $this->GetTwig()->load($sTwigFile);
$oPage->add($oTemplate->renderBlock('body', $aVars));
@@ -272,17 +265,14 @@ class LoginTwigRenderer
$oPage->add_style($oTemplate->renderBlock('css', $aVars));
// Render CSS links
foreach ($this->aPluginFormData as $oFormData)
{
foreach ($this->aPluginFormData as $oFormData) {
/** @var \LoginTwigContext $oFormData */
$aCSSFiles = $oFormData->GetCSSFiles();
foreach ($aCSSFiles as $sCSSFile)
{
foreach ($aCSSFiles as $sCSSFile) {
$oPage->LinkStylesheetFromURI($sCSSFile);
}
$aJsFiles = $oFormData->GetJsFiles();
foreach ($aJsFiles as $sJsFile)
{
foreach ($aJsFiles as $sJsFile) {
$oPage->LinkScriptFromURI($sJsFile);
}

View File

@@ -23,17 +23,15 @@ class LoginURL extends AbstractLoginFSMExtension
*/
public function ListSupportedLoginModes()
{
return array('url');
return ['url'];
}
protected function OnModeDetection(&$iErrorCode)
{
if (!Session::IsSet('login_mode') && !$this->bErrorOccurred)
{
if (!Session::IsSet('login_mode') && !$this->bErrorOccurred) {
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
if (!empty($sAuthUser) && !empty($sAuthPwd))
{
if (!empty($sAuthUser) && !empty($sAuthPwd)) {
Session::Set('login_mode', 'url');
}
}
@@ -42,8 +40,7 @@ class LoginURL extends AbstractLoginFSMExtension
protected function OnReadCredentials(&$iErrorCode)
{
if (Session::Get('login_mode') == 'url')
{
if (Session::Get('login_mode') == 'url') {
Session::Set('login_temp_auth_user', utils::ReadParam('auth_user', '', false, 'raw_data'));
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -51,12 +48,10 @@ class LoginURL extends AbstractLoginFSMExtension
protected function OnCheckCredentials(&$iErrorCode)
{
if (Session::Get('login_mode') == 'url')
{
if (Session::Get('login_mode') == 'url') {
$sAuthUser = utils::ReadParam('auth_user', '', false, 'raw_data');
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal'))
{
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal')) {
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
@@ -67,8 +62,7 @@ class LoginURL extends AbstractLoginFSMExtension
protected function OnCredentialsOK(&$iErrorCode)
{
if (Session::Get('login_mode') == 'url')
{
if (Session::Get('login_mode') == 'url') {
LoginWebPage::OnLoginSuccess(Session::Get('auth_user'), 'internal', Session::Get('login_mode'));
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -76,8 +70,7 @@ class LoginURL extends AbstractLoginFSMExtension
protected function OnError(&$iErrorCode)
{
if (Session::Get('login_mode') == 'url')
{
if (Session::Get('login_mode') == 'url') {
$this->bErrorOccurred = true;
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
@@ -85,8 +78,7 @@ class LoginURL extends AbstractLoginFSMExtension
protected function OnConnected(&$iErrorCode)
{
if (Session::Get('login_mode') == 'url')
{
if (Session::Get('login_mode') == 'url') {
Session::Set('can_logoff', true);
return LoginWebPage::CheckLoggedUser($iErrorCode);
}

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/>
/**
* Class LoginWebPage
*
@@ -37,33 +37,33 @@ use Combodo\iTop\Service\Events\EventService;
class LoginWebPage extends NiceWebPage
{
const EXIT_PROMPT = 0;
const EXIT_HTTP_401 = 1;
const EXIT_RETURN = 2;
public const EXIT_PROMPT = 0;
public const EXIT_HTTP_401 = 1;
public const EXIT_RETURN = 2;
const EXIT_CODE_OK = 0;
const EXIT_CODE_MISSINGLOGIN = 1;
const EXIT_CODE_MISSINGPASSWORD = 2;
const EXIT_CODE_WRONGCREDENTIALS = 3;
const EXIT_CODE_MUSTBEADMIN = 4;
const EXIT_CODE_PORTALUSERNOTAUTHORIZED = 5;
const EXIT_CODE_NOTAUTHORIZED = 6;
public const EXIT_CODE_OK = 0;
public const EXIT_CODE_MISSINGLOGIN = 1;
public const EXIT_CODE_MISSINGPASSWORD = 2;
public const EXIT_CODE_WRONGCREDENTIALS = 3;
public const EXIT_CODE_MUSTBEADMIN = 4;
public const EXIT_CODE_PORTALUSERNOTAUTHORIZED = 5;
public const EXIT_CODE_NOTAUTHORIZED = 6;
// Login FSM States
const LOGIN_STATE_START = 'start'; // Entry state
const LOGIN_STATE_MODE_DETECTION = 'login mode detection'; // Detect which login plugin to use
const LOGIN_STATE_READ_CREDENTIALS = 'read credentials'; // Read the credentials
const LOGIN_STATE_CHECK_CREDENTIALS = 'check credentials'; // Check if the credentials are valid
const LOGIN_STATE_CREDENTIALS_OK = 'credentials ok'; // User provisioning
const LOGIN_STATE_USER_OK = 'user ok'; // Additional check (2FA)
const LOGIN_STATE_CONNECTED = 'connected'; // User connected
const LOGIN_STATE_SET_ERROR = 'prepare for error'; // Internal state to trigger ERROR state
const LOGIN_STATE_ERROR = 'error'; // An error occurred, next state will be NONE
public const LOGIN_STATE_START = 'start'; // Entry state
public const LOGIN_STATE_MODE_DETECTION = 'login mode detection'; // Detect which login plugin to use
public const LOGIN_STATE_READ_CREDENTIALS = 'read credentials'; // Read the credentials
public const LOGIN_STATE_CHECK_CREDENTIALS = 'check credentials'; // Check if the credentials are valid
public const LOGIN_STATE_CREDENTIALS_OK = 'credentials ok'; // User provisioning
public const LOGIN_STATE_USER_OK = 'user ok'; // Additional check (2FA)
public const LOGIN_STATE_CONNECTED = 'connected'; // User connected
public const LOGIN_STATE_SET_ERROR = 'prepare for error'; // Internal state to trigger ERROR state
public const LOGIN_STATE_ERROR = 'error'; // An error occurred, next state will be NONE
// Login FSM Returns
const LOGIN_FSM_RETURN = 0; // End the FSM OK (connected)
const LOGIN_FSM_ERROR = 1; // Error signaled
const LOGIN_FSM_CONTINUE = 2; // Continue FSM
public const LOGIN_FSM_RETURN = 0; // End the FSM OK (connected)
public const LOGIN_FSM_ERROR = 1; // Error signaled
public const LOGIN_FSM_CONTINUE = 2; // Continue FSM
protected static $sHandlerClass = __class__;
private static $iOnExit;
@@ -78,7 +78,7 @@ class LoginWebPage extends NiceWebPage
*/
public static function NewLoginWebPage()
{
return new self::$sHandlerClass;
return new self::$sHandlerClass();
}
protected static $m_sLoginFailedMessage = '';
@@ -128,23 +128,18 @@ class LoginWebPage extends NiceWebPage
$oProfilesSet = $oUser->Get('profile_list');
//delete old profiles
$aExistingProfiles = [];
while ($oProfile = $oProfilesSet->Fetch())
{
while ($oProfile = $oProfilesSet->Fetch()) {
array_push($aExistingProfiles, $oProfile->Get('profileid'));
$iArrayKey = array_search($oProfile->Get('profileid'), $aProfiles);
if (!$iArrayKey)
{
if (!$iArrayKey) {
$oProfilesSet->RemoveItem($oProfile->Get('profileid'));
}
else
{
} else {
unset($aProfiles[$iArrayKey]);
}
}
//add profiles not already linked with user
foreach ($aProfiles as $iProfileId)
{
$oProfilesSet->AddItem(MetaModel::NewObject('URP_UserProfile', array('profileid' => $iProfileId, 'reason' => $sOrigin)));
foreach ($aProfiles as $iProfileId) {
$oProfilesSet->AddItem(MetaModel::NewObject('URP_UserProfile', ['profileid' => $iProfileId, 'reason' => $sOrigin]));
}
$oUser->Set('profile_list', $oProfilesSet);
}
@@ -154,56 +149,49 @@ class LoginWebPage extends NiceWebPage
$sVersionShort = Dict::Format('UI:iTopVersion:Short', ITOP_APPLICATION, ITOP_VERSION);
$sIconUrl = Utils::GetConfig()->Get('app_icon_url');
$sDisplayIcon = Branding::GetLoginLogoAbsoluteUrl();
$this->add("<div id=\"login-logo\"><a href=\"".htmlentities($sIconUrl, ENT_QUOTES,
self::PAGES_CHARSET)."\"><img title=\"$sVersionShort\" src=\"$sDisplayIcon\"></a></div>\n");
$this->add("<div id=\"login-logo\"><a href=\"".htmlentities(
$sIconUrl,
ENT_QUOTES,
self::PAGES_CHARSET
)."\"><img title=\"$sVersionShort\" src=\"$sDisplayIcon\"></a></div>\n");
}
public function DisplayLoginForm($bFailedLogin = false)
{
$oTwigContext = new LoginTwigRenderer();
$aPostedVars = array_merge(array('login_mode', 'loginop'), $oTwigContext->GetPostedVars());
$aPostedVars = array_merge(['login_mode', 'loginop'], $oTwigContext->GetPostedVars());
$sMessage = Dict::S('UI:Login:IdentifyYourself');
// Error message
if ($bFailedLogin)
{
if (self::$m_sLoginFailedMessage != '')
{
if ($bFailedLogin) {
if (self::$m_sLoginFailedMessage != '') {
$sMessage = self::$m_sLoginFailedMessage;
}
else
{
} else {
$sMessage = Dict::S('UI:Login:IncorrectLoginPassword');
}
}
// Keep the OTHER parameters posted
$aPreviousPostedVars = array();
foreach($_POST as $sPostedKey => $postedValue)
{
if (!in_array($sPostedKey, $aPostedVars))
{
if (is_array($postedValue))
{
foreach($postedValue as $sKey => $sValue)
{
$aPreviousPostedVars = [];
foreach ($_POST as $sPostedKey => $postedValue) {
if (!in_array($sPostedKey, $aPostedVars)) {
if (is_array($postedValue)) {
foreach ($postedValue as $sKey => $sValue) {
$sName = "{$sPostedKey}[{$sKey}]";
$aPreviousPostedVars[$sName] = $sValue;
}
}
else
{
} else {
$aPreviousPostedVars[$sPostedKey] = $postedValue;
}
}
}
$aVars = array(
$aVars = [
'bFailedLogin' => $bFailedLogin,
'sMessage' => $sMessage,
'aPreviousPostedVars' => $aPreviousPostedVars,
);
];
$aVars = array_merge($aVars, $oTwigContext->GetDefaultVars());
$oTwigContext->Render($this, 'login.html.twig', $aVars);
@@ -226,27 +214,22 @@ class LoginWebPage extends NiceWebPage
{
$sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data');
try
{
try {
UserRights::Login($sAuthUser); // Set the user's language (if possible!)
/** @var UserInternal $oUser */
$oUser = UserRights::GetUserObject();
if ($oUser != null)
{
if (!MetaModel::IsValidAttCode(get_class($oUser), 'reset_pwd_token'))
{
throw new Exception(Dict::S('UI:ResetPwd-Error-NotPossible'));
if ($oUser != null) {
if (!MetaModel::IsValidAttCode(get_class($oUser), 'reset_pwd_token')) {
throw new ForgotPasswordUserInputException('External accounts do not allow password reset');
}
if (!$oUser->CanChangePassword())
{
throw new Exception(Dict::S('UI:ResetPwd-Error-FixedPwd'));
if (!$oUser->CanChangePassword()) {
throw new ForgotPasswordUserInputException('The account does not allow password reset');
}
$sTo = $oUser->GetResetPasswordEmail(); // throws Exceptions if not allowed
if ($sTo == '')
{
throw new Exception(Dict::S('UI:ResetPwd-Error-NoEmail'));
if ($sTo == '') {
throw new ForgotPasswordUserInputException('Missing email address for this account');
}
// This token allows the user to change the password without knowing the previous one
@@ -265,29 +248,29 @@ class LoginWebPage extends NiceWebPage
$sResetUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?loginop=reset_pwd&auth_user='.urlencode($oUser->Get('login')).'&token='.urlencode($sToken);
$oEmail->SetBody(Dict::Format('UI:ResetPwd-EmailBody', $sResetUrl, $oUser->Get('login')));
$iRes = $oEmail->Send($aIssues, true /* force synchronous exec */);
switch ($iRes)
{
switch ($iRes) {
//case EMAIL_SEND_PENDING:
case EMAIL_SEND_OK:
break;
case EMAIL_SEND_ERROR:
default:
IssueLog::Error('Failed to send the email with the NEW password for '.$oUser->Get('friendlyname').': '.implode(', ', $aIssues));
throw new Exception(Dict::S('UI:ResetPwd-Error-Send'));
throw new ForgotPasswordApplicationException('Failed to send the password reset email for '.$oUser->Get('friendlyname').': '.implode(', ', $aIssues));
}
}
} catch (ForgotPasswordApplicationException $e) {
IssueLog::Error('Failed to process the forgot password request for user "'.$sAuthUser.'" [reason='.get_class($e).']: '.$e->getMessage());
} catch (ForgotPasswordUserInputException $e) {
IssueLog::Info('Failed to process the forgot password request for user "'.$sAuthUser.'" [reason='.get_class($e).']: '.$e->getMessage());
} catch (\Throwable $e) {
IssueLog::Error('Unexpected error while processing the forgot password request for user "'.$sAuthUser.'": '.$e->getMessage());
}
$oTwigContext = new LoginTwigRenderer();
$aVars = $oTwigContext->GetDefaultVars();
$oTwigContext->Render($this, 'forgotpwdsent.html.twig', $aVars);
}
catch(Exception $e)
{
$this->DisplayForgotPwdForm(true, $e->getMessage());
}
}
public function DisplayResetPwdForm($sErrorMessage = null)
{
@@ -304,22 +287,16 @@ class LoginWebPage extends NiceWebPage
$aVars['sToken'] = $sToken;
$aVars['sErrorMessage'] = $sErrorMessage;
if (($oUser == null))
{
if (($oUser == null)) {
$aVars['bNoUser'] = true;
}
else
{
} else {
$aVars['bNoUser'] = false;
$aVars['sUserName'] = $oUser->GetFriendlyName();
$oEncryptedToken = $oUser->Get('reset_pwd_token');
if (!$oEncryptedToken->CheckPassword($sToken))
{
if (!$oEncryptedToken->CheckPassword($sToken)) {
$aVars['bBadToken'] = true;
}
else
{
} else {
$aVars['bBadToken'] = false;
}
}
@@ -342,21 +319,15 @@ class LoginWebPage extends NiceWebPage
$aVars['sAuthUser'] = $sAuthUser;
$aVars['sToken'] = $sToken;
if (($oUser == null))
{
if (($oUser == null)) {
$aVars['bNoUser'] = true;
}
else
{
} else {
$aVars['bNoUser'] = false;
$oEncryptedToken = $oUser->Get('reset_pwd_token');
if (!$oEncryptedToken->CheckPassword($sToken))
{
if (!$oEncryptedToken->CheckPassword($sToken)) {
$aVars['bBadToken'] = true;
}
else
{
} else {
$aVars['bBadToken'] = false;
// Trash the token and change the password
$oUser->Set('reset_pwd_token', new ormPassword());
@@ -413,7 +384,7 @@ class LoginWebPage extends NiceWebPage
// Note: This will destroy the session, and not just the session data!
}
static function SecureConnectionRequired()
public static function SecureConnectionRequired()
{
return MetaModel::GetConfig()->GetSecureConnectionRequired();
}
@@ -423,7 +394,7 @@ class LoginWebPage extends NiceWebPage
* @param string $sString
* @return bool True if the string contains some typical UTF-8 multi-byte sequences
*/
static function LooksLikeUTF8($sString)
public static function LooksLikeUTF8($sString)
{
return preg_match('%(?:
[\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
@@ -446,22 +417,19 @@ class LoginWebPage extends NiceWebPage
protected static function Login($iOnExit)
{
self::$iOnExit = $iOnExit;
if (self::SecureConnectionRequired() && !utils::IsConnectionSecure())
{
if (self::SecureConnectionRequired() && !utils::IsConnectionSecure()) {
// Non secured URL... request for a secure connection
throw new Exception('Secure connection required!');
}
$bLoginDebug = MetaModel::GetConfig()->Get('login_debug');
if (Session::Get('login_state') == self::LOGIN_STATE_ERROR)
{
if (Session::Get('login_state') == self::LOGIN_STATE_ERROR) {
Session::Set('login_state', self::LOGIN_STATE_START);
}
$sLoginState = Session::Get('login_state');
$sSessionLog = '';
if ($bLoginDebug)
{
if ($bLoginDebug) {
IssueLog::Info("---------------------------------");
IssueLog::Info($_SERVER['REQUEST_URI']);
IssueLog::Info("--> Entering Login FSM with state: [$sLoginState]");
@@ -472,38 +440,30 @@ class LoginWebPage extends NiceWebPage
$iErrorCode = self::EXIT_CODE_OK;
// Finite state machine loop
while (true)
{
try
{
while (true) {
try {
$aLoginPlugins = self::GetLoginPluginList();
if (empty($aLoginPlugins))
{
if (empty($aLoginPlugins)) {
throw new Exception("Missing login classes");
}
/** @var iLoginFSMExtension $oLoginFSMExtensionInstance */
foreach ($aLoginPlugins as $oLoginFSMExtensionInstance)
{
if ($bLoginDebug)
{
foreach ($aLoginPlugins as $oLoginFSMExtensionInstance) {
if ($bLoginDebug) {
$sCurrSessionLog = session_id().' '.utils::GetSessionLog();
if ($sCurrSessionLog != $sSessionLog)
{
if ($sCurrSessionLog != $sSessionLog) {
$sSessionLog = $sCurrSessionLog;
IssueLog::Info("SESSION: $sSessionLog");
}
IssueLog::Info("Login: state: [$sLoginState] call: ".get_class($oLoginFSMExtensionInstance));
}
$iResponse = $oLoginFSMExtensionInstance->LoginAction($sLoginState, $iErrorCode);
if ($iResponse == self::LOGIN_FSM_RETURN)
{
if ($iResponse == self::LOGIN_FSM_RETURN) {
EventService::FireEvent(new EventData(EVENT_LOGIN, null, ['code' => $iErrorCode, 'state' => $sLoginState]));
Session::WriteClose();
return $iErrorCode; // Asked to exit FSM, generally login OK
}
if ($iResponse == self::LOGIN_FSM_ERROR)
{
if ($iResponse == self::LOGIN_FSM_ERROR) {
EventService::FireEvent(new EventData(EVENT_LOGIN, null, ['code' => $iErrorCode, 'state' => $sLoginState]));
$sLoginState = self::LOGIN_STATE_SET_ERROR; // Next state will be error
// An error was detected, skip the other plugins turn
@@ -515,9 +475,7 @@ class LoginWebPage extends NiceWebPage
// Every plugin has nothing else to do in this state, go forward
$sLoginState = self::AdvanceLoginFSMState($sLoginState);
Session::Set('login_state', $sLoginState);
}
catch (Exception $e)
{
} catch (Exception $e) {
EventService::FireEvent(new EventData(EVENT_LOGIN, null, ['state' => $_SESSION['login_state']]));
IssueLog::Error($e->getTraceAsString());
static::ResetSession();
@@ -537,30 +495,23 @@ class LoginWebPage extends NiceWebPage
*/
public static function GetLoginPluginList($sInterface = 'iLoginFSMExtension', $bFilterWithMode = true)
{
$aAllPlugins = array();
$aAllPlugins = [];
if ($bFilterWithMode)
{
if ($bFilterWithMode) {
$sCurrentLoginMode = Session::Get('login_mode', '');
}
else
{
} else {
$sCurrentLoginMode = '';
}
/** @var iLoginExtension $oLoginExtensionInstance */
foreach (MetaModel::EnumPlugins($sInterface) as $oLoginExtensionInstance)
{
foreach (MetaModel::EnumPlugins($sInterface) as $oLoginExtensionInstance) {
$aLoginModes = $oLoginExtensionInstance->ListSupportedLoginModes();
$aLoginModes = (is_array($aLoginModes) ? $aLoginModes : array());
foreach ($aLoginModes as $sLoginMode)
{
$aLoginModes = (is_array($aLoginModes) ? $aLoginModes : []);
foreach ($aLoginModes as $sLoginMode) {
// Keep only the plugins for the current login mode + before + after
if (empty($sCurrentLoginMode) || ($sLoginMode == $sCurrentLoginMode) || ($sLoginMode == 'before') || ($sLoginMode == 'after'))
{
if (!isset($aAllPlugins[$sLoginMode]))
{
$aAllPlugins[$sLoginMode] = array();
if (empty($sCurrentLoginMode) || ($sLoginMode == $sCurrentLoginMode) || ($sLoginMode == 'before') || ($sLoginMode == 'after')) {
if (!isset($aAllPlugins[$sLoginMode])) {
$aAllPlugins[$sLoginMode] = [];
}
$aAllPlugins[$sLoginMode][] = $oLoginExtensionInstance;
break; // Stop here to avoid registering a plugin twice
@@ -569,12 +520,10 @@ class LoginWebPage extends NiceWebPage
}
// Order and filter by the config list of allowed types (allowed_login_types)
$aAllowedLoginModes = array_merge(array('before'), MetaModel::GetConfig()->GetAllowedLoginTypes(), array('after'));
$aPlugins = array();
foreach ($aAllowedLoginModes as $sAllowedMode)
{
if (isset($aAllPlugins[$sAllowedMode]))
{
$aAllowedLoginModes = array_merge(['before'], MetaModel::GetConfig()->GetAllowedLoginTypes(), ['after']);
$aPlugins = [];
foreach ($aAllowedLoginModes as $sAllowedMode) {
if (isset($aAllPlugins[$sAllowedMode])) {
$aPlugins = array_merge($aPlugins, $aAllPlugins[$sAllowedMode]);
}
}
@@ -590,8 +539,7 @@ class LoginWebPage extends NiceWebPage
*/
private static function AdvanceLoginFSMState($sLoginState)
{
switch ($sLoginState)
{
switch ($sLoginState) {
case self::LOGIN_STATE_START:
return self::LOGIN_STATE_MODE_DETECTION;
@@ -638,8 +586,7 @@ class LoginWebPage extends NiceWebPage
public static function CheckUser($sAuthUser, $sAuthPassword = '', $sAuthentication = 'external')
{
$oUser = self::FindUser($sAuthUser, true, ucfirst(strtolower($sAuthentication)));
if (is_null($oUser))
{
if (is_null($oUser)) {
return false;
}
@@ -668,8 +615,7 @@ class LoginWebPage extends NiceWebPage
{
// User is Ok, let's save it in the session and proceed with normal login
$bLoginSuccess = UserRights::Login($sAuthUser, $sAuthentication); // Login & set the user's language
if (!$bLoginSuccess)
{
if (!$bLoginSuccess) {
throw new Exception("Bad user");
}
if (MetaModel::GetConfig()->Get('log_usage')) {
@@ -696,12 +642,10 @@ class LoginWebPage extends NiceWebPage
*/
public static function CheckLoggedUser(&$iErrorCode)
{
if (Session::IsSet('auth_user'))
{
if (Session::IsSet('auth_user')) {
// Already authenticated
$bRet = UserRights::Login(Session::Get('auth_user')); // Login & set the user's language
if ($bRet)
{
if ($bRet) {
$iErrorCode = self::EXIT_CODE_OK;
return self::LOGIN_FSM_RETURN;
}
@@ -727,8 +671,7 @@ class LoginWebPage extends NiceWebPage
public static function SetLoginModeAndReload($sNewLoginMode)
{
if (Session::Get('login_mode') == $sNewLoginMode)
{
if (Session::Get('login_mode') == $sNewLoginMode) {
return;
}
Session::Set('login_mode', $sNewLoginMode);
@@ -738,8 +681,7 @@ class LoginWebPage extends NiceWebPage
public static function HTTPReload()
{
$sOriginURL = utils::GetCurrentAbsoluteUrl();
if (!utils::StartsWith($sOriginURL, utils::GetAbsoluteUrlAppRoot()))
{
if (!utils::StartsWith($sOriginURL, utils::GetAbsoluteUrlAppRoot())) {
// If the found URL does not start with the configured AppRoot URL
$sOriginURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php';
}
@@ -753,7 +695,6 @@ class LoginWebPage extends NiceWebPage
exit;
}
/**
* Provisioning API: Find a User
*
@@ -767,26 +708,21 @@ class LoginWebPage extends NiceWebPage
*/
public static function FindUser($sAuthUser, $bMustBeValid = true, $sType = 'External')
{
try
{
$aArgs = array('login' => $sAuthUser);
try {
$aArgs = ['login' => $sAuthUser];
$sUserClass = "User$sType";
$oSearch = DBObjectSearch::FromOQL("SELECT $sUserClass WHERE login = :login");
if ($bMustBeValid)
{
if ($bMustBeValid) {
$oSearch->AddCondition('status', 'enabled');
}
$oSet = new DBObjectSet($oSearch, array(), $aArgs);
if ($oSet->CountExceeds(0))
{
$oSet = new DBObjectSet($oSearch, [], $aArgs);
if ($oSet->CountExceeds(0)) {
/** @var User $oUser */
$oUser = $oSet->Fetch();
return $oUser;
}
}
catch (Exception $e)
{
} catch (Exception $e) {
IssueLog::Error($e->getMessage());
}
return null;
@@ -805,19 +741,15 @@ class LoginWebPage extends NiceWebPage
{
/** @var \Person $oPerson */
$oPerson = null;
try
{
try {
$oSearch = new DBObjectSearch('Person');
$oSearch->AddCondition('email', $sEmail);
$oSet = new DBObjectSet($oSearch);
if ($oSet->CountExceeds(1))
{
if ($oSet->CountExceeds(1)) {
throw new Exception(Dict::S('UI:Login:Error:MultipleContactsHaveSameEmail'));
}
$oPerson = $oSet->Fetch();
}
catch (Exception $e)
{
} catch (Exception $e) {
IssueLog::Error($e->getMessage());
}
return $oPerson;
@@ -836,16 +768,14 @@ class LoginWebPage extends NiceWebPage
*
* @return \Person
*/
public static function ProvisionPerson($sFirstName, $sLastName, $sEmail, $sOrganization, $aAdditionalParams = array())
public static function ProvisionPerson($sFirstName, $sLastName, $sEmail, $sOrganization, $aAdditionalParams = [])
{
/** @var Person $oPerson */
$oPerson = null;
try
{
try {
CMDBObject::SetTrackOrigin('custom-extension');
$sInfo = 'External User provisioning';
if (Session::IsSet('login_mode'))
{
if (Session::IsSet('login_mode')) {
$sInfo .= " (".Session::Get('login_mode').")";
}
CMDBObject::SetTrackInfo($sInfo);
@@ -855,19 +785,15 @@ class LoginWebPage extends NiceWebPage
$oPerson->Set('name', $sLastName);
$oPerson->Set('email', $sEmail);
$oOrg = MetaModel::GetObjectByName('Organization', $sOrganization, false);
if (is_null($oOrg))
{
if (is_null($oOrg)) {
throw new Exception(Dict::S('UI:Login:Error:WrongOrganizationName'));
}
$oPerson->Set('org_id', $oOrg->GetKey());
foreach ($aAdditionalParams as $sAttCode => $sValue)
{
foreach ($aAdditionalParams as $sAttCode => $sValue) {
$oPerson->Set($sAttCode, $sValue);
}
$oPerson->DBInsert();
}
catch (Exception $e)
{
} catch (Exception $e) {
IssueLog::Error($e->getMessage());
}
return $oPerson;
@@ -886,27 +812,23 @@ class LoginWebPage extends NiceWebPage
*/
public static function ProvisionUser($sAuthUser, $oPerson, $aRequestedProfiles)
{
if (!MetaModel::IsValidClass('URP_Profiles'))
{
if (!MetaModel::IsValidClass('URP_Profiles')) {
IssueLog::Error("URP_Profiles is not a valid class. Automatic creation of Users is not supported in this context, sorry.");
return null;
}
/** @var UserExternal $oUser */
$oUser = null;
try
{
try {
CMDBObject::SetTrackOrigin('custom-extension');
$sInfo = 'External User provisioning';
if (Session::IsSet('login_mode'))
{
if (Session::IsSet('login_mode')) {
$sInfo .= " (".Session::Get('login_mode').")";
}
CMDBObject::SetTrackInfo($sInfo);
$oUser = MetaModel::GetObjectByName('UserExternal', $sAuthUser, false);
if (is_null($oUser))
{
if (is_null($oUser)) {
$oUser = MetaModel::NewObject('UserExternal');
$oUser->Set('login', $sAuthUser);
$oUser->Set('contactid', $oPerson->GetKey());
@@ -916,41 +838,33 @@ class LoginWebPage extends NiceWebPage
// read all the existing profiles
$oProfilesSearch = new DBObjectSearch('URP_Profiles');
$oProfilesSet = new DBObjectSet($oProfilesSearch);
$aAllProfiles = array();
while ($oProfile = $oProfilesSet->Fetch())
{
$aAllProfiles = [];
while ($oProfile = $oProfilesSet->Fetch()) {
$aAllProfiles[mb_strtolower($oProfile->GetName())] = $oProfile->GetKey();
}
$aProfiles = array();
foreach ($aRequestedProfiles as $sRequestedProfile)
{
$aProfiles = [];
foreach ($aRequestedProfiles as $sRequestedProfile) {
$sRequestedProfile = mb_strtolower($sRequestedProfile);
if (isset($aAllProfiles[$sRequestedProfile]))
{
if (isset($aAllProfiles[$sRequestedProfile])) {
$aProfiles[] = $aAllProfiles[$sRequestedProfile];
}
}
if (empty($aProfiles))
{
if (empty($aProfiles)) {
throw new Exception(Dict::S('UI:Login:Error:NoValidProfiles'));
}
// Now synchronize the profiles
$sOrigin = 'External User provisioning';
if (Session::IsSet('login_mode'))
{
if (Session::IsSet('login_mode')) {
$sOrigin .= " (".Session::Get('login_mode').")";
}
$aExistingProfiles = self::SynchronizeProfiles($oUser, $aProfiles, $sOrigin);
if ($oUser->IsModified())
{
if ($oUser->IsModified()) {
$oUser->DBWrite();
}
}
catch (Exception $e)
{
} catch (Exception $e) {
IssueLog::Error($e->getMessage());
}
@@ -964,23 +878,15 @@ class LoginWebPage extends NiceWebPage
*/
protected static function ChangeLocation($sRequestedPortalId = null, $iOnExit = self::EXIT_PROMPT)
{
$ret = call_user_func(array(self::$sHandlerClass, 'Dispatch'), $sRequestedPortalId);
if ($ret === true)
{
$ret = call_user_func([self::$sHandlerClass, 'Dispatch'], $sRequestedPortalId);
if ($ret === true) {
return self::EXIT_CODE_OK;
}
else if($ret === false)
{
} elseif ($ret === false) {
throw new Exception('Nowhere to go: Your combination of user Profiles denies you access to any '.ITOP_APPLICATION_SHORT.' portal. Please contact your administrator');
}
else
{
if ($iOnExit == self::EXIT_RETURN)
{
} else {
if ($iOnExit == self::EXIT_RETURN) {
return self::EXIT_CODE_PORTALUSERNOTAUTHORIZED;
}
else
{
} else {
// No rights to be here, redirect to the portal
header('Location: '.$ret);
die();
@@ -1002,7 +908,7 @@ class LoginWebPage extends NiceWebPage
* @return int|mixed|string
* @throws \Exception
*/
static function DoLogin($bMustBeAdmin = false, $bIsAllowedToPortalUsers = false, $iOnExit = self::EXIT_PROMPT)
public static function DoLogin($bMustBeAdmin = false, $bIsAllowedToPortalUsers = false, $iOnExit = self::EXIT_PROMPT)
{
$sRequestedPortalId = $bIsAllowedToPortalUsers ? 'legacy_portal' : 'backoffice';
return self::DoLoginEx($sRequestedPortalId, $bMustBeAdmin, $iOnExit);
@@ -1019,23 +925,18 @@ class LoginWebPage extends NiceWebPage
* @return int|mixed|string
* @throws \Exception
*/
static function DoLoginEx($sRequestedPortalId = null, $bMustBeAdmin = false, $iOnExit = self::EXIT_PROMPT)
public static function DoLoginEx($sRequestedPortalId = null, $bMustBeAdmin = false, $iOnExit = self::EXIT_PROMPT)
{
$operation = utils::ReadParam('loginop', '');
$sMessage = self::HandleOperations($operation); // May exit directly
$iRet = self::Login($iOnExit);
if ($iRet == self::EXIT_CODE_OK)
{
if ($bMustBeAdmin && !UserRights::IsAdministrator())
{
if ($iOnExit == self::EXIT_RETURN)
{
if ($iRet == self::EXIT_CODE_OK) {
if ($bMustBeAdmin && !UserRights::IsAdministrator()) {
if ($iOnExit == self::EXIT_RETURN) {
return self::EXIT_CODE_MUSTBEADMIN;
}
else
{
} else {
require_once(APPROOT.'/setup/setuppage.class.inc.php');
$oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError'));
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessAdmin')."</h1>\n");
@@ -1044,69 +945,52 @@ class LoginWebPage extends NiceWebPage
exit;
}
}
$iRet = call_user_func(array(self::$sHandlerClass, 'ChangeLocation'), $sRequestedPortalId, $iOnExit);
$iRet = call_user_func([self::$sHandlerClass, 'ChangeLocation'], $sRequestedPortalId, $iOnExit);
}
if ($iOnExit == self::EXIT_RETURN)
{
if ($iOnExit == self::EXIT_RETURN) {
return $iRet;
}
else
{
} else {
return $sMessage;
}
}
protected static function HandleOperations($operation)
{
$sMessage = ''; // most of the operations never return, but some can return a message to be displayed
if ($operation == 'logoff')
{
if ($operation == 'logoff') {
self::ResetSession();
$oPage = self::NewLoginWebPage();
$oPage->DisplayLoginForm(false /* not a failed attempt */);
$oPage->output();
exit;
}
else if ($operation == 'forgot_pwd')
{
} elseif ($operation == 'forgot_pwd') {
$oPage = self::NewLoginWebPage();
$oPage->DisplayForgotPwdForm();
$oPage->output();
exit;
}
else if ($operation == 'forgot_pwd_go')
{
} elseif ($operation == 'forgot_pwd_go') {
$oPage = self::NewLoginWebPage();
$oPage->ForgotPwdGo();
$oPage->output();
exit;
}
else if ($operation == 'reset_pwd')
{
} elseif ($operation == 'reset_pwd') {
$oPage = self::NewLoginWebPage();
$oPage->DisplayResetPwdForm();
$oPage->output();
exit;
}
else if ($operation == 'do_reset_pwd')
{
} elseif ($operation == 'do_reset_pwd') {
try {
$oPage = self::NewLoginWebPage();
$oPage->DoResetPassword();
}
catch (CoreCannotSaveObjectException $e)
{
} catch (CoreCannotSaveObjectException $e) {
$oPage = self::NewLoginWebPage();
$oPage->DisplayResetPwdForm($e->getIssue());
}
$oPage->output();
exit;
}
else if ($operation == 'change_pwd')
{
if (Session::IsSet('auth_user'))
{
} elseif ($operation == 'change_pwd') {
if (Session::IsSet('auth_user')) {
$sAuthUser = Session::Get('auth_user');
$sIssue = Session::Get('pwd_issue');
Session::Unset('pwd_issue');
@@ -1118,16 +1002,13 @@ class LoginWebPage extends NiceWebPage
$oPage->output();
exit;
}
}
else if ($operation == 'check_pwd_policy')
{
} elseif ($operation == 'check_pwd_policy') {
$sAuthUser = Session::Get('auth_user');
UserRights::Login($sAuthUser); // Set the user's language
$aPwdMap = array();
$aPwdMap = [];
foreach (array('new_pwd', 'retype_new_pwd') as $postedPwd)
{
foreach (['new_pwd', 'retype_new_pwd'] as $postedPwd) {
$oUser = new UserLocal();
$oUser->ValidatePassword($_POST[$postedPwd]);
@@ -1137,27 +1018,21 @@ class LoginWebPage extends NiceWebPage
echo json_encode($aPwdMap);
die();
}
if ($operation == 'do_change_pwd')
{
if (Session::IsSet('auth_user'))
{
if ($operation == 'do_change_pwd') {
if (Session::IsSet('auth_user')) {
$sAuthUser = Session::Get('auth_user');
UserRights::Login($sAuthUser); // Set the user's language
$sOldPwd = utils::ReadPostedParam('old_pwd', '', 'raw_data');
$sNewPwd = utils::ReadPostedParam('new_pwd', '', 'raw_data');
try
{
if (UserRights::CanChangePassword() && ((!UserRights::CheckCredentials($sAuthUser, $sOldPwd)) || (!UserRights::ChangePassword($sOldPwd, $sNewPwd))))
{
try {
if (UserRights::CanChangePassword() && ((!UserRights::CheckCredentials($sAuthUser, $sOldPwd)) || (!UserRights::ChangePassword($sOldPwd, $sNewPwd)))) {
$oPage = self::NewLoginWebPage();
$oPage->DisplayChangePwdForm(true); // old pwd was wrong
$oPage->output();
exit;
}
}
catch (CoreCannotSaveObjectException $e)
{
} catch (CoreCannotSaveObjectException $e) {
$oPage = self::NewLoginWebPage();
$oPage->DisplayChangePwdForm(true, $e->getIssue()); // password policy was not met.
$oPage->output();
@@ -1171,23 +1046,24 @@ class LoginWebPage extends NiceWebPage
protected static function Dispatch($sRequestedPortalId)
{
if ($sRequestedPortalId === null) return true; // allowed to any portal => return true
if ($sRequestedPortalId === null) {
return true;
} // allowed to any portal => return true
$aPortalsConf = PortalDispatcherData::GetData();
$aDispatchers = array();
foreach($aPortalsConf as $sPortalId => $aConf)
{
$aDispatchers = [];
foreach ($aPortalsConf as $sPortalId => $aConf) {
$sHandlerClass = $aConf['handler'];
$aDispatchers[$sPortalId] = new $sHandlerClass($sPortalId);
}
if (array_key_exists($sRequestedPortalId, $aDispatchers) && $aDispatchers[$sRequestedPortalId]->IsUserAllowed())
{
if (array_key_exists($sRequestedPortalId, $aDispatchers) && $aDispatchers[$sRequestedPortalId]->IsUserAllowed()) {
return true;
}
foreach($aDispatchers as $sPortalId => $oDispatcher)
{
if ($oDispatcher->IsUserAllowed()) return $oDispatcher->GetUrl();
foreach ($aDispatchers as $sPortalId => $oDispatcher) {
if ($oDispatcher->IsUserAllowed()) {
return $oDispatcher->GetUrl();
}
}
return false; // nothing matched !!
}

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -17,7 +18,6 @@
* You should have received a copy of the GNU Affero General Public License
*/
//
// Maintenance message display functions
// Only included by approot.inc.php
@@ -33,8 +33,7 @@ function _MaintenanceSetupPageMessage($sTitle, $sMessage)
{
// Web Page
@include_once(APPROOT.'setup/setuppage.class.inc.php');
if (class_exists('SetupPage'))
{
if (class_exists('SetupPage')) {
$oP = new ErrorPage($sTitle);
$oP->p("<h2 class=\"center\">$sMessage</h2>");
$oP->add_ready_script(
@@ -42,12 +41,9 @@ function _MaintenanceSetupPageMessage($sTitle, $sMessage)
// Reload in 30s to check if maintenance is over
setTimeout(function(){ window.location.reload(); }, 30000);
JS
);
$oP->output();
}
else
{
} else {
_MaintenanceTextMessage($sMessage);
}
}
@@ -78,14 +74,13 @@ function _MaintenanceHtmlMessage($sMessage)
*/
function _MaintenanceJsonMessage($sTitle, $sMessage)
{
if (class_exists('JsonPage'))
{
if (class_exists('JsonPage')) {
$oP = new JsonPage($sTitle);
$oP->add_header('Access-Control-Allow-Origin: *');
$aMessage = [
'code' => 100,
'message' =>$sMessage
'message' => $sMessage,
];
$oP->AddData($aMessage);

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -12,7 +13,6 @@ use Combodo\iTop\Application\WebPage\WebPage;
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT."/application/user.dashboard.class.inc.php");
/**
* This class manipulates, stores and displays the navigation menu used in the application
* In order to improve the modularity of the data model and to ease the update/migration
@@ -51,43 +51,40 @@ class ApplicationMenu
/**
* @var bool
*/
static $bAdditionalMenusLoaded = false;
public static $bAdditionalMenusLoaded = false;
/**
* @var array
*/
static $aRootMenus = array();
public static $aRootMenus = [];
/**
* @var array
*/
static $aMenusIndex = array();
public static $aMenusIndex = [];
/**
* @var array
*/
static $aMenusById = [];
public static $aMenusById = [];
/**
* @var string
*/
static $sFavoriteSiloQuery = 'SELECT Organization';
public static $sFavoriteSiloQuery = 'SELECT Organization';
/**
* @return void
*/
public static function LoadAdditionalMenus()
{
if (!self::$bAdditionalMenusLoaded)
{
if (!self::$bAdditionalMenusLoaded) {
// Build menus from module handlers
//
/** @var \ModuleHandlerApiInterface $oPHPClass */
foreach(MetaModel::EnumPlugins('ModuleHandlerApiInterface') as $oPHPClass)
{
foreach (MetaModel::EnumPlugins('ModuleHandlerApiInterface') as $oPHPClass) {
$oPHPClass::OnMenuCreation();
}
// Build menus from the menus themselves (e.g. the ShortcutContainerMenuNode will do that)
//
foreach(self::$aRootMenus as $aMenu)
{
foreach (self::$aRootMenus as $aMenu) {
$oMenuNode = self::GetMenuNode($aMenu['index']);
$oMenuNode->PopulateChildMenus();
}
@@ -124,8 +121,7 @@ class ApplicationMenu
*/
public static function CheckMenuIdEnabled($sMenuId)
{
if (self::IsMenuIdEnabled($sMenuId) === false)
{
if (self::IsMenuIdEnabled($sMenuId) === false) {
require_once(APPROOT.'/setup/setuppage.class.inc.php');
$oP = new ErrorPage(Dict::S('UI:PageTitle:FatalError'));
$oP->add("<h1>".Dict::S('UI:Login:Error:AccessRestricted')."</h1>\n");
@@ -159,22 +155,18 @@ class ApplicationMenu
public static function InsertMenu(MenuNode $oMenuNode, $iParentIndex, $fRank)
{
$index = self::GetMenuIndexById($oMenuNode->GetMenuId());
if ($index == -1)
{
if ($index == -1) {
// The menu does not already exist, insert it
$index = count(self::$aMenusIndex);
if ($iParentIndex == -1)
{
if ($iParentIndex == -1) {
$sParentId = '';
self::$aRootMenus[] = array ('rank' => $fRank, 'index' => $index);
}
else
{
self::$aRootMenus[] = ['rank' => $fRank, 'index' => $index];
} else {
/** @var \MenuNode $oNode */
$oNode = self::$aMenusIndex[$iParentIndex]['node'];
$sParentId = $oNode->GetMenuId();
self::$aMenusIndex[$iParentIndex]['children'][] = array ('rank' => $fRank, 'index' => $index);
self::$aMenusIndex[$iParentIndex]['children'][] = ['rank' => $fRank, 'index' => $index];
}
// Note: At the time when 'parent', 'rank' and 'source_file' have been added for the reflection API,
@@ -182,11 +174,9 @@ class ApplicationMenu
//
$aBacktrace = debug_backtrace();
$sFile = isset($aBacktrace[2]["file"]) ? $aBacktrace[2]["file"] : $aBacktrace[1]["file"];
self::$aMenusIndex[$index] = array('node' => $oMenuNode, 'children' => array(), 'parent' => $sParentId, 'rank' => $fRank, 'source_file' => $sFile);
self::$aMenusIndex[$index] = ['node' => $oMenuNode, 'children' => [], 'parent' => $sParentId, 'rank' => $fRank, 'source_file' => $sFile];
self::$aMenusById[$oMenuNode->GetMenuId()] = $index;
}
else
{
} else {
// the menu already exists, let's combine the conditions that make it visible
/** @var \MenuNode $oNode */
$oNode = self::$aMenusIndex[$index]['node'];
@@ -216,7 +206,7 @@ class ApplicationMenu
* @throws \DictExceptionMissingString
* @since 3.0.0
*/
public static function GetMenusCount($aExtraParams = array())
public static function GetMenusCount($aExtraParams = [])
{
$aMenuGroups = static::GetMenuGroups($aExtraParams);
@@ -259,18 +249,16 @@ class ApplicationMenu
* @throws \DictExceptionMissingString
* @since 3.0.0
*/
public static function GetMenuGroups($aExtraParams = array())
public static function GetMenuGroups($aExtraParams = [])
{
self::LoadAdditionalMenus();
// Sort the root menu based on the rank
usort(self::$aRootMenus, array('ApplicationMenu', 'CompareOnRank'));
usort(self::$aRootMenus, ['ApplicationMenu', 'CompareOnRank']);
$aMenuGroups = [];
foreach(static::$aRootMenus as $aMenuGroup)
{
if(!static::CanDisplayMenu($aMenuGroup))
{
foreach (static::$aRootMenus as $aMenuGroup) {
if (!static::CanDisplayMenu($aMenuGroup)) {
continue;
}
@@ -321,26 +309,23 @@ class ApplicationMenu
* @throws \Exception
* @since 3.0.0
*/
public static function GetSubMenuNodes($sMenuGroupIdx, $aExtraParams = array())
public static function GetSubMenuNodes($sMenuGroupIdx, $aExtraParams = [])
{
$aSubMenuItems = self::GetChildren($sMenuGroupIdx);
// Sort the children based on the rank
usort($aSubMenuItems, array('ApplicationMenu', 'CompareOnRank'));
usort($aSubMenuItems, ['ApplicationMenu', 'CompareOnRank']);
$aSubMenuNodes = [];
foreach($aSubMenuItems as $aSubMenuItem)
{
if(!static::CanDisplayMenu($aSubMenuItem))
{
foreach ($aSubMenuItems as $aSubMenuItem) {
if (!static::CanDisplayMenu($aSubMenuItem)) {
continue;
}
$sSubMenuItemIdx = $aSubMenuItem['index'];
$oSubMenuNode = static::GetMenuNode($sSubMenuItemIdx);
if(!$oSubMenuNode->IsEnabled())
{
if (!$oSubMenuNode->IsEnabled()) {
continue;
}
@@ -366,21 +351,15 @@ class ApplicationMenu
private static function CanDisplayMenu($aMenu)
{
$oMenuNode = self::GetMenuNode($aMenu['index']);
if ($oMenuNode->IsEnabled())
{
if ($oMenuNode->IsEnabled()) {
$aChildren = self::GetChildren($aMenu['index']);
if (count($aChildren) > 0)
{
foreach($aChildren as $aSubMenu)
{
if (self::CanDisplayMenu($aSubMenu))
{
if (count($aChildren) > 0) {
foreach ($aChildren as $aSubMenu) {
if (self::CanDisplayMenu($aSubMenu)) {
return true;
}
}
}
else
{
} else {
return true;
}
}
@@ -396,12 +375,10 @@ class ApplicationMenu
public static function CompareOnRank($a, $b)
{
$result = 1;
if ($a['rank'] == $b['rank'])
{
if ($a['rank'] == $b['rank']) {
$result = 0;
}
if ($a['rank'] < $b['rank'])
{
if ($a['rank'] < $b['rank']) {
$result = -1;
}
return $result;
@@ -449,8 +426,7 @@ class ApplicationMenu
{
$oAppContext = new ApplicationContext();
$sMenuId = $oAppContext->GetCurrentValue('menu', null);
if ($sMenuId === null)
{
if ($sMenuId === null) {
$sMenuId = self::GetDefaultMenuId();
}
return $sMenuId;
@@ -462,13 +438,12 @@ class ApplicationMenu
public static function GetDefaultMenuId()
{
static $sDefaultMenuId = null;
if (is_null($sDefaultMenuId))
{
if (is_null($sDefaultMenuId)) {
// Make sure the root menu is sorted on 'rank'
usort(self::$aRootMenus, array('ApplicationMenu', 'CompareOnRank'));
usort(self::$aRootMenus, ['ApplicationMenu', 'CompareOnRank']);
$oFirstGroup = self::GetMenuNode(self::$aRootMenus[0]['index']);
$aChildren = self::$aMenusIndex[$oFirstGroup->GetIndex()]['children'];
usort($aChildren, array('ApplicationMenu', 'CompareOnRank'));
usort($aChildren, ['ApplicationMenu', 'CompareOnRank']);
$oMenuNode = self::GetMenuNode($aChildren[0]['index']);
$sDefaultMenuId = $oMenuNode->GetMenuId();
}
@@ -482,13 +457,11 @@ class ApplicationMenu
public static function GetRootMenuId($sMenuId)
{
$iMenuIndex = self::GetMenuIndexById($sMenuId);
if ($iMenuIndex == -1)
{
if ($iMenuIndex == -1) {
return '';
}
$oMenu = ApplicationMenu::GetMenuNode($iMenuIndex);
while ($oMenu->GetParentIndex() != -1)
{
while ($oMenu->GetParentIndex() != -1) {
$oMenu = ApplicationMenu::GetMenuNode($oMenu->GetParentIndex());
}
return $oMenu->GetMenuId();
@@ -575,17 +548,17 @@ abstract class MenuNode
{
$this->sMenuId = $sMenuId;
$this->iParentIndex = $iParentIndex;
$this->aReflectionProperties = array();
$this->aReflectionProperties = [];
if (utils::IsNotNullOrEmptyString($sEnableClass)) {
$this->aReflectionProperties['enable_class'] = $sEnableClass;
$this->aReflectionProperties['enable_action'] = $iActionCode;
$this->aReflectionProperties['enable_permission'] = $iAllowedResults;
$this->aReflectionProperties['enable_stimulus'] = $sEnableStimulus;
}
$this->m_aEnableClasses = array($sEnableClass);
$this->m_aEnableActions = array($iActionCode);
$this->m_aEnableActionResults = array($iAllowedResults);
$this->m_aEnableStimuli = array($sEnableStimulus);
$this->m_aEnableClasses = [$sEnableClass];
$this->m_aEnableActions = [$iActionCode];
$this->m_aEnableActionResults = [$iAllowedResults];
$this->m_aEnableStimuli = [$sEnableStimulus];
$this->index = ApplicationMenu::InsertMenu($this, $iParentIndex, $fRank);
}
@@ -639,7 +612,6 @@ abstract class MenuNode
$oSearch->SetShowObsoleteData(utils::ShowObsoleteData());
DBSearchHelper::AddContextFilter($oSearch);
$oSet = new DBObjectSet($oSearch);
$iCount = $oSet->CountWithLimit(99);
if ($iCount > 99) {
@@ -690,8 +662,7 @@ abstract class MenuNode
*/
public function PopulateChildMenus()
{
foreach (ApplicationMenu::GetChildren($this->GetIndex()) as $aMenu)
{
foreach (ApplicationMenu::GetChildren($this->GetIndex()) as $aMenu) {
$index = $aMenu['index'];
$oMenu = ApplicationMenu::GetMenuNode($index);
$oMenu->PopulateChildMenus();
@@ -726,8 +697,7 @@ abstract class MenuNode
*/
public function AddCondition(MenuNode $oMenuNode)
{
foreach($oMenuNode->m_aEnableClasses as $index => $sClass )
{
foreach ($oMenuNode->m_aEnableClasses as $index => $sClass) {
$this->m_aEnableClasses[] = $sClass;
$this->m_aEnableActions[] = $oMenuNode->m_aEnableActions[$index];
$this->m_aEnableActionResults[] = $oMenuNode->m_aEnableActionResults[$index];
@@ -740,33 +710,24 @@ abstract class MenuNode
*/
public function IsEnabled()
{
foreach($this->m_aEnableClasses as $index => $sClass)
{
if ($sClass != null)
{
if (MetaModel::IsValidClass($sClass))
{
if ($this->m_aEnableStimuli[$index] != null)
{
if (!UserRights::IsStimulusAllowed($sClass, $this->m_aEnableStimuli[$index]))
{
foreach ($this->m_aEnableClasses as $index => $sClass) {
if ($sClass != null) {
if (MetaModel::IsValidClass($sClass)) {
if ($this->m_aEnableStimuli[$index] != null) {
if (!UserRights::IsStimulusAllowed($sClass, $this->m_aEnableStimuli[$index])) {
return false;
}
}
if ($this->m_aEnableActions[$index] != null)
{
if ($this->m_aEnableActions[$index] != null) {
// Menus access rights ignore the archive mode
utils::PushArchiveMode(false);
$iResult = UserRights::IsActionAllowed($sClass, $this->m_aEnableActions[$index]);
utils::PopArchiveMode();
if (!($iResult & $this->m_aEnableActionResults[$index]))
{
if (!($iResult & $this->m_aEnableActionResults[$index])) {
return false;
}
}
}
else
{
} else {
return false;
}
}
@@ -779,7 +740,7 @@ abstract class MenuNode
* @param array $aExtraParams
* @return mixed
*/
public abstract function RenderContent(WebPage $oPage, $aExtraParams = array());
abstract public function RenderContent(WebPage $oPage, $aExtraParams = []);
/**
* @param string $sHyperlink
@@ -788,16 +749,13 @@ abstract class MenuNode
*/
protected function AddParams($sHyperlink, $aExtraParams)
{
if (count($aExtraParams) > 0)
{
$aQuery = array();
if (count($aExtraParams) > 0) {
$aQuery = [];
$sSeparator = '?';
if (strpos($sHyperlink, '?') !== false)
{
if (strpos($sHyperlink, '?') !== false) {
$sSeparator = '&';
}
foreach($aExtraParams as $sName => $sValue)
{
foreach ($aExtraParams as $sName => $sValue) {
$aQuery[] = urlencode($sName).'='.urlencode($sValue);
}
$sHyperlink .= $sSeparator.implode('&', $aQuery);
@@ -813,7 +771,7 @@ abstract class MenuNode
class MenuGroup extends MenuNode
{
/** @var string DEFAULT_DECORATION_CLASSES Set to null by default so it is replaced by initials when none is specified */
const DEFAULT_DECORATION_CLASSES = null;
public const DEFAULT_DECORATION_CLASSES = null;
/** @var string The CSS classes used to display the menu group's icon */
protected $sDecorationClasses = self::DEFAULT_DECORATION_CLASSES;
@@ -833,8 +791,7 @@ class MenuGroup extends MenuNode
{
parent::__construct($sMenuId, -1 /* no parent, groups are at root level */, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
if(!empty($sDecorationClasses))
{
if (!empty($sDecorationClasses)) {
$this->sDecorationClasses = $sDecorationClasses;
}
}
@@ -875,7 +832,7 @@ class MenuGroup extends MenuNode
/**
* @inheritDoc
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
public function RenderContent(WebPage $oPage, $aExtraParams = [])
{
assert(false); // Shall never be called, groups do not display any content
}
@@ -887,7 +844,6 @@ class MenuGroup extends MenuNode
*/
class TemplateMenuNode extends MenuNode
{
/**
* Create a menu item based on a custom template and inserts it into the application's main menu
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
@@ -903,12 +859,18 @@ class TemplateMenuNode extends MenuNode
{
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
}
/**
* @inheritDoc
*/
public function GetHyperlink($aExtraParams)
{
return '';
}
/**
* @inheritDoc
* @throws \Exception
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
public function RenderContent(WebPage $oPage, $aExtraParams = [])
{
//DO NOTHING this type of menu is only used for title not clickable
}
@@ -942,7 +904,6 @@ class OQLMenuNode extends MenuNode
*/
protected $m_aParams;
/**
* Create a menu item based on an OQL query and inserts it into the application's main menu
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
@@ -963,7 +924,7 @@ class OQLMenuNode extends MenuNode
$this->sOQL = $sOQL;
$this->bSearch = $bSearch;
$this->bSearchFormOpen = $bSearchFormOpen;
$this->m_aParams = array();
$this->m_aParams = [];
$this->aReflectionProperties['oql'] = $sOQL;
$this->aReflectionProperties['do_search'] = $bSearch;
// Enhancement: we could set as the "enable" condition that the user has enough rights to "read" the objects
@@ -977,8 +938,7 @@ class OQLMenuNode extends MenuNode
public function SetParameters($aParams)
{
$this->m_aParams = $aParams;
foreach($aParams as $sKey => $value)
{
foreach ($aParams as $sKey => $value) {
$this->aReflectionProperties[$sKey] = $value;
}
}
@@ -987,12 +947,11 @@ class OQLMenuNode extends MenuNode
* @inheritDoc
* @throws \Exception
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
public function RenderContent(WebPage $oPage, $aExtraParams = [])
{
$oTag = new ContextTag(ContextTag::TAG_OBJECT_SEARCH);
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
OQLMenuNode::RenderOQLSearch
(
OQLMenuNode::RenderOQLSearch(
$this->sOQL,
Dict::S($this->sPageTitle),
'Menu_'.$this->GetMenuId(),
@@ -1017,7 +976,7 @@ class OQLMenuNode extends MenuNode
* @throws DictExceptionMissingString
* @throws OQLException
*/
public static function RenderOQLSearch($sOql, $sTitle, $sUsageId, $bSearchPane, $bSearchOpen, WebPage $oPage, $aExtraParams = array(), $bEnableBreadcrumb = false)
public static function RenderOQLSearch($sOql, $sTitle, $sUsageId, $bSearchPane, $bSearchOpen, WebPage $oPage, $aExtraParams = [], $bEnableBreadcrumb = false)
{
$sUsageId = utils::GetSafeId($sUsageId);
$oSearch = DBObjectSearch::FromOQL($sOql);
@@ -1028,15 +987,14 @@ class OQLMenuNode extends MenuNode
$oBlock = new DisplayBlock($oSearch, DisplayBlock::ENUM_STYLE_SEARCH, false /* Asynchronous */, $aParams);
$oBlock->Display($oPage, 0);
$oPage->add("<div class='sf_results_area ibo-add-margin-top-250' data-target='search_results'>");
}
else {
} else {
$oPage->add("<div class='sf_results_area' data-target='search_results'>");
}
$aExtraParams['panel_class'] = $sClass;
$aExtraParams['panel_title'] = $sTitle;
$aExtraParams['panel_icon'] = $sIcon;
$aParams = array_merge(array('table_id' => $sUsageId), $aExtraParams);
$aParams = array_merge(['table_id' => $sUsageId], $aExtraParams);
$oBlock = new DisplayBlock($oSearch, 'list', false /* Asynchronous */, $aParams);
$oBlock->Display($oPage, $sUsageId);
@@ -1101,14 +1059,14 @@ class SearchMenuNode extends MenuNode
* @throws \DictExceptionMissingString
* @throws \Exception
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
public function RenderContent(WebPage $oPage, $aExtraParams = [])
{
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
$oPage->SetBreadCrumbEntry("menu-".$this->sMenuId, $this->GetTitle(), '', '', 'fas fa-search', iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES);
$oSearch = new DBObjectSearch($this->sClass);
$sUsageId = 'Menu_'.utils::GetSafeId($this->GetMenuId());
$aParams = array_merge(array('table_id' =>$sUsageId), $aExtraParams);
$aParams = array_merge(['table_id' => $sUsageId], $aExtraParams);
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
$oBlock->Display($oPage, 0);
}
@@ -1145,10 +1103,16 @@ class WebPageMenuNode extends MenuNode
* @param bool $bIsLinkInNewWindow for the {@link WebPageMenuNode::IsHyperLinkInNewWindow} method
*/
public function __construct(
$sMenuId, $sHyperlink, $iParentIndex, $fRank = 0.0, $sEnableClass = null, $iActionCode = null,
$iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null, $bIsLinkInNewWindow = false
)
{
$sMenuId,
$sHyperlink,
$iParentIndex,
$fRank = 0.0,
$sEnableClass = null,
$iActionCode = null,
$iAllowedResults = UR_ALLOWED_YES,
$sEnableStimulus = null,
$bIsLinkInNewWindow = false
) {
parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus);
$this->sHyperlink = $sHyperlink;
$this->aReflectionProperties['url'] = $sHyperlink;
@@ -1175,7 +1139,7 @@ class WebPageMenuNode extends MenuNode
/**
* @inheritDoc
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
public function RenderContent(WebPage $oPage, $aExtraParams = [])
{
assert(false); // Shall never be called, the external web page will handle the display by itself
}
@@ -1236,10 +1200,8 @@ class NewObjectMenuNode extends MenuNode
$aSubClasses = MetaModel::EnumChildClasses($this->sClass, ENUM_CHILD_CLASSES_ALL); // Including the specified class itself
$bActionIsAllowed = false;
foreach($aSubClasses as $sCandidateClass)
{
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
{
foreach ($aSubClasses as $sCandidateClass) {
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES)) {
$bActionIsAllowed = true;
break; // Enough for now
}
@@ -1250,7 +1212,7 @@ class NewObjectMenuNode extends MenuNode
/**
* @inheritDoc
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
public function RenderContent(WebPage $oPage, $aExtraParams = [])
{
assert(false); // Shall never be called, the external web page will handle the display by itself
}
@@ -1290,7 +1252,9 @@ class DashboardMenuNode extends MenuNode
*/
public function GetHyperlink($aExtraParams)
{
if ($this->sDashboardFile == '') return '';
if ($this->sDashboardFile == '') {
return '';
}
return parent::GetHyperlink($aExtraParams);
}
@@ -1308,12 +1272,11 @@ class DashboardMenuNode extends MenuNode
* @inheritDoc
* @throws \Exception
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
public function RenderContent(WebPage $oPage, $aExtraParams = [])
{
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
$oDashboard = $this->GetDashboard();
if ($oDashboard != null)
{
if ($oDashboard != null) {
WebResourcesHelper::EnableC3JSToWebPage($oPage);
$sDivId = utils::Sanitize($this->sMenuId, '', 'element_identifier');
@@ -1344,9 +1307,7 @@ class DashboardMenuNode extends MenuNode
}
$oPage->SetBreadCrumbEntry("ui-dashboard-".$this->sMenuId, $this->GetTitle(), $sDescription, '', $sIcon, iTopWebPage::ENUM_BREADCRUMB_ENTRY_ICON_TYPE_CSS_CLASSES);
}
}
else
{
} else {
$oPage->p("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
}
}
@@ -1359,12 +1320,9 @@ class DashboardMenuNode extends MenuNode
public function RenderEditor(WebPage $oPage)
{
$oDashboard = $this->GetDashboard();
if ($oDashboard != null)
{
if ($oDashboard != null) {
$oDashboard->RenderEditor($oPage);
}
else
{
} else {
$oPage->p("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
}
}
@@ -1376,13 +1334,10 @@ class DashboardMenuNode extends MenuNode
public function AddDashlet($oDashlet)
{
$oDashboard = $this->GetDashboard();
if ($oDashboard != null)
{
if ($oDashboard != null) {
$oDashboard->AddDashlet($oDashlet);
$oDashboard->Save();
}
else
{
} else {
throw new Exception("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
}
}
@@ -1405,7 +1360,7 @@ class ShortcutContainerMenuNode extends MenuNode
/**
* @inheritDoc
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
public function RenderContent(WebPage $oPage, $aExtraParams = [])
{
}
@@ -1420,10 +1375,9 @@ class ShortcutContainerMenuNode extends MenuNode
//
$oBMSearch = new DBObjectSearch('Shortcut');
$oBMSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$oBMSet = new DBObjectSet($oBMSearch, array('friendlyname' => true)); // ascending on friendlyname
$oBMSet = new DBObjectSet($oBMSearch, ['friendlyname' => true]); // ascending on friendlyname
$fRank = 1;
while ($oShortcut = $oBMSet->Fetch())
{
while ($oShortcut = $oBMSet->Fetch()) {
$sName = $this->GetMenuId().'_'.$oShortcut->GetKey();
new ShortcutMenuNode($sName, $oShortcut, $this->GetIndex(), $fRank++);
}
@@ -1434,7 +1388,6 @@ class ShortcutContainerMenuNode extends MenuNode
}
}
require_once(APPROOT.'application/shortcut.class.inc.php');
/**
* This class defines a menu item which content is a shortcut.
@@ -1470,15 +1423,22 @@ class ShortcutMenuNode extends MenuNode
public function GetHyperlink($aExtraParams)
{
$sContext = $this->oShortcut->Get('context');
$aContext = unserialize($sContext);
if (isset($aContext['menu']))
{
try {
$aContext = utils::Unserialize($sContext);
if (isset($aContext['menu'])) {
unset($aContext['menu']);
}
foreach ($aContext as $sArgName => $sArgValue)
{
foreach ($aContext as $sArgName => $sArgValue) {
$aExtraParams[$sArgName] = $sArgValue;
}
} catch (Exception $e) {
IssueLog::Warning("User shortcut corrupted, delete the shortcut", LogChannels::CONSOLE, [
'shortcut_name' => $this->oShortcut->GetName(),
'root_cause' => $e->getMessage(),
]);
// delete the shortcut
$this->oShortcut->DBDelete();
}
return parent::GetHyperlink($aExtraParams);
}
@@ -1486,7 +1446,7 @@ class ShortcutMenuNode extends MenuNode
* @inheritDoc
* @throws \Exception
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
public function RenderContent(WebPage $oPage, $aExtraParams = [])
{
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
$this->oShortcut->RenderContent($oPage, $aExtraParams);
@@ -1523,11 +1483,9 @@ class ShortcutMenuNode extends MenuNode
return true;
}
public function GetEntriesCount()
{
return $this->GetEntriesCountFromOQL($this->oShortcut->Get('oql'));
}
}

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -33,7 +34,7 @@ interface iNewsroomProvider
* @param User $oUser The user for who to check if the provider is applicable.
* return bool
*/
public function IsApplicable(User $oUser = null);
public function IsApplicable(?User $oUser = null);
/**
* The human readable (localized) label for this provider
@@ -118,27 +119,27 @@ abstract class NewsroomProviderBase implements iNewsroomProvider
* {@inheritDoc}
* @see iNewsroomProvider::GetLabel()
*/
public abstract function GetLabel();
abstract public function GetLabel();
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetFetchURL()
*/
public abstract function GetFetchURL();
abstract public function GetFetchURL();
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetMarkAllURL()
*/
public abstract function GetMarkAllAsReadURL();
abstract public function GetMarkAllAsReadURL();
/**
* {@inheritDoc}
* @see iNewsroomProvider::GetViewAllURL()
*/
public abstract function GetViewAllURL();
abstract public function GetViewAllURL();
public function IsApplicable(User $oUser = null)
public function IsApplicable(?User $oUser = null)
{
return false;
}
@@ -149,7 +150,7 @@ abstract class NewsroomProviderBase implements iNewsroomProvider
*/
public function GetPlaceholders()
{
return array(); // By default, empty set of placeholders
return []; // By default, empty set of placeholders
}
public function GetTTL()

View File

@@ -1,4 +1,5 @@
<?php
class PortalDispatcher
{
protected $sPortalid;
@@ -21,25 +22,20 @@ class PortalDispatcher
$bRet = true;
$aProfiles = UserRights::ListProfiles($oUser);
foreach($this->aData['deny'] as $sDeniedProfile)
{
foreach ($this->aData['deny'] as $sDeniedProfile) {
// If one denied profile is present, it's enough => return false
if (in_array($sDeniedProfile, $aProfiles))
{
if (in_array($sDeniedProfile, $aProfiles)) {
return false;
}
}
// If there are some "allow" profiles, then by default the result is false
// since the user must have at least one of the profiles to be allowed
if (count($this->aData['allow']) > 0)
{
if (count($this->aData['allow']) > 0) {
$bRet = false;
}
foreach($this->aData['allow'] as $sAllowProfile)
{
foreach ($this->aData['allow'] as $sAllowProfile) {
// If one "allow" profile is present, it's enough => return true
if (in_array($sAllowProfile, $aProfiles))
{
if (in_array($sAllowProfile, $aProfiles)) {
return true;
}
}
@@ -49,12 +45,9 @@ class PortalDispatcher
public function GetURL()
{
$aOverloads = MetaModel::GetConfig()->Get('portal_dispatch_urls');
if (array_key_exists($this->sPortalid, $aOverloads))
{
if (array_key_exists($this->sPortalid, $aOverloads)) {
$sRet = $aOverloads[$this->sPortalid];
}
else
{
} else {
$sRet = utils::GetAbsoluteUrlAppRoot().$this->aData['url'];
}
return $sRet;

View File

@@ -1,4 +1,5 @@
<?php
/*
* Copyright (C) 2010-2024 Combodo SAS
*
@@ -31,101 +32,108 @@ abstract class Query extends cmdbAbstractObject
*/
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "core/cmdb,view_in_gui,application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"db_table" => "priv_query",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
);
];
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("name", array(
MetaModel::Init_AddAttribute(new AttributeString("name", [
"allowed_values" => null,
"sql" => "name",
"default_value" => null,
"is_null_allowed" => false,
"depends_on" => array(),
)));
"depends_on" => [],
]));
MetaModel::Init_AddAttribute(new AttributeText("description", array(
MetaModel::Init_AddAttribute(new AttributeText("description", [
"allowed_values" => null,
"sql" => "description",
"default_value" => null,
"is_null_allowed" => false,
"depends_on" => array(),
)));
"depends_on" => [],
]));
MetaModel::Init_AddAttribute(new AttributeEnum("is_template", array(
MetaModel::Init_AddAttribute(new AttributeEnum("is_template", [
'allowed_values' => new ValueSetEnum('yes,no'),
'sql' => 'is_template',
'default_value' => 'no',
'is_null_allowed' => false,
'depends_on' => [],
'display_style' => 'radio_horizontal',
)));
]));
MetaModel::Init_AddAttribute(new AttributeInteger("export_count", array(
MetaModel::Init_AddAttribute(new AttributeInteger("export_count", [
"allowed_values" => null,
"sql" => "export_count",
"default_value" => 0,
"is_null_allowed" => false,
"depends_on" => array(),
"depends_on" => [],
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
)));
]));
MetaModel::Init_AddAttribute(new AttributeDateTime("export_last_date", array(
MetaModel::Init_AddAttribute(new AttributeDateTime("export_last_date", [
"allowed_values" => null,
"sql" => "export_last_date",
"default_value" => null,
"is_null_allowed" => true,
"depends_on" => array(),
"depends_on" => [],
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
)));
]));
MetaModel::Init_AddAttribute(new AttributeExternalKey("export_last_user_id",
array(
MetaModel::Init_AddAttribute(new AttributeExternalKey(
"export_last_user_id",
[
"targetclass" => 'User',
"allowed_values" => null,
"sql" => 'user_id',
"is_null_allowed" => true,
"depends_on"=>array(),
"depends_on" => [],
"display_style" => 'select',
"always_load_in_tables" => false,
"on_target_delete" => DEL_SILENT,
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
)));
]
));
MetaModel::Init_AddAttribute(new AttributeExternalField("export_last_user_contact",
array(
MetaModel::Init_AddAttribute(new AttributeExternalField(
"export_last_user_contact",
[
"allowed_values" => null,
"extkey_attcode" => "export_last_user_id",
"target_attcode" => "contactid",
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
)));
]
));
// Display lists
MetaModel::Init_SetZListItems('details',
array('name', 'is_template', 'description')); // 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', 'is_template', 'description']
); // 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', 'is_template')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search',
array('name', 'description', 'is_template')); // Criteria of the default search form
MetaModel::Init_SetZListItems('standard_search', ['name', 'description', 'is_template']); // Criteria of the std search form
MetaModel::Init_SetZListItems(
'default_search',
['name', 'description', 'is_template']
); // Criteria of the default search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
/**
* @inheritdoc
*
* @since 3.1.0
*/
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
public function GetAttributeFlags($sAttCode, &$aReasons = [], $sTargetState = '')
{
// read only attribute
if (in_array($sAttCode, ['export_count', 'export_last_date', 'export_last_user_id'])) {
@@ -135,7 +143,6 @@ abstract class Query extends cmdbAbstractObject
return parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
}
/**
* Return export url.
*
@@ -144,7 +151,7 @@ abstract class Query extends cmdbAbstractObject
* @return string|null
* @since 3.1.0
*/
abstract public function GetExportUrl(array $aValues = null) : ?string;
abstract public function GetExportUrl(?array $aValues = null): ?string;
/**
* Update last export information.
@@ -173,51 +180,54 @@ class QueryOQL extends Query
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "core/cmdb,view_in_gui,application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('oql', 'is_template'),
"reconc_keys" => ['oql', 'is_template'],
"db_table" => "priv_query_oql",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
];
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeOQL("oql", array(
MetaModel::Init_AddAttribute(new AttributeOQL("oql", [
"allowed_values" => null,
"sql" => "oql",
"default_value" => null,
"is_null_allowed" => false,
"depends_on" => array(),
)));
MetaModel::Init_AddAttribute(new AttributeText("fields", array(
"depends_on" => [],
]));
MetaModel::Init_AddAttribute(new AttributeText("fields", [
"allowed_values" => null,
"sql" => "fields",
"default_value" => null,
"is_null_allowed" => true,
"depends_on" => array(),
)));
"depends_on" => [],
]));
// Rolled back to AttributeText until AttributeQueryAttCodeSet can manage fields order correctly
//MetaModel::Init_AddAttribute(new AttributeQueryAttCodeSet("fields", array("allowed_values"=>null,"max_items" => 1000, "query_field" => "oql", "sql"=>"fields", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array('oql'))));
// Display lists
MetaModel::Init_SetZListItems('details',
array(
'col:col1' => array('fieldset:Query:baseinfo' => array('name', 'is_template', 'description', 'oql', 'fields')),
'col:col2' => array('fieldset:Query:exportInfo' => array('export_count', 'export_last_date', 'export_last_user_id', 'export_last_user_contact'))
)
MetaModel::Init_SetZListItems(
'details',
[
'col:col1' => ['fieldset:Query:baseinfo' => ['name', 'is_template', 'description', 'oql', 'fields']],
'col:col2' => ['fieldset:Query:exportInfo' => ['export_count', 'export_last_date', 'export_last_user_id', 'export_last_user_contact']],
]
); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', ['description']); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search',
array('name', 'description', 'is_template', 'fields', 'oql')); // Criteria of the std search form
MetaModel::Init_SetZListItems(
'standard_search',
['name', 'description', 'is_template', 'fields', 'oql']
); // Criteria of the std search form
}
/** @inheritdoc */
public function GetExportUrl(array $aValues = null) : ?string
public function GetExportUrl(?array $aValues = null): ?string
{
try {
// retrieve attributes
@@ -237,13 +247,12 @@ class QueryOQL extends Query
}
return $sUrl;
}
catch(Exception $e){
} catch (Exception $e) {
return null;
}
}
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
public function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = [])
{
$aFieldsMap = parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
$oPage->add_script("$('[name=\"attr_oql\"]').addClass('ibo-query-oql ibo-is-code'); $('[data-attribute-code=\"oql\"]').addClass('ibo-query-oql ibo-is-code');");
@@ -267,16 +276,14 @@ class QueryOQL extends Query
if (count($aParameters) == 0) {
$oBlock = new DisplayBlock($oSearch, 'list');
$aExtraParams = array(
$aExtraParams = [
//'menu' => $sShowMenu,
'table_id' => 'query_preview_'.$this->getKey(),
);
];
$sBlockId = 'block_query_preview_'.$this->GetKey(); // make a unique id (edition occuring in the same DOM)
$oBlock->Display($oPage, $sBlockId, $aExtraParams);
}
}
catch
(OQLException $e) {
} catch (OQLException $e) {
$oAlert = AlertUIBlockFactory::MakeForFailure(Dict::S('UI:RunQuery:Error'), $e->getHtmlDesc())
->SetIsClosable(false)
->SetIsCollapsible(false);
@@ -287,7 +294,6 @@ class QueryOQL extends Query
return $aFieldsMap;
}
// Rolled back until 'fields' can be properly managed by AttributeQueryAttCodeSet
//
// public function ComputeValues()
@@ -323,5 +329,3 @@ class QueryOQL extends Query
// }
}
?>

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -18,7 +19,6 @@
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableSettings;
use Combodo\iTop\Application\WebPage\WebPage;
/**
* Persistent class Shortcut and derived
* Shortcuts of any kind
@@ -31,32 +31,32 @@ abstract class Shortcut extends DBObject implements iDisplay
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "gui,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"db_table" => "priv_shortcut",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
);
];
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
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 AttributeText("context", array("allowed_values"=>null, "sql"=>"context", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", ["targetclass" => "User", "allowed_values" => null, "sql" => "user_id", "is_null_allowed" => true, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("name", ["allowed_values" => null, "sql" => "name", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeText("context", ["allowed_values" => null, "sql" => "context", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'context')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('name')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', ['name', 'context']); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', ['name']); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
abstract public function RenderContent(WebPage $oPage, $aExtraParams = array());
abstract public function RenderContent(WebPage $oPage, $aExtraParams = []);
protected function OnInsert()
{
@@ -137,13 +137,13 @@ EOF
return '';
}
function DisplayDetails(WebPage $oPage, $bEditMode = false)
public function DisplayDetails(WebPage $oPage, $bEditMode = false)
{
}
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
public function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = [])
{
return array();
return [];
}
// End of the minimal implementation of iDisplay
}
@@ -152,41 +152,39 @@ class ShortcutOQL extends Shortcut
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "gui,view_in_gui",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"db_table" => "priv_shortcut_oql",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
];
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeOQL("oql", array("allowed_values"=>null, "sql"=>"oql", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("auto_reload", array("allowed_values"=>new ValueSetEnum('none,custom'), "sql"=>"auto_reload", "default_value"=>"none", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("auto_reload_sec", array("allowed_values"=>null, "sql"=>"auto_reload_sec", "default_value"=>60, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeOQL("oql", ["allowed_values" => null, "sql" => "oql", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeEnum("auto_reload", ["allowed_values" => new ValueSetEnum('none,custom'), "sql" => "auto_reload", "default_value" => "none", "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeInteger("auto_reload_sec", ["allowed_values" => null, "sql" => "auto_reload_sec", "default_value" => 60, "is_null_allowed" => false, "depends_on" => []]));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'context', 'oql')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('name')); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('details', ['name', 'context', 'oql']); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', ['name']); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
public function RenderContent(WebPage $oPage, $aExtraParams = array())
public function RenderContent(WebPage $oPage, $aExtraParams = [])
{
$oPage->set_title($this->Get('name'));
switch($this->Get('auto_reload'))
{
switch ($this->Get('auto_reload')) {
case 'custom':
$iRate = (int)$this->Get('auto_reload_sec');
if ($iRate > 0)
{
if ($iRate > 0) {
// Must a string otherwise it can be evaluated to 'true' and defaults to "standard" refresh rate!
$aExtraParams['auto_reload'] = (string)$iRate;
}
@@ -198,12 +196,9 @@ class ShortcutOQL extends Shortcut
$bSearchPane = true;
$bSearchOpen = true;
try
{
try {
OQLMenuNode::RenderOQLSearch($this->Get('oql'), $this->Get('name'), 'shortcut_'.$this->GetKey(), $bSearchPane, $bSearchOpen, $oPage, $aExtraParams, true);
}
catch (Exception $e)
{
} catch (Exception $e) {
throw new Exception("The OQL shortcut '".$this->Get('name')."' (id: ".$this->GetKey().") could not be displayed: ".$e->getMessage());
}
@@ -226,12 +221,9 @@ class ShortcutOQL extends Shortcut
// Find a unique default name
// -> The class of the query + an index if necessary
if ($sOQL == null)
{
if ($sOQL == null) {
$sDefault = '';
}
else
{
} else {
$oBMSearch = new DBObjectSearch('Shortcut');
$oBMSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$oBMSet = new DBObjectSet($oBMSearch);

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -23,7 +24,6 @@ require_once(APPROOT.'core/contexttag.class.inc.php');
require_once(APPROOT.'core/kpi.class.inc.php');
require_once(APPROOT.'setup/setuputils.class.inc.php');
/**
* File to include to initialize the datamodel in memory
*
@@ -36,12 +36,10 @@ ExecutionKPI::EnableMemory(1);
// This storage is freed on error (case of allowed memory exhausted)
$sReservedMemory = str_repeat('*', 1024 * 1024);
register_shutdown_function(function()
{
register_shutdown_function(function () {
global $sReservedMemory;
$sReservedMemory = null;
if (!is_null($err = error_get_last()) && ($err['type'] == E_ERROR))
{
if (!is_null($err = error_get_last()) && ($err['type'] == E_ERROR)) {
// Remove stack trace from MySQLException (since 2.7.2 see N°3174)
$sMessage = $err['message'];
if (strpos($sMessage, 'MySQLException') !== false) {
@@ -71,38 +69,30 @@ $oKPI->ComputeAndReport("Session Start");
$sSwitchEnv = utils::ReadParam('switch_env', null);
$bAllowCache = true;
if (($sSwitchEnv != null) && file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE) &&( Session::Get('itop_env') !== $sSwitchEnv))
{
if (($sSwitchEnv != null) && file_exists(APPCONF.$sSwitchEnv.'/'.ITOP_CONFIG_FILE) && (Session::Get('itop_env') !== $sSwitchEnv)) {
Session::Set('itop_env', $sSwitchEnv);
$sEnv = $sSwitchEnv;
$bAllowCache = false;
// Reset the opcache since otherwise the PHP "model" files may still be cached !!
if (function_exists('opcache_reset'))
{
if (function_exists('opcache_reset')) {
// Zend opcode cache
opcache_reset();
}
if (function_exists('apc_clear_cache'))
{
if (function_exists('apc_clear_cache')) {
// APC(u) cache
apc_clear_cache();
}
// TODO: reset the credentials as well ??
}
else if (Session::IsSet('itop_env'))
{
} elseif (Session::IsSet('itop_env')) {
$sEnv = Session::Get('itop_env');
}
else
{
} else {
$sEnv = ITOP_DEFAULT_ENV;
Session::Set('itop_env', ITOP_DEFAULT_ENV);
}
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
try {
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
}
catch (MySQLException $e) {
} catch (MySQLException $e) {
IssueLog::Debug($e->getMessage());
throw new MySQLException('Could not connect to the DB server', []);
}

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -25,7 +26,7 @@
*/
class ThemeHandler
{
const IMAGE_EXTENSIONS = ['png', 'gif', 'jpg', 'jpeg'];
public const IMAGE_EXTENSIONS = ['png', 'gif', 'jpg', 'jpeg'];
/** @var \CompileCSSService */
private static $oCompileCSSService;
@@ -50,7 +51,7 @@ class ThemeHandler
'imports' => [],
'stylesheets' => [
'main' => '../css/backoffice/main.scss',
]
],
],
];
}
@@ -63,8 +64,7 @@ class ThemeHandler
{
try {
$sThemeId = utils::GetConfig()->Get('backoffice_default_theme');
}
catch (CoreException $oCompileException) {
} catch (CoreException $oCompileException) {
// Fallback on our default theme in case the config. is not available yet
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
$sThemeId = $aDefaultTheme['name'];
@@ -85,8 +85,7 @@ class ThemeHandler
if (true === utils::GetConfig()->Get('user_preferences.allow_backoffice_theme_override')) {
$sThemeId = appUserPreferences::GetPref('backoffice_theme', null);
}
}
catch (Exception $oException) {
} catch (Exception $oException) {
// Do nothing, already handled by $sThemeId null by default
}
@@ -201,8 +200,7 @@ class ThemeHandler
if (static::ShouldThemeSignatureCheckBeForced($sThemeId)) {
static::CompileTheme($sThemeId);
}
}
catch (CoreException $oCompileException) {
} catch (CoreException $oCompileException) {
// Fallback on our default theme (should always be compilable) in case the previous theme doesn't exists
$aDefaultTheme = ThemeHandler::GetDefaultThemeInformation();
$sThemeId = $aDefaultTheme['name'];
@@ -258,13 +256,16 @@ class ThemeHandler
* @throws \CoreException
* @return boolean: indicate whether theme compilation occured
*/
public static function CompileTheme($sThemeId, $bSetup=false, $sSetupCompilationTimestamp="", $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null) {
public static function CompileTheme($sThemeId, $bSetup = false, $sSetupCompilationTimestamp = "", $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null)
{
if ($sSetupCompilationTimestamp === "") {
$sSetupCompilationTimestamp = microtime(true);
}
$sSetupCompilationTimestampInSecunds = (strpos($sSetupCompilationTimestamp, '.') !== false) ? explode('.',
$sSetupCompilationTimestamp)[0] : $sSetupCompilationTimestamp;
$sSetupCompilationTimestampInSecunds = (strpos($sSetupCompilationTimestamp, '.') !== false) ? explode(
'.',
$sSetupCompilationTimestamp
)[0] : $sSetupCompilationTimestamp;
$sEnv = APPROOT.'env-'.utils::GetCurrentEnvironment().'/';
@@ -330,10 +331,8 @@ class ThemeHandler
$iStyleLastModified = $oFindStylesheetObject->GetLastModified();
$aIncludedImages = static::GetIncludedImages($aThemeParametersWithVersion, $oFindStylesheetObject->GetAllStylesheetPaths(), $sThemeId);
foreach ($aIncludedImages as $sImage)
{
if (is_file($sImage))
{
foreach ($aIncludedImages as $sImage) {
if (is_file($sImage)) {
$iStylesheetLastModified = @filemtime($sImage);
$iStyleLastModified = $iStyleLastModified < $iStylesheetLastModified ? $iStylesheetLastModified : $iStyleLastModified;
}
@@ -343,8 +342,7 @@ class ThemeHandler
$iFilemetime = @filemtime($sThemeCssPath);
$bFileExists = file_exists($sThemeCssPath);
$bVarSignatureChanged = false;
if ($bFileExists && $bSetup)
{
if ($bFileExists && $bSetup) {
$sPrecompiledSignature = static::GetSignature($sThemeCssPath);
//check variable signature has changed which is independant from any file modification
if (!empty($sPrecompiledSignature)) {
@@ -354,22 +352,17 @@ class ThemeHandler
}
}
if (!$bFileExists || $bVarSignatureChanged || (is_writable($sThemeFolderPath) && ($iFilemetime < $iStyleLastModified)))
{
if (!$bFileExists || $bVarSignatureChanged || (is_writable($sThemeFolderPath) && ($iFilemetime < $iStyleLastModified))) {
// Dates don't match. Second chance: check if the already compiled stylesheet exists and is consistent based on its signature
$sActualSignature = static::ComputeSignature($aThemeParameters, $aImportsPaths, $aIncludedImages);
if ($bFileExists && !$bSetup)
{
if ($bFileExists && !$bSetup) {
$sPrecompiledSignature = static::GetSignature($sThemeCssPath);
}
if (!empty($sPrecompiledSignature) && $sActualSignature == $sPrecompiledSignature)
{
if (!empty($sPrecompiledSignature) && $sActualSignature == $sPrecompiledSignature) {
touch($sThemeCssPath); // Stylesheet is up to date, mark it as more recent to speedup next time
}
else
{
} else {
// Alas, we really need to recompile
// Add the signature to the generated CSS file so that the file can be used as a precompiled stylesheet if needed
$sSignatureComment =
@@ -381,14 +374,16 @@ $sActualSignature
*/
CSS;
if (!static::$oCompileCSSService)
{
if (!static::$oCompileCSSService) {
static::$oCompileCSSService = new CompileCSSService();
}
//store it again to change $version with latest compiled time
SetupLog::Info("Compiling theme $sThemeId...");
$sTmpThemeCssContent = static::$oCompileCSSService->CompileCSSFromSASS($sTmpThemeScssContent, $aImportsPaths,
$aThemeParametersWithVersion);
$sTmpThemeCssContent = static::$oCompileCSSService->CompileCSSFromSASS(
$sTmpThemeScssContent,
$aImportsPaths,
$aThemeParametersWithVersion
);
SetupLog::Info("$sThemeId theme compilation done.");
file_put_contents($sThemeFolderPath.'/theme-parameters.json', json_encode($aThemeParameters));
file_put_contents($sThemeCssPath, $sSignatureComment.$sTmpThemeCssContent);
@@ -413,13 +408,14 @@ CSS;
* @return string
* @throws \Exception
*/
public static function ComputeSignature($aThemeParameters, $aImportsPaths, $aIncludedImages) {
public static function ComputeSignature($aThemeParameters, $aImportsPaths, $aIncludedImages)
{
$aSignature = [
'variables' => md5(json_encode($aThemeParameters['variables'])),
'stylesheets' => [],
'variable_imports' => [],
'images' => [],
'utility_imports' => []
'utility_imports' => [],
];
$oFindStylesheetObject = new FindStylesheetObject();
@@ -461,8 +457,7 @@ CSS;
}
}
foreach ($aIncludedImages as $sImage)
{
foreach ($aIncludedImages as $sImage) {
if (is_file($sImage)) {
$sUri = str_replace(self::GetAppRootWithSlashes(), '', $sImage);
$aSignature['images'][$sUri] = md5_file($sImage);
@@ -497,14 +492,11 @@ CSS;
'aFoundVariables' => $aFoundVariables,
];
foreach ($aStylesheetFiles as $sStylesheetFile)
{
foreach ($aStylesheetFiles as $sStylesheetFile) {
$aRes = static::GetAllUrlFromScss($aThemeParametersVariables, $sStylesheetFile);
/** @var array $aVal */
foreach($aMap as $key => $aVal)
{
if (array_key_exists($key, $aMap))
{
foreach ($aMap as $key => $aVal) {
if (array_key_exists($key, $aMap)) {
$aMap[$key] = array_merge($aVal, $aRes[$key]);
}
}
@@ -513,17 +505,14 @@ CSS;
$aMap = static::ResolveUncompleteUrlsFromScss($aMap, $aThemeParametersVariables, $aStylesheetFiles);
$aImages = [];
foreach ($aMap ['aCompleteUrls'] as $sUri => $sUrl)
{
foreach ($aMap ['aCompleteUrls'] as $sUri => $sUrl) {
$sImg = $sUrl;
if (preg_match("/(.*)\?/", $sUrl, $aMatches))
{
if (preg_match("/(.*)\?/", $sUrl, $aMatches)) {
$sImg = $aMatches[1];
}
if (static::HasImageExtension($sImg)
&& ! array_key_exists($sImg, $aImages))
{
&& ! array_key_exists($sImg, $aImages)) {
$sFilePath = utils::RealPath($sImg, APPROOT);
if ($sFilePath !== false) {
$sFilePathWithSlashes = str_replace('\\', '/', $sFilePath);
@@ -555,7 +544,7 @@ CSS;
public static function CanonicalizePath($path)
{
$path = explode('/', str_replace('//', '/', $path));
$stack = array();
$stack = [];
foreach ($path as $seg) {
if ($seg == '..') {
// Ignore this segment, remove last segment from stack
@@ -586,10 +575,8 @@ CSS;
public static function ResolveUncompleteUrlsFromScss($aMap, $aThemeParametersVariables, $aStylesheetFile)
{
$sContent = "";
foreach ($aStylesheetFile as $sStylesheetFile)
{
if (is_file($sStylesheetFile))
{
foreach ($aStylesheetFile as $sStylesheetFile) {
if (is_file($sStylesheetFile)) {
$sContent .= '\n'.file_get_contents($sStylesheetFile);
}
}
@@ -622,40 +609,26 @@ CSS;
public static function FindMissingVariables($aThemeParametersVariables, $aMissingVariables, $aFoundVariables, $sContent, $bForceEmptyValueWhenNotFound = false)
{
$aNewMissingVars = [];
if (!empty($aMissingVariables))
{
foreach ($aMissingVariables as $var)
{
if (array_key_exists($var, $aThemeParametersVariables))
{
if (!empty($aMissingVariables)) {
foreach ($aMissingVariables as $var) {
if (array_key_exists($var, $aThemeParametersVariables)) {
$aFoundVariables[$var] = $aThemeParametersVariables[$var];
}
else
{
if (preg_match_all("/\\\$$var\s*:\s*[\"']{0,1}(.*)[\"']{0,1};/", $sContent, $aValues))
{
} else {
if (preg_match_all("/\\\$$var\s*:\s*[\"']{0,1}(.*)[\"']{0,1};/", $sContent, $aValues)) {
$sValue = $aValues[1][0];
if (preg_match_all("/([^!]+)!/", $sValue, $aSubValues))
{
if (preg_match_all("/([^!]+)!/", $sValue, $aSubValues)) {
$sValue = trim($aSubValues[1][0], ' "\'');
}
if (strpos($sValue, '$') === false)
{
if (strpos($sValue, '$') === false) {
$aFoundVariables[$var] = $sValue;
}
else{
} else {
$aNewMissingVars[] = $var;
}
}
else
{
if ($bForceEmptyValueWhenNotFound)
{
} else {
if ($bForceEmptyValueWhenNotFound) {
$aFoundVariables[$var] = '';
}
else
{
} else {
$aNewMissingVars[] = $var;
}
}
@@ -676,32 +649,23 @@ CSS;
*/
public static function ResolveUrls($aFoundVariables, array $aToCompleteUrls, array $aCompleteUrls)
{
if (!empty($aFoundVariables))
{
if (!empty($aFoundVariables)) {
$aFoundVariablesWithEmptyValue = [];
foreach ($aFoundVariables as $aFoundVariable => $sValue)
{
foreach ($aFoundVariables as $aFoundVariable => $sValue) {
$aFoundVariablesWithEmptyValue[$aFoundVariable] = '';
}
foreach ($aToCompleteUrls as $sUrlTemplate)
{
foreach ($aToCompleteUrls as $sUrlTemplate) {
unset($aToCompleteUrls[$sUrlTemplate]);
$sResolvedUrl = static::ResolveUrl($sUrlTemplate, $aFoundVariables);
if ($sResolvedUrl == false)
{
if ($sResolvedUrl == false) {
$aToCompleteUrls[$sUrlTemplate] = $sUrlTemplate;
}
else
{
} else {
$sUri = static::ResolveUrl($sUrlTemplate, $aFoundVariablesWithEmptyValue);
$aExplodedUri = explode('?', $sUri);
if (empty($aExplodedUri))
{
if (empty($aExplodedUri)) {
$aCompleteUrls[$sUri] = $sResolvedUrl;
}
else
{
} else {
$aCompleteUrls[$aExplodedUri[0]] = $sResolvedUrl;
}
}
@@ -726,41 +690,31 @@ CSS;
$aMissingVariables = [];
$aFoundVariables = [];
if (is_file($sStylesheetFile))
{
if (is_file($sStylesheetFile)) {
$sContent = file_get_contents($sStylesheetFile);
if (preg_match_all("/url\s*\((.*)\)/", $sContent, $aMatches))
{
foreach ($aMatches[1] as $path)
{
if (preg_match_all("/url\s*\((.*)\)/", $sContent, $aMatches)) {
foreach ($aMatches[1] as $path) {
$iRemainingClosingParenthesisPos = strpos($path, ')');
if ($iRemainingClosingParenthesisPos !== false) {
$path = substr($path, 0, $iRemainingClosingParenthesisPos);
}
if (!array_key_exists($path, $aCompleteUrls)
&& !array_key_exists($path, $aToCompleteUrls))
{
if (preg_match_all("/\\$([\w\-_]+)/", $path, $aCurrentVars))
{
&& !array_key_exists($path, $aToCompleteUrls)) {
if (preg_match_all("/\\$([\w\-_]+)/", $path, $aCurrentVars)) {
/** @var string $aCurrentVars */
foreach ($aCurrentVars[1] as $var)
{
if (!array_key_exists($var, $aMissingVariables))
{
foreach ($aCurrentVars[1] as $var) {
if (!array_key_exists($var, $aMissingVariables)) {
$aMissingVariables[$var] = $var;
}
}
$aToCompleteUrls[$path] = $path;
}
else
{
} else {
$aCompleteUrls[$path] = trim($path, "\"'");
}
}
}
}
if (!empty($aMissingVariables))
{
if (!empty($aMissingVariables)) {
list($aMissingVariables, $aFoundVariables) = static::FindMissingVariables($aThemeParametersVariables, $aMissingVariables, $aFoundVariables, $sContent);
list($aToCompleteUrls, $aCompleteUrls) = static::ResolveUrls($aFoundVariables, $aToCompleteUrls, $aCompleteUrls);
}
@@ -770,7 +724,7 @@ CSS;
'aCompleteUrls' => $aCompleteUrls,
'aToCompleteUrls' => $aToCompleteUrls,
'aMissingVariables' => $aMissingVariables,
'aFoundVariables' => $aFoundVariables
'aFoundVariables' => $aFoundVariables,
];
}
@@ -786,8 +740,7 @@ CSS;
{
$aPattern = [];
$aReplacement = [];
foreach ($aFoundVariables as $aFoundVariable => $aFoundVariableValue)
{
foreach ($aFoundVariables as $aFoundVariable => $aFoundVariableValue) {
//XX + $key + YY
$aPattern[] = "/['\"]\s*\+\s*\\\$".$aFoundVariable."[\s\+]+\s*['\"]/";
$aReplacement[] = $aFoundVariableValue;
@@ -799,8 +752,7 @@ CSS;
$aReplacement[] = $aFoundVariableValue;
}
$sResolvedUrl = preg_replace($aPattern, $aReplacement, $sUrlTemplate);
if (strpos($sResolvedUrl, "+")!==false)
{
if (strpos($sResolvedUrl, "+") !== false) {
return false;
}
return trim($sResolvedUrl, "\"'");
@@ -814,17 +766,14 @@ CSS;
*/
private static function HasImageExtension($path)
{
foreach (static::IMAGE_EXTENSIONS as $sExt)
{
if (endsWith($path, $sExt))
{
foreach (static::IMAGE_EXTENSIONS as $sExt) {
if (endsWith($path, $sExt)) {
return true;
}
}
return false;
}
/**
* @since 3.0.0 N°2982
* Extract the signature for a generated CSS file.
@@ -843,16 +792,13 @@ CSS;
$iCount = 0;
$sPreviousLine = '';
$hFile = @fopen($sFilepath, "r");
if ($hFile !== false)
{
if ($hFile !== false) {
$sLine = '';
do
{
do {
$iCount++;
$sPreviousLine = $sLine;
$sLine = rtrim(fgets($hFile)); // Remove the trailing \n
}
while (($sLine !== false) && ($sLine != '=== SIGNATURE END ===') && ($iCount <= 100));
} while (($sLine !== false) && ($sLine != '=== SIGNATURE END ===') && ($iCount <= 100));
fclose($hFile);
}
return $sPreviousLine;
@@ -867,8 +813,7 @@ CSS;
public static function GetVarSignature($JsonSignature)
{
$aJsonArray = json_decode($JsonSignature, true);
if (array_key_exists('variables', $aJsonArray))
{
if (array_key_exists('variables', $aJsonArray)) {
return $aJsonArray['variables'];
}
return false;
@@ -892,8 +837,7 @@ CSS;
$oFindStylesheetObject->ResetLastStyleSheet();
}
foreach($aImportsPaths as $sPath)
{
foreach ($aImportsPaths as $sPath) {
$sAlterableFileURI = $sFileURI;
$sFilePath = $sPath.'/'.$sAlterableFileURI;
$sImportedFile = realpath($sFilePath);
@@ -918,8 +862,7 @@ CSS;
}
if ((file_exists($sImportedFile))
&& (!$oFindStylesheetObject->AlreadyFetched($sImportedFile)))
{
&& (!$oFindStylesheetObject->AlreadyFetched($sImportedFile))) {
if ($bImports) {
$oFindStylesheetObject->AddImport($sAlterableFileURI, $sImportedFile);
} else {
@@ -952,8 +895,7 @@ CSS;
{
$iPos = strrpos($sSubject, $sSearch);
if($iPos !== false)
{
if ($iPos !== false) {
$sSubject = substr_replace($sSubject, $sReplace, $iPos, strlen($sSearch));
}
@@ -982,19 +924,18 @@ CSS;
public static function CloneThemeParameterAndIncludeVersion($aThemeParameters, $bSetupCompilationTimestamp, $aImportsPaths)
{
$aThemeParametersVariable = [];
if (array_key_exists('variables', $aThemeParameters))
{
if (is_array($aThemeParameters['variables']))
{
$aThemeParametersVariable = array_merge([], $aThemeParameters['variables']);
if (array_key_exists('variable_imports', $aThemeParameters)) {
if (is_array($aThemeParameters['variable_imports'])) {
$aThemeParametersVariable = array_merge($aThemeParametersVariable, static::GetVariablesFromFile($aThemeParameters['variable_imports'], $aImportsPaths));
}
}
if (array_key_exists('variable_imports', $aThemeParameters))
{
if (is_array($aThemeParameters['variable_imports']))
{
$aThemeParametersVariable = array_merge($aThemeParametersVariable, static::GetVariablesFromFile($aThemeParameters['variable_imports'], $aImportsPaths));
// Variables defined in theme XML have the priority over variables defined in XML imports files
// They're defined after so they overwrite previous parameters
if (array_key_exists('variables', $aThemeParameters)) {
if (is_array($aThemeParameters['variables'])) {
$aThemeParametersVariable = array_merge($aThemeParametersVariable, $aThemeParameters['variables']);
}
}
@@ -1009,10 +950,10 @@ CSS;
* @return array
* @since 3.0.0 N°3593
*/
public static function GetVariablesFromFile($aVariableFiles, $aImportsPaths){
$aVariablesResults = [];
foreach ($aVariableFiles as $sVariableFile)
public static function GetVariablesFromFile($aVariableFiles, $aImportsPaths)
{
$aVariablesResults = [];
foreach ($aVariableFiles as $sVariableFile) {
foreach ($aImportsPaths as $sPath) {
$sFilePath = $sPath.'/'.$sVariableFile;
$sImportedFile = realpath($sFilePath);
@@ -1029,9 +970,10 @@ CSS;
}
}
}
array_map( function($sVariableValue) { return ltrim($sVariableValue); }, $aVariablesResults );
array_map(function ($sVariableValue) {
return ltrim($sVariableValue);
}, $aVariablesResults);
return $aVariablesResults;
}
}

View File

@@ -1,4 +1,5 @@
<?php
/**
* Copyright (C) 2013-2024 Combodo SAS
*
@@ -29,7 +30,8 @@ class ThemeHandlerService
{
}
public function CompileTheme($sThemeId, $bSetup = false, $sSetupCompilationTimestamp = "", $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null){
public function CompileTheme($sThemeId, $bSetup = false, $sSetupCompilationTimestamp = "", $aThemeParameters = null, $aImportsPaths = null, $sWorkingPath = null)
{
return ThemeHandler::CompileTheme($sThemeId, $bSetup, $sSetupCompilationTimestamp, $aThemeParameters, $aImportsPaths, $sWorkingPath);
}
}

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -37,13 +38,11 @@ class privUITransaction
public static function GetNewTransactionId()
{
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
if (!$bTransactionsEnabled)
{
if (!$bTransactionsEnabled) {
return 'notransactions'; // Any value will do
}
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
if (!class_exists($sClass, false))
{
if (!class_exists($sClass, false)) {
IssueLog::Error("Incorrect value '".MetaModel::GetConfig()->Get('transaction_storage')."' for 'transaction_storage', the class '$sClass' does not exists. Using privUITransactionSession instead for storing sessions.");
$sClass = 'privUITransactionSession';
}
@@ -62,13 +61,11 @@ class privUITransaction
public static function IsTransactionValid($id, $bRemoveTransaction = true)
{
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
if (!$bTransactionsEnabled)
{
if (!$bTransactionsEnabled) {
return true; // All values are valid
}
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
if (!class_exists($sClass, false))
{
if (!class_exists($sClass, false)) {
$sClass = 'privUITransactionSession';
}
@@ -83,13 +80,11 @@ class privUITransaction
public static function RemoveTransaction($id)
{
$bTransactionsEnabled = MetaModel::GetConfig()->Get('transactions_enabled');
if (!$bTransactionsEnabled)
{
if (!$bTransactionsEnabled) {
return; // Nothing to do
}
$sClass = 'privUITransaction'.MetaModel::GetConfig()->Get('transaction_storage');
if (!class_exists($sClass, false))
{
if (!class_exists($sClass, false)) {
$sClass = 'privUITransactionSession';
}
@@ -114,14 +109,13 @@ class privUITransactionSession
*/
public static function GetNewTransactionId()
{
if (!Session::IsSet('transactions'))
{
if (!Session::IsSet('transactions')) {
Session::Set('transactions', []);
}
// Strictly speaking, the two lines below should be grouped together
// by a critical section
// sem_acquire($rSemIdentified);
$id = static::GetUserPrefix() . str_replace(array('.', ' '), '', microtime());
$id = static::GetUserPrefix().str_replace(['.', ' '], '', microtime());
Session::Set(['transactions', $id], true);
// sem_release($rSemIdentified);
@@ -139,16 +133,13 @@ class privUITransactionSession
public static function IsTransactionValid($id, $bRemoveTransaction = true)
{
$bResult = false;
if (Session::IsSet('transactions'))
{
if (Session::IsSet('transactions')) {
// Strictly speaking, the eight lines below should be grouped together
// inside the same critical section as above
// sem_acquire($rSemIdentified);
if (Session::IsSet(['transactions', $id]))
{
if (Session::IsSet(['transactions', $id])) {
$bResult = true;
if ($bRemoveTransaction)
{
if ($bRemoveTransaction) {
Session::Unset(['transactions', $id]);
}
}
@@ -164,13 +155,11 @@ class privUITransactionSession
*/
public static function RemoveTransaction($id)
{
if (Session::IsSet('transactions'))
{
if (Session::IsSet('transactions')) {
// Strictly speaking, the three lines below should be grouped together
// inside the same critical section as above
// sem_acquire($rSemIdentified);
if (Session::IsSet(['transactions', $id]))
{
if (Session::IsSet(['transactions', $id])) {
Session::Unset(['transactions', $id]);
}
// sem_release($rSemIdentified);
@@ -197,7 +186,7 @@ class privUITransactionSession
class privUITransactionFile
{
/** @var int Value to use when no user logged */
const UNAUTHENTICATED_USER_ID = -666;
public const UNAUTHENTICATED_USER_ID = -666;
/**
* @return int current user id, or {@see self::UNAUTHENTICATED_USER_ID} if no user logged
@@ -228,22 +217,18 @@ class privUITransactionFile
*/
public static function GetNewTransactionId()
{
if (!is_dir(utils::GetDataPath().'transactions'))
{
if (!is_writable(APPROOT.'data'))
{
if (!is_dir(utils::GetDataPath().'transactions')) {
if (!is_writable(APPROOT.'data')) {
throw new Exception('The directory "'.APPROOT.'data" must be writable to the application.');
}
// condition avoids race condition N°2345
// See https://github.com/kalessil/phpinspectionsea/blob/master/docs/probable-bugs.md#mkdir-race-condition
if (!mkdir($concurrentDirectory = utils::GetDataPath().'transactions') && !is_dir($concurrentDirectory))
{
if (!mkdir($concurrentDirectory = utils::GetDataPath().'transactions') && !is_dir($concurrentDirectory)) {
throw new Exception('Failed to create the directory "'.utils::GetDataPath().'transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
}
}
if (!is_writable(utils::GetDataPath().'transactions'))
{
if (!is_writable(utils::GetDataPath().'transactions')) {
throw new Exception('The directory "'.utils::GetDataPath().'transactions" must be writable to the application.');
}
@@ -277,8 +262,7 @@ class privUITransactionFile
// Constraint the transaction file within utils::GetDataPath().'transactions'
$sTransactionDir = realpath(utils::GetDataPath().'transactions');
$sFilepath = utils::RealPath($sTransactionDir.'/'.$id, $sTransactionDir);
if (($sFilepath === false) || (strlen($sTransactionDir) == strlen($sFilepath)))
{
if (($sFilepath === false) || (strlen($sTransactionDir) == strlen($sFilepath))) {
return false;
}
@@ -297,15 +281,11 @@ class privUITransactionFile
return false;
}
if ($bRemoveTransaction)
{
if ($bRemoveTransaction) {
$bResult = @unlink($sFilepath);
if (!$bResult)
{
if (!$bResult) {
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
}
else
{
} else {
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
}
}
@@ -350,10 +330,8 @@ class privUITransactionFile
$iLimit = time() - 24 * 3600;
$sPattern = $sTransactionDir ? "$sTransactionDir/*" : utils::GetDataPath().'transactions/*';
$aTransactions = glob($sPattern);
foreach($aTransactions as $sFileName)
{
if (filemtime($sFileName) < $iLimit)
{
foreach ($aTransactions as $sFileName) {
if (filemtime($sFileName) < $iLimit) {
@unlink($sFileName);
self::Info('CleanupOldTransactions: Deleted transaction: '.$sFileName);
}
@@ -367,10 +345,9 @@ class privUITransactionFile
protected static function GetPendingTransactions()
{
clearstatcache();
$aResult = array();
$aResult = [];
$aTransactions = glob(utils::GetDataPath().'transactions/'.self::GetUserPrefix().'*');
foreach($aTransactions as $sFileName)
{
foreach ($aTransactions as $sFileName) {
$aResult[] = date('Y-m-d H:i:s', filemtime($sFileName)).' - '.basename($sFileName);
}
sort($aResult);
@@ -404,7 +381,8 @@ class privUITransactionFile
self::Write('Error | '.$sText);
}
protected static function IsLogEnabled() {
protected static function IsLogEnabled()
{
$oConfig = MetaModel::GetConfig();
if (is_null($oConfig)) {
return false;

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -54,8 +55,8 @@ require_once(APPROOT.'/application/displayblock.class.inc.php');
*/
class UIExtKeyWidget
{
const ENUM_OUTPUT_FORMAT_CSV = 'csv';
const ENUM_OUTPUT_FORMAT_JSON = 'json';
public const ENUM_OUTPUT_FORMAT_CSV = 'csv';
public const ENUM_OUTPUT_FORMAT_JSON = 'json';
protected $iId;
protected $sTargetClass;
@@ -87,10 +88,20 @@ class UIExtKeyWidget
* @since 2.7.7 3.0.1 3.1.0 N°3129 Add default value for $aArgs for PHP 8.0 compat
*/
public static function DisplayFromAttCode(
$oPage, $sAttCode, $sClass, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName = '', $sFormPrefix = '',
$aArgs = [], $bSearchMode = false, &$sInputType = ''
)
{
$oPage,
$sAttCode,
$sClass,
$sTitle,
$oAllowedValues,
$value,
$iInputId,
$bMandatory,
$sFieldName = '',
$sFormPrefix = '',
$aArgs = [],
$bSearchMode = false,
&$sInputType = ''
) {
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sTargetClass = $oAttDef->GetTargetClass();
$iMaxComboLength = $oAttDef->GetMaximumComboLength();
@@ -102,8 +113,7 @@ class UIExtKeyWidget
}
$oWidget = new UIExtKeyWidget($sTargetClass, $iInputId, $sAttCode, $bSearchMode);
if (!$bSearchMode) {
switch ($sDisplayStyle)
{
switch ($sDisplayStyle) {
case 'radio':
case 'radio_horizontal':
case 'radio_vertical':
@@ -114,12 +124,38 @@ class UIExtKeyWidget
case 'select':
case 'list':
default:
return $oWidget->DisplaySelect($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value,
$bMandatory, $sFieldName, $sFormPrefix, $aArgs, $sInputType);
return $oWidget->DisplaySelect(
$oPage,
$iMaxComboLength,
$bAllowTargetCreation,
$sTitle,
$oAllowedValues,
$value,
$bMandatory,
$sFieldName,
$sFormPrefix,
$aArgs,
$sInputType
);
}
} else {
return $oWidget->Display($oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId,
$bMandatory, $sFieldName, $sFormPrefix, $aArgs, null, $sDisplayStyle, true, $sInputType);
return $oWidget->Display(
$oPage,
$iMaxComboLength,
$bAllowTargetCreation,
$sTitle,
$oAllowedValues,
$value,
$iInputId,
$bMandatory,
$sFieldName,
$sFormPrefix,
$aArgs,
null,
$sDisplayStyle,
true,
$sInputType
);
}
}
@@ -158,7 +194,7 @@ class UIExtKeyWidget
* @since 3.0.0 N°2508 - Include Obsolescence icon within list and autocomplete
* @since 3.0.0 N°3750 new $sInputType parameter
*/
public function DisplaySelect(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, DBObjectset $oAllowedValues, $value, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), &$sInputType = '')
public function DisplaySelect(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, DBObjectset $oAllowedValues, $value, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = [], &$sInputType = '')
{
$sTitle = addslashes($sTitle);
$oPage->LinkScriptFromAppRoot('js/extkeywidget.js');
@@ -281,21 +317,17 @@ EOF
$oPage->add_ready_script("$('#$this->iId').one('validate', function() { $(this).trigger('change'); } );");
}
$sHTMLValue .= "<div class=\"ibo-input-select--action-buttons\">";
}
else
{
} else {
// Too many choices, use an autocomplete
// Check that the given value is allowed
$oSearch = $oAllowedValues->GetFilter();
$oSearch->AddCondition('id', $value);
$oSet = new DBObjectSet($oSearch);
if ($oSet->Count() == 0)
{
if ($oSet->Count() == 0) {
$value = null;
}
if (is_null($value) || ($value == 0)) // Null values are displayed as ''
{
if (is_null($value) || ($value == 0)) { // Null values are displayed as ''
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : '';
} else {
$sDisplayValue = $this->GetObjectName($value);
@@ -376,36 +408,30 @@ JS
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey\">";
if (is_null($oAllowedValues))
{
if (is_null($oAllowedValues)) {
throw new Exception('Implementation: null value for allowed values definition');
}
$oAllowedValues->SetShowObsoleteData(utils::ShowObsoleteData());
// We just need to compare the number of entries with MaxComboLength, so no need to get the real count.
if (!$oAllowedValues->CountExceeds($iMaxComboLength))
{
if (!$oAllowedValues->CountExceeds($iMaxComboLength)) {
// Discrete list of values, use a SELECT or RADIO buttons depending on the config
$sValidationField = null;
$bVertical = ($sDisplayStyle != 'radio_horizontal');
$bExtensions = false;
$oAllowedValues->Rewind();
$aAllowedValues = array();
while($oObj = $oAllowedValues->Fetch())
{
$aAllowedValues = [];
while ($oObj = $oAllowedValues->Fetch()) {
$aAllowedValues[$oObj->GetKey()] = $oObj->GetName();
}
$sHTMLValue .= $oPage->GetRadioButtons($aAllowedValues, $value, $this->iId, "{$sAttrFieldPrefix}{$sFieldName}", false /* $bMandatory will be placed manually */, $bVertical, $sValidationField);
$aEventsList[] = 'change';
}
else
{
} else {
$sHTMLValue .= "unable to display. Too much values";
}
$sHTMLValue .= '<div class="ibo-input-select--action-buttons">';
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false)
{
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false) {
$sHTMLValue .= "<span class=\"field_input_btn\"><div class=\"mini_button\" id=\"mini_tree_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\"><i class=\"fas fa-sitemap\"></i></div></span>";
$oPage->add_ready_script(
<<<JS
@@ -416,8 +442,7 @@ JS
JS
);
}
if ($bCreate && $bExtensions)
{
if ($bCreate && $bExtensions) {
$sCallbackName = (MetaModel::IsAbstract($this->sTargetClass)) ? 'SelectObjectClass' : 'CreateObject';
$sHTMLValue .= "<span class=\"field_input_btn\"><div class=\"mini_button\" id=\"mini_add_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\"><i class=\"fas fa-plus\"></i></div></span>";
@@ -471,7 +496,7 @@ JS
*
* @since 3.0.0 N°3750 new $sInputType parameter
*/
public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, DBObjectset $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = null, $sDisplayStyle = 'select', $bSearchMultiple = true, &$sInputType = '')
public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, DBObjectset $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = [], $bSearchMode = null, $sDisplayStyle = 'select', $bSearchMultiple = true, &$sInputType = '')
{
if (!is_null($bSearchMode)) {
$this->bSearchMode = $bSearchMode;
@@ -521,7 +546,7 @@ JS
$bVertical = ($sDisplayStyle != 'radio_horizontal');
$bExtensions = false;
$oAllowedValues->Rewind();
$aAllowedValues = array();
$aAllowedValues = [];
while ($oObj = $oAllowedValues->Fetch()) {
$aAllowedValues[$oObj->GetKey()] = $oObj->GetName();
}
@@ -574,14 +599,14 @@ EOF
$sInputType = CmdbAbstractObject::ENUM_INPUT_TYPE_DROPDOWN_RAW;
if (($this->bSearchMode) && $bSearchMultiple) {
$sInputType = CmdbAbstractObject::ENUM_INPUT_TYPE_DROPDOWN_MULTIPLE_CHOICES;
$aOptions = array(
$aOptions = [
'header' => true,
'checkAllText' => Dict::S('UI:SearchValue:CheckAll'),
'uncheckAllText' => Dict::S('UI:SearchValue:UncheckAll'),
'noneSelectedText' => Dict::S('UI:SearchValue:Any'),
'selectedText' => Dict::S('UI:SearchValue:NbSelected'),
'selectedList' => 1,
);
];
$sJSOptions = json_encode($aOptions);
$oPage->add_ready_script("$('.multiselect').multiselect($sJSOptions);");
}
@@ -606,8 +631,7 @@ EOF
$value = null;
}
if (is_null($value) || ($value == 0)) // Null values are displayed as ''
{
if (is_null($value) || ($value == 0)) { // Null values are displayed as ''
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : '';
} else {
$sDisplayValue = $this->GetObjectName($value);
@@ -673,20 +697,22 @@ JS
$oAttDef = MetaModel::GetAttributeDef(get_class($oCurrObject), $this->sAttCode);
/** @var \DBObject $oCurrObject */
$aArgs = $oCurrObject->ToArgsForQuery();
$aParams = array('query_params' => $aArgs);
$aParams = ['query_params' => $aArgs];
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aArgs);
$oFilter = $oSet->GetFilter();
} elseif (!empty($this->sFilter)) {
$aParams = array();
$aParams = [];
$oFilter = DBObjectSearch::FromOQL($this->sFilter);
} else {
$aParams = array();
$aParams = [];
$oFilter = new DBObjectSearch($this->sTargetClass);
}
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oBlock = new DisplayBlock($oFilter, 'search', false, $aParams);
$oPage->AddUiBlock($oBlock->GetDisplay($oPage, 'dtc_'.$this->iId,
array(
$oPage->AddUiBlock($oBlock->GetDisplay(
$oPage,
'dtc_'.$this->iId,
[
'menu' => false,
'currentId' => $this->iId,
'table_id' => "dr_{$this->iId}",
@@ -694,12 +720,13 @@ JS
'selection_mode' => true,
'selection_type' => 'single',
'cssCount' => '#count_'.$this->iId.'_results',
)
]
));
$sCancel = Dict::S('UI:Button:Cancel');
$sOK = Dict::S('UI:Button:Ok');
$sEmptyList = Dict::S('UI:Message:EmptyList:UseSearchForm');
$oPage->add(<<<HTML
$oPage->add(
<<<HTML
<form id="fr_{$this->iId}" OnSubmit="return oACWidget_{$this->iId}.DoOk();">
<div id="dr_{$this->iId}">
<div><p>{$sEmptyList}</p></div>
@@ -711,7 +738,8 @@ HTML
);
$sDialogTitleSanitized = addslashes(utils::HtmlToText($sTitle));
$oPage->add_ready_script(<<<JS
$oPage->add_ready_script(
<<<JS
$('#ac_dlg_{$this->iId}').dialog({
width: $(window).width()*0.8,
height: $(window).height()*0.8,
@@ -751,14 +779,12 @@ JS
*/
public function SearchObjectsToSelect(WebPage $oP, $sFilter, $sRemoteClass = '', $oObj = null)
{
if (is_null($sFilter))
{
if (is_null($sFilter)) {
throw new Exception('Implementation: null value for allowed values definition');
}
$oFilter = DBObjectSearch::FromOQL($sFilter);
if (strlen($sRemoteClass) > 0)
{
if (strlen($sRemoteClass) > 0) {
$oFilter->ChangeClass($sRemoteClass);
}
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
@@ -766,8 +792,8 @@ JS
// Current extkey value, so we can display event if it is not available anymore (eg. archived).
$iCurrentExtKeyId = (is_null($oObj)) ? 0 : $oObj->Get($this->sAttCode);
$oBlock = new DisplayBlock($oFilter, 'list_search', false, array('query_params' => array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId)));
$oBlock->Display($oP, $this->iId.'_results', array('this' => $oObj, 'cssCount'=> '#count_'.$this->iId.'_results', 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'table_id' => 'select_'.$this->sAttCode)); // Don't display the 'Actions' menu on the results
$oBlock = new DisplayBlock($oFilter, 'list_search', false, ['query_params' => ['this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId]]);
$oBlock->Display($oP, $this->iId.'_results', ['this' => $oObj, 'cssCount' => '#count_'.$this->iId.'_results', 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'table_id' => 'select_'.$this->sAttCode]); // Don't display the 'Actions' menu on the results
}
/**
@@ -799,38 +825,32 @@ JS
$oValuesSet->SetSort(false);
$oValuesSet->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oValuesSet->SetLimit($iMax);
$aValuesStartWith = $oValuesSet->GetValuesForAutocomplete(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'start_with');
$aValuesStartWith = $oValuesSet->GetValuesForAutocomplete(['this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId], $sContains, 'start_with');
asort($aValuesStartWith);
$aValues = $aValuesStartWith;
if (sizeof($aValues) < $iMax) {
$aValuesContains = $oValuesSet->GetValuesForAutocomplete(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'contains');
$aValuesContains = $oValuesSet->GetValuesForAutocomplete(['this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId], $sContains, 'contains');
asort($aValuesContains);
$iSize = sizeof($aValues);
foreach ($aValuesContains as $sKey => $sFriendlyName)
{
if (!isset($aValues[$sKey]))
{
foreach ($aValuesContains as $sKey => $sFriendlyName) {
if (!isset($aValues[$sKey])) {
$aValues[$sKey] = $sFriendlyName;
if (++$iSize >= $iMax)
{
if (++$iSize >= $iMax) {
break;
}
}
}
}
elseif (!in_array($sContains, $aValues))
{
$aValuesEquals = $oValuesSet->GetValuesForAutocomplete(array('this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId), $sContains, 'equals');
} elseif (!in_array($sContains, $aValues)) {
$aValuesEquals = $oValuesSet->GetValuesForAutocomplete(['this' => $oObj, 'current_extkey_id' => $iCurrentExtKeyId], $sContains, 'equals');
// Note: Here we cannot use array_merge as it would reindex the numeric keys starting from 0 when keys are actually the objects ID.
// As a workaround we use array_replace as it does preserve numeric keys. It's ok if some values from $aValuesEquals are replaced with values from $aValues as they contain the same data.
$aValues = array_replace($aValuesEquals, $aValues);
}
switch($sOutputFormat)
{
switch ($sOutputFormat) {
case static::ENUM_OUTPUT_FORMAT_JSON:
$aJsonMap = array();
$aJsonMap = [];
foreach ($aValues as $sKey => $aValue) {
$aElt = ['value' => $sKey, 'label' => utils::EscapeHtml($aValue['label']), 'obsolescence_flag' => $aValue['obsolescence_flag']];
if ($aValue['additional_field'] != '') {
@@ -851,8 +871,7 @@ JS
break;
case static::ENUM_OUTPUT_FORMAT_CSV:
foreach($aValues as $sKey => $aValue)
{
foreach ($aValues as $sKey => $aValue) {
$oP->add(trim($aValue['label'])."\t".$sKey."\n");
}
break;
@@ -874,7 +893,7 @@ JS
*/
public function GetObjectName($iObjId, $sFormAttCode = null)
{
$aModifierProps = array();
$aModifierProps = [];
$aModifierProps['UserRightsGetSelectFilter']['bSearchMode'] = $this->bSearchMode;
$oObj = MetaModel::GetObject($this->sTargetClass, $iObjId, false, false, $aModifierProps);
@@ -884,9 +903,7 @@ JS
} else {
return $oObj->Get($sFormAttCode);
}
}
else
{
} else {
return '';
}
}
@@ -905,17 +922,16 @@ JS
// For security reasons: check that the "proposed" class is actually a subclass of the linked class
// and that the current user is allowed to create objects of this class
$aSubClasses = MetaModel::EnumChildClasses($this->sTargetClass, ENUM_CHILD_CLASSES_ALL);
$aPossibleClasses = array();
foreach($aSubClasses as $sCandidateClass)
{
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
{
$aPossibleClasses = [];
foreach ($aSubClasses as $sCandidateClass) {
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES)) {
$aPossibleClasses[$sCandidateClass] = MetaModel::GetName($sCandidateClass);
}
}
$sClassLabel = MetaModel::GetName($this->sTargetClass);
$sDialogTitle = Dict::Format('UI:CreationTitle_Class', $sClassLabel);;
$sDialogTitle = Dict::Format('UI:CreationTitle_Class', $sClassLabel);
;
$oBlock = UIContentBlockUIBlockFactory::MakeStandard('ac_create_'.$this->iId, ['ibo-is-visible']);
$oPage->AddSubBlock($oBlock);
$oClassForm = FormUIBlockFactory::MakeStandard();
@@ -941,15 +957,13 @@ JS
$oAppContext->InitObjectFromContext($oNewObj);
$oNewObj->PrefillForm('creation_from_extkey', $aPrefillFormParam);
// 2nd set the default values from the constraint on the external key... if any
if ( ($oCurrObject != null) && ($this->sAttCode != ''))
{
if (($oCurrObject != null) && ($this->sAttCode != '')) {
$oAttDef = MetaModel::GetAttributeDef(get_class($oCurrObject), $this->sAttCode);
$aParams = array('this' => $oCurrObject);
$aParams = ['this' => $oCurrObject];
$oSet = $oAttDef->GetAllowedValuesAsObjectSet($aParams);
$aConsts = $oSet->ListConstantFields();
$sClassAlias = $oSet->GetFilter()->GetClassAlias();
if (isset($aConsts[$sClassAlias]))
{
if (isset($aConsts[$sClassAlias])) {
foreach ($aConsts[$sClassAlias] as $sAttCode => $value) {
$oNewObj->Set($sAttCode, $value);
}
@@ -962,16 +976,17 @@ JS
$sClassLabel = MetaModel::GetName($this->sTargetClass);
$sHeaderTitleEscaped = utils::EscapeHtml(Dict::Format('UI:CreationTitle_Class', $sClassLabel));
$oPage->add(<<<HTML
$oPage->add(
<<<HTML
<div id="ac_create_{$this->iId}" title="{$sHeaderTitleEscaped}">
<div id="dcr_{$this->iId}">
HTML
);
$aFormExtraParams = array(
$aFormExtraParams = [
'formPrefix' => $this->iId,
'noRelations' => true,
);
];
// Remove blob edition from creation form @see N°5863 to allow blob edition in modal context
FormHelper::DisableAttributeBlobInputs($this->sTargetClass, $aFormExtraParams);
@@ -980,14 +995,16 @@ HTML
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal(FormHelper::ENUM_MANDATORY_BLOB_MODE_CREATE));
}
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), $aFormExtraParams);
$oPage->add(<<<HTML
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, [], $aFormExtraParams);
$oPage->add(
<<<HTML
</div>
</div>
HTML
);
$oPage->add_ready_script(<<<JS
$oPage->add_ready_script(
<<<JS
$('#ac_create_{$this->iId}').dialog({ width: $(window).width() * 0.6, height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true});
$('#dcr_{$this->iId} form').removeAttr('onsubmit');
$('#dcr_{$this->iId} form').find('button[type="submit"]').on('click', oACWidget_{$this->iId}.DoCreateObject);
@@ -1003,14 +1020,13 @@ JS
$sDialogTitle = addslashes(Dict::Format('UI:HierarchyOf_Class', MetaModel::GetName($this->sTargetClass)));
$oPage->add('<div id="dlg_tree_'.$this->iId.'"><div class="wizContainer" style="vertical-align:top;"><div style="margin-bottom:5px;" id="tree_'.$this->iId.'">');
$oPage->add('<table style="width:100%"><tr><td>');
if (is_null($sFilter))
{
if (is_null($sFilter)) {
throw new Exception('Implementation: null value for allowed values definition');
}
$oFilter = DBObjectSearch::FromOQL($sFilter);
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oSet = new DBObjectSet($oFilter, array(), array('this' => $oObj, 'current_extkey_id' => $currValue));
$oSet = new DBObjectSet($oFilter, [], ['this' => $oObj, 'current_extkey_id' => $currValue]);
$oSet->SetShowObsoleteData(utils::ShowObsoleteData());
@@ -1020,8 +1036,7 @@ JS
$oPage->add('</td></tr></table>');
$oPage->add('</div>');
if ($bHasChildLeafs)
{
if ($bHasChildLeafs) {
$oPage->add('<span class="treecontrol ibo-button-group" id="treecontrolid"><a class="ibo-button ibo-is-regular ibo-is-neutral" href="?#">'.Dict::S("UI:Treeview:CollapseAll").'</a><a class="ibo-button ibo-is-regular ibo-is-neutral" href="?#">'.Dict::S("UI:Treeview:ExpandAll").'</a></span>');
}
@@ -1030,7 +1045,8 @@ JS
$sOkButtonLabel = Dict::S('UI:Button:Ok');
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$oPage->add_ready_script("\$('#tree_$this->iId ul').treeview({ control: '#treecontrolid', persist: 'false'});\n");
$oPage->add_ready_script(<<<JS
$oPage->add_ready_script(
<<<JS
$('#dlg_tree_$this->iId').dialog({
width: 'auto',
height: 'auto',
@@ -1069,8 +1085,7 @@ JS
*/
public function DoCreateObject($oPage)
{
try
{
try {
$oObj = MetaModel::NewObject($this->sTargetClass);
$aErrors = $oObj->UpdateObjectFromPostedForm($this->iId);
if (count($aErrors) == 0) {
@@ -1088,13 +1103,12 @@ JS
]);
$oObj->DBInsertNoReload();
return array('name' => $oObj->GetName(), 'id' => $oObj->GetKey());
return ['name' => $oObj->GetName(), 'id' => $oObj->GetKey()];
} else {
return array('error' => implode(' ', $aErrors), 'id' => 0);
return ['error' => implode(' ', $aErrors), 'id' => 0];
}
}
catch (Exception $e) {
return array('error' => $e->getMessage(), 'id' => 0);
} catch (Exception $e) {
return ['error' => $e->getMessage(), 'id' => 0];
}
}
@@ -1110,32 +1124,27 @@ JS
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
function DumpTree($oP, $oSet, $sParentAttCode, $currValue)
{
$aTree = array();
$aNodes = array();
while($oObj = $oSet->Fetch())
public function DumpTree($oP, $oSet, $sParentAttCode, $currValue)
{
$aTree = [];
$aNodes = [];
while ($oObj = $oSet->Fetch()) {
$iParentId = $oObj->Get($sParentAttCode);
if (!isset($aTree[$iParentId]))
{
$aTree[$iParentId] = array();
if (!isset($aTree[$iParentId])) {
$aTree[$iParentId] = [];
}
$aTree[$iParentId][$oObj->GetKey()] = $oObj->GetName();
$aNodes[$oObj->GetKey()] = $oObj;
}
$aParents = array_keys($aTree);
$aRoots = array();
foreach($aParents as $id)
{
if (!array_key_exists($id, $aNodes))
{
$aRoots = [];
foreach ($aParents as $id) {
if (!array_key_exists($id, $aNodes)) {
$aRoots[] = $id;
}
}
foreach($aRoots as $iRootId)
{
foreach ($aRoots as $iRootId) {
$this->DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue);
}
@@ -1143,28 +1152,22 @@ JS
return !$bHasOnlyRootNodes;
}
function DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue)
public function DumpNodes($oP, $iRootId, $aTree, $aNodes, $currValue)
{
$bSelect = true;
$bMultiple = false;
$sSelect = '';
if (array_key_exists($iRootId, $aTree))
{
if (array_key_exists($iRootId, $aTree)) {
$aSortedRoots = $aTree[$iRootId];
asort($aSortedRoots);
$oP->add("<ul>\n");
$fUniqueId = microtime(true);
foreach($aSortedRoots as $id => $sName)
{
if ($bSelect)
{
foreach ($aSortedRoots as $id => $sName) {
if ($bSelect) {
$sChecked = ($aNodes[$id]->GetKey() == $currValue) ? 'checked' : '';
if ($bMultiple)
{
if ($bMultiple) {
$sSelect = '<input id="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'" type="checkbox" value="'.$aNodes[$id]->GetKey().'" name="selectObject[]" '.$sChecked.'>&nbsp;';
}
else
{
} else {
$sSelect = '<input id="input_'.$fUniqueId.'_'.$aNodes[$id]->GetKey().'" type="radio" value="'.$aNodes[$id]->GetKey().'" name="selectObject" '.$sChecked.'>&nbsp;';
}
}

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -63,7 +64,7 @@ class UIHTMLEditorWidget
*
* @return string The HTML fragment to be inserted into the page
*/
public function Display(WebPage $oPage, array $aArgs = array()) : string
public function Display(WebPage $oPage, array $aArgs = []): string
{
$iId = $this->m_iId;
$sCode = $this->m_sAttCode.$this->m_sNameSuffix;

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -38,7 +39,7 @@ class UILinksWidgetDirect
$this->sAttCode = $sAttCode;
$this->sInputid = $sInputId;
$this->sNameSuffix = $sNameSuffix;
$this->aZlist = array();
$this->aZlist = [];
$this->sLinkedClass = '';
// Compute the list of attributes visible from the given objet:
@@ -47,8 +48,7 @@ class UILinksWidgetDirect
$oLinksetDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$this->sLinkedClass = $oLinksetDef->GetLinkedClass();
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
switch($oLinksetDef->GetEditMode())
{
switch ($oLinksetDef->GetEditMode()) {
case LINKSET_EDITMODE_INPLACE: // The whole linkset can be edited 'in-place'
$aZList = MetaModel::FlattenZList(MetaModel::GetZListItems($this->sLinkedClass, 'details'));
break;
@@ -57,15 +57,12 @@ class UILinksWidgetDirect
$aZList = MetaModel::FlattenZList(MetaModel::GetZListItems($this->sLinkedClass, 'list'));
array_unshift($aZList, 'friendlyname');
}
foreach($aZList as $sLinkedAttCode)
{
if ($sLinkedAttCode != $sExtKeyToMe)
{
foreach ($aZList as $sLinkedAttCode) {
if ($sLinkedAttCode != $sExtKeyToMe) {
$oAttDef = MetaModel::GetAttributeDef($this->sLinkedClass, $sLinkedAttCode);
if ((!$oAttDef->IsExternalField() || ($oAttDef->GetKeyAttCode() != $sExtKeyToMe)) &&
(!$oAttDef->IsLinkSet()) )
{
(!$oAttDef->IsLinkSet())) {
$this->aZlist[] = $sLinkedAttCode;
}
}
@@ -101,21 +98,17 @@ class UILinksWidgetDirect
$sRealClass = '';
//$oPage->add('<div class="wizContainer" style="vertical-align:top;"><div>');
$aSubClasses = MetaModel::EnumChildClasses($this->sLinkedClass, ENUM_CHILD_CLASSES_ALL); // Including the specified class itself
$aPossibleClasses = array();
foreach($aSubClasses as $sCandidateClass)
{
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
{
if ($sCandidateClass == $sProposedRealClass)
{
$aPossibleClasses = [];
foreach ($aSubClasses as $sCandidateClass) {
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES)) {
if ($sCandidateClass == $sProposedRealClass) {
$sRealClass = $sProposedRealClass;
}
$aPossibleClasses[$sCandidateClass] = MetaModel::GetName($sCandidateClass);
}
}
// Only one of the subclasses can be instantiated...
if (count($aPossibleClasses) == 1)
{
if (count($aPossibleClasses) == 1) {
$aKeys = array_keys($aPossibleClasses);
$sRealClass = $aKeys[0];
}
@@ -123,11 +116,11 @@ class UILinksWidgetDirect
if ($sRealClass != '') {
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
$aFieldsFlags = array($sExtKeyToMe => OPT_ATT_HIDDEN);
$aFieldsFlags = [$sExtKeyToMe => OPT_ATT_HIDDEN];
$oObj = DBObject::MakeDefaultInstance($sRealClass);
$aPrefillParam = array('source_obj' => $oSourceObj);
$aPrefillParam = ['source_obj' => $oSourceObj];
$oObj->PrefillForm('creation_from_editinplace', $aPrefillParam);
$aFormExtraParams = array(
$aFormExtraParams = [
'formPrefix' => $this->sInputid,
'noRelations' => true,
'fieldsFlags' => $aFieldsFlags,
@@ -140,7 +133,7 @@ class UILinksWidgetDirect
JS
,
],
);
];
// Remove blob edition from creation form @see N°5863 to allow blob edition in modal context
FormHelper::DisableAttributeBlobInputs($sRealClass, $aFormExtraParams);
@@ -149,16 +142,13 @@ JS
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal(FormHelper::ENUM_MANDATORY_BLOB_MODE_CREATE));
}
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), $aFormExtraParams);
}
else
{
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, [], $aFormExtraParams);
} else {
$sClassLabel = MetaModel::GetName($this->sLinkedClass);
$oPage->add('<p>'.Dict::Format('UI:SelectTheTypeOf_Class_ToCreate', $sClassLabel));
$oPage->add('<nobr><select name="class">');
asort($aPossibleClasses);
foreach($aPossibleClasses as $sClassName => $sClassLabel)
{
foreach ($aPossibleClasses as $sClassName => $sClassLabel) {
$oPage->add("<option value=\"$sClassName\">$sClassLabel</option>");
}
$oPage->add('</select>');
@@ -178,7 +168,7 @@ JS
* @throws \MissingQueryArgument
* @throws \OQLException
*/
public function GetObjectsSelectionDlg($oPage, $oCurrentObj, $aAlreadyLinked, $aPrefillFormParam = array())
public function GetObjectsSelectionDlg($oPage, $oCurrentObj, $aAlreadyLinked, $aPrefillFormParam = [])
{
//$oPage->add("<div class=\"wizContainer\" style=\"vertical-align:top;\">\n");
@@ -199,8 +189,7 @@ JS
$oLinkSetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$valuesDef = $oLinkSetDef->GetValuesDef();
if ($valuesDef === null)
{
if ($valuesDef === null) {
$oFilter = new DBObjectSearch($this->sLinkedClass);
} else {
if (!$valuesDef instanceof ValueSetObjects) {
@@ -218,8 +207,10 @@ JS
$oCurrentObj->PrefillForm('search', $aPrefillFormParam);
}
$oBlock = new DisplayBlock($oFilter, 'search', false);
$oPage->AddUiBlock($oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->sInputid}",
array(
$oPage->AddUiBlock($oBlock->GetDisplay(
$oPage,
"SearchFormToAdd_{$this->sInputid}",
[
'result_list_outer_selector' => "SearchResultsToAdd_{$this->sInputid}",
'table_id' => "add_{$this->sInputid}",
'table_inner_id' => "ResultsToAdd_{$this->sInputid}",
@@ -227,16 +218,17 @@ JS
'cssCount' => "#count_{$this->sInputid}",
'query_params' => $oFilter->GetInternalParams(),
'hidden_criteria' => $sHiddenCriteria,
)
]
));
$sEmptyList = Dict::S('UI:Message:EmptyList:UseSearchForm');
$sCancel = Dict::S('UI:Button:Cancel');
$sAdd = Dict::S('UI:Button:Add');
$oPage->add(<<<HTML
$oPage->add(
<<<HTML
<form id="ObjectsAddForm_{$this->sInputid}">
<div id="SearchResultsToAdd_{$this->sInputid}">
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
<div style="border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
</div>
<input type="hidden" id="count_{$this->sInputid}" value="0"/>
</form>
@@ -256,38 +248,30 @@ HTML
* @throws \CoreException
* @throws \OQLException
*/
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinked = array(), $oCurrentObj = null, $aPrefillFormParam = array())
{
if ($sRemoteClass == '')
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinked = [], $oCurrentObj = null, $aPrefillFormParam = [])
{
if ($sRemoteClass == '') {
$sRemoteClass = $this->sLinkedClass;
}
$oLinkSetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$valuesDef = $oLinkSetDef->GetValuesDef();
if ($valuesDef === null)
{
if ($valuesDef === null) {
$oFilter = new DBObjectSearch($sRemoteClass);
}
else
{
if (!$valuesDef instanceof ValueSetObjects)
{
} else {
if (!$valuesDef instanceof ValueSetObjects) {
throw new Exception('Error: only ValueSetObjects are supported for "allowed_values" in AttributeLinkedSet ('.$this->sClass.'/'.$this->sAttCode.').');
}
$oFilter = DBObjectSearch::FromOQL($valuesDef->GetFilterExpression());
}
if (($oCurrentObj != null) && MetaModel::IsSameFamilyBranch($sRemoteClass, $this->sClass))
{
if (($oCurrentObj != null) && MetaModel::IsSameFamilyBranch($sRemoteClass, $this->sClass)) {
// Prevent linking to self if the linked object is of the same family
// and already present in the database
if (!$oCurrentObj->IsNew())
{
if (!$oCurrentObj->IsNew()) {
$oFilter->AddCondition('id', $oCurrentObj->GetKey(), '!=');
}
}
if ($oCurrentObj != null)
{
if ($oCurrentObj != null) {
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
$aArgs = array_merge($oCurrentObj->ToArgs('this'), $oFilter->GetInternalParams());
@@ -296,12 +280,11 @@ HTML
$aPrefillFormParam['filter'] = $oFilter;
$oCurrentObj->PrefillForm('search', $aPrefillFormParam);
}
if (count($aAlreadyLinked) > 0)
{
if (count($aAlreadyLinked) > 0) {
$oFilter->AddCondition('id', $aAlreadyLinked, 'NOTIN');
}
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oP, "ResultsToAdd_{$this->sInputid}", array('menu' => false, 'cssCount'=> '#count_'.$this->sInputid , 'selection_mode' => true, 'table_id' => 'add_'.$this->sInputid)); // Don't display the 'Actions' menu on the results
$oBlock->Display($oP, "ResultsToAdd_{$this->sInputid}", ['menu' => false, 'cssCount' => '#count_'.$this->sInputid , 'selection_mode' => true, 'table_id' => 'add_'.$this->sInputid]); // Don't display the 'Actions' menu on the results
}
/**
@@ -311,8 +294,7 @@ HTML
public function DoAddObjects(WebPage $oP, $oFullSetFilter)
{
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
foreach($aLinkedObjectIds as $iObjectId)
{
foreach ($aLinkedObjectIds as $iObjectId) {
$oLinkObj = MetaModel::GetObject($this->sLinkedClass, $iObjectId);
$oP->add($this->GetObjectRow($oP, $oLinkObj, $oLinkObj->GetKey()));
}
@@ -325,15 +307,15 @@ HTML
public function GetTableConfig()
{
$aAttribs = array();
$aAttribs['form::select'] = array(
$aAttribs = [];
$aAttribs['form::select'] = [
'label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->sInputid}:not(:disabled)', this.checked);oWidget".$this->sInputid.".directlinks('instance')._onSelectChange();\" class=\"checkAll\"></input>",
'description' => Dict::S('UI:SelectAllToggle+'),
);
];
foreach ($this->aZlist as $sLinkedAttCode) {
$oAttDef = MetaModel::GetAttributeDef($this->sLinkedClass, $sLinkedAttCode);
$aAttribs[$sLinkedAttCode] = array('label' => MetaModel::GetLabel($this->sLinkedClass, $sLinkedAttCode), 'description' => $oAttDef->GetOrderByHint());
$aAttribs[$sLinkedAttCode] = ['label' => MetaModel::GetLabel($this->sLinkedClass, $sLinkedAttCode), 'description' => $oAttDef->GetOrderByHint()];
}
return $aAttribs;
@@ -348,8 +330,7 @@ HTML
*/
public function GetRow($oPage, $sRealClass, $aValues, $iTempId)
{
if ($sRealClass == '')
{
if ($sRealClass == '') {
$sRealClass = $this->sLinkedClass;
}
$oLinkObj = new $sRealClass();
@@ -367,10 +348,9 @@ HTML
protected function GetObjectRow($oPage, $oLinkObj, $iTempId)
{
$aAttribs = $this->GetTableConfig();
$aRow = array();
$aRow = [];
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.($iTempId).'"/>';
foreach($this->aZlist as $sLinkedAttCode)
{
foreach ($this->aZlist as $sLinkedAttCode) {
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
}
return $oPage->GetTableRow($aRow, $aAttribs);
@@ -386,18 +366,16 @@ HTML
*/
public function GetFormRow($oPage, $sRealClass, $aValues, $iTempId)
{
if ($sRealClass == '')
{
if ($sRealClass == '') {
$sRealClass = $this->sLinkedClass;
}
$oLinkObj = new $sRealClass();
$oLinkObj->UpdateObjectFromPostedForm($this->sInputid);
$aAttribs = $this->GetTableConfig();
$aRow = array();
$aRow = [];
$aRow[] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.($iTempId).'"/>';
foreach($this->aZlist as $sLinkedAttCode)
{
foreach ($this->aZlist as $sLinkedAttCode) {
$aRow[] = $oLinkObj->GetAsHTML($sLinkedAttCode);
}
return $aRow;
@@ -413,26 +391,22 @@ HTML
$oAppContext = new ApplicationContext();
$sSrcClass = get_class($oSourceObj);
$sDestClass = $oSearch->GetClass();
foreach($oAppContext->GetNames() as $key)
{
foreach ($oAppContext->GetNames() as $key) {
// Find the value of the object corresponding to each 'context' parameter
$aCallSpec = array($sSrcClass, 'MapContextParam');
$aCallSpec = [$sSrcClass, 'MapContextParam'];
$sAttCode = '';
if (is_callable($aCallSpec))
{
if (is_callable($aCallSpec)) {
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
}
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode))
{
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode)) {
$defaultValue = $oSourceObj->Get($sAttCode);
// Find the attcode for the same 'context' parameter in the destination class
// and sets its value as the default value for the search condition
$aCallSpec = array($sDestClass, 'MapContextParam');
$aCallSpec = [$sDestClass, 'MapContextParam'];
$sAttCode = '';
if (is_callable($aCallSpec))
{
if (is_callable($aCallSpec)) {
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
}
@@ -443,7 +417,6 @@ HTML
}
}
public function GetClass(): string
{
return $this->sClass;

View File

@@ -1,4 +1,5 @@
<?php
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
@@ -54,7 +55,7 @@ class UILinksWidget
$this->m_sNameSuffix = $sNameSuffix;
$this->m_bDuplicatesAllowed = $bDuplicatesAllowed;
$this->m_aEditableFields = array();
$this->m_aEditableFields = [];
/** @var AttributeLinkedSetIndirect $oAttDef */
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode);
@@ -67,34 +68,33 @@ class UILinksWidget
$oLinkingAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $this->m_sExtKeyToRemote);
$this->m_sRemoteClass = $oLinkingAttDef->GetTargetClass();
$this->m_aEditableFields = array();
$this->m_aTableConfig = array();
$this->m_aTableConfig['form::checkbox'] = array(
$this->m_aEditableFields = [];
$this->m_aTableConfig = [];
$this->m_aTableConfig['form::checkbox'] = [
'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onClick=\"CheckAll('#linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget".$this->m_sInputId.".OnSelectChange();\">",
'description' => Dict::S('UI:SelectAllToggle+'),
);
];
$aLnkAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectLinkClass($sClass, $sAttCode);
foreach ($aLnkAttDefsToDisplay as $oLnkAttDef)
{
foreach ($aLnkAttDefsToDisplay as $oLnkAttDef) {
$sLnkAttCode = $oLnkAttDef->GetCode();
$this->m_aEditableFields[] = $sLnkAttCode;
$this->m_aTableConfig[$sLnkAttCode] = array('label' => $oLnkAttDef->GetLabel(), 'description' => $oLnkAttDef->GetDescription());
$this->m_aTableConfig[$sLnkAttCode] = ['label' => $oLnkAttDef->GetLabel(), 'description' => $oLnkAttDef->GetDescription()];
}
$this->m_aTableConfig['static::key'] = array(
$this->m_aTableConfig['static::key'] = [
'label' => MetaModel::GetName($this->m_sRemoteClass),
'description' => MetaModel::GetClassDescription($this->m_sRemoteClass),
);
];
$this->m_aEditableFields[] = $this->m_sExtKeyToRemote;
$aRemoteAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectRemoteClass($this->m_sRemoteClass);
foreach ($aRemoteAttDefsToDisplay as $oRemoteAttDef) {
$sRemoteAttCode = $oRemoteAttDef->GetCode();
$this->m_aTableConfig['static::'.$sRemoteAttCode] = array(
$this->m_aTableConfig['static::'.$sRemoteAttCode] = [
'label' => $oRemoteAttDef->GetLabel(),
'description' => $oRemoteAttDef->GetDescription(),
);
];
}
}
@@ -105,7 +105,6 @@ class UILinksWidget
return ($bSafe) ? utils::GetSafeId($sFieldId) : $sFieldId;
}
/**
* Display the table with the form for editing all the links at once
*
@@ -119,7 +118,6 @@ class UILinksWidget
return DataTableUIBlockFactory::MakeForForm("{$this->m_sAttCode}{$this->m_sNameSuffix}", $aConfig, $aData);
}
/**
* Get the HTML fragment corresponding to the linkset editing widget
*
@@ -157,7 +155,7 @@ class UILinksWidget
* @throws DictExceptionMissingString
* @throws Exception
*/
public function GetObjectPickerDialog($oPage, $oCurrentObj, $sJson, $aAlreadyLinkedIds = array(), $aPrefillFormParam = array())
public function GetObjectPickerDialog($oPage, $oCurrentObj, $sJson, $aAlreadyLinkedIds = [], $aPrefillFormParam = [])
{
$oAlreadyLinkedFilter = new DBObjectSearch($this->m_sRemoteClass);
if (!$this->m_bDuplicatesAllowed && count($aAlreadyLinkedIds) > 0) {
@@ -183,7 +181,9 @@ class UILinksWidget
$sLinkedSetId = $oBlock->oUILinksWidget->GetLinkedSetId();
$oDisplayBlock = new DisplayBlock($oFilter, 'search', false);
$oBlock->AddSubBlock($oDisplayBlock->GetDisplay($oPage, "SearchFormToAdd_{$sLinkedSetId}",
$oBlock->AddSubBlock($oDisplayBlock->GetDisplay(
$oPage,
"SearchFormToAdd_{$sLinkedSetId}",
[
'menu' => false,
'result_list_outer_selector' => "SearchResultsToAdd_{$sLinkedSetId}",
@@ -195,7 +195,8 @@ class UILinksWidget
'query_params' => $oFilter->GetInternalParams(),
'hidden_criteria' => $sAlreadyLinkedExpression,
'submit_on_load' => false,
]));
]
));
$oBlock->AddForm();
}
@@ -212,25 +213,21 @@ class UILinksWidget
* @throws \CoreException
* @throws \Exception
*/
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinkedIds = array(), $oCurrentObj = null)
{
if ($sRemoteClass != '')
public function SearchObjectsToAdd(WebPage $oP, $sRemoteClass = '', $aAlreadyLinkedIds = [], $oCurrentObj = null)
{
if ($sRemoteClass != '') {
// assert(MetaModel::IsParentClass($this->m_sRemoteClass, $sRemoteClass));
$oFilter = new DBObjectSearch($sRemoteClass);
}
else
{
} else {
// No remote class specified use the one defined in the linkedset
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
}
if (!$this->m_bDuplicatesAllowed && count($aAlreadyLinkedIds) > 0)
{
if (!$this->m_bDuplicatesAllowed && count($aAlreadyLinkedIds) > 0) {
$oFilter->AddCondition('id', $aAlreadyLinkedIds, 'NOTIN');
}
$this->SetSearchDefaultFromContext($oCurrentObj, $oFilter);
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oP, "ResultsToAdd_{$this->m_sAttCode}", array('menu' => false, 'cssCount'=> '#count_'.$this->m_sAttCode.$this->m_sNameSuffix , 'selection_mode' => true, 'table_id' => 'add_'.$this->m_sAttCode)); // Don't display the 'Actions' menu on the results
$oBlock->Display($oP, "ResultsToAdd_{$this->m_sAttCode}", ['menu' => false, 'cssCount' => '#count_'.$this->m_sAttCode.$this->m_sNameSuffix , 'selection_mode' => true, 'table_id' => 'add_'.$this->m_sAttCode]); // Don't display the 'Actions' menu on the results
}
/**
@@ -251,7 +248,7 @@ class UILinksWidget
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId, false);
if (is_object($oLinkedObj)) {
$oBlock = new BlockIndirectLinkSetEditTable($this);
$aRow = $oBlock->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
$aRow = $oBlock->GetFormRow($oP, $oLinkedObj, $iObjectId, [], $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
$oRow = new FormTableRow("{$this->m_sAttCode}{$this->m_sNameSuffix}", $this->m_aTableConfig, $aRow, -$iAdditionId);
$oP->AddUiBlock($oRow);
$iAdditionId++;
@@ -280,7 +277,7 @@ class UILinksWidget
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId, false);
if (is_object($oLinkedObj)) {
$oBlock = new BlockIndirectLinkSetEditTable($this);
$aRow = $oBlock->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId, false /* Default value */, $bAllowRemoteExtKeyEdit); // Not yet created link get negative Ids
$aRow = $oBlock->GetFormRow($oP, $oLinkedObj, $iObjectId, [], $oCurrentObj, $iAdditionId, false /* Default value */, $bAllowRemoteExtKeyEdit); // Not yet created link get negative Ids
$aData = [];
foreach ($aRow as $item) {
$aData[] = $item;
@@ -307,37 +304,30 @@ class UILinksWidget
$oAppContext = new ApplicationContext();
$sSrcClass = get_class($oSourceObj);
$sDestClass = $oSearch->GetClass();
foreach($oAppContext->GetNames() as $key)
{
foreach ($oAppContext->GetNames() as $key) {
// Find the value of the object corresponding to each 'context' parameter
$aCallSpec = array($sSrcClass, 'MapContextParam');
$aCallSpec = [$sSrcClass, 'MapContextParam'];
$sAttCode = '';
if (is_callable($aCallSpec))
{
if (is_callable($aCallSpec)) {
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
}
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode))
{
if (MetaModel::IsValidAttCode($sSrcClass, $sAttCode)) {
$defaultValue = $oSourceObj->Get($sAttCode);
// Find the attcode for the same 'context' parameter in the destination class
// and sets its value as the default value for the search condition
$aCallSpec = array($sDestClass, 'MapContextParam');
$aCallSpec = [$sDestClass, 'MapContextParam'];
$sAttCode = '';
if (is_callable($aCallSpec))
{
if (is_callable($aCallSpec)) {
$sAttCode = call_user_func($aCallSpec, $key); // Returns null when there is no mapping for this parameter
}
if (MetaModel::IsValidAttCode($sDestClass, $sAttCode) && !empty($defaultValue))
{
if (MetaModel::IsValidAttCode($sDestClass, $sAttCode) && !empty($defaultValue)) {
// Add Hierarchical condition if hierarchical key
$oAttDef = MetaModel::GetAttributeDef($sDestClass, $sAttCode);
if (isset($oAttDef) && ($oAttDef->IsExternalKey()))
{
try
{
if (isset($oAttDef) && ($oAttDef->IsExternalKey())) {
try {
/** @var AttributeExternalKey $oAttDef */
$sTargetClass = $oAttDef->GetTargetClass();
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sTargetClass);
@@ -348,8 +338,7 @@ class UILinksWidget
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
}
}
catch (Exception $e) {
} catch (Exception $e) {
}
} else {
$oSearch->AddCondition($sAttCode, $defaultValue);

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -49,7 +50,7 @@ class UIPasswordWidget
* @param Hash $aArgs Extra context arguments
* @return string The HTML fragment to be inserted into the page
*/
public function Display(WebPage $oPage, $aArgs = array())
public function Display(WebPage $oPage, $aArgs = [])
{
$oPage->add_dict_entry('UI:Component:Input:Password:DoesNotMatch');
@@ -94,4 +95,3 @@ class UIPasswordWidget
return $sHtmlValue;
}
}
?>

View File

@@ -1,4 +1,5 @@
<?php
/**
*
* Copyright (C) 2010-2024 Combodo SAS
@@ -20,13 +21,15 @@
*
*/
use Combodo\iTop\Application\WebPage\WebPage;
require_once(APPROOT.'/application/displayblock.class.inc.php');
class UISearchFormForeignKeys
{
private $m_sRemoteClass;
private $m_iInputId;
public function __construct($sTargetClass, $iInputId = null)
{
$this->m_sRemoteClass = $sTargetClass;
@@ -40,14 +43,16 @@ class UISearchFormForeignKeys
*
* @throws \Exception
*/
public function ShowModalSearchForeignKeys($oPage, $sTitle)
public function ShowModalSearchForeignKeys($oPage)
{
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
$oBlock = new DisplayBlock($oFilter, 'search', false);
$oPage->AddUiBlock($oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->m_iInputId}",
array(
$oPage->AddUiBlock($oBlock->GetDisplay(
$oPage,
"SearchFormToAdd_{$this->m_iInputId}",
[
'menu' => false,
'result_list_outer_selector' => "SearchResultsToAdd_{$this->m_iInputId}",
'table_id' => "add_{$this->m_iInputId}",
@@ -55,94 +60,31 @@ class UISearchFormForeignKeys
'selection_mode' => true,
'cssCount' => "#count_{$this->m_iInputId}",
'query_params' => $oFilter->GetInternalParams(),
)));
]
));
$sEmptyList = Dict::S('UI:Message:EmptyList:UseSearchForm');
$sCancel = Dict::S('UI:Button:Cancel');
$sAdd = Dict::S('UI:Button:Add');
$oPage->add(<<<HTML
$oPage->add(
<<<HTML
<form id="ObjectsAddForm_{$this->m_iInputId}">
<div id="SearchResultsToAdd_{$this->m_iInputId}" style="vertical-align:top;height:100%;overflow:auto;padding:0;border:0;">
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
<div style="border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
</div>
<input type="hidden" id="count_{$this->m_iInputId}" value="0"/>
</form>
HTML
);
$oPage->add_ready_script(
<<<JS
$('#dlg_{$this->m_iInputId}').dialog({
width: $(window).width()*0.8,
height: $(window).height()*0.8,
autoOpen: false,
modal: true,
resizeStop: oForeignKeysWidget{$this->m_iInputId}.UpdateSizes,
buttons: [
{
text: Dict.S('UI:Button:Cancel'),
class: "cancel ibo-is-alternative ibo-is-neutral",
click: function() {
$('#dlg_{$this->m_iInputId}').dialog('close');
}
},
{
text: Dict.S('UI:Button:Add'),
id: 'btn_ok_{$this->m_iInputId}',
class: "ok ibo-is-regular ibo-is-primary",
click: function() {
oForeignKeysWidget{$this->m_iInputId}.DoAddObjects(this.id);
}
},
],
});
$('#dlg_{$this->m_iInputId}').dialog('option', {title:'$sTitle'});
$('#SearchFormToAdd_{$this->m_iInputId} form').on('submit.uilinksWizard', oForeignKeysWidget{$this->m_iInputId}.SearchObjectsToAdd);
$('#SearchFormToAdd_{$this->m_iInputId}').on('resize', oForeignKeysWidget{$this->m_iInputId}.UpdateSizes);
JS
);
}
public function GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter)
{
try
{
try {
$aLinkedObjects = utils::ReadMultipleSelectionWithFriendlyname($oFullSetFilter);
$oPage->add(json_encode($aLinkedObjects));
}
catch (CoreException $e)
{
} catch (CoreException $e) {
http_response_code(500);
$oPage->add(json_encode(array('error' => $e->GetMessage())));
$oPage->add(json_encode(['error' => $e->GetMessage()]));
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
}
}
/**
* Search for objects to be linked to the current object (i.e "remote" objects)
*
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of m_sRemoteClass
*
* @throws \Exception
*/
public function ListResultsSearchForeignKeys(WebPage $oP, $sRemoteClass = '')
{
if ($sRemoteClass != '')
{
// assert(MetaModel::IsParentClass($this->m_sRemoteClass, $sRemoteClass));
$oFilter = new DBObjectSearch($sRemoteClass);
}
else
{
// No remote class specified use the one defined in the linkedset
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
}
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oP, "ResultsToAdd_{$this->m_iInputId}",
array('menu' => false, 'cssCount' => "#count_{$this->m_iInputId}", 'selection_mode' => true, 'table_id' => "add_{$this->m_iInputId}"));
}
}

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -17,7 +18,6 @@
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Application\WebPage\iTopWebPage;
/**
* Class UIWizard
*
@@ -36,51 +36,53 @@ class UIWizard
{
$this->m_oPage = $oPage;
$this->m_sClass = $sClass;
if (empty($sTargetState))
{
if (empty($sTargetState)) {
$sTargetState = MetaModel::GetDefaultState($sClass);
}
$this->m_sTargetState = $sTargetState;
$this->m_aWizardSteps = $this->ComputeWizardStructure();
}
public function GetObjectClass() { return $this->m_sClass; }
public function GetTargetState() { return $this->m_sTargetState; }
public function GetWizardStructure() { return $this->m_aWizardSteps; }
public function GetObjectClass()
{
return $this->m_sClass;
}
public function GetTargetState()
{
return $this->m_sTargetState;
}
public function GetWizardStructure()
{
return $this->m_aWizardSteps;
}
/**
* Displays one step of the wizard
*/
public function DisplayWizardStep($aStep, $iStepIndex, &$iMaxInputId, &$aFieldsMap, $bFinishEnabled = false, $aArgs = array())
{
if ($iStepIndex == 1) // one big form that contains everything, to make sure that the uploaded files are posted too
public function DisplayWizardStep($aStep, $iStepIndex, &$iMaxInputId, &$aFieldsMap, $bFinishEnabled = false, $aArgs = [])
{
if ($iStepIndex == 1) { // one big form that contains everything, to make sure that the uploaded files are posted too
$this->m_oPage->add("<form method=\"post\" enctype=\"multipart/form-data\" action=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php\">\n");
}
$this->m_oPage->add("<div class=\"wizContainer\" id=\"wizStep$iStepIndex\" style=\"display:none;\">\n");
$this->m_oPage->add("<a name=\"step$iStepIndex\" />\n");
$aStates = MetaModel::EnumStates($this->m_sClass);
$aDetails = array();
$aDetails = [];
$sJSHandlerCode = ''; // Javascript code to be executed each time this step of the wizard is entered
foreach($aStep as $sAttCode)
{
foreach ($aStep as $sAttCode) {
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
if ($oAttDef->IsWritable())
{
if ($oAttDef->IsWritable()) {
$sAttLabel = $oAttDef->GetLabel();
$iOptions = isset($aStates[$this->m_sTargetState]['attribute_list'][$sAttCode]) ? $aStates[$this->m_sTargetState]['attribute_list'][$sAttCode] : 0;
$aPrerequisites = $oAttDef->GetPrerequisiteAttributes();
if ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT))
{
$aFields[$sAttCode] = array();
foreach($aPrerequisites as $sCode)
{
if ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) {
$aFields[$sAttCode] = [];
foreach ($aPrerequisites as $sCode) {
$aFields[$sAttCode][$sCode] = '';
}
}
if (count($aPrerequisites) > 0)
{
if (count($aPrerequisites) > 0) {
$aOptions[] = 'Prerequisites: '.implode(', ', $aPrerequisites);
}
@@ -88,17 +90,14 @@ class UIWizard
$oDefaultValuesSet = $oAttDef->GetDefaultValue(/* $oObject->ToArgs() */); // @@@ TO DO: get the object's current value if the object exists
$sHTMLValue = cmdbAbstractObject::GetFormElementForField($this->m_oPage, $this->m_sClass, $sAttCode, $oAttDef, $oDefaultValuesSet, '', "att_$iMaxInputId", '', $iOptions, $aArgs);
$aFieldsMap["att_$iMaxInputId"] = $sAttCode;
$aDetails[] = array('label' => '<span title="'.$oAttDef->GetDescription().'">'.$oAttDef->GetLabel().$sFieldFlag.'</span>', 'value' => "<span id=\"field_att_$iMaxInputId\">$sHTMLValue</span>");
if ($oAttDef->GetValuesDef() != null)
{
$aDetails[] = ['label' => '<span title="'.$oAttDef->GetDescription().'">'.$oAttDef->GetLabel().$sFieldFlag.'</span>', 'value' => "<span id=\"field_att_$iMaxInputId\">$sHTMLValue</span>"];
if ($oAttDef->GetValuesDef() != null) {
$sJSHandlerCode .= "\toWizardHelper.RequestAllowedValues('$sAttCode');\n";
}
if ($oAttDef->GetDefaultValue() != null)
{
if ($oAttDef->GetDefaultValue() != null) {
$sJSHandlerCode .= "\toWizardHelper.RequestDefaultValue('$sAttCode');\n";
}
if ($oAttDef->IsLinkSet())
{
if ($oAttDef->IsLinkSet()) {
$sJSHandlerCode .= "\toLinkWidgetatt_$iMaxInputId.Init();";
}
$iMaxInputId++;
@@ -141,8 +140,7 @@ $sJSHandlerCode
$this->m_oPage->add("<input type=\"hidden\" name=\"transaction_id\" value=\"".utils::GetNewTransactionId()."\" />\n");
$this->m_oPage->add("<input type=\"hidden\" id=\"wizard_json_obj\" name=\"json_obj\" value=\"\" />\n");
$sScript = "function OnEnterStep$iStepIndex() {\n";
foreach($aFieldsMap as $iInputId => $sAttCode)
{
foreach ($aFieldsMap as $iInputId => $sAttCode) {
$sScript .= "\toWizardHelper.UpdateCurrentValue('$sAttCode');\n";
}
$sScript .= "\toWizardHelper.Preview('object_preview');\n";
@@ -166,39 +164,32 @@ $sJSHandlerCode
*/
protected function ComputeWizardStructure()
{
$aWizardSteps = array( 'mandatory' => array(), 'optional' => array());
$aFieldsDone = array(); // Store all the fields that are already covered by a previous step of the wizard
$aWizardSteps = [ 'mandatory' => [], 'optional' => []];
$aFieldsDone = []; // Store all the fields that are already covered by a previous step of the wizard
$aStates = MetaModel::EnumStates($this->m_sClass);
$sStateAttCode = MetaModel::GetStateAttributeCode($this->m_sClass);
$aMandatoryAttributes = array();
$aMandatoryAttributes = [];
// Some attributes are always mandatory independently of the state machine (if any)
foreach(MetaModel::GetAttributesList($this->m_sClass) as $sAttCode)
{
foreach (MetaModel::GetAttributesList($this->m_sClass) as $sAttCode) {
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
if (!$oAttDef->IsExternalField() && !$oAttDef->IsNullAllowed() &&
$oAttDef->IsWritable() && ($sAttCode != $sStateAttCode) )
{
$oAttDef->IsWritable() && ($sAttCode != $sStateAttCode)) {
$aMandatoryAttributes[$sAttCode] = OPT_ATT_MANDATORY;
}
}
// Now check the attributes that are mandatory in the specified state
if ( (!empty($this->m_sTargetState)) && (count($aStates[$this->m_sTargetState]['attribute_list']) > 0) )
{
if ((!empty($this->m_sTargetState)) && (count($aStates[$this->m_sTargetState]['attribute_list']) > 0)) {
// Check all the fields that *must* be included in the wizard for this
// particular target state
$aFields = array();
foreach($aStates[$this->m_sTargetState]['attribute_list'] as $sAttCode => $iOptions)
{
$aFields = [];
foreach ($aStates[$this->m_sTargetState]['attribute_list'] as $sAttCode => $iOptions) {
if ((isset($aMandatoryAttributes[$sAttCode])) &&
($aMandatoryAttributes[$sAttCode] & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) )
{
($aMandatoryAttributes[$sAttCode] & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT))) {
$aMandatoryAttributes[$sAttCode] |= $iOptions;
}
else
{
} else {
$aMandatoryAttributes[$sAttCode] = $iOptions;
}
}
@@ -209,17 +200,14 @@ $sJSHandlerCode
// not also read-only or hidden.
// Some fields may be required (null not allowed) from the database
// perspective, but hidden or read-only from the user interface perspective
$aFields = array();
foreach($aMandatoryAttributes as $sAttCode => $iOptions)
{
$aFields = [];
foreach ($aMandatoryAttributes as $sAttCode => $iOptions) {
if (($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) &&
!($iOptions & (OPT_ATT_READONLY | OPT_ATT_HIDDEN)) )
{
!($iOptions & (OPT_ATT_READONLY | OPT_ATT_HIDDEN))) {
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
$aPrerequisites = $oAttDef->GetPrerequisiteAttributes();
$aFields[$sAttCode] = array();
foreach($aPrerequisites as $sCode)
{
$aFields[$sAttCode] = [];
foreach ($aPrerequisites as $sCode) {
$aFields[$sAttCode][$sCode] = '';
}
}
@@ -229,48 +217,39 @@ $sJSHandlerCode
// Start from the order of the 'details'
$aList = MetaModel::FlattenZlist(MetaModel::GetZListItems($this->m_sClass, 'details'));
$index = 0;
$aOrder = array();
foreach($aFields as $sAttCode => $void)
{
$aOrder = [];
foreach ($aFields as $sAttCode => $void) {
$aOrder[$sAttCode] = 999; // At the end of the list...
}
foreach($aList as $sAttCode)
{
if (array_key_exists($sAttCode, $aFields))
{
foreach ($aList as $sAttCode) {
if (array_key_exists($sAttCode, $aFields)) {
$aOrder[$sAttCode] = $index;
}
$index++;
}
foreach($aFields as $sAttCode => $aDependencies)
{
foreach ($aFields as $sAttCode => $aDependencies) {
// All fields with no remaining dependencies can be entered at this
// step of the wizard
if (count($aDependencies) > 0)
{
if (count($aDependencies) > 0) {
$iMaxPos = 0;
// Remove this field from the dependencies of the other fields
foreach($aDependencies as $sDependentAttCode => $void)
{
foreach ($aDependencies as $sDependentAttCode => $void) {
// position the current field after the ones it depends on
$iMaxPos = max($iMaxPos, 1 + $aOrder[$sDependentAttCode]);
}
}
}
asort($aOrder);
$aCurrentStep = array();
foreach($aOrder as $sAttCode => $rank)
{
$aCurrentStep = [];
foreach ($aOrder as $sAttCode => $rank) {
$aCurrentStep[] = $sAttCode;
$aFieldsDone[$sAttCode] = '';
}
$aWizardSteps['mandatory'][] = $aCurrentStep;
// Now computes the steps to fill the optional fields
$aFields = array(); // reset
foreach(MetaModel::ListAttributeDefs($this->m_sClass) as $sAttCode=>$oAttDef)
{
$aFields = []; // reset
foreach (MetaModel::ListAttributeDefs($this->m_sClass) as $sAttCode => $oAttDef) {
$iOptions = (isset($aStates[$this->m_sTargetState]['attribute_list'][$sAttCode])) ? $aStates[$this->m_sTargetState]['attribute_list'][$sAttCode] : 0;
if (($sStateAttCode != $sAttCode) &&
(!$oAttDef->IsExternalField()) &&
@@ -282,7 +261,7 @@ $sJSHandlerCode
// are removed from the 'optional' part of the wizard
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
$aPrerequisites = $oAttDef->GetPrerequisiteAttributes();
$aFields[$sAttCode] = array();
$aFields[$sAttCode] = [];
foreach ($aPrerequisites as $sCode) {
if (!isset($aFieldsDone[$sCode])) {
// retain only the dependencies that were not covered
@@ -293,28 +272,23 @@ $sJSHandlerCode
}
}
// Now use the dependencies between the fields to order them
while(count($aFields) > 0)
{
$aCurrentStep = array();
foreach($aFields as $sAttCode => $aDependencies)
{
while (count($aFields) > 0) {
$aCurrentStep = [];
foreach ($aFields as $sAttCode => $aDependencies) {
// All fields with no remaining dependencies can be entered at this
// step of the wizard
if (count($aDependencies) == 0)
{
if (count($aDependencies) == 0) {
$aCurrentStep[] = $sAttCode;
$aFieldsDone[$sAttCode] = '';
unset($aFields[$sAttCode]);
// Remove this field from the dependencies of the other fields
foreach($aFields as $sUpdatedCode => $aDummy)
{
foreach ($aFields as $sUpdatedCode => $aDummy) {
// remove the dependency
unset($aFields[$sUpdatedCode][$sAttCode]);
}
}
}
if (count($aCurrentStep) == 0)
{
if (count($aCurrentStep) == 0) {
// This step of the wizard would contain NO field !
$this->m_oPage->add(Dict::S('UI:Error:WizardCircularReferenceInDependencies'));
print_r($aFields);
@@ -326,4 +300,3 @@ $sJSHandlerCode
}
}
?>

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -31,31 +32,31 @@ class UserDashboard extends DBObject
{
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "gui",
"key_type" => "autoincrement",
"name_attcode" => array('user_id', 'menu_code'),
"name_attcode" => ['user_id', 'menu_code'],
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => [],
"db_table" => "priv_app_dashboards",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
];
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("menu_code", array("allowed_values"=>null, "sql"=>"menu_code", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("contents", array("allowed_values"=>null, "sql"=>"contents", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", ["targetclass" => "User", "allowed_values" => null, "sql" => "user_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeString("menu_code", ["allowed_values" => null, "sql" => "menu_code", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeText("contents", ["allowed_values" => null, "sql" => "contents", "default_value" => null, "is_null_allowed" => false, "depends_on" => []]));
MetaModel::Init_SetZListItems('default_search', array (
MetaModel::Init_SetZListItems('default_search', [
0 => 'user_id',
1 => 'menu_code',
));
MetaModel::Init_SetZListItems('list', array (
]);
MetaModel::Init_SetZListItems('list', [
0 => 'user_id',
1 => 'menu_code',
));
]);
}
/**
@@ -67,4 +68,3 @@ class UserDashboard extends DBObject
$this->DBDelete($oDeletionPlan);
}
}
?>

View File

@@ -1,4 +1,5 @@
<?php
// Copyright (C) 2010-2024 Combodo SAS
//
// This file is part of iTop.
@@ -227,13 +228,12 @@ class appUserPreferences extends DBObject
// No prefs (yet) for this user, create the object
$oObj = new appUserPreferences();
$oObj->Set('userid', $sUserId);
$oObj->Set('preferences', array()); // Default preferences: an empty array
$oObj->Set('preferences', []); // Default preferences: an empty array
try {
utils::PushArchiveMode(false);
$oObj->DBInsert();
utils::PopArchiveMode();
}
catch (Exception $e) {
} catch (Exception $e) {
// Ignore errors
}
}
@@ -245,25 +245,25 @@ class appUserPreferences extends DBObject
*/
public static function Init()
{
$aParams = array
(
$aParams =
[
"category" => "gui",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",
"reconc_keys" => array("userid","login"),
"reconc_keys" => ["userid","login"],
"db_table" => "priv_app_preferences",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
];
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributePropertySet("preferences", array("allowed_values"=>null, "sql"=>"preferences", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("org_id", array("allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "org_id")));
MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "login")));
MetaModel::Init_SetZListItems('list', array('org_id','preferences'));
MetaModel::Init_SetZListItems('default_search', array('userid','login','org_id'));
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", ["targetclass" => "User", "allowed_values" => null, "sql" => "userid", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributePropertySet("preferences", ["allowed_values" => null, "sql" => "preferences", "default_value" => null, "is_null_allowed" => true, "depends_on" => []]));
MetaModel::Init_AddAttribute(new AttributeExternalField("org_id", ["allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "org_id"]));
MetaModel::Init_AddAttribute(new AttributeExternalField("login", ["allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "login"]));
MetaModel::Init_SetZListItems('list', ['org_id','preferences']);
MetaModel::Init_SetZListItems('default_search', ['userid','login','org_id']);
}
/**

File diff suppressed because it is too large Load Diff

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