Compare commits

...

538 Commits

Author SHA1 Message Date
Molkobain
6c4289a491 Tests: Rename file to match conventions 2023-02-17 15:33:09 +01:00
Eric Espie
8940051c3d N°5906 - CRUD Event - fire event EVENT_DB_LINKS_CHANGED when an n-n link is created/updated/deleted 2023-02-17 14:25:01 +01:00
Eric Espie
72dad5dd07 N°5891 - Make CSV import unit tests work 2023-02-16 16:14:37 +01:00
Molkobain
dc38db1c3d PHP 8.1: Reduce time interval to something more pragmatic for quicker test execution 2023-02-16 11:40:59 +01:00
Molkobain
45df1002ec N°5388 - PHP 8.2: Fix dynamic properties to standard declared properties 2023-02-15 23:02:40 +01:00
Molkobain
737388053f PHP 8.1: Fix FunctionExpression::Evaluate() "TO_DAYS" misalignment due to PHP 8.1 bug fix 2023-02-15 21:31:11 +01:00
dariocodes
d0b000d125 Update README.md to improve grammar
- Using hyphens to combine "web-based" ensures that the reader understands that it is a compound modifier.
- Adding a comma after "platform" helps to separate the list of items that follows and improve readability.
- Using commas to separate the list of items in the series (CMDB, helpdesk system, and document management tool) helps to clarify that each item is a distinct element.
- Fixed a typo where "adds-on" was written instead of "add-ons".
- Uses the wrong form of the verb "be." In this context, the correct form of the verb to use is "become," not "being." 
- Adding a comma after led is grammatically better as is referenced here: https://github.com/Combodo/combodo-workflow-graphical-view/pull/1
2023-02-15 18:02:54 +01:00
Eric Espie
1d0ce1862d N°4756 - Ease extensibility for CRUD operations : Event Service - Fix unit tests 2023-02-14 15:53:12 +01:00
Eric Espie
c0589715c1 N°4756 - Ease extensibility for CRUD operations : Event Service - Fix RegisterListener() signature and calls 2023-02-14 11:26:47 +01:00
Eric Espie
8b18fd7cc0 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-02-14 09:35:59 +01:00
Eric Espie
9f81e4875a Merge branch 'hotfix/5944_GetClassesForInterface_filter' into support/3.0 2023-02-14 09:16:49 +01:00
Molkobain
33717b9610 N°5951 - Fix crash when creating a user with a non valid password
/!\ Mind that this could have impact on DoCheckToWrite() overloads for people using \DBObject::GetKey() instead of \DBObject::IsNew()
2023-02-13 20:22:39 +01:00
Molkobain
b97a464bb1 N°2783 - Initialize property correctly (highlighted by 58c0da7e) 2023-02-13 19:36:46 +01:00
Pierre Goiffon
58c0da7e87 N°2783 Fix setup crashing because of a php warning (#389) 2023-02-13 17:09:24 +01:00
Molkobain
4203382920 N°2783 - Add support for custom zlists (#389) 2023-02-13 16:13:51 +01:00
Molkobain
825e402dd6 N°5279 - Improve DBBackup::MakeName() unit test by evaluating placeholders 2023-02-13 15:57:29 +01:00
Molkobain
fa75c8964a Tests: Improve REST API tests robustness when output JSON is not always ordered the same (even though the content is semantically the same) 2023-02-13 10:27:28 +01:00
Pierre Goiffon
fc58c6f4ce N°4577 setup params : add itop-bridge-cmdb-services 2023-02-13 09:23:19 +01:00
Molkobain
ef4e38f6b9 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-02-11 09:55:15 +01:00
Molkobain
4f102d764a Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-02-10 23:18:37 +01:00
Molkobain
dbd58cfeb6 Tests: Force RestAPI unit tests not to verify SSL certificate as most dev / test envs are self-signed 2023-02-10 23:07:27 +01:00
Molkobain
1086eaaffe N°3190 - Fix XML conversion unit test 2023-02-10 22:37:24 +01:00
Molkobain
f7ee21f1d7 N°5279 - PHP 8.1: Migrate usages of deprecated strftime() function
In the end we made an adapter to keep using the strftime() format (https://www.php.net/manual/fr/function.strftime.php); not to ease migration but because we couldn't use \DateTime::format().
We can't use \DateTime::format() directly on the whole filename as it would also format characters that are not supposed to be. eg. "__DB__-Y-m-d-production" would become "itopdb-2023-02-09-+01:00Thu, 09 Feb 2023 11:34:01 +0100202309", mind the "production" part being converted.
2023-02-10 22:15:18 +01:00
Pierre Goiffon
35b0b16e20 N°4756 Add listenerId to register call for debug purposes 2023-02-10 17:31:48 +01:00
Pierre Goiffon
ca72ec736e N°4756 DBObject Rename Event* methods to FireEvent* 2023-02-10 15:22:33 +01:00
Pierre Goiffon
e63d66aa5d N°4756 Fix TypeError on EventData::$aCallbackData 2023-02-10 15:01:56 +01:00
bdalsass
2803f0be49 N°3190 - Add iTop design format compatibility converter
- Add converter for  for display_style property
- Update php unit test to control conversion
2023-02-10 13:47:10 +01:00
BenGrenoble
278b0d643f N°5968 - Add structural data for Brand, OSFamily and OSVersion
Refactor file model.itop-bridge-cmdb-services.php
2023-02-10 11:21:19 +01:00
vdumas
569045f675 N°5946 - Error in a french translation - user preference 2023-02-09 17:30:17 +01:00
vdumas
25989bc00f N°5947 - Typos in french translation for incident 2023-02-09 17:25:25 +01:00
vdumas
fc60759db5 N°5920 - Align icon, title and tooltip for extra tabs in standard DM 2023-02-09 17:13:07 +01:00
BenGrenoble
3a931ff79e N°5968 - Add structural data for Brand, OSFamily and OSVersion
Change path and correct osversion id
2023-02-09 11:32:57 +01:00
BenGrenoble
f6bd380ad0 N°5968 - Add structural data for Brand, OSFamily and OSVersion
Remove sample brand
2023-02-09 11:10:00 +01:00
BenGrenoble
97c4ecf8bc N°5968 - Add structural data for Brand, OSFamily and OSVersion
Correct brand id
2023-02-09 10:50:59 +01:00
BenGrenoble
5f4ef74d34 N°5968 - Add structural data for Brand, OSFamily and OSVersion
Move data to config-mgmt
2023-02-09 10:45:10 +01:00
BenGrenoble
6184dbafff N°4577 - Move service dependencies from "itop-bridge-cmdb-ticket" to another module 2023-02-09 10:32:39 +01:00
BenGrenoble
630c1dcdc8 N°5968 - Add structural data for Brand, Model, OSFamily and OSVersion 2023-02-09 09:59:06 +01:00
vdumas
cb84cbf7a8 N°5106 - Add Associated User tab on Person 2023-02-09 09:40:19 +01:00
vdumas
b025f86b21 N°5106 - Add Associated User tab on Person 2023-02-09 09:06:48 +01:00
vdumas
5a76175ec1 N°5908 - add a description on "known error" tab on UserRequest and Incident 2023-02-09 09:03:18 +01:00
vdumas
ce162f05da N°5908 - add a description on "known error" tab on UserRequest and Incident 2023-02-08 19:00:57 +01:00
Molkobain
d358c9e8fa N°5920 - Tab manager: Fallback on dict entry code convention (tab code followed by "+" sign) for tab's description / tooltip if none given 2023-02-08 18:23:20 +01:00
bdalsass
86a208a86d N°5904 - Use attribute linked set edit mode to enable actions in view (improvement) 2023-02-08 14:34:46 +01:00
vdumas
372888c7a7 N°5106 - Add Associated User tab on Person 2023-02-08 13:01:41 +01:00
Molkobain
547a520ab1 N°5951 - Temporary fix to unblock 3.1.0-alpha1, we need to discuss the DBObject::DBInsertNoReload() sequence before going further 2023-02-07 19:41:03 +01:00
Molkobain
8bf788dcb9 N°5904 - PHP 8.1: Migrate usage of deprecated FILTER_SANITIZE_STRING constant 2023-02-07 14:57:24 +01:00
Molkobain
a157e754a9 N°5074 - Clean up of dynamically created class properties due to bdb29fd99 2023-02-07 14:51:42 +01:00
Molkobain
eae84ab399 N°5074 - Clean up of dynamically created class properties due to bdb29fd99 2023-02-07 14:51:00 +01:00
Molkobain
4d4d3bd9a2 N°5904 - Fix crash when adding a 1:n link item in object edition 2023-02-07 14:35:49 +01:00
Benjamin Dalsass
9f9630d011 N°5904 - remove link as tagset add button 2023-02-07 09:42:41 +01:00
Benjamin Dalsass
de1c58a84c N°5904 - Use attribute linked set edit mode to enable actions in view 2023-02-06 17:22:30 +01:00
bdalsass
1fe9520b7e N°5904 - Use attribute linked set edit mode to enable actions (#440)
* Add corresponding buttons depending on old edit mode (need to check with piR pour récuperer l'ancienne valeur.

* N°5904 - Handle attribute linked set edit_mode

* N°5904 Move calls to private jquery widget methods to public

* N°5904 - Worker improvements add button on link tagset

* Change itop set widget to new set block UI (5)

* Change itop set widget to new set block UI (5)

* Renommage variables JS avec le prefix combodo

* Search dialog block id conflict with form id

* add moved js files in iTopWebPage compatibility list

---------

Co-authored-by: Stephen Abello <stephen.abello@combodo.com>
2023-02-06 16:07:55 +01:00
Stephen Abello
3fbc5e1ce0 N°5904 Hide decorative plus icon on attribute set edit 2023-02-06 14:24:44 +01:00
Molkobain
786e7d647c N°5388 - PHP 8.2: Fix partially supported callable 2023-02-03 23:01:24 +01:00
Eric Espie
99b7d66cf2 N°5944 - Wrong filter for utils::GetClassesForInterface() 2023-02-03 11:10:37 +01:00
Molkobain
1768274aaf N°5943 - Fix undefined array key "Remote" in AjaxRenderController.php with DokuWiki extension 2023-02-03 09:38:28 +01:00
Stephen Abello
7cc1e33950 N°5904 Fix attribute set / linkset wrapping 2023-02-02 10:05:55 +01:00
Molkobain
72a586e490 N°5905 - Fix linkset panel description still in an alert when editing host object 2023-02-01 18:09:46 +01:00
Stephen Abello
0cce58e0cf N°5904 Fix hardcoded string when existing dict entry was available 2023-02-01 09:40:38 +01:00
Stephen Abello
53b68f88b8 Fix typo in dictionaries 2023-02-01 09:39:33 +01:00
Stephen Abello
7fb10da013 N°5904 Fix hardcoded strings when existing dict entries were available 2023-02-01 09:33:58 +01:00
Stephen Abello
0b1352fe37 N°5904 Move dict entries 2023-01-31 09:57:06 +01:00
acognet
e0cc00e772 N°5849 - Fix wrong encoding of external keys in "Header with statstics" dashlet - fix for 3.1 2023-01-30 17:29:51 +01:00
Molkobain
002e52cc3e N°5929 - Fix parse error due to not closed array from bbea9782 2023-01-30 17:21:41 +01:00
Thomas Casteleyn
bbea978259 N°5929 - Update hungarian translations thanks to @tacsaby (#438)
Co-authored-by: tacsaby
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-01-30 17:15:08 +01:00
Molkobain
bddfa4cd7a Merge remote-tracking branch 'origin/support/3.0' into develop 2023-01-30 17:09:36 +01:00
Anne-Catherine
12ef74ec42 N°5849 - Fix wrong encoding of external keys in "Header with statstics" dashlet (#384)
N°5849 - Fix wrong encoding of external keys in "Header with statstics" dashlet #384
2023-01-30 16:06:10 +01:00
Eric Espie
cd1b441025 N°4756 - Ease extensibility for CRUD operations: 🎨 add log 2023-01-30 14:45:58 +01:00
Pierre Goiffon
0f97b93617 N°5856 Fix object lists always ajax loaded
Regression introduced in N°3200 with 4f7a1ea9
2023-01-30 09:31:10 +01:00
Molkobain
b61c8ea4ca N°5388 - PHP 8.2: Fix dynamic properties to standard declared properties 2023-01-27 19:00:59 +01:00
Molkobain
f48df74933 N°5388 - PHP 8.2: Fix dynamic properties that were actually just case-sensitive typos 2023-01-27 17:57:50 +01:00
Molkobain
6fb00cbbf3 N°5388 - PHP 8.2: Fix dynamic properties to standard declared properties 2023-01-27 17:56:55 +01:00
Molkobain
af2421a2cb N°5905 - Move tab description to a tooltip on the panel's title instead of a big blue alert 2023-01-27 17:25:03 +01:00
Molkobain
0374e303e4 N°5905 - Refactor CSS class .ibo-has-description so it can be used on any element 2023-01-27 17:25:03 +01:00
Molkobain
27bd78d76b Refactor SCSS misc. partial to dedicated partials as it was growing too much 2023-01-27 17:25:03 +01:00
Stephen Abello
bc3b43958c N°5904 When getting an error in a popup (e.g. uniqueness rule), remove submit state, so we can submit the form again 2023-01-27 14:33:49 +01:00
Molkobain
fd4a2f17d2 N°4481 - Fix CSS class name 2023-01-27 13:24:54 +01:00
vdumas
d741656028 N°5904 - Linksets: tooltip & pop-up title 2023-01-27 11:00:08 +01:00
Molkobain
f7e8bd31f5 N°5905 - Fix "+" button displayed twice on linksets with 1 item 2023-01-26 17:49:17 +01:00
Eric Espie
1e37370789 N°4756 - Ease extensibility for CRUD operations : 🎨 refactor after code review with PG 2023-01-26 17:32:08 +01:00
Tarjányi Csaba
5844717980 Update hu.dict.authent-local.php
a small fix
2023-01-26 16:20:11 +01:00
Vincent Dumas
9437968d0e N°5825 - Add friendlyname & uniqueness rule on Links (#416)
* N°5825 - Add label, friendlyname, filter, details view, uniqueness rules on Link classes
* N°5825 - Add label, friendlyname, details view, uniqueness rules on Link classes
2023-01-26 14:26:31 +01:00
Molkobain
e72ed33a40 N°5920 - Add linkset's description as corresponding tab's tooltip in object details 2023-01-26 12:01:56 +01:00
Molkobain
2a825c6ba0 N°5905 - Fix "+" button displayed twice on linksets with no item 2023-01-26 11:03:01 +01:00
vdumas
6b8d9ea08d N°5915 - Complementary Name on Trigger and Action 2023-01-26 09:03:52 +01:00
vdumas
52d57293c9 N°5825 - Add label, friendlyname, details view (on LnkPersonToTeam) 2023-01-25 18:37:38 +01:00
Molkobain
40cebf1eb7 N°5923 - Align panel's header within another panel when it has no icon 2023-01-25 16:50:21 +01:00
Molkobain
f1d6f3e5c2 N°5905 - Harmonize how known errors are displayed in an object 2023-01-25 15:33:44 +01:00
Pierre Goiffon
834c4a2654 N°5563 Restore edit_mode getter
Woops forgot in 13550fd6
2023-01-25 14:57:30 +01:00
Molkobain
ed1a076ebb N°5905 - Harmonize how notifications are displayed in an object 2023-01-25 10:41:18 +01:00
Molkobain
1dcf38f2ea N°5905 - Harmonize how linksets are displayed in edition part I
* Always display a table and its actions to ease link creation
* Display linkset description in an alert block
2023-01-25 10:29:48 +01:00
odain
2f034253e7 N°5891 - Make CSV import unit tests work-use another password for user authentication 2023-01-25 08:56:15 +01:00
Molkobain
a10ac7fdcb N°5905 - Harmonize how linksets are displayed part I
* Always display a table and its actions to ease link creation
* Display linkset description in an alert block
2023-01-24 21:12:49 +01:00
vdumas
3775ccea74 N°5915 - Display n:n in Trigger and Action using tagset widget 2023-01-24 17:23:44 +01:00
bdalsass
f743971c93 N°5914 - Wrong encoding in modules configuration editor (#392) 2023-01-24 16:33:02 +01:00
Stephen Abello
dd213df9c4 N°5822 Fix 7127b192 SCSS rule 2023-01-24 14:33:41 +01:00
Molkobain
96825c646e N°4756 - Move event register to dedicated folder 2023-01-24 14:20:35 +01:00
Stephen Abello
1152b2f401 N°5904 Hide transitions in modals 2023-01-24 11:21:35 +01:00
Stephen Abello
8f7003c694 N°5904 Change dict entry value 2023-01-24 11:21:33 +01:00
bdalsass
fb1ceebaa4 N°3190 - Edit n:n LinkedSetIndirect in object details using a tagset-like widget
- Add generic set block ui component
- Add model link set (direct and indirect) attribute (display style)
- Add model link set direct allowed values
- Create link set viewer block UI (BlockLinksSetDisplayAsProperty)
- Add set block ui factory for linkset
- Add object factory and create new endpoint in object controller (with data binder)
- Add link set model, link set repository and link set data transformer services
2023-01-24 10:03:10 +01:00
Stephen Abello
9482139b5a N°5904 Fix undefined attribute when refreshing Datatables 2023-01-23 16:42:43 +01:00
Stephen Abello
7ea2315323 Fix php warning and missing dict entry for 1-n (3d9a85d follow-up) 2023-01-23 16:36:13 +01:00
Stephen Abello
9487d63b9c N°5904 When detaching/deleting a linked object, refresh datatable or display an error message 2023-01-23 16:16:23 +01:00
Stephen Abello
3d9a85d577 Fix php warning and missing dict entry 2023-01-23 15:36:38 +01:00
Molkobain
b2397fc570 N°4517 - Fix missing use statement from 7ec12f1e1 2023-01-20 15:30:53 +01:00
Molkobain
0d15b01b3f Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	pages/UI.php
2023-01-20 11:59:26 +01:00
vdumas
f7e87c1ea1 N°5347 - Change default value of allow_menu_on_linkset 2023-01-20 11:30:55 +01:00
Molkobain
3ca4122673 N°5834 - Fix activity panel disappearing when creating a Ticket in 'resolved' state 2023-01-20 11:30:05 +01:00
Stephen Abello
f21f6aec30 Missing class import 2023-01-20 10:10:05 +01:00
Pierre Goiffon
c76d351379 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	datamodels/2.x/combodo-db-tools/module.combodo-db-tools.php
2023-01-19 17:54:29 +01:00
Pierre Goiffon
efa20e77d0 N°3769 Add data-input-id HTML attribute to fields in a transition form
Was only present in object edit mode :(
2023-01-19 17:53:14 +01:00
Eric Espie
effc4141c7 N°5900 - DB integrity analysis crashes when too many errors - module version 2023-01-19 09:30:37 +01:00
Eric Espie
b4813de0a5 Merge branch 'support/3.0' into develop
# Conflicts:
#	datamodels/2.x/combodo-db-tools/module.combodo-db-tools.php
2023-01-19 08:52:47 +01:00
Eric Espie
f75a51d59e N°5900 - DB integrity analysis crashes when too many errors - limit the results to 100 to avoid memory errors 2023-01-19 08:51:02 +01:00
Molkobain
8e3bad11ae N°5563 - Fix XML conversion XPaths 2023-01-18 19:35:59 +01:00
Pierre Goiffon
13550fd643 N°5563 Relations XML : restore edit_mode node
There are (yet !) not change in the relations UI when in object is in edit mode, so we need to keep previous behaviors.
2023-01-18 18:11:02 +01:00
Molkobain
44977d69b6 N°5608 - Fix event service unit tests namespace 2023-01-18 17:50:22 +01:00
Molkobain
15f108152c N°5608 - Fix Twig unit test and re-enable all tests under /sources dir that were not run! (Application/Helper, Application/Service, Application/TwigBase, Composer, Router) 2023-01-18 17:41:47 +01:00
Molkobain
9be167cf5e N°5608 - Rename unit tests dirs "sources/application" to match counter-part dirs name 2023-01-18 17:39:55 +01:00
Eric Espie
34688c2bf6 Merge branch 'support/3.0' into develop 2023-01-18 16:42:30 +01:00
Eric Espie
9dc54d6e4c N°5901 - Warnings in Details part of file system Tab 2023-01-18 16:39:40 +01:00
Eric Espie
640b1b9176 ✏️ Added missing use of class Toolbar 2023-01-18 16:38:16 +01:00
Stephen Abello
c1922e2b3f N°3300 - Add creation and modification of 1-n objects in object details (#385)
* Rebase onto develop

* Use exit condition instead of englobing condition

* Add informative modals that can be called from modal toolbox

* Refactor "apply_modify" and "apply_new" into own controller, handle ajax requests with a json response and handle these responses in linkset creation/edition

* Fix merge issues

* Remove inverted condition

* Move linkset create button to a better place, still needs to fix duplicate "New" button caused by a refactor

* Handle "Cancel" button in modals

* Do not display relations when editing an object in a modal

* More elegant way to add "New" button to relations lists

* Factorize vertical highlights in alerts and modal in a single mixin

* Replace button name with dict entry code

* Change route name to snake case

* More elegant way to add "Create in modal" button to relations lists

* Replace triple if with in_array

* Move listener to body

* Rename variable to match boolean rules

* Rename event

* Rename extra param

* Add phpdoc

* Revert changes

* Check indirect linkset rights before allowing creation in modal

* Allow to modify linked object from 1-n relation in modal

* Add "New" button to 1-n relations

* Fix "new" button on 1-n

* Add todo

* Handle multiple classes choice

* Rework multiple classes choice and only allow LinksetController to be called from an ajax context

* PhpDoc

* Update sources/Application/UI/Links/Direct/BlockDirectLinksViewTable.php

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-01-18 15:43:31 +01:00
Stephen Abello
e1ffa65d8b N°3136 - Add creation and modification of n-n objects in object details (#378)
* Rebase onto develop

* Use exit condition instead of englobing condition

* Add informative modals that can be called from modal toolbox

* Refactor "apply_modify" and "apply_new" into own controller, handle ajax requests with a json response and handle these responses in linkset creation/edition

* Fix merge issues

* Remove inverted condition

* Move linkset create button to a better place, still needs to fix duplicate "New" button caused by a refactor

* Handle "Cancel" button in modals

* Do not display relations when editing an object in a modal

* More elegant way to add "New" button to relations lists

* Factorize vertical highlights in alerts and modal in a single mixin

* Replace button name with dict entry code

* Change route name to snake case

* More elegant way to add "Create in modal" button to relations lists

* Replace triple if with in_array

* Move listener to body

* Rename variable to match boolean rules

* Rename event

* Rename extra param

* Add phpdoc

* Revert changes

* Check indirect linkset rights before allowing creation in modal
2023-01-18 13:35:48 +01:00
Molkobain
cc2881a7b0 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-01-17 19:21:52 +01:00
Molkobain
0d8a21f6ef N°5897 - Improve deprecated logs relevance for PHP "trigger_deprecation" 2023-01-17 16:30:35 +01:00
Molkobain
3459dc5997 N°5897 - Improve deprecated logs relevance for PHP "trigger_deprecation" 2023-01-17 16:29:25 +01:00
Pierre Goiffon
01d9d34118 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-01-17 15:37:43 +01:00
Pierre Goiffon
de7c9d965e 💡 PHPDoc in \DeprecatedCallsLog 2023-01-17 15:37:23 +01:00
Eric Espie
6549c95d4f N°4756 - Ease extensibility for CRUD operations : XML format migration 2023-01-17 10:17:11 +01:00
Molkobain
da07fadfb3 N°5302 - PHP 8.1: Migrate some strlen() usages to internal method 2023-01-16 16:47:26 +01:00
Molkobain
b2c256e51e Merge remote-tracking branch 'origin/support/3.0' into develop 2023-01-16 16:37:22 +01:00
Pierre Goiffon
7305a30b50 N°3200 Remove "Filter list" icon
The link is still here but on objects total (see c693d03a)
2023-01-16 15:29:18 +01:00
Molkobain
4c127b6f61 Revert "N°4517 - PHP 8.1: Fix return type hint for iterable classes"
This reverts commit 61be903eb2.
2023-01-16 14:38:20 +01:00
Molkobain
61be903eb2 N°4517 - PHP 8.1: Fix return type hint for iterable classes 2023-01-16 14:21:00 +01:00
Pierre Goiffon
59792b2a3d N°4660 Remove useless chmod 2023-01-16 11:55:18 +01:00
Pierre Goiffon
19dda61d4f Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	tests/php-unit-tests/unitary-tests/webservices/RestTest.php
2023-01-16 11:54:24 +01:00
Pierre Goiffon
a50ed02057 N°4660 Fix OQLTest 2023-01-16 11:39:38 +01:00
Pierre Goiffon
0a7c8f9fd1 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	tests/php-unit-tests/unitary-tests/core/OQLTest.php
#	tests/php-unit-tests/unitary-tests/core/iTopConfigParserTest.php
#	tests/php-unit-tests/unitary-tests/webservices/RestTest.php
2023-01-16 11:36:02 +01:00
Pierre Goiffon
f65e14397c N°4660 Fix permissions changes in tests 2023-01-16 11:22:23 +01:00
Eric Espie
06a5d645da N°4756 - Ease extensibility for CRUD operations : Add unit tests 2023-01-12 17:40:17 +01:00
Pierre Goiffon
5d1852fe45 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-01-12 16:43:41 +01:00
Pierre Goiffon
d7b94fb123 N°3769 Add data-input-type HTML attribute to fields in a transition form
Was only present in object edit mode :(
2023-01-12 16:40:31 +01:00
Eric Espie
ced4f82585 N°5412 - Upgrade to PHPUnit 9 to fix PHPUnit 8.5 error with PHP 8.1 - replaced removed function 2023-01-12 16:34:14 +01:00
Pierre Goiffon
de0110abc6 RestTest : test json_decode return value 2023-01-12 15:50:57 +01:00
Molkobain
a53bea5eb9 Add @jf-cbd to the sample data, welcome! 👋 2023-01-12 14:41:41 +01:00
Pierre Goiffon
47435869b3 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-01-12 10:43:31 +01:00
Pierre Goiffon
80a9ded404 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-01-12 10:43:09 +01:00
Pierre Goiffon
c696a81c3a N°5821 JenkinsFile : introduce buildDiscarder 2023-01-12 10:42:06 +01:00
Molkobain
e04ea2d178 N°5608 - Fix status unit tests for PHPUNit 9 2023-01-11 18:22:48 +01:00
Molkobain
42ce397e13 N°5608 - Fix event service unit tests suite 2023-01-11 18:22:48 +01:00
Pierre Goiffon
22162aa55f Merge remote-tracking branch 'origin/support/3.0' into develop 2023-01-11 18:09:49 +01:00
Pierre Goiffon
c693d03a77 N°3200 Add "Filter list" link on datatable count 2023-01-11 17:19:49 +01:00
Pierre Goiffon
b9ed00d53f N°4660 Fix iTopConfigParserTest setting permissions of iTop config file
Alternative method to restore config file
chmod cannot be used with +w
In \Config::WriteToFile we are using fopen with mode=w, so using the same
In consequence we are not modifying anymore the iTop config file permissions \o/
2023-01-11 16:04:21 +01:00
Molkobain
29e0df9bbc Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	test/sources/application/Helper/WebResourcesHelperTest.php
#	tests/php-unit-tests/ItopTestCase.php
#	tests/php-unit-tests/composer.lock
#	tests/php-unit-tests/unitary-tests/.make/release/DatamodelsXmlFilesTest.php
#	tests/php-unit-tests/unitary-tests/application/UI/Base/Layout/NavigationMenuTest.php
#	tests/php-unit-tests/unitary-tests/application/query/QueryTest.php
#	tests/php-unit-tests/unitary-tests/core/DBSearchTest.php
#	tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-config/BulkChangeExtKeyTest.inc.php
#	tests/php-unit-tests/unitary-tests/datamodels/2.x/itop-config/CRUD/DBObjectTest.php
#	tests/php-unit-tests/unitary-tests/service/EventTest.php
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.0_to_3.1.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.0_to_3.1.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.1_to_3.0.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.1_to_3.0.input.xml
#	tests/php-unit-tests/unitary-tests/sources/Application/Helper/WebResourcesHelperTest.php
#	tests/php-unit-tests/unitary-tests/sources/Router/RouterTest.php
#	tests/php-unit-tests/unitary-tests/sources/application/Helper/WebResourcesHelperTest.php
#	tests/php-unit-tests/unitary-tests/webservices/ImportTest.inc.php
#	tests/php-unit-tests/unitary-tests/webservices/RestTest.php
2023-01-11 15:55:08 +01:00
Molkobain
c4508ea80c N°4660 - Fix data synchro unit test failure due to another test not restoring correct user permissions on conf file 2023-01-11 14:26:23 +01:00
Molkobain
16390c9b00 N°5857 - PHPUnit: Replace usage of deprecated assetContains with assertStringContainsString 2023-01-11 13:26:54 +01:00
Molkobain
1271a895d1 N°5857 - PHPUnit: Replace usage of deprecated assetContains with assertStringContainsString 2023-01-11 11:59:01 +01:00
Eric Espie
030f1e2463 Fix Status test 2023-01-11 10:28:32 +01:00
Molkobain
ec90c0b6cd N°5872 - Navigation menu: Wrap menu group label instead of ellipsis in drawer 2023-01-11 10:21:46 +01:00
Molkobain
7b79da3f32 N°5871 - Navigation menu: Show ellipsis on long menu group labels 2023-01-11 10:18:15 +01:00
Pierre Goiffon
5729d6a9cd 🎨 StatusIncTest : fix phpdoc, replace @expectedException 2023-01-11 10:02:59 +01:00
acognet
4d7bac89f3 N°3805 - Collectors not working on itop 3.0 2023-01-11 09:47:53 +01:00
Molkobain
a5efef900c N°5608 - Harmonize namespaces and merge duplicated test files 2023-01-10 23:02:54 +01:00
Molkobain
c851a10982 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-01-10 22:49:13 +01:00
Molkobain
845adf43c6 N°5608 - Harmonize namespaces and merge duplicated test files 2023-01-10 22:36:35 +01:00
Molkobain
5916e4ea39 N°5608 - Ensure both old & new tests structure are ran for extensions for backward compatibility 2023-01-10 22:03:40 +01:00
Molkobain
7f37de777e N°5608 - Fix unit tests following restructuring Part III
* Missed a usage of "use_legacy_search" conf param on the last commit
* Fix log tests which didn't moved correctly from Git PoV 😕
2023-01-10 21:11:10 +01:00
Molkobain
bcf880f327 N°5608 - Fix unit tests following restructuring Part II
"use_legacy_search" conf param was removed some time ago
2023-01-10 19:23:54 +01:00
Thomas Casteleyn
b593beb8c7 N°5867 Display binary data size in SynchroReplica details (#286) 2023-01-10 18:53:00 +01:00
Lars Kaltefleiter
57ab3a0336 N°1350 - link audit catecories to domains and evaluate only categories of a given domain via interactive audit 2023-01-10 18:20:15 +01:00
Lars Kaltefleiter
4a54b2f8ff N°1350 - Configurable error tolerance for report color of audit categories 2023-01-10 18:20:15 +01:00
Molkobain
6aa9aa2831 N°5608 - Fix unit tests following restructuring Part I 2023-01-10 18:15:53 +01:00
Eric Espie
9b47ee12e7 N°4307 - Replace SwiftMailer by laminas-mail - Fix connecting to server without password 2023-01-10 17:58:03 +01:00
Molkobain
d177ee4a7f Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	tests/manual-visual-tests/Backoffice/RenderAllUiBlocks.php
#	tests/php-unit-tests/ItopDataTestCase.php
#	tests/php-unit-tests/ItopTestCase.php
#	tests/php-unit-tests/integration-tests/dictionaries-test/fr.dictionary.itop.core.KO.wrong_php
#	tests/php-unit-tests/integration-tests/dictionaries-test/fr.dictionary.itop.core.OK.php
#	tests/php-unit-tests/integration-tests/iTopModulesPhpVersionChecklistTest.php
#	tests/php-unit-tests/integration-tests/iTopXmlVersionChecklistTest.php
#	tests/php-unit-tests/phpunit.xml.dist
#	tests/php-unit-tests/unitary-tests/application/SCSSCompilationTest.php
#	tests/php-unit-tests/unitary-tests/application/Session/SessionTest.php
#	tests/php-unit-tests/unitary-tests/application/ThemeHandlerTest.php
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/DO_NOT_CHANGE.css-variables.scss
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/DO_NOT_CHANGE.light-grey.scss
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/README.md
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/_included_file3.scss
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/cross_reference1.scss
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/cross_reference2.scss
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/feature1/_feature1.scss
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/included_file1.scss
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/included_scss/included_file2.scss
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/included_scss/included_file4.scss
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/multi_imports.scss
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/shortcut.scss
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/shortcut2.scss
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/simple_import.scss
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/simple_import2.scss
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/typography.scss
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/css/ui-lightness/DO_NOT_CHANGE.jqueryui.scss
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/css/ui-lightness/images/ui-icons_1c94c4_256x240.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/css/ui-lightness/images/ui-icons_222222_256x240.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/css/ui-lightness/images/ui-icons_E87C1E_256x240.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/css/ui-lightness/images/ui-icons_F26522_256x240.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/css/ui-lightness/images/ui-icons_ffd27a_256x240.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/css/ui-lightness/images/ui-icons_ffffff_256x240.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/ac-background.gif
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/actions_right.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/bg.gif
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/breadcrumb-separator.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/calendar.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/delete.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/desc.gif
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/error.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/eye-closed-555.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/eye-closed-fff.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/eye-open-555.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/eye-open-fff.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/full-screen.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/green-header.gif
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/green-square.gif
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/indicator.gif
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/info-mini.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/minus.gif
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/ok.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/orange-header.gif
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/plus.gif
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/red-header.gif
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/truncated.png
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/tv-collapsable-last.gif
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/tv-collapsable.gif
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/tv-expandable-last.gif
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/tv-expandable.gif
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/tv-item-last.gif
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/testimages/images/tv-item.gif
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/main.css
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/main_imagemodified.css
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/main_importmodified.css
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/main_stylesheet.css
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/main_testcompilethemes.css
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/main_varchanged.css
#	tests/php-unit-tests/unitary-tests/application/theme-handler/expected/themes/basque-red/theme-parameters.json
#	tests/php-unit-tests/unitary-tests/application/theme-handler/getimages/expected-getimages.json
#	tests/php-unit-tests/unitary-tests/application/theme-handler/getimages/test-getimages.scss
#	tests/php-unit-tests/unitary-tests/core/ActionEmailTest.php
#	tests/php-unit-tests/unitary-tests/core/AttributeDefTest.inc.php
#	tests/php-unit-tests/unitary-tests/core/AttributeURLDefaultPattern.php
#	tests/php-unit-tests/unitary-tests/core/AttributeURLTest.php
#	tests/php-unit-tests/unitary-tests/core/BulkChangeTest.inc.php
#	tests/php-unit-tests/unitary-tests/core/CSVParserTest.php
#	tests/php-unit-tests/unitary-tests/core/DBObjectTest.php
#	tests/php-unit-tests/unitary-tests/core/DBSearchAddConditionPointingTo.php
#	tests/php-unit-tests/unitary-tests/core/ExpressionEvaluateTest.php
#	tests/php-unit-tests/unitary-tests/core/GetSelectFilterTest.php
#	tests/php-unit-tests/unitary-tests/core/InlineImageTest.php
#	tests/php-unit-tests/unitary-tests/core/Log/ExceptionLogTest.php
#	tests/php-unit-tests/unitary-tests/core/Log/ExceptionLogTest/Exceptions.php
#	tests/php-unit-tests/unitary-tests/core/Log/LogAPITest.php
#	tests/php-unit-tests/unitary-tests/core/Log/LogFileNameBuilderTest.php
#	tests/php-unit-tests/unitary-tests/core/LogAPITest.php
#	tests/php-unit-tests/unitary-tests/core/LogFileNameBuilderTest.php
#	tests/php-unit-tests/unitary-tests/core/MetaModelTest.php
#	tests/php-unit-tests/unitary-tests/core/OQLTest.php
#	tests/php-unit-tests/unitary-tests/core/UniquenessConstraintTest.php
#	tests/php-unit-tests/unitary-tests/core/XMLDataLoaderTest.php
#	tests/php-unit-tests/unitary-tests/core/dictApcuTest.php
#	tests/php-unit-tests/unitary-tests/core/dictTest.php
#	tests/php-unit-tests/unitary-tests/core/ormCaseLogTest.php
#	tests/php-unit-tests/unitary-tests/core/ormPasswordTest.php
#	tests/php-unit-tests/unitary-tests/core/ormStyleTest.php
#	tests/php-unit-tests/unitary-tests/setup/MFCompilerTest.php
#	tests/php-unit-tests/unitary-tests/setup/SubMFCompiler.php
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/1.7_to_1.6.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/1.7_to_1.6.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/1.6_to_1.7_2.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/1.6_to_1.7_2.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/1.7.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/1.7_to_1.6.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/1.7_to_1.6.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/1.7_to_1.6_2.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/1.7_to_1.6_2.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/1.7_to_3.0.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/1.7_to_3.0.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.0_to_1.7.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/3.0_to_1.7.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/Bug_4569.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/Convert-samples/Bug_4569.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_deleted_to_deleted.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_deleted_to_deleted.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_deleted_to_in-definition.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_deleted_to_in-definition.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_deleted_to_not-in-definition.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_deleted_to_not-in-definition.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_in-definition_to_deleted.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_in-definition_to_deleted.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_in-definition_to_in-definition.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_in-definition_to_in-definition.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_in-definition_to_not-in-definition.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_in-definition_to_not-in-definition.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_not-in-definition_to_deleted.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_not-in-definition_to_deleted.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_not-in-definition_to_in-definition.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_not-in-definition_to_in-definition.input.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_not-in-definition_to_not-in-definition.expected.xml
#	tests/php-unit-tests/unitary-tests/setup/iTopDesignFormat/MoveNode-samples/from_not-in-definition_to_not-in-definition.input.xml
#	tests/php-unit-tests/unitary-tests/setup/ressources/datamodels/datamodel-branding.xml
#	tests/php-unit-tests/unitary-tests/sources/application/Helper/WebResourcesHelperTest.php
#	tests/php-unit-tests/unitary-tests/sources/application/status/StatusIncTest.php
#	tests/php-unit-tests/unitary-tests/sources/application/status/status.php
#	tests/php-unit-tests/unitary-tests/synchro/DataSynchroTest.php
2023-01-10 15:27:44 +01:00
Lars Kaltefleiter
b354058eb5 N°5622 Fix backup cannot be done if TLS enabled with no CA (#354) 2023-01-10 12:13:33 +01:00
Molkobain
fbc0a898ae N°5608 - Move test files to corresponding directories after branch rebase 2023-01-10 12:11:12 +01:00
Molkobain
36f8e58e25 N°5608 - Use new ItopTestCase::RequireOnceXXX in unit tests 2023-01-10 12:11:12 +01:00
Molkobain
6a7dbb06b0 N°5608 - Add methods to require_once an iTop or a unit test file to avoid crashes when tests dir is moved 2023-01-10 12:11:12 +01:00
Pierre Goiffon
43dd0b7df8 N°2013 Fix regression when installing from scratch
Error was "Call to a member function UpdateFromParams() on null"
Regression introduced by 15d3201a
2023-01-10 10:56:21 +01:00
Pierre Goiffon
43d86ad8e2 N°2013 Setup : fix crash on setting readonly mode if initial DB info were wrong
Same as 15d3201a, but this error happened before compilation, when setting read only mode.
As before this is another fix for #351.

This fix introduces the new method \WizardController::GetParamForConfigArray
I replaced existing duplicate code with a call to this new generic method.
2023-01-10 10:56:21 +01:00
Eric Espie
d3d89b1ee2 N°5857 Fix PHPUnitPrettyPrint with PHPUnit 9 2023-01-10 09:42:13 +01:00
Pierre Goiffon
15d3201a40 N°2013 Setup : fix crash on compilation if initial DB info were wrong
Use case :
- having in the iTop config file wrong DB parameters (unreachable server for example)
- launch the setup in update mode
- fix the DB config in the wizard

The PR #351 solved the errors that were happening in the following steps.
But we still had an error during compilation, caused by the maintenance mode activation.
2023-01-09 16:11:27 +01:00
Eric Espie
cf4616d9a1 N°4756 - Ease extensibility for CRUD operations : Unit tests 2023-01-09 13:59:55 +01:00
Molkobain
5721a324c1 Tests: Always display test status for better feedback 2023-01-06 22:30:09 +01:00
Molkobain
7de6c72154 Tests: Rename provider method name to match convention 2023-01-06 22:30:09 +01:00
Molkobain
c0cee02351 N°5608 - Factorize all core modules tests to a single test suite 2023-01-06 22:30:09 +01:00
Molkobain
bb674fb873 N°5608 - Move/rename "status" unit tests to match their counterpart location/name 2023-01-06 22:30:09 +01:00
Molkobain
6136eadd31 N°5608 - Fix some broken require paths since move/rename 2023-01-06 22:30:08 +01:00
Molkobain
87cb73c038 N°5608 - Rename "test" folder to "tests" to better match conventions 2023-01-06 22:30:08 +01:00
Molkobain
11d8547cef N°5608 - Move/rename unit tests to match their counterpart location/name 2023-01-06 22:30:08 +01:00
Molkobain
0998c73a1a N°5608 - Add README files 2023-01-06 22:30:07 +01:00
Molkobain
471f66649a N°5608 - Rename unitary test folders for better understanding 2023-01-06 22:30:07 +01:00
Molkobain
e8bf9cf688 N°5608 - Move "twig" PHP unit test to new folder
Notice: Test was not working, still not working
2023-01-06 22:30:07 +01:00
Molkobain
4f88a0e7d2 N°5608 - Move legacy PHP unit tests (not run by CI) to a dedicated folder 2023-01-06 22:30:07 +01:00
Molkobain
c6b0e273e6 N°5608 - Rename "VisualTests" folder to match new convention 2023-01-06 22:30:07 +01:00
Molkobain
d9539f9d01 N°5608 - Add comments to main autoloader 2023-01-06 22:30:06 +01:00
Molkobain
a3e309acb5 N°5608 - Revert "authent-local" test suite to its original rank as it is crashing the CI 🤔 2023-01-06 22:30:06 +01:00
Molkobain
c06cbfd4a9 N°5608 - Rename "coreExtensions" test suite to correct datamodel module (authent-local) 2023-01-06 22:30:06 +01:00
Molkobain
1d7e4e1a42 N°5608 - Move unit tests to a dedicated folder and start reorganizing to match iTop folder structure 2023-01-06 22:30:06 +01:00
Molkobain
083d19c688 📝 PHPDoc 2023-01-05 21:19:56 +01:00
Eric Espie
4e9a34d8d8 Merge branch 'support/3.0' into develop 2023-01-05 09:25:11 +01:00
Eric Espie
524e65a29b 📝 fix PHPDoc 2023-01-05 09:23:48 +01:00
Pierre Goiffon
25048ba0cc N°3200 🎨 Replace !empty() by \utils::IsNotNullOrEmptyString
Thanks Molkobain !
2023-01-05 09:07:13 +01:00
Pierre Goiffon
4f7a1ea9da N°3200 Datatable "filter list" icon : specific query for 1,n and n,n objects relations
This specific query passes attcodes list to display to UI.php, but this endpoint doesn't handle it yet. Adding this enhancements would require too much time for now (datatables legacy code), hopefully a refactoring work will be done soon and we'll get back to it !

Note that dedicated ContextTag is renamed from Search to ObjectSearch
2023-01-04 16:23:52 +01:00
Pierre Goiffon
a62a373f64 N°3200 ♻️ Refactor relations attcode list generation
The existing code was added in N°2334
Its generic part is now callable in the MetaModel::GetAttributeLinkedSetIndirectDatatableAttCodesToDisplay method
2023-01-04 16:23:52 +01:00
Pierre Goiffon
6b23171d65 N°3200 New "Filter list" icon in datatables
Every datatables will open its query in a new UI.php?operation=search page
2023-01-04 16:23:52 +01:00
Pierre Goiffon
09b69728a0 📝 Version history : highlight for major versions 2023-01-04 14:24:27 +01:00
Pierre Goiffon
bc0dbc7860 📝 Version history : add link to versions details on the wiki 2023-01-04 14:20:34 +01:00
Eric Espie
a693c343e8 N°4756 - Ease extensibility for CRUD operations : Reduce events after review 2023-01-04 14:18:01 +01:00
Eric Espie
cd17eb484a N°4756 - Ease extensibility for CRUD operations : Reduce events after review 2023-01-04 12:23:23 +01:00
Vincent Dumas
259eb60f97 N°5841 - Non-admin managing User can't see Administrator by default (#383) 2023-01-03 14:08:28 +01:00
Eric Espie
1afa3e2003 Merge branch 'support/3.0' into develop 2023-01-03 08:52:10 +01:00
Eric Espie
df7d7c877d N°5490 - PHP 8.0: Fix crash of bulk modify 2023-01-03 08:50:18 +01:00
Eric Espie
4c4d7c12e8 N°4756 - Ease extensibility for CRUD operations : Coherency in method parameters 2023-01-02 14:23:36 +01:00
Eric Espie
97c3cacfaa N°4756 - Ease extensibility for CRUD operations : Pass DeletionPlan to EVENT_DB_CHECK_TO_DELETE 2023-01-02 14:20:17 +01:00
Eric Espie
98ec568788 N°4756 - Ease extensibility for CRUD operations : Events not sent in overridable methods 2023-01-02 14:06:02 +01:00
Eric Espie
626ba7ee66 📝 PHPDoc reworked packages 2022-12-30 14:17:57 +01:00
Eric Espie
3920f84f45 📝 PHPDoc reworked for auto-doc 2022-12-30 10:11:11 +01:00
Eric Espie
30d2557342 Merge branch 'support/3.0' into develop 2022-12-29 16:00:23 +01:00
Eric Espie
4bcad431aa 📝 Change packages for auto-documentation 2022-12-29 15:59:59 +01:00
Eric Espie
f3cea730af Merge branch 'support/3.0' into develop
# Conflicts:
#	application/applicationextension.inc.php
2022-12-29 14:40:26 +01:00
Eric Espie
5fe32aac15 Merge branch 'support/2.7' into support/3.0
# Conflicts:
#	application/applicationextension.inc.php
2022-12-29 12:40:37 +01:00
Eric Espie
88d9a29599 📝 Change packages for auto-documentation 2022-12-29 12:38:31 +01:00
Eric Espie
92a36dcfdd 📝 Change packages for auto-documentation 2022-12-29 12:24:56 +01:00
Eric Espie
23b4d4cb6b Merge branch 'support/2.7' into support/3.0 2022-12-28 09:52:52 +01:00
Eric Espie
b37e74b407 📝 Change packages for auto-documentation 2022-12-28 09:51:46 +01:00
Eric Espie
0fad249124 N°5564 - ⬆️ twig bump 2022-12-26 09:42:56 +01:00
Pierre Goiffon
94d7d94bff 💡 Fix PHPDoc in \ApplicationMenu::GetSubMenuNodes 2022-12-23 16:14:38 +01:00
Vincent Dumas
48645dd519 N°4852 - Navigation menu: Display "+" dict entries as a tooltip on sub-nodes (#380) 2022-12-23 15:48:52 +01:00
Stephen Abello
9df9077595 N°4737 - Adjust progress bar position in iTop hub connector 2022-12-23 15:45:58 +01:00
Stephen Abello
756241918e N°4737 - Adjust button position in iTop hub connector 2022-12-23 15:44:22 +01:00
Stephen Abello
f819fdae98 N°5507 - Fix impact analysis page title when displaying "Depends on" graph 2022-12-23 15:09:13 +01:00
Stephen Abello
7127b192a2 N°5822 - Do not display the tab separator in scroll mode when there is only one tab 2022-12-23 14:28:22 +01:00
Eric Espie
1cac189077 N°4756 - Ease extensibility for CRUD operations : Fix CRUD after review 2022-12-23 10:37:08 +01:00
Eric Espie
0383fb124b N°4756 - Ease extensibility for CRUD operations : Events renamed 2022-12-22 15:28:31 +01:00
Eric Espie
f378fd3d72 N°4756 - Ease extensibility for CRUD operations : Events renamed 2022-12-22 15:22:06 +01:00
Eric Espie
44fac686f9 N°4756 - Ease extensibility for CRUD operations : Events renamed 2022-12-22 15:16:52 +01:00
Molkobain
eff2afbc69 N°4756 - Improve PHPDoc and type hints 2022-12-22 10:25:24 +01:00
Molkobain
86b3129c08 N°4756 - Fix event listeners for modules
Counter part is that we can no longer fire events before MetaModel::Startup
2022-12-22 10:21:39 +01:00
Eric Espie
bdd5741290 N°4756 - Ease extensibility for CRUD operations : Protect against update loops 2022-12-22 08:56:40 +01:00
Eric Espie
53091fc5df N°4756 - Ease extensibility for CRUD operations : 🌐 dictionaries 2022-12-21 09:03:16 +01:00
Eric Espie
06db3561dc N°4756 - Ease extensibility for CRUD operations : Protect against update loops 2022-12-20 14:26:44 +01:00
Molkobain
5cb2f19ea0 N°4756 - Improve PHPDoc and type hints 2022-12-20 11:11:14 +01:00
Eric Espie
a668b92051 N°4307 - Add option verify_peer => not novalidatecert in SMTP 2022-12-20 10:27:41 +01:00
bdalsass
eda11e97e3 N°5074 - Linkset datatable actions row improvements
Linkset updates, feedbacks 09/12:

- Distinguish action label and tool tip message
- Change default action button label Ok => Confirm
- Use action label in place of default confirm button label
- Attach do not show again preference key to table instance
- Obsolescence flag for link selection (select all)  issue correction
2022-12-20 10:10:25 +01:00
Molkobain
64146b6e1b N°5655 - Add dedicated dict entries for bulk modify/delete actions on linksets 2022-12-20 09:43:25 +01:00
Eric Espie
7b85cb92b0 💡 phpdoc 2022-12-19 09:27:46 +01:00
Eric Espie
a28fcb7c8c N°4756 - Ease extensibility for CRUD operations : Event Service - 💄 better model view display 2022-12-16 11:47:26 +01:00
Eric Espie
f8ec180a0f N°4756 - Ease extensibility for CRUD operations : Event Service - 💚 Fix CI 2022-12-16 11:42:40 +01:00
Eric Espie
0810a113c1 N°4756 - Ease extensibility for CRUD operations : Event Service - Moved Events source code under sources/application/events 2022-12-16 11:30:00 +01:00
Eric Espie
1b4a21fafa N°4756 - Ease extensibility for CRUD operations : Event Service - Moved Events source code under sources/application/events 2022-12-16 11:28:52 +01:00
Eric Espie
fae857175b N°4756 - Ease extensibility for CRUD operations : Event Service - Changed CheckToWrite and CheckToDelete events and add two new events to set attributes flags 2022-12-16 11:07:41 +01:00
Eric Espie
9c21ca2ee6 💡 phpdoc @package ventilation and @api on methods 2022-12-16 11:03:15 +01:00
Pierre Goiffon
ccdb5ec79f Merge remote-tracking branch 'origin/support/3.0' into develop 2022-12-15 15:37:09 +01:00
Pierre Goiffon
cb6232cc05 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2022-12-15 15:36:43 +01:00
Pierre Goiffon
0d49c605e2 💡 Fix \DBSearch::FromOQL phpdoc + modifiers order 2022-12-15 15:36:14 +01:00
Eric Espie
207d312b12 N°4756 - Ease extensibility for CRUD operations : Event Service - Moved ApplicationEvents 2022-12-15 14:03:23 +01:00
Eric Espie
08d83478da N°4756 - Ease extensibility for CRUD operations : Event Service - Revert default value for utils::GetClassesForInterface() 2022-12-15 13:54:51 +01:00
Eric Espie
8665f4ff06 N°4756 - Ease extensibility for CRUD operations : Event Service - PHP Doc 2022-12-15 13:46:06 +01:00
Pierre Goiffon
6a5beb89cc 📝 Version history : 2.7.0-1 was a major version
2.7.0 community was never released
2022-12-15 11:55:51 +01:00
Molkobain
3d09e3b57b Merge remote-tracking branch 'origin/support/3.0' into develop 2022-12-14 20:35:47 +01:00
Molkobain
6d47b0f4f8 N°4517 - PHP 8.0: Fix undefined array key due to type cast being done before coalesce 2022-12-14 20:34:40 +01:00
Molkobain
27cf8159d0 N°5388 - PHP 8.2: Fix dynamic properties 2022-12-14 19:55:08 +01:00
Eric Espie
314dd7d7ad Merge remote-tracking branch 'origin/develop' into develop 2022-12-14 15:50:17 +01:00
Eric Espie
6412f1ab23 Merge branch 'support/3.0' into develop
# Conflicts:
#	sources/Core/Email/EmailSwiftMailer.php
2022-12-14 15:48:52 +01:00
jbostoen
32ee1a8226 N°5793 - HTML Sanitizer: Allow 'start' attribute in 'ol' tag (#368)
* * Extended allowed attributes on ol, li (based on W3Schools)

* Change unit test case label

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2022-12-14 15:46:59 +01:00
Eric Espie
e1c28a5c22 Merge branch 'support/2.7' into support/3.0 2022-12-14 15:45:27 +01:00
Eric Espie
296ee019a0 Merge branch 'support/2.7' into support/3.0 - nothing to do 2022-12-14 15:44:05 +01:00
Thomas Casteleyn
fe997d1254 N°2013 Fix setup when doing update to new DB (#351)
During the setup there are some queries done to the DB.
Only the config file parameters were used, meaning changing those parameters in the setup form wasn't effective :(
This is a regression made in 2.5.0 with N°1260 (MySQL TLS)
2022-12-14 10:29:58 +01:00
Eric Espie
e38951ebae N°4756 - Ease extensibility for CRUD operations : Event Service - Changed some tags 2022-12-14 09:43:41 +01:00
Molkobain
7c2f8f4d93 N°5765 - Setup: Never cache folder permissions test response (#374) 2022-12-14 09:33:54 +01:00
Pierre Goiffon
1f76ff940d N°5797 Replace wrong config load (#338) 2022-12-13 18:23:09 +01:00
Pierre Goiffon
fde6cac7a2 📝 SECURITY: Add disclosure policy (#372)
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2022-12-13 18:15:25 +01:00
jbostoen
93209265f2 N°5792 - Update dutch translations thanks to @jbostoen 2022-12-13 17:49:32 +01:00
Eric Espie
b50675fc29 N°4756 - Ease extensibility for CRUD operations : Event Service - Fix CI 2022-12-13 13:44:20 +01:00
Eric Espie
deb6d1fd9a N°4756 - Ease extensibility for CRUD operations : Event Service - move event description to an object 2022-12-13 11:55:05 +01:00
Eric Espie
432579657c N°4756 - Ease extensibility for CRUD operations : Event Service - cleanup 2022-12-13 11:52:21 +01:00
Molkobain
f09bd5fdff N°5655 - Add icon to linkset bulk actions in case they are set as shortcut actions 2022-12-13 11:47:32 +01:00
Molkobain
4f37a9e447 N°5655 - Router: Add method to dump available routes 2022-12-13 11:47:32 +01:00
Molkobain
32297d93dd N°5655 - Add utils::ToSnakeCase() method 2022-12-13 11:47:32 +01:00
Pierre Goiffon
7083e1263d 📝 Version history : add 2.7.8 2022-12-13 10:53:59 +01:00
Eric Espie
329d1a7c41 N°4756 - Ease extensibility for CRUD operations : Event Service - documentation 2022-12-13 10:43:05 +01:00
Eric Espie
22749caeb4 N°4756 - Ease extensibility for CRUD operations : Event Service - renamed service setup interface 2022-12-13 10:19:25 +01:00
Eric Espie
baaa4474f9 N°5102 - Allow to send emails using GSuite SMTP and OAuth - cleanup unnecessary include 2022-12-13 10:01:57 +01:00
Molkobain
af804a9dc1 N°5655 - Enable bulk modify/delete on linksets 2022-12-12 23:44:31 +01:00
Molkobain
899f4fdc0a N°5655 - Fix regression from e5d67d22
Can't use "let" as this snippet seems to be evaluated several with the same variables 😭
2022-12-12 23:38:55 +01:00
Molkobain
8cced3a79e N°5655 - Refactor of menu actions generation to reduce duplicated code 2022-12-12 17:25:48 +01:00
Eric Espie
bb26e48d38 Update version to next release 2.7.9 2022-12-12 16:19:42 +01:00
Pierre Goiffon
4eb45f1b34 N°5779 iTopDesignFormatTest : test file conversion from version N to N
For example for a 3.1 version content will try to convert the file from 3.0 to 3.1
2022-12-12 10:48:59 +01:00
Pierre Goiffon
c2f503e2ac N°5779 update-xml : update code using iterators 2022-12-12 09:28:42 +01:00
Pierre Goiffon
e4e654100e N°5779 update-xml : check we are indeed targeting all of the design files 2022-12-09 18:19:15 +01:00
Pierre Goiffon
3270c3f775 🔨 N°5779 update-xml : if XML files already at current version, tries to convert from previous one 2022-12-08 17:08:00 +01:00
Eric Espie
7f88a6aa1a Merge branch 'support/3.0' into develop
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/src/Twig/AppExtension.php
2022-12-08 08:39:55 +01:00
Eric Espie
26042b990f Merge branch 'support/2.7' into support/3.0
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/src/Twig/AppExtension.php
2022-12-08 08:34:16 +01:00
Eric Espie
cf433f2f80 N°5725 - Twig update 'filter', 'map' and 'reduce' filters 2022-12-08 08:25:11 +01:00
Eric Espie
c02f84969a N°4756 - Ease extensibility for CRUD operations : Event Service 2022-12-08 08:21:13 +01:00
Eric Espie
bdebea62b6 N°5394 - use session for the FSM (use Session object) 2022-12-08 08:21:13 +01:00
Pierre Goiffon
4a22361f39 🔨 N°5633 update-xml.php : add new XML dir (/application and /core) 2022-12-07 17:40:48 +01:00
Pierre Goiffon
025f8b9dc3 🔨 N°5633 update-xml.php : if no version passed, assuming current version 2022-12-07 17:40:48 +01:00
Benjamin Dalsass
9defedb862 N°5073 - Issue, row actions listeners lost on refresh 2022-12-07 15:31:19 +01:00
Eric Espie
ae94e58a43 N°5725 - Twig update 'filter', 'map' and 'reduce' filters 2022-12-07 13:53:15 +01:00
Eric Espie
e4d8489292 Merge branch 'support/3.0' into develop
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/src/Twig/AppExtension.php
2022-12-07 13:52:42 +01:00
Eric Espie
f66692d5cf Merge branch 'support/2.7' into support/3.0
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/src/Twig/AppExtension.php
2022-12-07 13:44:54 +01:00
Eric Espie
2b917f6647 Merge branch 'support/2.7' into support/3.0
# Conflicts:
#	datamodels/2.x/itop-oauth-client/src/Service/PopupMenuExtension.php
2022-12-07 13:42:36 +01:00
Eric Espie
cda017fa4f N°5725 - Twig update 'filter', 'map' and 'reduce' filters 2022-12-07 13:37:52 +01:00
Pierre Goiffon
dad22f6f83 📄 Update Licenses 2022-12-07 11:04:33 +01:00
Eduardo Mozart de Oliveira
0579f1b1dc Add Windows / macOS auto-generated files to .gitignore (#367)
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2022-12-06 18:08:51 +01:00
Pierre Goiffon
a551bf0499 📝 CONTRIBUTING: Improvements (#371) 2022-12-06 17:19:44 +01:00
vdumas
4f36819ab2 Fix @param & @return comments for GetTagFromLabel 2022-12-06 16:43:55 +01:00
Stephen Abello
26359cf1ee Move datatable SCSS files under the same directory 2022-12-06 16:14:42 +01:00
Stephen Abello
00a25f3e8b Move datatable SCSS files under the same directory 2022-12-06 16:10:58 +01:00
Molkobain
2d122448c8 N°5655 - Fix is_callable() call for PHP 8.0+ 2022-12-06 16:04:26 +01:00
Molkobain
e5d67d2219 N°5655 - Small refactor for better reading 2022-12-06 14:07:15 +01:00
Molkobain
4ac0de5fff N°5655 - Code format 2022-12-06 14:07:15 +01:00
bdalsass
dbe7fae82e Feature/modals - generic modal API (#373)
Default modal JS Implementation:

Add title option
Add buttons option
Change template cloning
Confirmation Modal:

Add implementation
Do not show again functionality
Web Page:

Add blocks array with twig loop insertion
2022-12-06 10:18:59 +01:00
Stephen Abello
98a53a46f0 Fix "Configure this list" modal content display 2022-12-02 15:33:35 +01:00
Eric Espie
e6946d33e6 Merge branch 'support/3.0' into develop 2022-12-02 11:21:04 +01:00
Eric Espie
9474f43d61 Merge branch 'support/2.7' into support/3.0
# Conflicts:
#	datamodels/2.x/itop-oauth-client/src/Service/PopupMenuExtension.php
2022-12-02 11:20:31 +01:00
Eric Espie
9077f7ba37 N°5430 - OAuth authentication : customize redirect landing URL - remove unnecessary parameter to JS function OAuthConnect 2022-12-02 11:17:01 +01:00
Eric Espie
3548251997 Merge branch 'support/3.0' into develop 2022-12-02 09:27:40 +01:00
Eric Espie
4d365c8a44 Merge branch 'support/2.7' into support/3.0 2022-12-02 09:26:40 +01:00
Eric Espie
957ff40f30 N°5155 - Email by SMTP with self-signed certificate (changed default values to the previous behaviour) 2022-12-02 09:25:53 +01:00
Eric Espie
93e0dde1a1 Merge branch 'support/3.0' into develop
# Conflicts:
#	sources/Core/Email/EmailSwiftMailer.php

Email by SMTP with self-signed certificate ported from EmailSwiftMailer to EmailLaminas
2022-11-30 14:35:13 +01:00
Eric Espie
7bb12c35ea Merge branch 'support/2.7' into support/3.0
# Conflicts:
#	core/config.class.inc.php
2022-11-30 14:27:32 +01:00
Eric Espie
aff9c7748b N°5155 - Email by SMTP with self-signed certificate 2022-11-30 14:18:11 +01:00
Eric Espie
e518d34bc9 N°5553 - OAuth 2 : Hide Client Secret
* client_id is now 255 chars (AttributeString)
 * client_secret is now 64 chars (AttributePassword) and cannot be anymore in the uniqueness rules
 * The modification of redirect_url, client_id or client_secret change the status to inactive and generate a session message to ask for token regeneration
2022-11-30 14:15:37 +01:00
Eric Espie
f02e95e8bf Merge branch 'support/3.0' into develop
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/src/Twig/AppExtension.php
#	test/twig/test.html
#	test/twig/test.html.twig
2022-11-30 13:57:28 +01:00
Eric Espie
51a305b445 N°5725 - Twig update 'filter', 'map' and 'reduce' filters (twig functions signature changed) 2022-11-30 13:40:44 +01:00
Eric Espie
5577f4a62e Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2022-11-30 13:36:18 +01:00
Eric Espie
f0141530b9 N°5725 - Twig update 'filter', 'map' and 'reduce' filters (+1 squashed commits)
Squashed commits:

[00148dec5] N°5725 - Twig update 'filter', 'map' and 'reduce' filters
2022-11-30 13:28:33 +01:00
Vladimir Kunin
98bcfeb2ba 🐛 N°5369 - Fix BrowseBrick tree "opening_target" mode for "self" and "new" values (#299)
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2022-11-30 11:45:06 +01:00
Pierre Goiffon
669c69ff39 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-11-29 19:04:04 +01:00
Pierre Goiffon
92a802947d Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	setup/wizardsteps.class.inc.php
2022-11-29 19:02:51 +01:00
xtophe38
ce5096a896 N°5758 Change setup test for GDPR consent (#336)
We were using SetupUtils::IsProductVersion, but this was blocking for certain packages like TeemIP standalone.
After this change we are now relying on a new method : \SetupUtils::IsConnectableToITopHub. It will check the iTop Hub Connector module presence instead.
2022-11-29 19:00:17 +01:00
Pierre Goiffon
c5e00194b7 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	pages/ajax.render.php
2022-11-29 18:22:35 +01:00
Pierre Goiffon
5efd45eafc Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	pages/ajax.render.php
2022-11-29 18:18:24 +01:00
Pierre Goiffon
23e0ed5e56 N°4449 Test for FPD detection in RuntimeDashboard 2022-11-29 18:10:17 +01:00
Pierre Goiffon
d412a52fcc N°4449 Fix FPD in dashboard export/import 2022-11-29 18:10:17 +01:00
Eric Espie
ded2f8af9a Fix CI 2022-11-29 14:39:16 +01:00
Molkobain
084f3ec52b N°5655 - Continue work on CombodoModal Part 2 2022-11-28 15:57:25 +01:00
Molkobain
bedc8bbf46 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-11-26 00:25:05 +01:00
Molkobain
cf5745b985 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	core/pdfbulkexport.class.inc.php
2022-11-26 00:09:10 +01:00
Molkobain
3e18ad590f Fix image attributes not being visible in PDF exports 2022-11-25 19:30:35 +01:00
Eric Espie
b174aa9aeb Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	datamodels/2.x/itop-oauth-client/vendor/composer/InstalledVersions.php
#	datamodels/2.x/itop-oauth-client/vendor/composer/installed.php
2022-11-25 17:37:01 +01:00
Pierre Goiffon
4ec3f22173 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	core/log.class.inc.php
2022-11-24 17:28:39 +01:00
Pierre Goiffon
34f03715b6 LogChannel : add missing @since 2022-11-24 17:27:30 +01:00
Eric Espie
ebf3f48584 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	datamodels/2.x/itop-oauth-client/vendor/composer/installed.php
2022-11-24 14:37:35 +01:00
Eric Espie
6a0cdb8705 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	datamodels/2.x/itop-oauth-client/vendor/composer/InstalledVersions.php
#	datamodels/2.x/itop-oauth-client/vendor/composer/installed.php
2022-11-24 14:35:51 +01:00
Eric Espie
22111bf667 N°5611 - Fix missing composer files in itop-oauth-client 2022-11-24 14:32:51 +01:00
Eric Espie
6d0c46595d N°5611 - Fix missing composer files in itop-oauth-client 2022-11-24 14:27:42 +01:00
Eric Espie
8db89c3d5f Merge remote-tracking branch 'origin/support/3.0' into develop 2022-11-24 13:58:52 +01:00
Eric Espie
1e2205ecb0 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2022-11-24 13:58:12 +01:00
Eric Espie
d292a6b0c3 N°5333 - OAuth and iTop url change 2022-11-24 13:55:36 +01:00
Eric Espie
74702c8d06 N°5430 - OAuth authentication : customize redirect landing URL 2022-11-24 13:55:36 +01:00
Eric Espie
649674c786 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	application/twigextension.class.inc.php
#	datamodels/2.x/authent-cas/composer.json
#	datamodels/2.x/authent-cas/composer.lock
#	datamodels/2.x/authent-cas/main.php
#	datamodels/2.x/authent-cas/vendor/apereo/phpcas/composer.json
#	datamodels/2.x/authent-cas/vendor/composer/ClassLoader.php
#	datamodels/2.x/authent-cas/vendor/composer/autoload_classmap.php
#	datamodels/2.x/authent-cas/vendor/composer/autoload_psr4.php
#	datamodels/2.x/authent-cas/vendor/composer/autoload_real.php
#	datamodels/2.x/authent-cas/vendor/composer/autoload_static.php
#	datamodels/2.x/authent-cas/vendor/composer/installed.json
#	datamodels/2.x/itop-portal-base/portal/src/Twig/AppExtension.php
#	lib/apereo/phpcas/source/CAS.php
#	lib/apereo/phpcas/source/CAS/Client.php
#	lib/apereo/phpcas/source/CAS/Languages/Galego.php
#	lib/apereo/phpcas/source/CAS/Languages/Portuguese.php
#	lib/symfony/cache-contracts/InstalledVersions.php
#	lib/symfony/cache-contracts/installed.php
#	lib/symfony/cache-contracts/platform_check.php
#	sources/Application/UI/Base/Layout/NavigationMenu/NavigationMenu.php
#	sources/application/TwigBase/Twig/Extension.php
2022-11-24 13:17:09 +01:00
Benjamin Dalsass
038e6d7f3f datatable - row actions - allow returning null 2022-11-24 09:16:02 +01:00
Pierre Goiffon
df4a205ba0 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2022-11-24 09:00:56 +01:00
Pierre Goiffon
e9c91d986d 📝 CONTRIBUTING : fix typo (stash in stead of squash)
Thanks Molkobain ! (https://github.com/Combodo/iTop/pull/371#discussion_r1030759606)
2022-11-24 09:00:32 +01:00
Eric Espie
feafd5e2c9 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
N°5741 - remove use of get_config_parameter and get_module_setting in Twig

# Conflicts:
#	application/twigextension.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/Twig/AppExtension.php
#	sources/application/TwigBase/Twig/Extension.php
2022-11-23 17:38:27 +01:00
bdalsass
bdb29fd99a Issue/5074 - Routage, Block UI relations, Links row actions (#369)
* datatable row actions

Below is a sample of extra param to enable feature:

		$aExtraParams['row_actions'] = [
			[
				'tooltip'        => 'remove an element',
				'icon_css_class' => 'fa-minus',
				'js_row_action'   => 'console.log("You clicked the remove button");',
				'confirmation'  => [
					'message'                  => 'UI:ConfirmationMessage',
					'message_row_data'         => "name",
					'remember_choice_pref_key' => 'remove_do_not_show_again',
				],
			],
			[
				'tooltip'        => 'open in new tab',
				'icon_css_class' => 'fa-external-link-square-alt',
				'on_action_js'   => 'window.open("http://localhost/itop-branchs/dev/pages/UI.php?operation=details&class=UserRequest&id=" + aData.id + "&c[menu]=UserRequest%3AOpenRequests");',
			],
			[
				'tooltip'        => 'other actions',
				'icon_css_class' => 'fa-ellipsis-v',
				'on_action_js'   => 'console.log(event);',
			],
		];

* Contrôleur pour la suppression et le détachement de liens
* Block UI pour l'édition des relations
* Block UI pour la visualisation des relations
* Boutons d'actions pour la suppression et le détachement de liens
* Gestion dialogue de confirmation pour les row actions
2022-11-23 16:10:34 +01:00
Molkobain
a0a5037554 Merge remote-tracking branch 'origin/feature/5655-edit-object-in-modal' into develop
# Conflicts:
#	pages/ajax.render.php
2022-11-23 15:59:23 +01:00
Eric Espie
70a6b276ca Merge branch 'issue/5685-UpgradeApereoPHPCas' into support/2.7 2022-11-23 15:58:36 +01:00
Eric Espie
f77361ceb2 N°5685 - Upgrade apereo/phpcas 2022-11-23 15:53:43 +01:00
Eric Espie
75f4751b82 N°5741 - remove use of get_config_parameter in Twig 2022-11-23 15:09:20 +01:00
Eric Espie
6af62c4a50 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-11-23 11:07:09 +01:00
Eric Espie
65b6c0f4ea Fix CI 2022-11-23 11:04:58 +01:00
Eric Espie
c05684945f Fix CI 2022-11-23 10:34:24 +01:00
Eric Espie
6b1c033ec1 Fix CI 2022-11-23 09:54:30 +01:00
Eric Espie
4f14d1fb23 N°4974 - Session rework 2022-11-22 15:38:02 +01:00
odain
0ef5e9d3ff N°5620 - Hide the organization filter with a conf parameter 2022-11-22 13:57:42 +01:00
odain
1c705bdb94 ci: fix ItopDataTestCase CreateUser contactid unset 2022-11-22 13:57:37 +01:00
odain
28a1324d3a ci: adapt impersonate test to any friendlyname output 2022-11-22 13:57:37 +01:00
odain
af51f05c56 ci: add CreateContactlessUser method in test framework 2022-11-22 13:57:37 +01:00
Pierre Goiffon
1a7b3f0631 N°5563 Fix setup crash when notices enabled and invalid edit_mode value 2022-11-22 09:56:31 +01:00
Eric Espie
4ff82f5c12 Fix CI 2022-11-21 09:54:36 +01:00
Eric Espie
e2e08351b0 Fix CI 2022-11-21 09:51:11 +01:00
Pierre Goiffon
d83c45812d N°5405 UpdateXML update tool now do a full file conversion 2022-11-21 09:15:43 +01:00
Pierre Goiffon
6258b97b92 N°5563 Convert datamodels files with new 3.1 conversion methods 2022-11-21 09:15:23 +01:00
Pierre Goiffon
3aab49c372 N°5563 AttributeLinkedSet & LinkedSetIndirect conversions between 3.0 and 3.1 formats 2022-11-21 09:15:17 +01:00
Pierre Goiffon
ecfae75e10 Setup : catch also fatal error during conversion 2022-11-18 20:15:00 +01:00
Pierre Goiffon
f60331e0f9 iTopFormatTest : better info on conversion error 2022-11-18 20:14:59 +01:00
Pierre Goiffon
8f6065d4f4 ♻️ iTopDesignFormatTest : get version directly from expected content 2022-11-18 19:15:56 +01:00
Eric Espie
52c984d5e7 N°4756 - Fix Unit tests 2022-11-18 10:49:59 +01:00
Eric Espie
6585f717c5 N°4756 - Event service initialization 2022-11-16 21:03:01 +01:00
Molkobain
363a1507cf Merge remote-tracking branch 'origin/support/3.0' into develop 2022-11-16 19:06:51 +01:00
Molkobain
8c95bd5a65 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2022-11-16 19:04:21 +01:00
Romain Quetiez
b56f2f56f1 N°5704 - Fix compatibility with PHP <7.3 (HEREDOC syntax) 2022-11-16 17:12:53 +01:00
Eric Espie
0bba9b0fad Merge remote-tracking branch 'origin/support/3.0' into develop 2022-11-16 13:57:07 +01:00
Eric Espie
282d47aed4 N°5727 - REST API/get_related: Seemingly wrong results, when using [impacts, up] with [redundancy: true] 2022-11-16 13:51:38 +01:00
Eric Espie
2f8f0b658c N°5722 - code hardening 2022-11-16 09:40:19 +01:00
Eric Espie
e4884470ad Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2022-11-16 09:38:31 +01:00
Eric Espie
68d44fa981 N°5724 - code hardening 2022-11-16 09:32:47 +01:00
Eric Espie
7e5307bd96 N°4867 - "Twig content not allowed" error 2022-11-16 09:31:42 +01:00
Stephen Abello
75c8738538 Add an event to refresh Datatables 2022-11-15 10:08:29 +01:00
Molkobain
a553616ea2 N°5655 - Introduce auto-routing mechanism for backoffice pages (Part 1) 2022-11-15 08:42:27 +01:00
Molkobain
8c14d36dfe TwigBase: Allow operation param to be in snake case in order to handle/redirect existing operations 2022-11-15 08:42:26 +01:00
Molkobain
4486842169 N°5655 - Align "activity panel" and "preferences" ajax operation code to new convention 2022-11-15 08:42:26 +01:00
Molkobain
f5b216fd9a N°5655 - Refactor object modification logic to a dedicated controller for both page / modal modes 2022-11-15 08:42:26 +01:00
Molkobain
dcdce52608 N°5655 - Add new sanitization filters for routing
* utils::ENUM_SANITIZATION_FILTER_OPERATION filter to enable operation "namespaces" in current operations for better reading
  * utils::ENUM_SANITIZATION_FILTER_ROUTE filter for upcoming auto routing
2022-11-15 08:42:26 +01:00
Molkobain
73139e82cb N°5655 - Switch backoffice controllers' methods to non-static and now extend an abstract controller 2022-11-15 08:42:25 +01:00
Molkobain
36ec455e6d N°5655 - Refactor portal modal dialog helper to global helper for both GUIs 2022-11-15 08:42:25 +01:00
Pierre Goiffon
df3fc8be7f N°5563 XML for new relations UI : constants for AttributeLinkedSet relation_type values 2022-11-14 18:16:24 +01:00
Pierre Goiffon
3191caa700 N°5563 XML for new relations UI : mock methods in AttributeDefinition 2022-11-14 18:10:48 +01:00
Molkobain
9a3324de07 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	setup/modelfactory.class.inc.php
2022-11-08 21:18:11 +01:00
Molkobain
5839d0e8e3 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	test/ItopTestCase.php
2022-11-08 21:14:50 +01:00
DudekArtur
07fc8c1e5b N°5706 - Update polish translations thanks to @DudekArtur ! (#357)
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2022-11-08 21:06:25 +01:00
Romain Quetiez
cd010afb48 N°5704 - Unit tests on XML assembly (#329)
* Add a complete test suite for XML assembly

* Add a complete test suite for XML assembly

* Dispatched the test of GetDelta into real unit tests

* Add test for GetDelta on a rename operation

* Add comments on a weird case and a case on rename

* Update XML version after rebase from develop to support/2.7

* Fix phpdoc about coverage

* Remove ModelFactory::GetRootDirs and ItopTestCase::RecurseRmDir+CreateTmpDir+RecurseMkDir+RecurseCopy, that were meant to be introduced in iTop 3.0 and have been copied here by mistake, when rebasing the branch from develop to 2.7.0

* Update test/ItopTestCase.php

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

* Update test/setup/ModelFactoryTest.php

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

* Update test/ItopTestCase.php

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

Co-authored-by: Pierre Goiffon <pierre.goiffon@combodo.com>
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2022-11-08 19:43:07 +01:00
Pierre Goiffon
e14cf23ecb Merge remote-tracking branch 'origin/support/3.0' into develop 2022-11-08 18:14:55 +01:00
Pierre Goiffon
be5a252be7 iTopDesignTestFormat : improve code readability 2022-11-08 18:14:20 +01:00
Pierre Goiffon
974dddbe19 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-11-08 17:29:24 +01:00
Molkobain
5e0698b7f3 Add support for "Ctrl + Enter" and "Meta (Cmd) + Enter" submit on multi-line fields 2022-11-08 17:28:42 +01:00
Pierre Goiffon
073488a7bd Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2022-11-08 17:27:26 +01:00
Lars Kaltefleiter
2dccedf8d7 🐛 navigation menu z-index workaround for safari
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2022-11-08 17:25:49 +01:00
Pierre Goiffon
0cf8d731bb Rename iTopDesignFormat test class 2022-11-08 15:59:14 +01:00
acognet
3d083fca05 N°5671 - Export data in Excel does not work with XLSX files 2022-11-04 18:11:59 +01:00
Eric Espie
e496513275 cleanup code (php warning) 2022-10-31 15:09:16 +01:00
Eric Espie
e70e9de1c8 N°4756 - restrict readonly mode to events only for CheckToWrite (keep legacy calls unprotected) 2022-10-31 12:15:52 +01:00
Benjamin Dalsass
fcdfd54066 N°4454 - Measuring the use of the queryphrase book 2022-10-25 14:34:42 +02:00
Pierre Goiffon
efdf780c79 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-10-25 11:58:56 +02:00
Pierre Goiffon
4e7df37931 📝 jqueryui.scss : add comment to inform it is deprecated 2022-10-25 11:54:02 +02:00
Benjamin Dalsass
e90335e943 N°4454 - Measuring the use of the queryphrase book 2022-10-25 10:37:43 +02:00
Molkobain
f0bce86e28 N°4765 - Revert unwanted change on ActionEmail::status values tooltip translations 2022-10-24 23:15:41 +02:00
Pierre Goiffon
fac689ecf7 N°5651 Fix JS method GetAbsoluteUrlModulePage() not reporting parameters values
This JS method is legacy and never used with parameters, so we are choosing to fix it only in the develop branch.
2022-10-21 16:19:10 +02:00
Pierre Goiffon
6e8949a880 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-10-20 16:47:38 +02:00
Pierre Goiffon
4b670cfa90 N°5625 Fix crash when opening DocumentFile in ES language
Regression introduced in 3.0.1 with a merge : 67fa156c
The % was removed in 530ec111 but we kept the call to Dict::Format, and the merge restored the % in ES dict

A larger fix will be done later with N°5491
2022-10-20 16:46:37 +02:00
Molkobain
7028b2f76c N°4703 - Fix english translations of "in-person" 2022-10-19 17:24:05 +02:00
Molkobain
a547358be4 N°5270 - Move cURL from optional to mandatory PHP extension as it is required for authent-cas dependency "apereo/phpcas" 2022-10-19 17:22:23 +02:00
Molkobain
ddcff1f8ba Fix typo 2022-10-19 17:06:49 +02:00
Molkobain
1d7c527ea1 Activity panel: Fix compose button opening log picker menu when only 1 log available 2022-10-19 17:06:49 +02:00
Thomas Casteleyn
4baff1b3d1 N°4703 Add Dutch translations to new origins (#352)
* Add Dutch translations

* Update description
2022-10-19 16:51:48 +02:00
Molkobain
9c4f1419b1 N°4703 - Add "chat" / "in person" as possible "origin" value for tickets 2022-10-19 16:51:48 +02:00
Molkobain
848c4ca204 N°4798 - Align attribute "description" type for Service class 2022-10-19 16:31:07 +02:00
Molkobain
e9aa9cc8df N°5550 - Add missing french translation for "Other Transitions" button (#348)
* Fix typo

* N°5550 - Add missing french translation for "Other Transitions" button
2022-10-19 16:29:00 +02:00
acognet
2eadb632df N°5618 - Setup : Compatibility PHP 8.1 2022-10-19 09:21:16 +02:00
acognet
a57056a035 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-10-18 09:18:57 +02:00
acognet
87db88254b N°4517 - PHP 8.1 compatibility - Fix uppercase 2022-10-17 21:34:26 +02:00
acognet
c1dde51404 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-10-17 16:35:19 +02:00
acognet
5ddc006f51 N°4517 - PHP 8.1 compatibility 2022-10-17 16:27:42 +02:00
acognet
1fcb3af4c0 N°5618 - Setup : Compatibility PHP 8.1 2022-10-14 17:09:35 +02:00
acognet
9ccff4f8de Fix comment. filter created only since 3.1. 2022-10-14 09:27:59 +02:00
BenGrenoble
1e0fc4714d Correction of the system tooltip, following discussion with Molkobain 2022-10-13 09:31:09 +02:00
Eric Espie
8fc8342358 Revert default value for AttributeDateTime 2022-10-12 10:28:10 +02:00
Eric Espie
14673ff1f5 Return HTTP 400 when operation is not found (instead of 404 page not found) 2022-10-11 15:24:54 +02:00
Pierre Goiffon
ae95cf37f5 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-10-11 14:29:26 +02:00
Pierre Goiffon
85a72d6a10 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2022-10-11 14:29:07 +02:00
Pierre Goiffon
189ca3c555 🚚 Move visual test file to the dedicated directory 2022-10-11 14:28:44 +02:00
Eric Espie
567f333017 Remove default value for AttributeDateTime 2022-10-10 14:28:55 +02:00
BenGrenoble
acd8944aec Add tooltip on System 2022-10-06 16:01:30 +02:00
Pierre Goiffon
5b1b5b0cc1 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-10-03 15:56:16 +02:00
Pierre Goiffon
c02b36a00c Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2022-10-03 15:56:02 +02:00
Pierre Goiffon
1e1f1f78bf 📝 Backup : more details on check-backup parameters 2022-10-03 14:41:44 +02:00
Pierre Goiffon
1494604740 📝 Backup : move info from wiki to distrib file 2022-10-03 14:35:14 +02:00
Timmy38
30664bc5ec N°4765 fixing a typo 2022-09-30 11:30:42 +02:00
Molkobain
59b20ac1ee Fix typo 2022-09-27 20:14:53 +02:00
Eric Espie
f32f36fc74 N°5551 - System information database size is way off 2022-09-26 11:51:51 +02:00
bdalsass
5157f511fc N°5073 - Implements line actions in a datatable (#337)
* datatable row actions

Below is a sample of extra param to enable feature:

		$aExtraParams['row_actions'] = [
			[
				'tooltip'        => 'add an element',
				'icon_css_class' => 'fa-plus',
				'css_class'      => 'ibo-is-success',
				'level'          => 'secondary',
				'on_action_js'   => 'console.log(aData);',
			],
			[
				'tooltip'        => 'remove an element',
				'icon_css_class' => 'fa-minus',
				'css_class'      => 'ibo-is-danger',
				'level'          => 'secondary',
				'on_action_js'   => 'console.log("You clicked the remove button");',
			],
			[
				'tooltip'        => 'open in new tab',
				'icon_css_class' => 'fa-external-link-square-alt',
				'on_action_js'   => 'window.open("http://localhost/itop-branchs/dev/pages/UI.php?operation=details&class=UserRequest&id=" + aData.id + "&c[menu]=UserRequest%3AOpenRequests");',
			],
			[
				'tooltip'        => 'other actions',
				'icon_css_class' => 'fa-ellipsis-v',
				'on_action_js'   => 'console.log(event);',
			],
		];

* datatable row actions (update)

* datatable row actions (update)

* datatable row actions (add template role)

* datatable row actions (align actions)

* datatable row actions (change template factory make to make standard)

* datatable row actions (use trait to handle row actions)

* datatable row actions (row actions templates)

* datatable row actions (row actions templates)

* datatable row actions (row actions templates)

* datatable row actions (extends to static and form)

* datatable row actions (extends to static and form)

* datatable row actions (code review S)

* datatable row actions (code review S)

* datatable row actions (code review S)

* Update js/dataTables.main.js

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

* Update js/dataTables.main.js

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

* Update sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php

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

* Update templates/base/components/datatable/row-actions/handler.js.twig

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

* datatable row actions (code review M)

* Update js/dataTables.main.js

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

* Update js/dataTables.main.js

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

* Update js/dataTables.main.js

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

* Update sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php

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

* Update sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php

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

* Update sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php

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

* Update js/dataTables.main.js

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

* Update sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php

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

* Update sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php

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

* Update application/utils.inc.php

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

* datatable row actions (code review M2)

* datatable row actions (code review M3)

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2022-09-26 08:20:28 +02:00
Eric Espie
3196e105a1 N°4756 - small adjustments 2022-09-23 15:10:21 +02:00
Eric Espie
b9d865f881 N°4756 - Fix DBInsert/NoReload call order 2022-09-21 18:08:44 +02:00
Molkobain
2e39a650eb Merge remote-tracking branch 'origin/support/3.0' into develop 2022-09-21 17:48:57 +02:00
Molkobain
c753b57265 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2022-09-21 16:23:42 +02:00
Molkobain
583ab98210 Fix typo 2022-09-21 16:11:24 +02:00
Pierre Goiffon
e4f6a02de6 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	lib/composer/autoload_files.php
#	lib/composer/autoload_real.php
#	lib/composer/autoload_static.php
2022-09-21 14:28:25 +02:00
Pierre Goiffon
a5b5518533 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	core/cmdbsource.class.inc.php
#	test/core/CMDBSource/TransactionsTest.php
2022-09-21 14:21:28 +02:00
Pierre Goiffon
88d743b1cc N°5538 Make PHPUnit test fail if transaction opened but not closed 2022-09-21 14:05:27 +02:00
Pierre Goiffon
7ac4bc95bb ItopDataTestCase : improve log message 2022-09-21 11:51:07 +02:00
Eric Espie
fac455da48 N°4756 - Global event listeners 2022-09-21 10:34:38 +02:00
odain
b01627f39d Merge branch 'saas/3.0' into develop 2022-09-20 16:04:55 +02:00
odain-cbd
766c9f0e7e N°5305 - CSV import ergonomy PR (#332)
Reworked UI feedbacks on following attributes:
- enum
- date
- external key
2022-09-20 16:00:33 +02:00
Eric Espie
d1414a3f34 N°4756 - revert events for datamodel/2.x objects for backward compatibility 2022-09-20 15:35:59 +02:00
Molkobain
6386a302b2 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	composer.json
2022-09-20 13:44:11 +02:00
Molkobain
7071712a0a N°5535 - Fix PHP max version to 8.1 in composer for iTop 3.0.2+ 2022-09-20 13:42:45 +02:00
Molkobain
31454b2946 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	core/config.class.inc.php
#	setup/setuputils.class.inc.php
2022-09-20 13:27:20 +02:00
Molkobain
e55ac6002a Increase ITOP_VERSION to 3.0.3-dev 2022-09-20 13:22:18 +02:00
Molkobain
e9c6549847 N°5535 - Fix PHP not validated version to 8.1 in iTop 3.0.2+ 2022-09-20 13:17:59 +02:00
Eric Espie
4aad555649 N°4756 - Fix unit tests, 2022-09-20 12:18:53 +02:00
Eric Espie
93ee565d29 N°4756 - Fix unit tests 2022-09-19 16:59:51 +02:00
Eric Espie
6c097a128b N°4756 - Fix unit tests 2022-09-19 16:49:40 +02:00
Eric Espie
eea3f78cec N°4756 - Fix unit tests 2022-09-19 16:28:13 +02:00
Eric Espie
6c4caf64c8 N°4756 - Fix unit tests 2022-09-19 16:26:02 +02:00
Eric Espie
88f0013330 N°4756 - Fix unit tests 2022-09-19 16:09:02 +02:00
Eric Espie
aa31da34e5 N°5389 - Email Notification templates with AttributeLinkedSetIndirect failed 2022-09-19 15:30:24 +02:00
Eric Espie
71464f6d0e Merge branch 'develop' into feature/faf_event_service
# Conflicts:
#	core/cmdbobject.class.inc.php
#	core/dbobject.class.php
#	core/designdocument.class.inc.php
#	lib/composer/autoload_files.php
#	lib/composer/autoload_static.php
2022-09-19 09:45:08 +02:00
Eric Espie
2c265aab44 N°4756 - Ease extensibility for CRUD operations
- Change event order in CRUD
 - Add LinkHostObject for link update
 - Add events EVENT_SERVICE_DB_ARCHIVE and  EVENT_SERVICE_DB_UNARCHIVE
2022-07-08 10:19:54 +02:00
Eric Espie
fe28319d22 Merge from Develop 2022-07-06 14:28:55 +02:00
Eric Espie
7f6f5c0c3b Refactor event name 2022-06-30 16:24:13 +02:00
Eric Espie
682ab44dea Merge branch 'develop' into feature/faf_event_service 2022-06-30 14:22:52 +02:00
Eric Espie
6aef59e42d Merge branch 'develop' into feature/faf_event_service
# Conflicts:
#	core/dbobject.class.php
#	datamodels/2.x/itop-config/config.php
#	lib/composer/autoload_classmap.php
#	lib/composer/autoload_static.php
#	lib/composer/installed.php
2022-06-08 16:51:44 +02:00
Eric Espie
86024107af deprecated includes 2022-06-08 15:55:20 +02:00
Eric Espie
754f87fd0c debug log 2022-06-08 15:54:27 +02:00
Eric Espie
972e894bc5 debug log 2022-06-02 17:32:15 +02:00
Eric Espie
86a2db7e7f Remove AFTER_DISPLAY_PAGE Event 2022-06-02 17:23:07 +02:00
Eric Espie
4c31081de2 Remove AFTER_DISPLAY_PAGE Event 2022-06-02 17:19:43 +02:00
Eric Espie
23c95ebbf3 Block if event is not registered 2022-06-02 13:48:59 +02:00
Eric Espie
e77f21a0b5 refactor Event Listeners 2022-06-01 14:29:43 +02:00
Eric Espie
35e1f080b8 Attachments events 2022-06-01 11:46:00 +02:00
Eric Espie
812c1f6bb4 Migrate datamodel 2022-06-01 11:19:46 +02:00
Eric Espie
9adb7f20ce Event "Compute Values" 2022-06-01 09:11:03 +02:00
Eric Espie
c7e54c66c8 Event "Compute Values" 2022-06-01 09:09:16 +02:00
Eric Espie
f6855b0d2b Display events in the datamodel page 2022-06-01 08:44:12 +02:00
Eric Espie
1ceef602f0 Remove debug 2022-05-31 15:44:56 +02:00
Eric Espie
93cc29f4d9 refactor 2022-05-31 15:42:09 +02:00
Eric Espie
c9317542c8 refactor 2022-05-31 15:37:29 +02:00
Eric Espie
aed8337c51 Protection against reentrance for DBUpdate 2022-05-30 17:03:47 +02:00
Eric Espie
af4a5e1b8d New CRUD behaviour (removed Reload in DBInsert and DBUpdate) and protection against reentrance 2022-05-27 17:46:10 +02:00
Eric Espie
e7c09c83f0 internal doc 2022-05-25 10:59:38 +02:00
Eric Espie
56103d1952 Refactor 2022-05-25 10:38:07 +02:00
Eric Espie
301c308fec Refactor 2022-05-25 10:32:41 +02:00
Eric Espie
b827c68187 Merge branch 'develop' into feature/faf_event_service 2022-05-25 10:04:54 +02:00
Eric Espie
8ba28adf68 Refactor 2022-04-08 15:10:58 +02:00
Eric Espie
34a26d33a1 Fix reloads 2022-04-07 18:15:18 +02:00
Eric Espie
b0a55e057b Events to cmdbAbstract 2022-04-07 17:02:19 +02:00
Eric Espie
5ac9b05b2d new CRUD 2022-04-06 23:51:21 +02:00
Eric Espie
63e582a07f Unit tests 2022-04-05 10:30:47 +02:00
Eric Espie
470076daa2 Unit tests 2022-04-05 10:29:52 +02:00
Eric Espie
f6d92a189b CRUD reentrance protection 2022-04-05 10:28:12 +02:00
Eric Espie
c788c93542 Merge branch 'develop' into feature/faf_event_service 2022-03-30 08:22:47 +02:00
Eric Espie
a773f0d8a2 EventService: refactoring 2022-03-15 17:53:38 +01:00
Eric Espie
29c6b73d93 EventService: refactoring 2022-03-15 17:50:13 +01:00
Eric Espie
5b52ca4776 EventService: phpdoc 2022-03-15 15:45:03 +01:00
Eric Espie
8ddaf1b731 EventService: call FireEvent with only one parameter (PSR14) 2022-03-15 15:41:55 +01:00
Eric Espie
964ce44577 EventService: code cleanup 2022-03-15 15:11:57 +01:00
Eric Espie
cea6c557ce Merge branch 'develop' into feature/faf_event_service 2022-03-15 10:52:45 +01:00
Eric Espie
99819527db Changed the <Hooks> grammar (typo) 2022-02-07 16:28:19 +01:00
Eric Espie
965273009c Changed the <Hooks> grammar to allow module extensibility 2022-02-07 16:10:00 +01:00
Eric Espie
7bee616b1b Update application events definition 2022-02-07 15:25:38 +01:00
Eric Espie
f5302133d9 Add application events definition 2022-02-07 15:06:00 +01:00
Eric Espie
bf2aba1b06 Reformat code 2022-02-07 14:11:00 +01:00
Eric Espie
c04beea38c OnInsert and OnUpdate replacement 2021-12-31 17:07:59 +01:00
Eric Espie
93d88cca37 refactor 2021-12-31 15:26:54 +01:00
Eric Espie
0997750816 cleanup 2021-12-31 15:06:27 +01:00
Eric Espie
427c8b0794 * better comparison 2021-12-31 14:33:23 +01:00
Eric Espie
06008ed8eb * Add KPI object loaded counter 2021-12-31 12:12:32 +01:00
Eric Espie
374b71c017 * refactor
* Add event AFTER_DISPLAY_PAGE
2021-12-31 11:06:03 +01:00
Eric Espie
fba78e7d9b Changed event name to DISPLAY_OBJECT_DETAILS 2021-12-31 08:53:36 +01:00
Eric Espie
551abc861e Merge branch 'develop' into feature/faf_event_service
# Conflicts:
#	application/cmdbabstract.class.inc.php
#	application/loginwebpage.class.inc.php
#	core/dbobject.class.php
#	core/log.class.inc.php
#	lib/composer/autoload_classmap.php
#	lib/composer/autoload_static.php
#	setup/compiler.class.inc.php
#	test/phpunit.xml.dist
2021-12-31 08:42:16 +01:00
Eric
78f51d40f6 DBObject GetValues() 2020-07-17 15:41:52 +02:00
Eric
8bfc54e6b4 Event Service 2020-07-15 14:42:33 +02:00
807 changed files with 51758 additions and 31844 deletions

View File

@@ -3,14 +3,14 @@
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'showBranches': true,'mainBranchName': 'develop','rotateCommitLabel': true}} }%%
gitGraph
commit id: "2016-07-06" tag: "2.3.0"
commit id: "2016-07-06" tag: "2.3.0" type: HIGHLIGHT
branch support/2.3 order: 900
commit id: "2016-07-08" tag: "2.3.1"
commit id: "2016-12-22" tag: "2.3.3"
commit id: "2017-04-14" tag: "2.3.4"
checkout develop
commit id: "2017-07-12" tag: "2.4.0-beta" type: REVERSE
commit id: "2017-11-16" tag: "2.4.0"
commit id: "2017-11-16" tag: "2.4.0" type: HIGHLIGHT
branch support/2.4 order: 890
commit id: "2018-02-14" tag: "2.4.1"
checkout develop
@@ -18,10 +18,10 @@ gitGraph
checkout support/2.4
commit id: "2018-06-14" tag: "2.4.2"
checkout develop
commit id: "2018-06-27" tag: "2.5.0"
commit id: "2018-06-27" tag: "2.5.0" type: HIGHLIGHT
branch support/2.5 order: 880
checkout develop
commit id: "2019-01-09" tag: "2.6.0"
commit id: "2019-01-09" tag: "2.6.0" type: HIGHLIGHT
branch support/2.6 order: 870
commit id: "2019-03-28" tag: "2.6.1"
checkout develop
@@ -32,11 +32,11 @@ gitGraph
commit id: "2020-01-23" tag: "2.6.3"
checkout develop
commit id: "2020-01-29" tag: "2.7.0-beta2" type: REVERSE
branch support/2.7 order: 860
commit id: "2020-04-01" tag: "2.7.0-1"
commit id: "2020-04-01" tag: "2.7.0-1" type: HIGHLIGHT
checkout support/2.6
commit id: "2020-04-22" tag: "2.6.4"
checkout support/2.7
checkout develop
branch support/2.7 order: 860
commit id: "2020-06-26" tag: "2.7.1"
checkout support/2.7
commit id: "2020-12-09" tag: "2.7.3"
@@ -50,7 +50,7 @@ gitGraph
checkout support/2.7
commit id: "2021-12-17" tag: "2.7.6"
checkout develop
commit id: "2022-01-04" tag: "3.0.0"
commit id: "2022-01-04" tag: "3.0.0" type: HIGHLIGHT
branch support/3.0 order: 850
commit id: "2022-04-08" tag: "3.0.1"
checkout support/2.7
@@ -58,4 +58,8 @@ gitGraph
checkout support/3.0
commit id: "2022-09-12" tag: "3.0.2-1"
checkout develop
checkout support/2.7
commit id: "2022-12-28" tag: "2.7.8"
```
To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start).

11
.gitignore vendored
View File

@@ -19,7 +19,7 @@
# composer reserver directory, from sources, populate/update using "composer install"
vendor/*
test/vendor/*
tests/*/vendor/*
# all conf but listing prevention
/conf/**
@@ -46,7 +46,7 @@ test/vendor/*
!/log/web.config
# PHPUnit cache file
/test/.phpunit.result.cache
/tests/php-unit-tests/.phpunit.result.cache
# Jetbrains
@@ -146,3 +146,10 @@ local.properties
.cache-main
.scala_dependencies
.worksheet
# Mac
.DS_Store
# Windows
Thumbs.db

View File

@@ -6,11 +6,19 @@
* Will update version in the following files :
*
* datamodels/2.x/.../datamodel.*.xml
* application/*.xml
* core/*.xml
*
* Usage :
* `php .make\release\update-xml.php "1.7"`
* `php .make\release\update-xml.php`
*
* @since 2.7.0
* If no parameter provided then the current XML version will be used as target version
*
* @since 2.7.0 simple version change using regexp (not doing conversion)
* @since 3.1.0 N°5405 now does a real conversion
* @since 3.1.0 N°5633 allow to use without parameter
* @since 3.1.0 N°5633 add /application and /core XML files
******************************************************************************/
@@ -22,10 +30,12 @@ require_once (__DIR__.DIRECTORY_SEPARATOR.'update.classes.inc.php');
if (count($argv) === 1)
{
echo '/!\ You must pass the new version as parameter';
exit(1);
echo '/!\ No version passed: assuming target XML version is current XML version ('.ITOP_DESIGN_LATEST_VERSION.")\n";
$sVersionLabel = ITOP_DESIGN_LATEST_VERSION;
} else {
$sVersionLabel = $argv[1];
}
$sVersionLabel = $argv[1];
if (empty($sVersionLabel))
{
echo 'Version passed as parameter is empty !';

View File

@@ -125,16 +125,31 @@ class iTopVersionFileUpdater extends AbstractSingleFileVersionUpdater
abstract class AbstractGlobFileVersionUpdater extends FileVersionUpdater
{
protected $sGlobPattern;
/** @var array|string glob patterns to seek for files to modify */
protected $globPattern;
public function __construct($sGlobPattern)
public function __construct($globPattern)
{
$this->sGlobPattern = $sGlobPattern;
$this->globPattern = $globPattern;
}
public function GetFiles()
{
return glob($this->sGlobPattern);
$aGlobPatterns = (is_array($this->globPattern))
? $this->globPattern
: [$this->globPattern];
$aFiles = [];
foreach ($aGlobPatterns as $sGlobPattern) {
$result = glob($sGlobPattern);
if (false === $result) {
continue;
}
/** @noinspection SlowArrayOperationsInLoopInspection */
$aFiles = array_merge($aFiles, $result);
}
return $aFiles;
}
}
@@ -166,7 +181,11 @@ class DatamodelsXmlFiles extends AbstractGlobFileVersionUpdater
{
public function __construct()
{
parent::__construct(APPROOT.'datamodels/2.x/*/datamodel.*.xml');
parent::__construct([
APPROOT.'datamodels/2.x/*/datamodel.*.xml',
APPROOT.'application/*.xml',
APPROOT.'core/*.xml',
]);
}
/**
@@ -174,10 +193,40 @@ class DatamodelsXmlFiles extends AbstractGlobFileVersionUpdater
*/
public function UpdateFileContent($sVersionLabel, $sFileContent, $sFileFullPath)
{
return preg_replace(
'/(<itop_design .* version=")[^"]+(">)/',
'${1}'.$sVersionLabel.'${2}',
$sFileContent
);
require_once APPROOT.'setup/itopdesignformat.class.inc.php';
$oFileXml = new DOMDocument();
/** @noinspection PhpComposerExtensionStubsInspection */
libxml_clear_errors();
$oFileXml->formatOutput = true;
$oFileXml->preserveWhiteSpace = false;
$oFileXml->loadXML($sFileContent);
$oFileItopFormat = new iTopDesignFormat($oFileXml);
$sDesignVersionToSet = static::GetDesignVersionToSet($oFileItopFormat->GetVersion());
if (false === is_null($sDesignVersionToSet)) {
// N°5779 if same as target version, we will try to convert from version below
$oFileItopFormat->GetITopDesignNode()->setAttribute('version', $sDesignVersionToSet);
}
$bConversionResult = $oFileItopFormat->Convert($sVersionLabel);
if (false === $bConversionResult) {
throw new Exception("Error when converting $sFileFullPath");
}
return $oFileItopFormat->GetXmlAsString();
}
/**
* @return ?string version to use : if file version is same as current version then return previous version, else return null
* @since 3.1.0 N°5779
*/
protected static function GetDesignVersionToSet($sFileDesignVersion):?string {
if ($sFileDesignVersion !== ITOP_DESIGN_LATEST_VERSION) {
return null;
}
return iTopDesignFormat::GetPreviousDesignVersion(ITOP_DESIGN_LATEST_VERSION);
}
}

View File

@@ -134,15 +134,16 @@ Our tests are located in the `test/` directory, containing a PHPUnit config file
When your code is working, please:
* stash as much as possible your commits,
* rebase your branch on our repo last commit,
* create a pull request
* mind to check the "[Allow edits from maintainers](https://docs.github.com/en/github-ae@latest/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork)" option !
Detailed procedure to work on fork and create PR is available [in GitHub help pages](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
* Squash as much as possible your commits,
* Rebase your branch on our repo last commit,
* Create a pull request. _Detailed procedure to work on fork and create PR is available [in GitHub help pages](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)_.
* Pull request description: mind to add all the information useful to understand why you're suggesting this modification and anything necessary to dive into your work. Especially:
- Bugfixes: exact steps to reproduce the bug (given/when/then), description of the bug cause and what solution is implemented
- Enhancements: use cases, implementation details if needed
* Mind to check the "[Allow edits from maintainers](https://docs.github.com/en/github-ae@latest/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork)" option !
### 🙏 We are thankful
## 🙏 We are thankful
We are thankful for all your contributions to the iTop universe! As a thank you gift, we will send stickers to every iTop (& extensions) contributors!

8
Jenkinsfile vendored
View File

@@ -1,6 +1,14 @@
def infra
node(){
properties([
buildDiscarder(
logRotator(
daysToKeepStr: "28",
numToKeepStr: "500")
)
])
checkout scm
infra = load '/var/lib/jenkins/workspace/itop-test-infra_master/src/Infra.groovy'

View File

@@ -3,9 +3,9 @@
</a></p>
iTop stands for IT Operations Portal. It is a complete open source and web based IT service management platform including a fully customizable CMDB, a helpdesk system and a document management tool. It is ITIL compliant and easily customizable and extensible thanks to a high number of adds-on and web services to integrate with your IT.
iTop stands for IT Operations Portal. It is a complete open source and web-based IT service management platform, including a fully customizable CMDB, a helpdesk system, and a document management tool. It is ITIL compliant and easily customizable and extensible thanks to a high number of add-ons and web services to integrate with your IT.
iTop also offers mass import tools to help you being even more efficient.
iTop also offers mass import tools to help you become even more efficient.
## Features
- Fully configurable [Configuration Management (CMDB)][10]
@@ -65,7 +65,7 @@ iTop also offers mass import tools to help you being even more efficient.
## About Us
iTop development is sponsored, led and supported by [Combodo][0].
iTop development is sponsored, led, and supported by [Combodo][0].
[0]: https://www.combodo.com

View File

@@ -18,8 +18,7 @@ to [itop-security@combodo.com](mailto:itop-security@combodo.com).
## 📆 Disclosure Policy
## 🔍 Combodo acknowledgment and investigation
Report sent to us will be acknowledged within the week.
Then, a Combodo developer will be assigned to the reported issue and will:
@@ -34,3 +33,12 @@ Then, a Combodo developer will be assigned to the reported issue and will:
Security issues always take precedence over bug fixes and feature work.
The assignee will keep you informed of the resolution progress, and may ask you for additional information or guidance.
## 📆 Disclosure Policy
Once the fix is done and acknowledged by every stakeholder, it will be included in the next iTop version.
Mind we have at least 2 active branches (LTS and STS, see [iTop Community Releases [iTop Documentation]](https://www.itophub.io/wiki/page?id=latest:release:start))
The release communications will include the information of the vulnerability fix.
Corresponding GitHub advisories and CVE will be published 3 months after the iTop version release date so that iTop instances can be updated.

View File

@@ -12,6 +12,7 @@ require_once(APPROOT.'/application/applicationcontext.class.inc.php');
require_once(APPROOT.'/application/cmdbabstract.class.inc.php');
require_once(APPROOT.'/application/displayblock.class.inc.php');
require_once(APPROOT.'/application/audit.category.class.inc.php');
require_once(APPROOT.'/application/audit.domain.class.inc.php');
require_once(APPROOT.'/application/audit.rule.class.inc.php');
require_once(APPROOT.'/application/query.class.inc.php');
require_once(APPROOT.'/setup/moduleinstallation.class.inc.php');

View File

@@ -376,26 +376,19 @@ class ApplicationContext
{
$oAppContext = new ApplicationContext();
if (is_null($sUrlMakerClass))
{
$sUrlMakerClass = self::GetUrlMakerClass();
}
if (is_null($sUrlMakerClass)) {
$sUrlMakerClass = self::GetUrlMakerClass();
}
$sUrl = call_user_func(array($sUrlMakerClass, 'MakeObjectUrl'), $sObjClass, $sObjKey);
if (strlen($sUrl) > 0)
{
if ($bWithNavigationContext)
{
return $sUrl."&".$oAppContext->GetForLink();
}
else
{
return $sUrl;
}
}
else
{
return '';
}
if (utils::StrLen($sUrl) > 0) {
if ($bWithNavigationContext) {
return $sUrl."&".$oAppContext->GetForLink();
} else {
return $sUrl;
}
} else {
return '';
}
}
/**

File diff suppressed because it is too large Load Diff

View File

@@ -20,7 +20,7 @@
/**
* 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
* inside the set
* inside the set
*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
@@ -48,13 +48,39 @@ class AuditCategory extends cmdbAbstractObject
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 lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'definition_set', 'rules_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('name', 'description', 'definition_set', 'ok_error_tolerance', 'warning_error_tolerance', 'rules_list', 'domains_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description', )); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'definition_set')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
}
/**
* @param int $iTotal
* @param int $iErrors
*
* @return string A semantic color name (eg. red, green, orange, success, failure, ... {@see css/backoffice/utils/variables/colors/_semantic-palette.scss}) to use for this category depending on its error count and tolerance
* @throws \CoreException
*
* @since 3.1.0
*/
public function GetReportColor($iTotal, $iErrors)
{
$sResult = 'red';
if ( ($iTotal == 0) || ($iErrors / $iTotal) <= ($this->Get('ok_error_tolerance') / 100) )
{
$sResult = 'green';
}
else if ( ($iErrors / $iTotal) <= ($this->Get('warning_error_tolerance') / 100) )
{
$sResult = 'orange';
}
return $sResult;
}
}
?>

View File

@@ -0,0 +1,99 @@
<?php
// Copyright (C) 2010-2021 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* 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
* inside the set
*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* @since 3.1.0
*/
class AuditDomain extends cmdbAbstractObject
{
public static function Init()
{
$aParams = array
(
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_auditdomain",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
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())));
// 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
// 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
}
}
/**
* @since 3.1.0
*/
class lnkAuditCategoryToAuditDomain extends cmdbAbstractObject
{
/**
* @throws \CoreException
* @throws \Exception
*/
public static function Init()
{
$aParams = array
(
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array('category_id', 'domain_id'),
"db_table" => "priv_link_audit_category_domain",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => 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")));
// Display lists
MetaModel::Init_SetZListItems('details', array('category_id', 'domain_id'));
MetaModel::Init_SetZListItems('list', array('category_id', 'domain_id'));
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('category_id', 'domain_id'));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1549,6 +1549,29 @@ JS
return $this->sDefinitionFile;
}
/**
* @param string $sDashboardFileRelative can also be an absolute path (compatibility with old URL)
*
* @return string full path to the Dashboard file
* @throws \SecurityException if path isn't under approot
* @uses utils::RealPath()
* @since 2.7.8 3.0.3 3.1.0 N°4449 remove FPD
*/
public static function GetDashboardFileFromRelativePath($sDashboardFileRelative)
{
if (utils::RealPath($sDashboardFileRelative, APPROOT)) {
// compatibility with old URL containing absolute path !
return $sDashboardFileRelative;
}
$sDashboardFile = APPROOT.$sDashboardFileRelative;
if (false === utils::RealPath($sDashboardFile, APPROOT)) {
throw new SecurityException('Invalid dashboard file !');
}
return $sDashboardFile;
}
/**
* @param string $sDefinitionFile
*/

View File

@@ -55,9 +55,9 @@
<menus>
<menu id="WelcomeMenu" xsi:type="MenuGroup" _delta="define">
<rank>10</rank>
<style>
<decoration_classes>fas fa-home</decoration_classes>
</style>
<style>
<decoration_classes>fas fa-home</decoration_classes>
</style>
</menu>
<menu id="WelcomeMenuPage" xsi:type="DashboardMenuNode" _delta="define">
<rank>10</rank>
@@ -151,9 +151,9 @@
</menu>
<menu id="ConfigurationTools" xsi:type="MenuGroup" _delta="define_if_not_exists">
<rank>90</rank>
<style>
<decoration_classes>fas fa-cog</decoration_classes>
</style>
<style>
<decoration_classes>fas fa-cog</decoration_classes>
</style>
</menu>
<menu id="DataSources" xsi:type="OQLMenuNode" _delta="define">
<rank>20</rank>
@@ -172,19 +172,356 @@
</menu>
<menu id="AdminTools" xsi:type="MenuGroup" _delta="define">
<rank>80</rank>
<style>
<decoration_classes>fas fa-tools</decoration_classes>
</style>
<style>
<decoration_classes>fas fa-tools</decoration_classes>
</style>
</menu>
<menu id="SystemTools" xsi:type="MenuGroup" _delta="define">
<rank>100</rank>
<enable_class>ResourceSystemMenu</enable_class>
<enable_action>UR_ACTION_MODIFY</enable_action>
<style>
<decoration_classes>fas fa-terminal</decoration_classes>
</style>
<style>
<decoration_classes>fas fa-terminal</decoration_classes>
</style>
</menu>
</menus>
<events>
<event id="EVENT_DB_CHECK_TO_WRITE" _delta="define">
<description>Check an object before it is written into the database (no change possible). Call DBObject::AddCheckIssue() to signal an issue</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>cmdbAbstractObject::DoCheckToWrite</replaces>
<event_data>
<event_datum id="object">
<description>The object to check</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_CREATE_DONE" _delta="define">
<description>An object has been created into the database. The modifications can be propagated to other objects.</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::AfterInsert</replaces>
<event_data>
<event_datum id="object">
<description>The object inserted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_UPDATE_DONE" _delta="define">
<description>An object has been updated into the database and reloaded.</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::AfterUpdate</replaces>
<event_data>
<event_datum id="object">
<description>The object updated</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_CHECK_TO_DELETE" _delta="define">
<description>Check an object before it is deleted from the database. Call DBObject::AddDeleteIssue() to signal an issue</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>cmdbAbstractObject::DoCheckToDelete</replaces>
<event_data>
<event_datum id="object">
<description>The object to check</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_DELETE_DONE" _delta="define">
<description>An object has been deleted into the database</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::AfterDelete</replaces>
<event_data>
<event_datum id="object">
<description>The object deleted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_BEFORE_APPLY_STIMULUS" _delta="define">
<description>A stimulus is about to be applied to an object</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<event_data>
<event_datum id="object">
<description>The object where the stimulus is targeted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="stimulus">
<description>Current stimulus applied</description>
<type>string</type>
</event_datum>
<event_datum id="previous_state">
<description>Object previous state</description>
<type>string</type>
</event_datum>
<event_datum id="new_state">
<description>Object new state</description>
<type>string</type>
</event_datum>
<event_datum id="save_object">
<description>The object must be saved in the database</description>
<type>boolean</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_AFTER_APPLY_STIMULUS" _delta="define">
<description>A stimulus has been applied to an object</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<event_data>
<event_datum id="object">
<description>The object where the stimulus is targeted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="stimulus">
<description>Current stimulus applied</description>
<type>string</type>
</event_datum>
<event_datum id="previous_state">
<description>Object previous state</description>
<type>string</type>
</event_datum>
<event_datum id="new_state">
<description>Object new state</description>
<type>string</type>
</event_datum>
<event_datum id="save_object">
<description>The object is asked to be saved in the database</description>
<type>boolean</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_APPLY_STIMULUS_FAILED" _delta="define">
<description>A stimulus has failed</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<event_data>
<event_datum id="action">
<description>The action that failed to apply the stimulus</description>
<type>string</type>
</event_datum>
<event_datum id="object">
<description>The object where the stimulus is targeted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="stimulus">
<description>Current stimulus applied</description>
<type>string</type>
</event_datum>
<event_datum id="previous_state">
<description>Object previous state</description>
<type>string</type>
</event_datum>
<event_datum id="new_state">
<description>Object new state</description>
<type>string</type>
</event_datum>
<event_datum id="save_object">
<description>The object must be saved in the database</description>
<type>boolean</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_LINKS_CHANGED" _delta="define">
<description>At least one link class was changed</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<event_data>
<event_datum id="object">
<description>The object where the link is or was pointing to</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_OBJECT_RELOAD" _delta="define">
<description>An object has been re-loaded from the database</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<event_data>
<event_datum id="object">
<description>The object re-loaded</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_COMPUTE_VALUES" _delta="define">
<description>An object needs to be recomputed after changes</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::ComputeValues</replaces>
<event_data>
<event_datum id="object">
<description>The object inserted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_ARCHIVE" _delta="define">
<description>An object has been archived</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<event_data>
<event_datum id="object">
<description>The object archived</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_UNARCHIVE" _delta="define">
<description>An object has been unarchived</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<event_data>
<event_datum id="object">
<description>The object unarchived</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_SET_ATTRIBUTES_FLAGS" _delta="define">
<description>Set object attributes flags. Call cmdbAbstractObject::AddAttributeFlags() for all the attributes to be set for this target state.</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<event_data>
<event_datum id="object">
<description>The current object</description>
<type>DBObject</type>
</event_datum>
<event_datum id="target_state">
<description>The target state in which to evaluate the flags</description>
<type>array</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_SET_INITIAL_ATTRIBUTES_FLAGS" _delta="define">
<description>Set object initial attributes flags. Call cmdbAbstractObject::AddInitialAttributeFlags() for all the initial attributes to be set initially.</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<event_data>
<event_datum id="object">
<description>The current object</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DOWNLOAD_DOCUMENT" _delta="define">
<description>A document has been downloaded from the GUI</description>
<sources>
<source id="Document">Document</source>
</sources>
<event_data>
<event_datum id="object">
<description>The object containing the document</description>
<type>DBObject</type>
</event_datum>
<event_datum id="document">
<description>The document downloaded</description>
<type>ormDocument</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_LOGIN" _delta="define">
<description>Inform the listeners about the connection states</description>
<event_data>
<event_datum id="code">
<description>The login step result code (LoginWebPage::EXIT_CODE_...) </description>
<type>integer</type>
</event_datum>
<event_datum id="state">
<description>Current login state (LoginWebPage::LOGIN_STATE_CONNECTED...)</description>
<type>string</type>
</event_datum>
</event_data>
</event>
</events>
<meta>
<classes>
<class id="cmdbAbstractObject" _delta="define">

View File

@@ -272,6 +272,8 @@ class DisplayBlock
'panel_title',
/** string true if panel title should be displayed as html */
'panel_title_is_html',
/** string Description of the panel content, displayed as a hint on the title */
'panel_title_tooltip',
/** string class for panel block style */
'panel_class',
/** string class for panel block style */
@@ -1045,9 +1047,18 @@ JS
$aCount = $aCounts[$sStateValue];
$sHyperlink = $aCount['link'];
$sCountLabel = $aCount['label'];
$oPill = PillFactory::MakeForState($sClass, $sStateValue)
->SetTooltip($sStateLabel)
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--count\">$sCountLabel</span><span class=\"ibo-dashlet-header-dynamic--label ibo-text-truncated-with-ellipsis\">".utils::HtmlEntities($sStateLabel)."</span>");
$oPill = PillFactory::MakeForState($sClass, $sStateValue);
// N°5849 - Unencode label for ExternalKey attribute because friendlyname is already html encoded thanks to DBObject::GetName() in AttributeExternalKey::GetAllowedValues(). (A fix in this function may have too much impact).
if ($oAttDef instanceof AttributeExternalKey) {
$sPillTooltip = htmlspecialchars_decode($sStateLabel, ENT_QUOTES | ENT_DISALLOWED | ENT_HTML5);
$sPillLabel = $sStateLabel;
} else {
$sPillTooltip = $sStateLabel;
$sPillLabel = utils::HtmlEntities($sStateLabel);
}
$oPill->SetTooltip($sPillTooltip)
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--count\">$sCountLabel</span><span class=\"ibo-dashlet-header-dynamic--label ibo-text-truncated-with-ellipsis\">".$sPillLabel."</span>");
if ($sHyperlink != '-') {
$oPill->SetUrl($sHyperlink);
}
@@ -1304,17 +1315,12 @@ JS
}
}
if (count($aAuthorizedClasses) > 0) {
if ($this->m_oSet->CountWithLimit(1) > 0) {
if (empty($aExtraParams['currentId'])) {
$iListId = utils::GetUniqueId(); // Works only if not in an Ajax page !!
} else {
$iListId = $aExtraParams['currentId'];
}
$oBlock->AddSubBlock(DataTableUIBlockFactory::MakeForObject($oPage, $iListId, $this->m_oSet, $aExtraParams));
if (empty($aExtraParams['currentId'])) {
$iListId = utils::GetUniqueId(); // Works only if not in an Ajax page !!
} else {
// Empty set
$oBlock->bEmptySet = true;
$iListId = $aExtraParams['currentId'];
}
$oBlock->AddSubBlock(DataTableUIBlockFactory::MakeForObject($oPage, $iListId, $this->m_oSet, $aExtraParams));
} else {
// Not authorized
$oBlock->bNotAuthorized = true;
@@ -1337,43 +1343,13 @@ JS
}
// The list is made of only 1 class of objects, actions on the list are possible
if (($this->m_oSet->CountWithLimit(1) > 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES)) {
if (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) {
$oBlock->AddSubBlock(cmdbAbstractObject::GetDisplaySetBlock($oPage, $this->m_oSet, $aExtraParams));
} else {
$oBlock->bEmptySet = true;
$oBlock->sClass = $this->m_oFilter->GetClass();
$oBlock->sClassLabel = MetaModel::GetName($oBlock->sClass);
$bDisplayMenu = isset($aExtraParams['menu']) ? ($aExtraParams['menu'] == true) : true;
if ($bDisplayMenu) {
if ((UserRights::IsActionAllowed($oBlock->sClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES)) {
$oBlock->sLinkTarget = '';
$oAppContext = new ApplicationContext();
$oBlock->sParams = $oAppContext->GetForLink();
// 1:n links, populate the target object as a default value when creating a new linked object
if (isset($aExtraParams['target_attr'])) {
$oBlock->sLinkTarget = ' target="_blank" ';
$aExtraParams['default'][$aExtraParams['target_attr']] = $aExtraParams['object_id'];
}
if (!empty($aExtraParams['default'])) {
foreach ($aExtraParams['default'] as $sKey => $sValue) {
$oBlock->sDefault .= "&default[$sKey]=$sValue";
}
}
$oBlock->bCreateNew = true;
}
}
if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) {
$oPanel = PanelUIBlockFactory::MakeForClass($aExtraParams["panel_class"], $aExtraParams["panel_title"]);
if(isset($aExtraParams["panel_icon"]) && strlen($aExtraParams["panel_icon"]) > 0){
$oPanel->SetIcon($aExtraParams["panel_icon"]);
}
$oPanel->AddSubBlock($oBlock);
return $oPanel;
}
}
}
return $oBlock;
@@ -1471,6 +1447,7 @@ JS
} else {
$iListId = $aExtraParams['currentId'];
}
$oBlock = DataTableUIBlockFactory::MakeForRendering($iListId, $oSet, $aExtraParams);
$oHtml->AddHtml("<tr><td>");
$oContentBlock->AddSubBlock($oBlock);
@@ -1752,9 +1729,14 @@ class MenuBlock extends DisplayBlock
$this->m_sStyle = 'list';
}
$sClass = $this->m_oFilter->GetClass();
$oSet = new CMDBObjectSet($this->m_oFilter);
$sClass = $this->GetFilter()->GetClass();
$aSelectedClasses = $this->GetFilter()->GetSelectedClasses();
$bIsForLinkset = isset($aExtraParams['target_attr']);
$oSet = new CMDBObjectSet($this->GetFilter());
$iSetCount = $oSet->Count();
/** @var string $sRefreshAction JS snippet to run when clicking on the refresh button of the menu */
$sRefreshAction = $aExtraParams['refresh_action'] ?? '';
$bIsCreationInModalAllowed = isset($aExtraParams['creation_in_modal_is_allowed']) && $aExtraParams['creation_in_modal_is_allowed'] === true;
/** @var array $aRegularActions Any action other than a transition */
$aRegularActions = [];
@@ -1762,275 +1744,240 @@ class MenuBlock extends DisplayBlock
$aTransitionActions = [];
/** @var array $aToolkitActions Any "legacy" toolkit menu item, which are now displayed in the same menu as the $aRegularActions, after them */
$aToolkitActions = [];
if ((!isset($aExtraParams['selection_mode']) || $aExtraParams['selection_mode'] == "") && $this->m_sStyle != 'listInObject') {
if (!isset($aExtraParams['selection_mode']) || ($aExtraParams['selection_mode'] == "")) {
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
if (!empty($sContext)) {
if (utils::IsNotNullOrEmptyString($sContext)) {
$sContext = '&'.$sContext;
}
$oReflectionClass = new ReflectionClass($sClass);
$sFilter = $this->m_oFilter->serialize();
$sFilter = $this->GetFilter()->serialize();
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($sClass);
$sRootUrl = utils::GetAbsoluteUrlAppRoot();
// Common params that will be applied to actions
$aActionParams = array();
if (isset($aExtraParams['menu_actions_target'])) {
$aActionParams['target'] = $aExtraParams['menu_actions_target'];
}
$aActionParams = $this->GetDefaultParamsForMenuAction();
// 1:n links, populate the target object as a default value when creating a new linked object
if (isset($aExtraParams['target_attr'])) {
if ($bIsForLinkset) {
$aExtraParams['default'][$aExtraParams['target_attr']] = $aExtraParams['object_id'];
}
$sDefault = '';
/** @var string $sDefaultValuesAsUrlParams Default values for the object to create, already formatted as URL params (eg. "&default[org_id]=3&default[title]=Foo") */
$sDefaultValuesAsUrlParams = '';
if (!empty($aExtraParams['default'])) {
foreach ($aExtraParams['default'] as $sKey => $sValue) {
$sDefault .= "&default[$sKey]=$sValue";
$sDefaultValuesAsUrlParams .= "&default[$sKey]=$sValue";
}
}
$bIsCreationAllowed = (UserRights::IsActionAllowed($sClass,
UR_ACTION_CREATE) == UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
switch ($oSet->Count()) {
case 0:
// No object in the set, the only possible action is "new"
if ($bIsCreationAllowed) {
$aRegularActions['UI:Menu:New'] = array(
'label' => Dict::S('UI:Menu:New'),
'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}",
) + $aActionParams;
// Check rights
$bIsCreationAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_CREATE) === UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
$bIsModifyAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) === UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
// Create in new tab
if ($bIsCreationAllowed && !$bIsCreationInModalAllowed) {
$this->AddNewObjectMenuAction($aRegularActions, $sClass, $sDefaultValuesAsUrlParams);
}
// Any style actions
// - Bulk actions on objects set
if ($iSetCount > 1) {
// Bulk actions for each selected classes (eg. "link" and "remote" on n:n relations)
foreach ($aSelectedClasses as $sSelectedAlias => $sSelectedClass) {
$sSelectedClassName = MetaModel::GetName($sSelectedClass);
// Check rights on class
$bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sSelectedClass)) && UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_MODIFY, $oSet) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
$bIsBulkDeleteAllowed = (bool) UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_DELETE, $sSelectedClass);
// Refine filter on selected class so bullk actions occur on the right class
$oSelectedClassFilter = $this->GetFilter()->DeepClone();
$oSelectedClassFilter->SetSelectedClasses([$sSelectedAlias]);
// Action identifier is using the alias on purpose so they can be used as "shortcut actions" easily for "Link" or "Remote" aliases on linksets.
// Action label dict code has a specific suffix for "Link" / "Remote" aliases to allow dedicated labels in linksets.
$sActionLabelCodeSuffix = in_array($sSelectedAlias, ['Link', 'Remote']) ? $sSelectedAlias : 'Class';
if ($bIsBulkModifyAllowed) {
$this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:ModifyAll:'.$sSelectedAlias, Dict::Format('UI:Menu:ModifyAll_'.$sActionLabelCodeSuffix, $sSelectedClassName));
}
break;
if ($bIsBulkDeleteAllowed) {
$this->AddBulkDeleteObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:BulkDelete:'.$sSelectedAlias, Dict::Format('UI:Menu:BulkDelete_'.$sActionLabelCodeSuffix, $sSelectedClassName));
}
}
case 1:
$oObj = $oSet->Fetch();
if (is_null($oObj)) {
if (!isset($aExtraParams['link_attr'])) {
if ($bIsCreationAllowed) {
$aRegularActions['UI:Menu:New'] = array(
'label' => Dict::S('UI:Menu:New'),
'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}",
) + $aActionParams;
}
}
} else {
$id = $oObj->GetKey();
if (empty($sRefreshAction) && utils::ReadParam('operation') == 'details') {
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
$sRefreshAction = "window.location.reload();";
} else {
$sRefreshAction = "window.location.href='".ApplicationContext::MakeObjectUrl(get_class($oObj), $id)."';";
}
}
// Stimuli
$aStates = MetaModel::EnumStates($sClass);
// Do not perform time-consuming computations if there are too many objects in the list
$iLimit = MetaModel::GetConfig()->Get('complex_actions_limit');
$bLocked = false;
if (MetaModel::GetConfig()->Get('concurrent_lock_enabled')) {
$aLockInfo = iTopOwnershipLock::IsLocked(get_class($oObj), $id);
if ($aLockInfo['locked']) {
$bLocked = true;
//$this->AddMenuSeparator($aActions);
//$aActions['concurrent_lock_unlock'] = array ('label' => Dict::S('UI:Menu:ReleaseConcurrentLock'), 'url' => "{$sRootUrl}pages/$sUIPage?operation=kill_lock&class=$sClass&id=$id{$sContext}");
}
}
$bRawModifiedAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
$bIsModifyAllowed = !$bLocked && $bRawModifiedAllowed;
$bIsDeleteAllowed = !$bLocked && UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet);
// 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(
'label' => Dict::S('UI:Menu:Modify'),
'url' => "{$sRootUrl}pages/$sUIPage?operation=modify&class=$sClass&id=$id{$sContext}#",
) + $aActionParams;
}
if ($bIsCreationAllowed) {
$aRegularActions['UI:Menu:New'] = array(
'label' => Dict::S('UI:Menu:New'),
'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}",
) + $aActionParams;
}
if ($bIsDeleteAllowed) {
$aRegularActions['UI:Menu:Delete'] = array(
'label' => Dict::S('UI:Menu:Delete'),
'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$id{$sContext}",
) + $aActionParams;
}
if ((count($aStates) > 0) && (($iLimit == 0) || ($oSet->CountWithLimit($iLimit + 1) < $iLimit))) {
// 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();
if (isset($aExtraParams['query_params'])) {
$aQueryParams = $aExtraParams['query_params'];
}
// Transitions / Stimuli
if (!$bLocked) {
$aTransitions = $oObj->EnumTransitions();
if (count($aTransitions)) {
$aStimuli = Metamodel::EnumStimuli(get_class($oObj));
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass,
$sStimulusCode, $oSet) : UR_ALLOWED_NO;
switch ($iActionAllowed) {
case UR_ALLOWED_YES:
$aTransitionActions[$sStimulusCode] = array(
'label' => $aStimuli[$sStimulusCode]->GetLabel(),
'url' => "{$sRootUrl}pages/UI.php?operation=stimulus&stimulus=$sStimulusCode&class=$sClass&id=$id{$sContext}",
) + $aActionParams;
break;
default:
// Do nothing
}
}
}
}
// Relations...
$aRelations = MetaModel::EnumRelationsEx($sClass);
if (count($aRelations)) {
$this->AddMenuSeparator($aRegularActions);
foreach ($aRelations as $sRelationCode => $aRelationInfo) {
if (array_key_exists('down', $aRelationInfo)) {
$aRegularActions[$sRelationCode.'_down'] = array(
'label' => $aRelationInfo['down'],
'url' => "{$sRootUrl}pages/$sUIPage?operation=view_relations&relation=$sRelationCode&direction=down&class=$sClass&id=$id{$sContext}",
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy);
$aRes = CMDBSource::QueryToArray($sSql);
if (count($aRes) == 1) {
// All objects are in the same state...
$sState = $aRes[0]['__state__'];
$aTransitions = Metamodel::EnumTransitions($sClass, $sState);
if (count($aTransitions)) {
$aStimuli = Metamodel::EnumStimuli($sClass);
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
$oSet->Rewind();
// As soon as the user rights implementation will browse the object set,
// then we might consider using OptimizeColumnLoad() here
$iActionAllowed = UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oSet);
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? $iActionAllowed : UR_ALLOWED_NO;
switch ($iActionAllowed) {
case UR_ALLOWED_YES:
case UR_ALLOWED_DEPENDS:
$aTransitionActions[$sStimulusCode] = array(
'label' => $aStimuli[$sStimulusCode]->GetLabel(),
'url' => "{$sRootUrl}pages/UI.php?operation=select_bulk_stimulus&stimulus=$sStimulusCode&state=$sState&class=$sClass&filter=".urlencode($sFilter)."{$sContext}",
) + $aActionParams;
}
if (array_key_exists('up', $aRelationInfo)) {
$aRegularActions[$sRelationCode.'_up'] = array(
'label' => $aRelationInfo['up'],
'url' => "{$sRootUrl}pages/$sUIPage?operation=view_relations&relation=$sRelationCode&direction=up&class=$sClass&id=$id{$sContext}",
) + $aActionParams;
}
}
}
// Add a special menu to kill the lock, but only to allowed users who can also modify this object
if ($bLocked && $bRawModifiedAllowed) {
/** @var array $aAllowedProfiles */
$aAllowedProfiles = MetaModel::GetConfig()->Get('concurrent_lock_override_profiles');
$bCanKill = false;
$oUser = UserRights::GetUserObject();
$aUserProfiles = array();
if (!is_null($oUser)) {
$oProfileSet = $oUser->Get('profile_list');
while ($oProfile = $oProfileSet->Fetch()) {
$aUserProfiles[$oProfile->Get('profile')] = true;
}
}
foreach ($aAllowedProfiles as $sProfile) {
if (array_key_exists($sProfile, $aUserProfiles)) {
$bCanKill = true;
break;
}
}
if ($bCanKill) {
$this->AddMenuSeparator($aRegularActions);
$aRegularActions['concurrent_lock_unlock'] = array(
'label' => Dict::S('UI:Menu:KillConcurrentLock'),
'url' => "{$sRootUrl}pages/$sUIPage?operation=kill_lock&class=$sClass&id=$id{$sContext}",
);
default:
// Do nothing
}
}
}
$this->AddMenuSeparator($aRegularActions);
$this->GetEnumAllowedActions($oSet, function ($sLabel, $data) use (&$aRegularActions, $aActionParams) {
$aRegularActions[$sLabel] = array('label' => $sLabel, 'url' => $data) + $aActionParams;
});
}
break;
}
}
default:
// Check rights
// New / Modify
$bIsModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY,
$oSet) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
$bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sClass)) && UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY,
$oSet) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
$bIsBulkDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_DELETE, $oSet);
if (isset($aExtraParams['link_attr'])) {
$id = $aExtraParams['object_id'];
$sTargetAttr = $aExtraParams['target_attr'];
$oAttDef = MetaModel::GetAttributeDef($sClass, $sTargetAttr);
$sTargetClass = $oAttDef->GetTargetClass();
if ($bIsModifyAllowed) {
$aRegularActions['UI:Menu:Add'] = array(
'label' => Dict::S('UI:Menu:Add'),
'url' => "{$sRootUrl}pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&addObjects=true{$sContext}",
) + $aActionParams;
}
if ($bIsBulkModifyAllowed) {
$aRegularActions['UI:Menu:Manage'] = array(
'label' => Dict::S('UI:Menu:Manage'),
'url' => "{$sRootUrl}pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id{$sContext}",
) + $aActionParams;
}
//if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All...', 'url' => "#") + $aActionParams; }
} else {
// many objects in the set, possible actions are: new / modify all / delete all
if ($bIsCreationAllowed) {
$aRegularActions['UI:Menu:New'] = array(
'label' => Dict::S('UI:Menu:New'),
'url' => "{$sRootUrl}pages/$sUIPage?operation=new&class=$sClass{$sContext}{$sDefault}",
) + $aActionParams;
}
if ($bIsBulkModifyAllowed) {
$aRegularActions['UI:Menu:ModifyAll'] = array(
'label' => Dict::S('UI:Menu:ModifyAll'),
'url' => "{$sRootUrl}pages/$sUIPage?operation=select_for_modify_all&class=$sClass&filter=".urlencode($sFilter)."{$sContext}",
) + $aActionParams;
}
if ($bIsBulkDeleteAllowed) {
$aRegularActions['UI:Menu:BulkDelete'] = array(
'label' => Dict::S('UI:Menu:BulkDelete'),
'url' => "{$sRootUrl}pages/$sUIPage?operation=select_for_deletion&filter=".urlencode($sFilter)."{$sContext}",
) + $aActionParams;
}
// Stimuli
$aStates = MetaModel::EnumStates($sClass);
// Do not perform time consuming computations if there are too may objects in the list
$iLimit = MetaModel::GetConfig()->Get('complex_actions_limit');
if ((count($aStates) > 0) && (($iLimit == 0) || ($oSet->CountWithLimit($iLimit + 1) < $iLimit))) {
// 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();
if (isset($aExtraParams['query_params'])) {
$aQueryParams = $aExtraParams['query_params'];
// NOT "listInObject" style actions
if ($this->m_sStyle !== 'listInObject') {
switch ($iSetCount) {
case 1:
$oObj = $oSet->Fetch();
if (false === is_null($oObj)) {
$id = $oObj->GetKey();
if (empty($sRefreshAction) && utils::ReadParam('operation') == 'details') {
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
$sRefreshAction = "window.location.reload();";
} else {
$sRefreshAction = "window.location.href='".ApplicationContext::MakeObjectUrl(get_class($oObj), $id)."';";
}
}
$sSql = $this->m_oFilter->MakeGroupByQuery($aQueryParams, $aGroupBy);
$aRes = CMDBSource::QueryToArray($sSql);
if (count($aRes) == 1) {
// All objects are in the same state...
$sState = $aRes[0]['__state__'];
$aTransitions = Metamodel::EnumTransitions($sClass, $sState);
if (count($aTransitions)) {
$aStimuli = Metamodel::EnumStimuli($sClass);
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
$oSet->Rewind();
// As soon as the user rights implementation will browse the object set,
// then we might consider using OptimizeColumnLoad() here
$iActionAllowed = UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oSet);
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? $iActionAllowed : UR_ALLOWED_NO;
switch ($iActionAllowed) {
case UR_ALLOWED_YES:
case UR_ALLOWED_DEPENDS:
$aTransitionActions[$sStimulusCode] = array(
'label' => $aStimuli[$sStimulusCode]->GetLabel(),
'url' => "{$sRootUrl}pages/UI.php?operation=select_bulk_stimulus&stimulus=$sStimulusCode&state=$sState&class=$sClass&filter=".urlencode($sFilter)."{$sContext}",
) + $aActionParams;
break;
$bLocked = false;
if (MetaModel::GetConfig()->Get('concurrent_lock_enabled')) {
$aLockInfo = iTopOwnershipLock::IsLocked(get_class($oObj), $id);
if ($aLockInfo['locked']) {
$bLocked = true;
}
}
$bRawModifiedAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
$bIsModifyAllowed = !$bLocked && $bRawModifiedAllowed;
$bIsDeleteAllowed = !$bLocked && UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet);
// 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(
'label' => Dict::S('UI:Menu:Modify'),
'url' => "{$sRootUrl}pages/$sUIPage?route=object.modify&class=$sClass&id=$id{$sContext}#",
) + $aActionParams;
}
if ($bIsDeleteAllowed) {
$aRegularActions['UI:Menu:Delete'] = array(
'label' => Dict::S('UI:Menu:Delete'),
'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$id{$sContext}",
) + $aActionParams;
}
default:
// Do nothing
// Transitions / Stimuli
if (!$bLocked) {
$aTransitions = $oObj->EnumTransitions();
if (count($aTransitions)) {
$aStimuli = Metamodel::EnumStimuli(get_class($oObj));
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass,
$sStimulusCode, $oSet) : UR_ALLOWED_NO;
switch ($iActionAllowed) {
case UR_ALLOWED_YES:
$aTransitionActions[$sStimulusCode] = array(
'label' => $aStimuli[$sStimulusCode]->GetLabel(),
'url' => "{$sRootUrl}pages/UI.php?operation=stimulus&stimulus=$sStimulusCode&class=$sClass&id=$id{$sContext}",
) + $aActionParams;
break;
default:
// Do nothing
}
}
}
}
// Relations...
$aRelations = MetaModel::EnumRelationsEx($sClass);
if (count($aRelations)) {
$this->AddMenuSeparator($aRegularActions);
foreach ($aRelations as $sRelationCode => $aRelationInfo) {
if (array_key_exists('down', $aRelationInfo)) {
$aRegularActions[$sRelationCode.'_down'] = array(
'label' => $aRelationInfo['down'],
'url' => "{$sRootUrl}pages/$sUIPage?operation=view_relations&relation=$sRelationCode&direction=down&class=$sClass&id=$id{$sContext}",
) + $aActionParams;
}
if (array_key_exists('up', $aRelationInfo)) {
$aRegularActions[$sRelationCode.'_up'] = array(
'label' => $aRelationInfo['up'],
'url' => "{$sRootUrl}pages/$sUIPage?operation=view_relations&relation=$sRelationCode&direction=up&class=$sClass&id=$id{$sContext}",
) + $aActionParams;
}
}
}
// Add a special menu to kill the lock, but only to allowed users who can also modify this object
if ($bLocked && $bRawModifiedAllowed) {
/** @var array $aAllowedProfiles */
$aAllowedProfiles = MetaModel::GetConfig()->Get('concurrent_lock_override_profiles');
$bCanKill = false;
$oUser = UserRights::GetUserObject();
$aUserProfiles = array();
if (!is_null($oUser)) {
$oProfileSet = $oUser->Get('profile_list');
while ($oProfile = $oProfileSet->Fetch()) {
$aUserProfiles[$oProfile->Get('profile')] = true;
}
}
foreach ($aAllowedProfiles as $sProfile) {
if (array_key_exists($sProfile, $aUserProfiles)) {
$bCanKill = true;
break;
}
}
if ($bCanKill) {
$this->AddMenuSeparator($aRegularActions);
$aRegularActions['concurrent_lock_unlock'] = array(
'label' => Dict::S('UI:Menu:KillConcurrentLock'),
'url' => "{$sRootUrl}pages/$sUIPage?operation=kill_lock&class=$sClass&id=$id{$sContext}",
);
}
}
}
$this->AddMenuSeparator($aRegularActions);
$this->GetEnumAllowedActions($oSet, function ($sLabel, $data) use (&$aRegularActions, $aActionParams) {
$aRegularActions[$sLabel] = array('label' => $sLabel, 'url' => $data) + $aActionParams;
});
}
}
break;
}
}
$this->AddMenuSeparator($aRegularActions);
@@ -2055,8 +2002,9 @@ class MenuBlock extends DisplayBlock
$sRefreshAction = "window.location.reload();";
}
} else {
//it's easier just display configure this list and MENU_OBJLIST_TOOLKIT
// It's easier just display configure this list and MENU_OBJLIST_TOOLKIT
}
$param = null;
if (is_null($sId)) {
$sId = uniqid();
@@ -2069,14 +2017,7 @@ class MenuBlock extends DisplayBlock
case 'listInObject':
$oSet->Rewind();
$param = $oSet;
$bToolkitMenu = true;
if (isset($aExtraParams['toolkit_menu'])) {
$bToolkitMenu = (bool)$aExtraParams['toolkit_menu'];
}
if ($bToolkitMenu) {
$sLabel = Dict::S('UI:ConfigureThisList');
$aRegularActions['iTop::ConfigureList'] = ['label' => $sLabel, 'url' => '#', 'onclick' => "$('#datatable_dlg_datatable_{$sId}').dialog('open'); return false;"];
}
utils::GetPopupMenuItemsBlock($oPopupMenuItemsBlock, iPopupMenuExtension::MENU_OBJLIST_ACTIONS, $param, $aRegularActions, $sId);
utils::GetPopupMenuItemsBlock($oPopupMenuItemsBlock, iPopupMenuExtension::MENU_OBJLIST_TOOLKIT, $param, $aToolkitActions, $sId);
break;
@@ -2088,6 +2029,7 @@ class MenuBlock extends DisplayBlock
break;
}
if ($oPopupMenuItemsBlock->HasSubBlocks()) {
$oRenderBlock->AddSubBlock($oPopupMenuItemsBlock);
} else {
@@ -2098,6 +2040,7 @@ class MenuBlock extends DisplayBlock
$oRenderBlock->AddCssFileRelPath($sCssPath);
}
}
// Extract favorite actions from their menus
$aFavoriteRegularActions = [];
$aFavoriteTransitionActions = [];
@@ -2183,12 +2126,16 @@ class MenuBlock extends DisplayBlock
break;
case 'UI:Menu:ModifyAll':
case 'UI:Menu:ModifyAll:Link': // Link class on linkset
case 'UI:Menu:ModifyAll:Remote': // Remote class on linkset
case 'UI:Menu:Modify':
$sIconClass = 'fas fa-pen';
$sLabel = '';
break;
case 'UI:Menu:BulkDelete':
case 'UI:Menu:BulkDelete:Link': // Link class on linkset
case 'UI:Menu:BulkDelete:Remote': // Remote class on linkset
case 'UI:Menu:Delete':
$sIconClass = 'fas fa-trash';
$sLabel = '';
@@ -2221,8 +2168,28 @@ class MenuBlock extends DisplayBlock
$oActionsToolbar->AddSubBlock($oActionButton);
}
// - Creation in modal
if ($bIsCreationInModalAllowed === true) {
$oAddLinkActionButton = ButtonUIBlockFactory::MakeIconAction(
'fas fa-plus',
Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($sClass)),
'UI:Links:New',
'',
false
);
// - If we are used in a Datatable, 'datatable_' will be prefixed to our $sId, so we do the same here
$sRealId = $sId;
if(in_array($this->m_sStyle, ['list', 'links', 'listInObject'])){
$sRealId = 'datatable_' . $sId;
}
$oAddLinkActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button'])
->SetOnClickJsCode("$('#$sRealId').trigger('open_creation_modal.object.itop');");
$oActionsToolbar->AddSubBlock($oAddLinkActionButton);
}
// - Refresh
if ($sRefreshAction != '') {
if (utils::IsNotNullOrEmptyString($sRefreshAction)) {
$oActionButton = ButtonUIBlockFactory::MakeAlternativeNeutral('', 'UI:Button:Refresh');
$oActionButton->SetIconClass('fas fa-sync-alt')
->SetOnClickJsCode($sRefreshAction)
@@ -2232,7 +2199,7 @@ class MenuBlock extends DisplayBlock
}
// - Search
if ($this->m_sStyle == 'details') {
if ($this->m_sStyle === 'details') {
$oActionButton = ButtonUIBlockFactory::MakeIconLink('fas fa-search', Dict::Format('UI:SearchFor_Class', MetaModel::GetName($sClass)), "{$sRootUrl}pages/UI.php?operation=search_form&do_search=0&class=$sClass{$sContext}", '', 'UI:SearchFor_Class');
$oActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']);
$oActionsToolbar->AddSubBlock($oActionButton);
@@ -2257,6 +2224,12 @@ class MenuBlock extends DisplayBlock
// Toolkit actions
if (!empty($aToolkitActions)) {
// Add separator if necessary
if (count($aRegularActions) > 0) {
$oRegularActionsMenu->AddItem('separator-regular-actions-toolkit-actions', PopoverMenuItemFactory::MakeSeparator());
}
// Add actions
foreach ($aToolkitActions as $sActionId => $aActionData) {
$oRegularActionsMenu->AddItem('toolkit-actions', PopoverMenuItemFactory::MakeFromApplicationPopupMenuItemData($sActionId, $aActionData));
}
@@ -2337,5 +2310,106 @@ class MenuBlock extends DisplayBlock
$aActions['sep_'.(count($aActions)-1)] = array('label' => $sSeparator, 'url' => '');
}
}
}
}
/**
* Add a create action (to $aActions) for an $sClass object
*
* @param array &$aActions Pointer to the array in which the action will be added
* @param string $sClass Datamodel class concerned by the action
* @param string $sDefaultValuesAsUrlParams Default values for the new object form,
*
* @return void
* @since 3.1.0
* @internal
*/
protected function AddNewObjectMenuAction(array &$aActions, string $sClass, string $sDefaultValuesAsUrlParams = ''): void
{
$aActions['UI:Menu:New'] = [
'label' => Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($sClass)),
'url' => $this->PrepareUrlForStandardMenuAction($sClass, "operation=new&class=$sClass{$sDefaultValuesAsUrlParams}"),
] + $this->GetDefaultParamsForMenuAction();
}
/**
* Add a bulk modify action (to $aActions) on objects defined by $sFilter
*
* @param array &$aActions Pointer to the array in which the action will be added
* @param string $sClass Datamodel class concerned by the action
* @param string $sFilter OQL of the objects to propose for bulk modify
* @param string $sActionIdentifier Unique identifier for the action. Default if 'UI:Menu:ModifyAll'
* @param string $sActionLabel Label for the action, can be either a dict code to translate or an hardcoded label. Default is 'UI:Menu:ModifyAll'.
*
* @return void
* @since 3.1.0
* @internal
*/
protected function AddBulkModifyObjectsMenuAction(array &$aActions, string $sClass, string $sFilter, string $sActionIdentifier = 'UI:Menu:ModifyAll', $sActionLabel = 'UI:Menu:ModifyAll'): void
{
$aActions[$sActionIdentifier] = [
'label' => Dict::S($sActionLabel),
'url' => $this->PrepareUrlForStandardMenuAction($sClass, "operation=select_for_modify_all&class=$sClass&filter=".urlencode($sFilter)),
] + $this->GetDefaultParamsForMenuAction();
}
/**
* Add a bulk delete action (to $aActions) on objects defined by $sFilter
*
* @param array &$aActions Pointer to the array in which the action will be added
* @param string $sClass Datamodel class concerned by the action
* @param string $sFilter OQL of the objects to propose for bulk deletion
* @param string $sActionIdentifier Unique identifier for the action. Default if 'UI:Menu:BulkDelete'
* @param string $sActionLabel Label for the action, can be either a dict code to translate or an hardcoded label. Default is 'UI:Menu:BulkDelete'.
*
* @return void
* @since 3.1.0
* @internal
*/
protected function AddBulkDeleteObjectsMenuAction(array &$aActions, string $sClass, string $sFilter, string $sActionIdentifier = 'UI:Menu:BulkDelete', $sActionLabel = 'UI:Menu:BulkDelete')
{
$aActions[$sActionIdentifier] = array(
'label' => Dict::S($sActionLabel),
'url' => $this->PrepareUrlForStandardMenuAction($sClass, "operation=select_for_deletion&filter=".urlencode($sFilter)),
) + $this->GetDefaultParamsForMenuAction();
}
/**
* @return array Default parameters of a menu action
* @since 3.1.0
* @internal
*/
private function GetDefaultParamsForMenuAction(): array
{
$aDefaultParams = [];
if (isset($aExtraParams['menu_actions_target'])) {
$aDefaultParams['target'] = $aExtraParams['menu_actions_target'];
}
return $aDefaultParams;
}
/**
* @param string $sClass Datamodel class for which the URL is prepared
* @param string $sUrlParams URL parameters to add to the URL, must already be concatenated as a string (eg. "foo=bar&some=thing&third=param")
*
* @return string An absolute URL for a menu action on the $sClass class with $sUrlParams
* @throws \Exception
* @internal
*/
private function PrepareUrlForStandardMenuAction(string $sClass, string $sUrlParams)
{
$sRootUrl = utils::GetAbsoluteUrlAppRoot();
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($sClass);
$sUrl = "{$sRootUrl}pages/{$sUIPage}?{$sUrlParams}";
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
if (utils::IsNotNullOrEmptyString($sContext)) {
$sUrl .= '&'.$sContext;
}
return $sUrl;
}
}

View File

@@ -6,6 +6,10 @@
class CoreException extends Exception
{
protected $m_sIssue;
protected $m_sImpact;
protected $m_aContextData;
/**
* CoreException constructor.
*

View File

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

View File

@@ -0,0 +1,13 @@
<?php
/*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* @since 2.7.8 3.0.3 3.1.0 N°5538
*/
class MySQLTransactionNotClosedException extends MySQLException
{
}

View File

@@ -88,4 +88,4 @@ class LoginExternal extends AbstractLoginFSMExtension
/** @var string $sAuthUser */
return $sAuthUser; // Retrieve the value
}
}
}

View File

@@ -92,4 +92,4 @@ class LoginURL extends AbstractLoginFSMExtension
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
}
}

View File

@@ -26,6 +26,8 @@
use Combodo\iTop\Application\Branding;
use Combodo\iTop\Application\Helper\Session;
use Combodo\iTop\Service\Events\EventData;
use Combodo\iTop\Service\Events\EventService;
/**
* Web page used for displaying the login form
@@ -479,11 +481,13 @@ class LoginWebPage extends NiceWebPage
$iResponse = $oLoginFSMExtensionInstance->LoginAction($sLoginState, $iErrorCode);
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)
{
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
break;
@@ -497,6 +501,7 @@ class LoginWebPage extends NiceWebPage
}
catch (Exception $e)
{
EventService::FireEvent(new EventData(EVENT_LOGIN, null, ['state' => $_SESSION['login_state']]));
IssueLog::Error($e->getTraceAsString());
static::ResetSession();
die($e->getMessage());

View File

@@ -291,7 +291,17 @@ class ApplicationMenu
* @param string $sMenuGroupIdx
* @param array $aExtraParams
*
* @return array
* @return array{
* array{
* sId: string,
* sTitle: string,
* sLabel: string,
* bHasCount: boolean,
* sUrl: string,
* bOpenInNewWindow: boolean,
* aSubMenuNodes: array
* }
* } The aSubMenuNodes key contains the same structure recursively
* @throws \DictExceptionMissingString
* @throws \Exception
* @since 3.0.0
@@ -320,12 +330,13 @@ class ApplicationMenu
}
$aSubMenuNodes[] = [
'sId' => $oSubMenuNode->GetMenuId(),
'sTitle' => $oSubMenuNode->GetTitle(),
'bHasCount' => $oSubMenuNode->HasCount(),
'sUrl' => $oSubMenuNode->GetHyperlink($aExtraParams),
'sId' => $oSubMenuNode->GetMenuId(),
'sTitle' => $oSubMenuNode->GetTitle(),
'sLabel' => $oSubMenuNode->GetLabel(),
'bHasCount' => $oSubMenuNode->HasCount(),
'sUrl' => $oSubMenuNode->GetHyperlink($aExtraParams),
'bOpenInNewWindow' => $oSubMenuNode->IsHyperLinkInNewWindow(),
'aSubMenuNodes' => static::GetSubMenuNodes($sSubMenuItemIdx, $aExtraParams),
'aSubMenuNodes' => static::GetSubMenuNodes($sSubMenuItemIdx, $aExtraParams),
];
}
@@ -746,7 +757,7 @@ abstract class MenuNode
}
/**
* @return string
* @return string The "+" dictionary entry for this menu if exists, otherwise the Title (if we have a parent title, will output parentTitle / currentTitle)
*/
public function GetLabel()
{
@@ -758,7 +769,6 @@ abstract class MenuNode
} else {
$sRet = $this->GetTitle();
}
//$sRet = $this->GetTitle();
}
return $sRet;
}
@@ -1101,6 +1111,7 @@ class OQLMenuNode extends MenuNode
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
ContextTag::AddContext(ContextTag::TAG_OBJECT_SEARCH);
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
OQLMenuNode::RenderOQLSearch
(

View File

@@ -156,6 +156,7 @@ abstract class Query extends cmdbAbstractObject
// last export information
$this->Set('export_last_date', date(AttributeDateTime::GetSQLFormat()));
$this->Set('export_last_user_id', UserRights::GetUserObject());
$this->AllowWrite(true);
$this->DBUpdate();
// increment usage counter

View File

@@ -15,12 +15,16 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Application\EventRegister\ApplicationEvents;
use Combodo\iTop\Application\Helper\Session;
use Combodo\iTop\Service\Events\EventData;
use Combodo\iTop\Service\Events\EventService;
require_once(APPROOT.'/core/cmdbobject.class.inc.php');
require_once(APPROOT.'/application/utils.inc.php');
require_once(APPROOT.'/core/contexttag.class.inc.php');
require_once(APPROOT.'/core/kpi.class.inc.php');
require_once(APPROOT.'core/cmdbobject.class.inc.php');
require_once(APPROOT.'application/utils.inc.php');
require_once(APPROOT.'core/contexttag.class.inc.php');
require_once(APPROOT.'core/kpi.class.inc.php');
require_once(APPROOT.'setup/setuputils.class.inc.php');
/**
@@ -99,3 +103,6 @@ else
}
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
// Event service must be initialized after the MetaModel startup, otherwise it cannot discover classes implementing the iEventServiceSetup interface
EventService::InitService();
EventService::FireEvent(new EventData(ApplicationEvents::APPLICATION_EVENT_METAMODEL_STARTED));

View File

@@ -111,23 +111,6 @@ class TwigExtension
return utils::IsDevelopmentEnvironment();
}));
// Function to get configuration parameter
// Usage in twig: {{ get_config_parameter('foo') }}
$oTwigEnv->addFunction(new TwigFunction('get_config_parameter', function ($sParamName) {
$oConfig = MetaModel::GetConfig();
return $oConfig->Get($sParamName);
}));
// Function to get a module setting
// Usage in twig: {{ get_module_setting(<MODULE_CODE>, <PROPERTY_CODE> [, <DEFAULT_VALUE>]) }}
// since 3.0.0, but see N°4034 for upcoming evolutions in the 3.1
$oTwigEnv->addFunction(new TwigFunction('get_module_setting', function (string $sModuleCode, string $sPropertyCode, $defaultValue = null) {
$oConfig = MetaModel::GetConfig();
return $oConfig->GetModuleSetting($sModuleCode, $sPropertyCode, $defaultValue);
}));
// Function to get the URL of a static page in a module
// Usage in twig: {{ get_static_page_module_url('itop-my-module', 'path-to-my-page') }}
$oTwigEnv->addFunction(new TwigFunction('get_static_page_module_url', function ($sModuleName, $sPage) {

View File

@@ -685,15 +685,15 @@ JS
}
$oFilter->SetModifierProperty('UserRightsGetSelectFilter', 'bSearchMode', $this->bSearchMode);
$oBlock = new DisplayBlock($oFilter, 'search', false, $aParams);
$oPage->AddUiBlock($oBlock->GetDisplay($oPage, $this->iId,
$oPage->AddUiBlock($oBlock->GetDisplay($oPage, 'dtc_'.$this->iId,
array(
'menu' => false,
'currentId' => $this->iId,
'table_id' => "dr_{$this->iId}",
'menu' => false,
'currentId' => $this->iId,
'table_id' => "dr_{$this->iId}",
'table_inner_id' => "{$this->iId}_results",
'selection_mode' => true,
'selection_type' => 'single',
'cssCount' => '#count_'.$this->iId.'_results',
'cssCount' => '#count_'.$this->iId.'_results',
)
));
$sCancel = Dict::S('UI:Button:Cancel');

View File

@@ -4,8 +4,8 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
use Combodo\iTop\Application\UI\Links\Direct\BlockDirectLinksEditTable;
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
/**
* Class UILinksWidgetDirect
@@ -19,10 +19,12 @@ class UILinksWidgetDirect
protected $sAttCode;
protected $sInputid;
protected $sNameSuffix;
protected $aZlist;
protected $sLinkedClass;
/**
* UILinksWidgetDirect constructor.
*
* @param string $sClass
* @param string $sAttCode
* @param string $sInputId
@@ -80,97 +82,10 @@ class UILinksWidgetDirect
*/
public function Display(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj)
{
if (empty($aArgs)) {
$aArgs = [];
}
$oBlock = new BlockDirectLinksEditTable($this, $this->sInputid);
$oBlock->InitTable($oPage, $oValue, $sFormPrefix);
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
switch($oLinksetDef->GetEditMode())
{
case LINKSET_EDITMODE_NONE: // The linkset is read-only
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, false /* bDisplayMenu*/);
break;
case LINKSET_EDITMODE_ADDONLY: // The only possible action is to open (in a new window) the form to create a new object
if ($oCurrentObj && !$oCurrentObj->IsNew())
{
$sTargetClass = $oLinksetDef->GetLinkedClass();
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
$sDefault = "default[$sExtKeyToMe]=".$oCurrentObj->GetKey();
$oAppContext = new ApplicationContext();
$sParams = $oAppContext->GetForLink();
$oPage->p("<a target=\"_blank\" href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=new&class=$sTargetClass&$sParams&{$sDefault}\">".Dict::Format('UI:ClickToCreateNew', Metamodel::GetName($sTargetClass))."</a>\n");
}
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, false /* bDisplayMenu*/);
break;
case LINKSET_EDITMODE_INPLACE: // The whole linkset can be edited 'in-place'
$this->DisplayEditInPlace($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj);
break;
case LINKSET_EDITMODE_ADDREMOVE: // The whole linkset can be edited 'in-place'
$sTargetClass = $oLinksetDef->GetLinkedClass();
$sExtKeyToMe = $oLinksetDef->GetExtKeyToMe();
$oExtKeyDef = MetaModel::GetAttributeDef($sTargetClass, $sExtKeyToMe);
$aButtons = array('add');
if ($oExtKeyDef->IsNullAllowed())
{
$aButtons = array('add', 'remove');
}
$this->DisplayEditInPlace($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $aButtons);
break;
case LINKSET_EDITMODE_ACTIONS:
default:
$this->DisplayAsBlock($oPage, $oValue, $aArgs = array(), $sFormPrefix, $oCurrentObj, true /* bDisplayMenu*/);
}
}
/**
* @param WebPage $oPage
* @param DBObjectSet $oValue
* @param array $aArgs
* @param string $sFormPrefix
* @param DBObject $oCurrentObj
* @param bool $bDisplayMenu
*
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $aArgs for PHP 8.0 compatibility (protected method, always called with default value)
*/
protected function DisplayAsBlock(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $bDisplayMenu)
{
$oLinksetDef = MetaModel::GetAttributeDef($this->sClass, $this->sAttCode);
$sTargetClass = $oLinksetDef->GetLinkedClass();
if ($oCurrentObj && $oCurrentObj->IsNew() && $bDisplayMenu)
{
$oPage->p(Dict::Format('UI:BeforeAdding_Class_ObjectsSaveThisObject', MetaModel::GetName($sTargetClass)));
}
else
{
$oFilter = new DBObjectSearch($sTargetClass);
$oFilter->AddCondition($oLinksetDef->GetExtKeyToMe(), $oCurrentObj->GetKey(),'=');
$aDefaults = array($oLinksetDef->GetExtKeyToMe() => $oCurrentObj->GetKey());
$oAppContext = new ApplicationContext();
foreach($oAppContext->GetNames() as $sKey)
{
// The linked object inherits the parent's value for the context
if (MetaModel::IsValidAttCode($this->sClass, $sKey) && $oCurrentObj)
{
$aDefaults[$sKey] = $oCurrentObj->Get($sKey);
}
}
$aParams = array(
'target_attr' => $oLinksetDef->GetExtKeyToMe(),
'object_id' => $oCurrentObj ? $oCurrentObj->GetKey() : null,
'menu' => $bDisplayMenu,
'menu_actions_target' => '_blank',
'default' => $aDefaults,
'table_id' => $this->sClass.'_'.$this->sAttCode,
);
$oBlock = new DisplayBlock($oFilter, 'list', false);
$oBlock->Display($oPage, $this->sInputid, $aParams);
}
return ConsoleBlockRenderer::RenderBlockTemplateInPage($oPage, $oBlock);
}
/**
@@ -229,55 +144,6 @@ class UILinksWidgetDirect
$oPage->add('</div></div>');
}
/**
* @param WebPage $oPage
* @param DBObjectSet $oValue
* @param array $aArgs
* @param string $sFormPrefix
* @param DBObject $oCurrentObj
* @param array $aButtons
*
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $aArgs for PHP 8.0 compatibility (protected method, caller already handles it)
*/
protected function DisplayEditInPlace(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $aButtons = array('create', 'delete'))
{
$aAttribs = $this->GetTableConfig();
$oValue->Rewind();
$aData = array();
while ($oLinkObj = $oValue->Fetch()) {
$aRow = array();
$aRow['form::select'] = '<input type="checkbox" class="selectList'.$this->sInputid.'" value="'.$oLinkObj->GetKey().'"/>';
foreach ($this->aZlist as $sLinkedAttCode) {
$aRow[$sLinkedAttCode] = $oLinkObj->GetAsHTML($sLinkedAttCode);
}
$aData[] = $aRow;
}
$oDiv = UIContentBlockUIBlockFactory::MakeStandard($this->sInputid, ['listContainer']);
$oPage->AddSubBlock($oDiv);
$oDatatable = DataTableUIBlockFactory::MakeForForm($this->sInputid, $aAttribs, $aData);
$oDatatable->SetOptions(['select_mode' => 'custom', 'disable_hyperlinks' => true]);
$oDiv->AddSubBlock($oDatatable);
$sInputName = $sFormPrefix.'attr_'.$this->sAttCode;
$aLabels = array(
'delete' => Dict::S('UI:Button:Delete'),
// 'modify' => 'Modify...' ,
'creation_title' => Dict::Format('UI:CreationTitle_Class', MetaModel::GetName($this->sLinkedClass)),
'create' => Dict::Format('UI:ClickToCreateNew', MetaModel::GetName($this->sLinkedClass)),
'remove' => Dict::S('UI:Button:Remove'),
'add' => Dict::Format('UI:AddAnExisting_Class', MetaModel::GetName($this->sLinkedClass)),
'selection_title' => Dict::Format('UI:SelectionOf_Class', MetaModel::GetName($this->sLinkedClass)),
);
$oContext = new ApplicationContext();
$sSubmitUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?'.$oContext->GetForLink();
$sJSONLabels = json_encode($aLabels);
$sJSONButtons = json_encode($aButtons);
$sWizHelper = 'oWizardHelper'.$sFormPrefix;
// Don't automatically launch the search if the table is huge
$bDoSearch = !utils::IsHighCardinality($this->sLinkedClass);
$sJSDoSearch = $bDoSearch ? 'true' : 'false';
$oPage->add_ready_script("$('#{$this->sInputid}').directlinks({class_name: '$this->sClass', att_code: '$this->sAttCode', input_name:'$sInputName', labels: $sJSONLabels, submit_to: '$sSubmitUrl', buttons: $sJSONButtons, oWizardHelper: $sWizHelper, do_search: $sJSDoSearch});");
}
/**
* @param WebPage $oPage
* @param DBObject $oCurrentObj
@@ -433,18 +299,21 @@ HTML
{
}
protected function GetTableConfig()
public function GetTableConfig()
{
$aAttribs = array();
$aAttribs['form::select'] = array('label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$this->sInputid}:not(:disabled)', this.checked);\" class=\"checkAll\"></input>", 'description' => Dict::S('UI:SelectAllToggle+'));
$aAttribs['form::select'] = array(
'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)
{
foreach ($this->aZlist as $sLinkedAttCode) {
$oAttDef = MetaModel::GetAttributeDef($this->sLinkedClass, $sLinkedAttCode);
$aAttribs[$sLinkedAttCode] = array('label' => MetaModel::GetLabel($this->sLinkedClass, $sLinkedAttCode), 'description' => $oAttDef->GetOrderByHint());
}
return $aAttribs;
return $aAttribs;
}
/**
@@ -543,12 +412,43 @@ HTML
{
$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)) {
$oSearch->AddCondition($sAttCode, $defaultValue);
}
}
}
}
public function GetClass(): string
{
return $this->sClass;
}
public function GetLinkedClass(): string
{
return $this->sLinkedClass;
}
public function GetAttCode(): string
{
return $this->sAttCode;
}
public function GetInputId(): string
{
return $this->sInputid;
}
public function GetNameSuffix(): string
{
return $this->sNameSuffix;
}
public function GetZList(): array
{
return $this->aZlist;
}
}

View File

@@ -6,18 +6,18 @@
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\DataTable\StaticTable\FormTableRow\FormTableRow;
use Combodo\iTop\Application\UI\Links\Indirect\BlockIndirectLinksEdit\BlockIndirectLinksEdit;
use Combodo\iTop\Application\UI\Links\Indirect\BlockObjectPickerDialog\BlockObjectPickerDialog;
use Combodo\iTop\Application\UI\Links\Indirect\BlockIndirectLinksEditTable;
use Combodo\iTop\Application\UI\Links\Indirect\BlockObjectPickerDialog;
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
require_once(APPROOT.'application/displayblock.class.inc.php');
class UILinksWidget
class UILinksWidget
{
protected $m_sClass;
protected $m_sAttCode;
protected $m_sNameSuffix;
protected $m_iInputId;
protected $m_sInputId;
protected $m_aAttributes;
protected $m_sExtKeyToRemote;
protected $m_sExtKeyToMe;
@@ -33,7 +33,7 @@ class UILinksWidget
*
* @param string $sClass
* @param string $sAttCode AttributeLinkedSetIndirect attcode
* @param int $iInputId
* @param string $sInputId
* @param string $sNameSuffix
* @param bool $bDuplicatesAllowed
*
@@ -41,13 +41,14 @@ class UILinksWidget
* @throws \DictExceptionMissingString
* @throws \Exception
*/
public function __construct($sClass, $sAttCode, $iInputId, $sNameSuffix = '', $bDuplicatesAllowed = false)
public function __construct($sClass, $sAttCode, $sInputId, $sNameSuffix = '', $bDuplicatesAllowed = false)
{
$this->m_sClass = $sClass;
$this->m_sAttCode = $sAttCode;
$this->m_sInputId = $sInputId;
$this->m_sNameSuffix = $sNameSuffix;
$this->m_iInputId = $iInputId;
$this->m_bDuplicatesAllowed = $bDuplicatesAllowed;
$this->m_aEditableFields = array();
/** @var AttributeLinkedSetIndirect $oAttDef */
@@ -63,7 +64,7 @@ class UILinksWidget
$this->m_aEditableFields = array();
$this->m_aTableConfig = array();
$this->m_aTableConfig['form::checkbox'] = array(
'label' => "<input class=\"select_all\" type=\"checkbox\" value=\"1\" onClick=\"CheckAll('#linkedset_{$this->m_sAttCode}{$this->m_sNameSuffix} .selection', this.checked); oWidget".$this->m_iInputId.".OnSelectChange();\">",
'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+'),
);
@@ -91,234 +92,13 @@ class UILinksWidget
}
}
/**
* A one-row form for editing a link record
*
* @param WebPage $oP Web page used for the ouput
* @param DBObject $oLinkedObj Remote object
* @param DBObject|int $linkObjOrId Either the lnk object or a unique number for new link records to add
* @param array $aArgs Extra context arguments
* @param DBObject $oCurrentObj The object to which all the elements of the linked set refer to
* @param int $iUniqueId A unique identifier of new links
* @param boolean $bReadOnly Display link as editable or read-only. Default is false (editable)
*
* @return array The HTML fragment of the one-row form
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \Exception
*/
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false)
{
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
$aRow = array();
$aFieldsMap = array();
$iKey = 0;
if (is_object($linkObjOrId) && (!$linkObjOrId->IsNew()))
{
$iKey = $linkObjOrId->GetKey();
$iRemoteObjKey = $linkObjOrId->Get($this->m_sExtKeyToRemote);
$sPrefix .= "[$iKey][";
$sNameSuffix = "]"; // To make a tabular form
$aArgs['prefix'] = $sPrefix;
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}{$iKey}";
$aArgs['this'] = $linkObjOrId;
if ($bReadOnly)
{
$aRow['form::checkbox'] = "";
foreach ($this->m_aEditableFields as $sFieldCode)
{
$sDisplayValue = $linkObjOrId->GetEditValue($sFieldCode);
$aRow[$sFieldCode] = $sDisplayValue;
}
}
else
{
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"$iKey\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$iKey\">";
foreach ($this->m_aEditableFields as $sFieldCode)
{
$sSafeFieldId = $this->GetFieldId($linkObjOrId->GetKey(), $sFieldCode);
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $linkObjOrId, $oP, $sNameSuffix, $sSafeFieldId);
$aFieldsMap[$sFieldCode] = $sSafeFieldId;
}
}
$sState = $linkObjOrId->GetState();
$sRemoteKeySafeFieldId = $this->GetFieldId($aArgs['this']->GetKey(), $this->m_sExtKeyToRemote);;
}
else
{
// form for creating a new record
if (is_object($linkObjOrId))
{
// New link existing only in memory
$oNewLinkObj = $linkObjOrId;
$iRemoteObjKey = $oNewLinkObj->Get($this->m_sExtKeyToRemote);
$oNewLinkObj->Set($this->m_sExtKeyToMe,
$oCurrentObj); // Setting the extkey with the object also fills the related external fields
}
else
{
$iRemoteObjKey = $linkObjOrId;
$oNewLinkObj = MetaModel::NewObject($this->m_sLinkedClass);
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, $iRemoteObjKey);
$oNewLinkObj->Set($this->m_sExtKeyToRemote,
$oRemoteObj); // Setting the extkey with the object alsoo fills the related external fields
$oNewLinkObj->Set($this->m_sExtKeyToMe,
$oCurrentObj); // Setting the extkey with the object also fills the related external fields
}
$sPrefix .= "[-$iUniqueId][";
$sNameSuffix = "]"; // To make a tabular form
$aArgs['prefix'] = $sPrefix;
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}_".($iUniqueId < 0 ? -$iUniqueId : $iUniqueId);
$aArgs['this'] = $oNewLinkObj;
$sInputValue = $iUniqueId > 0 ? "-$iUniqueId" : "$iUniqueId";
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"0\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$sInputValue\">";
if ($iUniqueId > 0)
{
// Rows created with ajax call need OnLinkAdded call.
//
$oP->add_ready_script(
<<<EOF
PrepareWidgets();
oWidget{$this->m_iInputId}.OnLinkAdded($iUniqueId, $iRemoteObjKey);
EOF
);
}
else
{
// Rows added before loading the form don't have to call OnLinkAdded.
// Listeners are already present and DOM is not recreated
$iPositiveUniqueId = -$iUniqueId;
$oP->add_ready_script(<<<EOF
oWidget{$this->m_iInputId}.AddLink($iPositiveUniqueId, $iRemoteObjKey);
EOF
);
}
foreach($this->m_aEditableFields as $sFieldCode)
{
$sSafeFieldId = $this->GetFieldId($iUniqueId, $sFieldCode);
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $oNewLinkObj, $oP, $sNameSuffix, $sSafeFieldId);
$aFieldsMap[$sFieldCode] = $sSafeFieldId;
$sValue = $oNewLinkObj->Get($sFieldCode);
$oP->add_ready_script(
<<<JS
oWidget{$this->m_iInputId}.OnValueChange($iKey, $iUniqueId, '$sFieldCode', '$sValue');
JS
);
}
$sState = '';
$sRemoteKeySafeFieldId = $this->GetFieldId($iUniqueId, $this->m_sExtKeyToRemote);
}
if (!$bReadOnly)
{
$sExtKeyToMeId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToMe);
$aFieldsMap[$this->m_sExtKeyToMe] = $sExtKeyToMeId;
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToMeId\" value=\"".$oCurrentObj->GetKey()."\">";
$sExtKeyToRemoteId = utils::GetSafeId($sPrefix.$this->m_sExtKeyToRemote);
$aFieldsMap[$this->m_sExtKeyToRemote] = $sExtKeyToRemoteId;
$aRow['form::checkbox'] .= "<input type=\"hidden\" id=\"$sExtKeyToRemoteId\" value=\"$iRemoteObjKey\">";
}
// Adding fields from remote class
// all fields are embedded in a span + added to $aFieldsMap array so that we can refresh them after extkey change
$aRemoteFieldsMap = [];
foreach (MetaModel::GetZListItems($this->m_sRemoteClass, 'list') as $sFieldCode)
{
$sSafeFieldId = $this->GetFieldId($aArgs['this']->GetKey(), $sFieldCode);
$aRow['static::'.$sFieldCode] = "<span id='field_$sSafeFieldId'>".$oLinkedObj->GetAsHTML($sFieldCode).'</span>';
$aRemoteFieldsMap[$sFieldCode] = $sSafeFieldId;
}
// id field is needed so that remote object could be load server side
$aRemoteFieldsMap['id'] = $sRemoteKeySafeFieldId;
// Generate WizardHelper to update dependant fields
$this->AddWizardHelperInit($oP, $aArgs['wizHelper'], $this->m_sLinkedClass, $sState, $aFieldsMap);
//instantiate specific WizarHelper instance for remote class fields refresh
$bHasExtKeyUpdatingRemoteClassFields = (
array_key_exists('replaceDependenciesByRemoteClassFields', $aArgs)
&& ($aArgs['replaceDependenciesByRemoteClassFields'])
);
if ($bHasExtKeyUpdatingRemoteClassFields)
{
$this->AddWizardHelperInit($oP, $aArgs['wizHelperRemote'], $this->m_sRemoteClass, $sState, $aRemoteFieldsMap);
}
return $aRow;
}
private function AddRowForFieldCode(&$aRow, $sFieldCode, &$aArgs, $oLnk, $oP, $sNameSuffix, $sSafeFieldId): void
{
if (($sFieldCode === $this->m_sExtKeyToRemote))
{
// current field is the lnk extkey to the remote class
$aArgs['replaceDependenciesByRemoteClassFields'] = true;
$sRowFieldCode = 'static::key';
$aArgs['wizHelperRemote'] = $aArgs['wizHelper'].'_remote';
$aRemoteAttDefs = MetaModel::GetZListAttDefsFilteredForIndirectRemoteClass($this->m_sRemoteClass);
$aRemoteCodes = array_map(
function ($value) {
return $value->GetCode();
},
$aRemoteAttDefs
);
$aArgs['remoteCodes'] = $aRemoteCodes;
}
else
{
$aArgs['replaceDependenciesByRemoteClassFields'] = false;
$sRowFieldCode = $sFieldCode;
}
$sValue = $oLnk->Get($sFieldCode);
$sDisplayValue = $oLnk->GetEditValue($sFieldCode);
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
$aRow[$sRowFieldCode] = '<div class="field_container" style="border:none;"><div class="field_data"><div class="field_value">'
.cmdbAbstractObject::GetFormElementForField(
$oP,
$this->m_sLinkedClass,
$sFieldCode,
$oAttDef,
$sValue,
$sDisplayValue,
$sSafeFieldId,
$sNameSuffix,
0,
$aArgs
)
.'</div></div></div>';
}
private function GetFieldId($iLnkId, $sFieldCode, $bSafe = true)
{
$sFieldId = $this->m_iInputId.'_'.$sFieldCode.'['.$iLnkId.']';
$sFieldId = $this->m_sInputId.'_'.$sFieldCode.'['.$iLnkId.']';
return ($bSafe) ? utils::GetSafeId($sFieldId) : $sFieldId;
}
private function AddWizardHelperInit($oP, $sWizardHelperVarName, $sWizardHelperClass, $sState, $aFieldsMap): void
{
$iFieldsCount = count($aFieldsMap);
$sJsonFieldsMap = json_encode($aFieldsMap);
$oP->add_script(
<<<JS
var $sWizardHelperVarName = new WizardHelper('$sWizardHelperClass', '', '$sState');
$sWizardHelperVarName.SetFieldsMap($sJsonFieldsMap);
$sWizardHelperVarName.SetFieldsCount($iFieldsCount);
$sWizardHelperVarName.SetReturnNotEditableFields(true);
$sWizardHelperVarName.SetWizHelperJsVarName('$sWizardHelperVarName');
JS
);
}
/**
* Display the table with the form for editing all the links at once
@@ -356,92 +136,12 @@ JS
*/
public function Display(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj): string
{
$sLinkedSetId = "{$this->m_sAttCode}{$this->m_sNameSuffix}";
$oBlock = new BlockIndirectLinksEdit("linkedset_{$sLinkedSetId}", ["ibo-block-indirect-links--edit"]);
$oBlock->sLinkedSetId = $sLinkedSetId;
$oBlock->sClass = $this->m_sClass;
$oBlock->sAttCode = $this->m_sAttCode;
$oBlock->iInputId = $this->m_iInputId;
$oBlock->sNameSuffix = $this->m_sNameSuffix;
$oBlock->bDuplicates = ($this->m_bDuplicatesAllowed) ? 'true' : 'false';
$oBlock->oWizHelper = 'oWizardHelper'.$sFormPrefix;
$oBlock->sExtKeyToRemote = $this->m_sExtKeyToRemote;
// Don't automatically launch the search if the table is huge
$oBlock->bJSDoSearch = utils::IsHighCardinality($this->m_sRemoteClass) ? 'false' : 'true';
$oBlock->sFormPrefix = $sFormPrefix;
$oBlock->sRemoteClass = $this->m_sRemoteClass;
$oValue->Rewind();
$aForm = array();
$iMaxAddedId = 0;
$iAddedId = -1; // Unique id for new links
$oBlock->aRemoved = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$this->m_sAttCode}_tbd", '[]', 'raw_data'));
while ($oCurrentLink = $oValue->Fetch()) {
// We try to retrieve the remote object as usual
if (!in_array($oCurrentLink->GetKey(), $oBlock->aRemoved)) {
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote), false /* Must not be found */);
// If successful, it means that we can edit its link
if ($oLinkedObj !== null) {
$bReadOnly = false;
} // Else we retrieve it without restrictions (silos) and will display its link as readonly
else {
$bReadOnly = true;
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote), false /* Must not be found */, true);
}
if ($oCurrentLink->IsNew()) {
$key = $iAddedId--;
} else {
$key = $oCurrentLink->GetKey();
}
$iMaxAddedId = max($iMaxAddedId, $key);
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly);
}
}
$oBlock->iMaxAddedId = (int)$iMaxAddedId;
$oDataTable = DataTableUIBlockFactory::MakeForForm("{$this->m_sAttCode}{$this->m_sNameSuffix}", $this->m_aTableConfig, $aForm);
$oDataTable->SetOptions(['select_mode' => 'custom', 'disable_hyperlinks' => true]);
$oBlock->AddSubBlock($oDataTable);
$oBlock->AddControls();
$oBlock = new BlockIndirectLinksEditTable($this);
$oBlock->InitTable($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $this->m_aTableConfig);
return ConsoleBlockRenderer::RenderBlockTemplateInPage($oPage, $oBlock);
}
/**
* @param string $sClass
* @param string $sAttCode
*
* @return string
* @throws \Exception
*/
protected static function GetTargetClass($sClass, $sAttCode)
{
/** @var AttributeLinkedSet $oAttDef */
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sLinkedClass = $oAttDef->GetLinkedClass();
$sTargetClass = '';
switch(get_class($oAttDef))
{
case 'AttributeLinkedSetIndirect':
/** @var AttributeExternalKey $oLinkingAttDef */
/** @var AttributeLinkedSetIndirect $oAttDef */
$oLinkingAttDef = MetaModel::GetAttributeDef($sLinkedClass, $oAttDef->GetExtKeyToRemote());
$sTargetClass = $oLinkingAttDef->GetTargetClass();
break;
case 'AttributeLinkedSet':
$sTargetClass = $sLinkedClass;
break;
}
return $sTargetClass;
}
/**
* @param WebPage $oPage
* @param DBObject $oCurrentObj
@@ -471,15 +171,10 @@ JS
$oCurrentObj->PrefillForm('search', $aPrefillFormParam);
}
$sLinkedSetId = "{$this->m_sAttCode}{$this->m_sNameSuffix}";
$oBlock = new BlockObjectPickerDialog();
$oBlock = new BlockObjectPickerDialog($this);
$oPage->AddUiBlock($oBlock);
$oBlock->sLinkedSetId = $sLinkedSetId;
$oBlock->iInputId = $this->m_iInputId;
$oBlock->sLinkedClassName = MetaModel::GetName($this->m_sLinkedClass);
$oBlock->sClassName = MetaModel::GetName($this->m_sClass);
$sLinkedSetId = $oBlock->oUILinksWidget->GetLinkedSetId();
$oDisplayBlock = new DisplayBlock($oFilter, 'search', false);
$oBlock->AddSubBlock($oDisplayBlock->GetDisplay($oPage, "SearchFormToAdd_{$sLinkedSetId}",
@@ -548,7 +243,8 @@ JS
foreach ($aLinkedObjectIds as $iObjectId) {
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId, false);
if (is_object($oLinkedObj)) {
$aRow = $this->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
$oBlock = new BlockIndirectLinksEditTable($this);
$aRow = $oBlock->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $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++;
@@ -575,7 +271,8 @@ JS
foreach ($aLinkedObjectIds as $iObjectId) {
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId, false);
if (is_object($oLinkedObj)) {
$aRow = $this->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
$oBlock = new BlockIndirectLinksEditTable($this);
$aRow = $oBlock->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
$aData = [];
foreach ($aRow as $item) {
$aData[] = $item;
@@ -636,24 +333,77 @@ JS
/** @var AttributeExternalKey $oAttDef */
$sTargetClass = $oAttDef->GetTargetClass();
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sTargetClass);
if ($sHierarchicalKeyCode !== false)
{
if ($sHierarchicalKeyCode !== false) {
$oFilter = new DBObjectSearch($sTargetClass);
$oFilter->AddCondition('id', $defaultValue);
$oHKFilter = new DBObjectSearch($sTargetClass);
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW);
$oSearch->AddCondition_PointingTo($oHKFilter, $sAttCode);
}
} catch (Exception $e)
{
}
}
else
{
catch (Exception $e) {
}
} else {
$oSearch->AddCondition($sAttCode, $defaultValue);
}
}
}
}
}
public function GetLinkedSetId(): string
{
return "{$this->m_sAttCode}{$this->m_sNameSuffix}";
}
public function GetClass(): string
{
return $this->m_sClass;
}
public function GetLinkedClass(): string
{
return $this->m_sLinkedClass;
}
public function GetAttCode(): string
{
return $this->m_sAttCode;
}
public function GetInputId(): string
{
return $this->m_sInputId;
}
public function GetNameSuffix(): string
{
return $this->m_sNameSuffix;
}
public function IsDuplicatesAllowed(): bool
{
return $this->m_bDuplicatesAllowed;
}
public function GetExternalKeyToRemote(): string
{
return $this->m_sExtKeyToRemote;
}
public function GetExternalKeyToMe(): string
{
return $this->m_sExtKeyToMe;
}
public function GetRemoteClass(): string
{
return $this->m_sRemoteClass;
}
public function GetEditableFields(): array
{
return $this->m_aEditableFields;
}
}

View File

@@ -69,6 +69,16 @@ class utils
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_CONTEXT_PARAM = 'context_param';
/**
* @var string To filter routes passed to back-end router before being redirected to corresponding controller / method
* @since 3.1.0
*/
public const ENUM_SANITIZATION_FILTER_ROUTE = 'route';
/**
* @var string To filter operation codes passed to back-end router before being redirected to corresponding controller (/ business logic in case of legacy operations)
* @since 3.1.0
*/
public const ENUM_SANITIZATION_FILTER_OPERATION = 'operation';
/**
* @var string
* @since 3.0.0
@@ -406,6 +416,8 @@ class utils
break;
case static::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM:
case static::ENUM_SANITIZATION_FILTER_ROUTE:
case static::ENUM_SANITIZATION_FILTER_OPERATION:
case static::ENUM_SANITIZATION_FILTER_PARAMETER:
case static::ENUM_SANITIZATION_FILTER_FIELD_NAME:
case static::ENUM_SANITIZATION_FILTER_TRANSACTION_ID:
@@ -427,27 +439,31 @@ class utils
switch ($sSanitizationFilter)
{
case static::ENUM_SANITIZATION_FILTER_TRANSACTION_ID:
// same as parameter type but keep the dot character
// see N°1835 : when using file transaction_id on Windows you get *.tmp tokens
// it must be included at the regexp beginning otherwise you'll get an invalid character error
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[\. A-Za-z0-9_=-]*$/')));
// Same as parameter type but keep the dot character
// transaction_id, the dot is mostly for Windows servers when using file storage as the tokens are named *.tmp
// - See N°1835
// - Note: It must be included at the regexp beginning otherwise you'll get an invalid character error
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[\. A-Za-z0-9_=-]*$/')));
break;
case static::ENUM_SANITIZATION_FILTER_ROUTE:
case static::ENUM_SANITIZATION_FILTER_OPERATION:
// - Routes should be of the "controller_namespace_code.controller_method_name" form
// - Operations should be allowed to be namespaced as well even though then don't have dedicated controller yet
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[\.A-Za-z0-9_-]*$/')));
break;
case static::ENUM_SANITIZATION_FILTER_PARAMETER:
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[ A-Za-z0-9_=-]*$/'))); // the '=', '%3D, '%2B', '%2F'
// characters are used in serialized filters (starting 2.5, only the url encoded versions are presents, but the "=" is kept for BC)
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[ A-Za-z0-9_=-]*$/'))); // the '=', '%3D, '%2B', '%2F'
// Characters are used in serialized filters (starting 2.5, only the url encoded versions are presents, but the "=" is kept for BC)
break;
case static::ENUM_SANITIZATION_FILTER_FIELD_NAME:
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[A-Za-z0-9_]+(->[A-Za-z0-9_]+)*$/'))); // att_code or att_code->name or AttCode->Name or AttCode->Key2->Name
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[A-Za-z0-9_]+(->[A-Za-z0-9_]+)*$/'))); // att_code or att_code->name or AttCode->Name or AttCode->Key2->Name
break;
case static::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM:
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
array("options" => array("regexp" => '/^[ A-Za-z0-9_=%:+-]*$/')));
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[ A-Za-z0-9_=%:+-]*$/')));
break;
}
@@ -858,11 +874,50 @@ class utils
*/
public static function DateTimeFormatToPHP($sOldDateTimeFormat)
{
$aSearch = array('%d', '%m', '%y', '%Y', '%H', '%i', '%s');
$aReplacement = array('d', 'm', 'y', 'Y', 'H', 'i', 's');
$aSearch = ['%d', '%m', '%y', '%Y', '%H', '%i', '%s'];
$aReplacement = ['d', 'm', 'y', 'Y', 'H', 'i', 's'];
return str_replace($aSearch, $aReplacement, $sOldDateTimeFormat);
}
/**
* Convert an old strtime() date/time format specification {@link https://www.php.net/manual/fr/function.strftime.php}
* to a format compatible with \DateTime::format {@link https://www.php.net/manual/fr/datetime.format.php}
*
* Example: '%Y-%m-%d %H:%M:%S' => 'Y-m-d H:i:s'
*
* Note: Not all strftime() formats can be converted, in which case they will be present in the returned string (eg. '%U' or '%W')
*
* @param string $sOldStrftimeFormat
*
* @return string
* @since 3.1.0
*/
public static function StrftimeFormatToDateTimeFormat(string $sOldStrftimeFormat): string
{
$aSearch = [
'%d', '%m', '%y', '%Y', '%H', '%M', '%S', // Most popular formats
'%a', '%A', '%e', '%j', '%u', '%w', // Day formats
'%U', '%V', '%W', // Week formats
'%b', '%B', '%h', // Month formats
'%C', '%g', '%G', // Year formats
'%k', '%I', '%l', '%p', '%P', '%r', '%R', '%T', '%X', '%z', '%Z', // Time formats
'%c', '%D', '%F', '%s', '%x', // Datetime formats
'%n', '%t', '%%', // Misc. formats
];
$aReplacement = [
'd', 'm', 'y', 'Y', 'H', 'i', 's',
'D', 'l', 'j', 'z', 'N', 'w',
'%U', 'W', '%W',
'M', 'F', 'M',
'%C', 'y', 'Y',
'G', 'h', 'g', 'A', 'a', 'h:i:s A', 'H:i', 'H:i:s', '%X', 'O', 'T',
'%c', 'm/d/y', 'Y-m-d', 'U', '%x',
'%n', '%t', '%',
];
return str_replace($aSearch, $aReplacement, $sOldStrftimeFormat);
}
/**
* Allow to set cached config. Useful when running with {@link Parameters} for example.
* @param \Config $oConfig
@@ -1394,71 +1449,83 @@ class utils
*/
public static function GetPopupMenuItemsBlock(iUIBlock &$oContainerBlock, $iMenuId, $param, &$aActions, $sDataTableId = null)
{
$aResult = [];
// 1st - add standard built-in menu items
//
switch($iMenuId)
{
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
// $param is a DBObjectSet
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$sDataTableId = is_null($sDataTableId) ? '' : $sDataTableId;
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($param->GetFilter()->GetClass());
$sOQL = addslashes($param->GetFilter()->ToOQL(true));
$sFilter = urlencode($param->GetFilter()->serialize());
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
case iPopupMenuExtension::MENU_OBJLIST_ACTIONS:
// No native action there yet
break;
$aResult = array();
if (strlen($sUrl) < SERVER_MAX_URL_LENGTH)
{
$aResult[] = new SeparatorPopupMenuItem();
// Static menus: Email this page, CSV Export & Add to Dashboard
$aResult[] = new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'),
"mailto:?body=".urlencode($sUrl).' ' // Add an extra space to make it work in Outlook
);
}
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_BULK_READ, $param) != UR_ALLOWED_NO)
{
// Bulk export actions
$aResult[] = new JSPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), "ExportListDlg('$sOQL', '$sDataTableId', 'csv', ".json_encode(Dict::S('UI:Menu:CSVExport')).")");
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportXLSX', Dict::S('ExcelExporter:ExportMenu'), "ExportListDlg('$sOQL', '$sDataTableId', 'xlsx', ".json_encode(Dict::S('ExcelExporter:ExportMenu')).")");
if (extension_loaded('gd'))
{
// PDF export requires GD
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportPDF', Dict::S('UI:Menu:ExportPDF'), "ExportListDlg('$sOQL', '$sDataTableId', 'pdf', ".json_encode(Dict::S('UI:Menu:ExportPDF')).")");
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
/** @var \DBObjectSet $param */
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$sDataTableId = is_null($sDataTableId) ? '' : $sDataTableId;
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($param->GetFilter()->GetClass());
$sOQL = addslashes($param->GetFilter()->ToOQL(true));
$sFilter = urlencode($param->GetFilter()->serialize());
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
// Configure this list on datatables
if (utils::IsNotNullOrEmptyString($sDataTableId)) {
$aResult[] = new JSPopupMenuItem(
'iTop::ConfigureList',
Dict::S('UI:ConfigureThisList'),
"$('#datatable_dlg_datatable_{$sDataTableId}').dialog('open'); return false;"
);
$aResult[] = new SeparatorPopupMenuItem();
}
}
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL', '$sContext')");
$aResult[] = new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')");
break;
if (strlen($sUrl) < SERVER_MAX_URL_LENGTH) {
// Static menus: Email this page, CSV Export & Add to Dashboard
$aResult[] = new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'),
"mailto:?body=".urlencode($sUrl).' ' // Add an extra space to make it work in Outlook
);
}
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_BULK_READ, $param) != UR_ALLOWED_NO) {
// Bulk export actions
$aResult[] = new JSPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), "ExportListDlg('$sOQL', '$sDataTableId', 'csv', ".json_encode(Dict::S('UI:Menu:CSVExport')).")");
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportXLSX', Dict::S('ExcelExporter:ExportMenu'), "ExportListDlg('$sOQL', '$sDataTableId', 'xlsx', ".json_encode(Dict::S('ExcelExporter:ExportMenu')).")");
if (extension_loaded('gd'))
{
// PDF export requires GD
$aResult[] = new JSPopupMenuItem('UI:Menu:ExportPDF', Dict::S('UI:Menu:ExportPDF'), "ExportListDlg('$sOQL', '$sDataTableId', 'pdf', ".json_encode(Dict::S('UI:Menu:ExportPDF')).")");
}
}
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL', '$sContext')");
$aResult[] = new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')");
break;
case iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS:
// $param is a DBObject
$oObj = $param;
$sOQL = "SELECT ".get_class($oObj)." WHERE id=".$oObj->GetKey();
$sUrl = ApplicationContext::MakeObjectUrl(get_class($oObj), $oObj->GetKey());
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
$aResult = array(
new SeparatorPopupMenuItem(),
// Static menus: Email this page & CSV Export
new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'), "mailto:?subject=".urlencode($oObj->GetRawName())."&body=".urlencode($sUrl).' '), // Add an extra space to make it work in Outlook
new JSPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), "ExportListDlg('$sOQL', '', 'csv', ".json_encode(Dict::S('UI:Menu:CSVExport')).")"),
new JSPopupMenuItem('UI:Menu:ExportXLSX', Dict::S('ExcelExporter:ExportMenu'), "ExportListDlg('$sOQL', '', 'xlsx', ".json_encode(Dict::S('ExcelExporter:ExportMenu')).")"),
new SeparatorPopupMenuItem(),
new URLPopupMenuItem('UI:Menu:PrintableVersion', Dict::S('UI:Menu:PrintableVersion'), $sUrl.'&printable=1', '_blank'),
);
break;
/** @var \DBObject $param */
$oObj = $param;
$sOQL = "SELECT ".get_class($oObj)." WHERE id=".$oObj->GetKey();
$sUrl = ApplicationContext::MakeObjectUrl(get_class($oObj), $oObj->GetKey());
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
$aResult = array(
new SeparatorPopupMenuItem(),
// Static menus: Email this page & CSV Export
new URLPopupMenuItem('UI:Menu:EMail', Dict::S('UI:Menu:EMail'), "mailto:?subject=".urlencode($oObj->GetRawName())."&body=".urlencode($sUrl).' '), // Add an extra space to make it work in Outlook
new JSPopupMenuItem('UI:Menu:CSVExport', Dict::S('UI:Menu:CSVExport'), "ExportListDlg('$sOQL', '', 'csv', ".json_encode(Dict::S('UI:Menu:CSVExport')).")"),
new JSPopupMenuItem('UI:Menu:ExportXLSX', Dict::S('ExcelExporter:ExportMenu'), "ExportListDlg('$sOQL', '', 'xlsx', ".json_encode(Dict::S('ExcelExporter:ExportMenu')).")"),
new SeparatorPopupMenuItem(),
new URLPopupMenuItem('UI:Menu:PrintableVersion', Dict::S('UI:Menu:PrintableVersion'), $sUrl.'&printable=1', '_blank'),
);
break;
case iPopupMenuExtension::MENU_DASHBOARD_ACTIONS:
// $param is a Dashboard
@@ -1466,19 +1533,19 @@ class utils
$oDashboard = $param;
$sDashboardId = $oDashboard->GetId();
$sDashboardFile = $oDashboard->GetDefinitionFile();
$sDashboardFileRelative = utils::LocalPath($sDashboardFile);
$sDlgTitle = addslashes(Dict::S('UI:ImportDashboardTitle'));
$sDlgText = addslashes(Dict::S('UI:ImportDashboardText'));
$sCloseBtn = addslashes(Dict::S('UI:Button:Cancel'));
$sDashboardFileJS = addslashes($sDashboardFile);
$sDashboardFileURL = urlencode($sDashboardFile);
$sDashboardFileJS = addslashes($sDashboardFileRelative);
$sDashboardFileURL = urlencode($sDashboardFileRelative);
$sUploadDashboardTransactId = utils::GetNewTransactionId();
$aResult = array(
new SeparatorPopupMenuItem(),
new URLPopupMenuItem('UI:ExportDashboard', Dict::S('UI:ExportDashBoard'), utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=export_dashboard&id='.$sDashboardId.'&file='.$sDashboardFileURL),
new JSPopupMenuItem('UI:ImportDashboard', Dict::S('UI:ImportDashBoard'), "UploadDashboard({dashboard_id: '$sDashboardId', file: '$sDashboardFileJS', title: '$sDlgTitle', text: '$sDlgText', close_btn: '$sCloseBtn', transaction: '$sUploadDashboardTransactId' })"),
);
if ($oDashboard->GetReloadURL())
{
if ($oDashboard->GetReloadURL()) {
$aResult[] = new SeparatorPopupMenuItem();
$aResult[] = new URLPopupMenuItem('UI:Menu:PrintableVersion', Dict::S('UI:Menu:PrintableVersion'), $oDashboard->GetReloadURL().'&printable=1', '_blank');
}
@@ -1487,34 +1554,25 @@ class utils
default:
// Unknown type of menu, do nothing
$aResult = array();
}
foreach ($aResult as $oMenuItem)
{
foreach ($aResult as $oMenuItem) {
$aActions[$oMenuItem->GetUID()] = $oMenuItem->GetMenuItem();
}
// Invoke the plugins
//
/** @var \iPopupMenuExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iPopupMenuExtension') as $oExtensionInstance)
{
if (is_object($param) && !($param instanceof DBObject))
{
foreach (MetaModel::EnumPlugins('iPopupMenuExtension') as $oExtensionInstance) {
if (is_object($param) && !($param instanceof DBObject)) {
$tmpParam = clone $param; // In case the parameter is an DBObjectSet, clone it to prevent alterations
}
else
{
} else {
$tmpParam = $param;
}
foreach($oExtensionInstance->EnumItems($iMenuId, $tmpParam) as $oMenuItem)
{
if (is_object($oMenuItem))
{
foreach ($oExtensionInstance->EnumItems($iMenuId, $tmpParam) as $oMenuItem) {
if (is_object($oMenuItem)) {
$aActions[$oMenuItem->GetUID()] = $oMenuItem->GetMenuItem();
foreach($oMenuItem->GetLinkedScripts() as $sLinkedScript)
{
foreach ($oMenuItem->GetLinkedScripts() as $sLinkedScript) {
$oContainerBlock->AddJsFileRelPath($sLinkedScript);
}
}
@@ -1522,6 +1580,136 @@ class utils
}
}
/**
* We cannot use iMenuId (corresponding values in {@see \iPopupMenuExtension} constants) as value is always {@see \iPopupMenuExtension::MENU_OBJLIST_TOOLKIT}
* whenever we are in a datatable, whereas it is included in a object tab, a dashlet or a search.
*
* So a {@see \ContextTag} is set on the corresponding calls.
*
* @return bool true if we are in a search page context, either directly or by the datatable ajax call
*
* @since 3.1.0 N°3200
*
* @uses \ContextTag::TAG_OBJECT_SEARCH
*/
public static function IsCurrentPageASearch(): bool
{
if (ContextTag::Check(ContextTag::TAG_OBJECT_SEARCH)) {
return true;
}
return false;
}
/**
* @param \DBObjectSearch $oFilter object list
* @param array $aExtraParams
*
* @return string|null null if we are already in a search, otherwise the URL to open this list in a search
*
* @throws \ArchivedObjectException
* @throws \CoreException
* @uses utils::IsCurrentPageASearch()
*
* @since 3.1.0 N°3200
*/
public static function GetDataTableSearchUrl(DBSearch $oFilter, array $aExtraParams): ?string
{
if (static::IsCurrentPageASearch()) {
// we don't want to add the link when already in a search page !
return null;
}
$bIsObjectRelation = isset($aExtraParams['object_id'], $aExtraParams['target_attr']);
if ($bIsObjectRelation) {
[$oDataTableSearchFilter, $aParams] = static::GetDataTableSearchForRelations($oFilter, $aExtraParams);
} else {
$oDataTableSearchFilter = $oFilter;
$aParams = [];
}
if (isset($aExtraParams['table_id'])) {
$aParams['table_id'] = $aExtraParams['table_id'];
}
$sParams = json_encode($aParams);
$sAppRootUrl = static::GetAbsoluteUrlAppRoot();
$oAppContext = new ApplicationContext();
$sUrl = $sAppRootUrl
.'pages/UI.php?operation=search&'
.$oAppContext->GetForLink()
.'&filter='.rawurlencode($oDataTableSearchFilter->serialize());
$sUrl .= '&aParams='.rawurlencode($sParams); // Not working... yet, cause not handled by UI.php
return $sUrl;
}
/**
* Rewrites filter for object relations, so that in the search page we will have the correct criteria and will be able to use "configure this list"
*
* @param \DBSearch $oFilter object list
* @param array{link_attr: string, target_attr: string, object_id: string} $aExtraParams
*
* @return array{\DBObjectSearch, string[]}
* @throws \CoreException
* @throws \OQLException
*
* @since 3.1.0 N°3200
*/
private static function GetDataTableSearchForRelations(DBSearch $oFilter, array $aExtraParams): array
{
$sObjectId = $aExtraParams['object_id'];
$bIsLinkedSetIndirect = isset($aExtraParams['link_attr']);
if ($bIsLinkedSetIndirect) {
//--- AttributeLinkedSetIndirect (n,n => lnk class)
$sLnkClass = $oFilter->GetClass();
$sExtKeyToObjectClass = $aExtraParams['link_attr'];
$sExtKeyToRemoteClass = $aExtraParams['target_attr'];
/** @var \AttributeExternalKey $oLnkExtKeyToRemote */
$oLnkExtKeyToRemote = MetaModel::GetAttributeDef($sLnkClass, $sExtKeyToRemoteClass);
$sRemoteClass = $oLnkExtKeyToRemote->GetTargetClass();
/** @var \AttributeExternalKey $oLnkExtKeyToRemote */
$oLnkExtKeyToObject = MetaModel::GetAttributeDef($sLnkClass, $sExtKeyToObjectClass);
$sObjectClass = $oLnkExtKeyToObject->GetTargetClass();
/** @var \AttributeExternalKey $oLnkExtKeyToRemote */
$oObjectExtKeyToLnk = $oLnkExtKeyToObject->GetMirrorLinkAttribute();
$sObjectExtKeyToLnkClass = $oObjectExtKeyToLnk->GetCode();
$sRemoteClassAliasName = ormLinkSet::REMOTE_ALIAS;
$sLnkClassAliasName = ormLinkSet::LINK_ALIAS;
$sOql = <<<SQL
SELECT {$sRemoteClassAliasName},{$sLnkClassAliasName}
FROM {$sRemoteClass} AS {$sRemoteClassAliasName}
JOIN {$sLnkClass} AS {$sLnkClassAliasName} ON {$sLnkClassAliasName}.$sExtKeyToRemoteClass = {$sRemoteClassAliasName}.id
WHERE {$sLnkClassAliasName}.$sExtKeyToObjectClass = $sObjectId
SQL;
$aAttCodesToDisplay = MetaModel::GetAttributeLinkedSetIndirectDatatableAttCodesToDisplay($sObjectClass, $sObjectExtKeyToLnkClass, $sRemoteClass, $sExtKeyToRemoteClass);
/** @noinspection PhpUnnecessaryLocalVariableInspection */
$sAttCodesToDisplay = implode(',', $aAttCodesToDisplay);
$aParams = [
'zlist' => false,
'extra_fields' => $sAttCodesToDisplay,
];
} else {
//--- AttributeLinkedSet (1,n => AttributeExternalKey)
$sClass = $oFilter->GetClass();
$sExtKeyCode = $aExtraParams['target_attr'];
$sOql = "SELECT $sClass WHERE $sExtKeyCode = $sObjectId";
$aParams = [];
}
$oDataTableSearchFilter = DBSearch::FromOQL($sOql);
return [$oDataTableSearchFilter, $aParams];
}
/**
* @param string $sEnvironment
*
@@ -1529,10 +1717,10 @@ class utils
*/
public static function GetConfigFilePath($sEnvironment = null)
{
if (is_null($sEnvironment))
{
if (is_null($sEnvironment)) {
$sEnvironment = self::GetCurrentEnvironment();
}
return APPCONF.$sEnvironment.'/'.ITOP_CONFIG_FILE;
}
@@ -2749,12 +2937,18 @@ HTML;
$bSkipped = true;
}
else {
foreach ($aExcludedPath as $sExcludedPath) {
// Note: We use '#' as delimiters as usual '/' is often used in paths.
if ($sExcludedPath !== '' && preg_match('#'.$sExcludedPath.'#', $sPHPFile) === 1) {
$bSkipped = true;
break;
$sPHPFile = self::LocalPath($sPHPFile);
if ($sPHPFile !== false) {
$sPHPFile = '/'.$sPHPFile; // for regex
foreach ($aExcludedPath as $sExcludedPath) {
// Note: We use '#' as delimiters as usual '/' is often used in paths.
if ($sExcludedPath !== '' && preg_match('#'.$sExcludedPath.'#', $sPHPFile) === 1) {
$bSkipped = true;
break;
}
}
} else {
$bSkipped = true; // file not found
}
}
@@ -2792,7 +2986,7 @@ HTML;
$aResultPref = [];
$aShortcutPrefs = appUserPreferences::GetPref('keyboard_shortcuts', []);
// Note: Mind the 4 blackslashes, see utils::GetClassesForInterface()
$aShortcutClasses = utils::GetClassesForInterface('iKeyboardShortcut', '', array('[\\\\/]lib[\\\\/]', '[\\\\/]node_modules[\\\\/]', '[\\\\/]test[\\\\/]'));
$aShortcutClasses = utils::GetClassesForInterface('iKeyboardShortcut', '', array('[\\\\/]lib[\\\\/]', '[\\\\/]node_modules[\\\\/]', '[\\\\/]test[\\\\/]', '[\\\\/]tests[\\\\/]'));
foreach ($aShortcutClasses as $cShortcutPlugin) {
$sTriggeredElement = $cShortcutPlugin::GetShortcutTriggeredElementSelector();
@@ -3019,18 +3213,35 @@ HTML;
}
/**
* Transform a snake_case $sInput into a CamelCase string
*
* @param string $sInput
*
* @return string
* @return string Camel case representation of $sInput (eg. "something_new" becomes "SomethingNew")
* @since 2.7.0
*/
public static function ToCamelCase($sInput)
public static function ToCamelCase($sInput): string
{
return str_replace(' ', '', ucwords(strtr($sInput, '_-', ' ')));
}
/**
* @param string $sInput
*
* @return string Snake case representation of $sInput (eg. "SomethingNew" becomes "something_new")
* @since 3.1.0
* @link https://stackoverflow.com/a/19533226/2710325
*/
public static function ToSnakeCase(string $sInput): string
{
// Remove special chars to join words
$sOutput = preg_replace('/(\W)/', '_', $sInput);
// Transform camel case words to snake case
$sOutput = preg_replace('/[A-Z]([A-Z](?![a-z]))*/', '_$0', $sOutput);
// Lowercase everything
$sOutput = mb_strtolower($sOutput);
// Trim outer underscores
return trim($sOutput, '_');
}
/**
* @param string $sInput
*
@@ -3143,15 +3354,50 @@ HTML;
*/
public static function AddParameterToUrl(string $sUrl, string $sParamName, string $sParamValue): string
{
if (strpos($sUrl, '?') === false)
{
if (strpos($sUrl, '?') === false) {
$sUrl = $sUrl.'?'.urlencode($sParamName).'='.urlencode($sParamValue);
}
else
{
} else {
$sUrl = $sUrl.'&'.urlencode($sParamName).'='.urlencode($sParamValue);
}
return $sUrl;
}
/**
* Return traits array used by a class and by parent classes hierarchy.
*
* @see https://www.php.net/manual/en/function.class-uses.php#110752
*
* @param string $sClass Class to scan
* @param bool $bAutoload Autoload flag
*
* @return array traits used
* @since 3.1.0
*/
public static function TraitsUsedByClass(string $sClass, bool $bAutoload = true): array
{
$aTraits = [];
do {
$aTraits = array_merge(class_uses($sClass, $bAutoload), $aTraits);
} while ($sClass = get_parent_class($sClass));
foreach ($aTraits as $sTrait => $same) {
$aTraits = array_merge(class_uses($sTrait, $bAutoload), $aTraits);
}
return array_unique($aTraits);
}
/**
* Test trait usage by a class or by parent classes hierarchy.
*
* @param string $sTrait Trait to search for
* @param string $sClass Class to check
*
* @return bool
* @since 3.1.0
*/
public static function IsTraitUsedByClass(string $sTrait, string $sClass): bool
{
return in_array($sTrait, self::TraitsUsedByClass($sClass, true));
}
}

View File

@@ -60,16 +60,20 @@ class WizardHelper
// special handling for lists
// assumes this is handled as an array of objects
// thus encoded in json like: [ { name:'link1', 'id': 123}, { name:'link2', 'id': 124}...]
$aData = json_decode($value, true); // true means decode as a hash array (not an object)
if (!is_array($value)) {
$aData = json_decode($value, true); // true means decode as a hash array (not an object)
} else {
$aData = $value;
}
// Check what are the meaningful attributes
$aFields = $this->GetLinkedWizardStructure($oAttDef);
$sLinkedClass = $oAttDef->GetLinkedClass();
$aLinkedObjectsArray = array();
if (!is_array($aData))
{
echo ("aData: '$aData' (value: '$value')\n");
if (!is_array($aData)) {
echo("aData: '$aData' (value: '$value')\n");
}
foreach($aData as $aLinkedObject)
foreach ($aData as $aLinkedObject)
{
$oLinkedObj = MetaModel::NewObject($sLinkedClass);
foreach($aFields as $sLinkedAttCode)

View File

@@ -12,7 +12,7 @@
"ext-json": "*",
"ext-mysqli": "*",
"ext-soap": "*",
"apereo/phpcas" : "~1.3",
"apereo/phpcas" : "~1.6.0",
"combodo/tcpdf": "~6.4.4",
"guzzlehttp/guzzle": "^7.4.5",
"laminas/laminas-mail": "^2.11",

26
composer.lock generated
View File

@@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "276d2024de344c0d4105b15850560696",
"content-hash": "1fee7c7fd7649286a09641ae53e216de",
"packages": [
{
"name": "apereo/phpcas",
"version": "1.5.0",
"version": "1.6.0",
"source": {
"type": "git",
"url": "https://github.com/apereo/phpCAS.git",
"reference": "d6f5797fb568726f34c8e48741776d81e4a2646b"
"reference": "f817c72a961484afef95ac64a9257c8e31f063b9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/apereo/phpCAS/zipball/d6f5797fb568726f34c8e48741776d81e4a2646b",
"reference": "d6f5797fb568726f34c8e48741776d81e4a2646b",
"url": "https://api.github.com/repos/apereo/phpCAS/zipball/f817c72a961484afef95ac64a9257c8e31f063b9",
"reference": "f817c72a961484afef95ac64a9257c8e31f063b9",
"shasum": ""
},
"require": {
@@ -70,9 +70,9 @@
],
"support": {
"issues": "https://github.com/apereo/phpCAS/issues",
"source": "https://github.com/apereo/phpCAS/tree/1.5.0"
"source": "https://github.com/apereo/phpCAS/tree/1.6.0"
},
"time": "2022-05-03T21:12:54+00:00"
"time": "2022-10-31T20:39:27+00:00"
},
{
"name": "combodo/tcpdf",
@@ -4974,16 +4974,16 @@
},
{
"name": "twig/twig",
"version": "v3.4.2",
"version": "v3.4.3",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077"
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077",
"reference": "e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/c38fd6b0b7f370c198db91ffd02e23b517426b58",
"reference": "c38fd6b0b7f370c198db91ffd02e23b517426b58",
"shasum": ""
},
"require": {
@@ -5034,7 +5034,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.4.2"
"source": "https://github.com/twigphp/Twig/tree/v3.4.3"
},
"funding": [
{
@@ -5046,7 +5046,7 @@
"type": "tidelift"
}
],
"time": "2022-08-12T06:47:24+00:00"
"time": "2022-09-28T08:42:51+00:00"
},
{
"name": "webmozart/assert",

View File

@@ -43,22 +43,24 @@ abstract class Action extends cmdbAbstractObject
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_action",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-in-transit.svg'),
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "name",
"complementary_name_attcode" => array('finalclass', 'description'),
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_action",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-in-transit.svg'),
);
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"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum(array('test'=>'Being tested' ,'enabled'=>'In production', 'disabled'=>'Inactive')), "sql"=>"status", "default_value"=>"test", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("trigger_list", array("linked_class"=>"lnkTriggerAction", "ext_key_to_me"=>"action_id", "ext_key_to_remote"=>"trigger_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values" => new ValueSetEnum(array('test' => 'Being tested', 'enabled' => 'In production', 'disabled' => 'Inactive')), "sql" => "status", "default_value" => "test", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("trigger_list",
array("linked_class" => "lnkTriggerAction", "ext_key_to_me" => "action_id", "ext_key_to_remote" => "trigger_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array(), "display_style" => 'property')));
// Display lists
// - Attributes to be displayed for the complete details

View File

@@ -181,7 +181,7 @@ abstract class AsyncTask extends DBObject
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
$bExponential = (bool)$aConfig['exponential_delay'] ?? $bExponential;
$bExponential = (bool) ($aConfig['exponential_delay'] ?? $bExponential);
}
return $bExponential;
}

View File

@@ -5,12 +5,15 @@
*/
use Combodo\iTop\Application\UI\Base\Component\FieldBadge\FieldBadgeUIBlockFactory;
use Combodo\iTop\Application\UI\Links\Indirect\BlockLinksSetDisplayAsProperty;
use Combodo\iTop\Form\Field\LabelField;
use Combodo\iTop\Form\Field\TextAreaField;
use Combodo\iTop\Form\Form;
use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator;
use Combodo\iTop\Form\Validator\Validator;
use Combodo\iTop\Renderer\BlockRenderer;
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
use Combodo\iTop\Service\Links\LinkSetModel;
require_once('MyHelpers.class.inc.php');
require_once('ormdocument.class.inc.php');
@@ -89,8 +92,15 @@ define('LINKSET_EDITMODE_ACTIONS', 2); // Show the usual 'Actions' popup menu
define('LINKSET_EDITMODE_INPLACE', 3); // The "linked" objects can be created/modified/deleted in place
define('LINKSET_EDITMODE_ADDREMOVE', 4); // The "linked" objects can be added/removed in place
define('LINKSET_RELATIONTYPE_PROPERTY', 'property');
define('LINKSET_RELATIONTYPE_LINK', 'link');
define('LINKSET_DISPLAY_STYLE_PROPERTY', 'property');
define('LINKSET_DISPLAY_STYLE_TAB', 'tab');
/**
* Attributes implementing this interface won't be accepted as `group by` field
*
* @since 2.7.4 N°3473
*/
interface iAttributeNoGroupBy
@@ -136,6 +146,15 @@ abstract class AttributeDefinition
abstract public function GetEditClass();
/**
* @return array Css classes
* @since 3.1.0 N°3190
*/
public function GetCssClasses(): array
{
return $this->aCSSClasses;
}
/**
* Return the search widget type corresponding to this attribute
*
@@ -357,6 +376,18 @@ abstract class AttributeDefinition
return false;
}
/**
* Returns true if the attribute can be used in bulk modify.
*
* @return bool
* @since 3.1.0 N°3190
*
*/
public static function IsBulkModifyCompatible(): bool
{
return static::IsScalar();
}
/**
* Returns true if the attribute value is a set of related objects (1-N or N-N)
*
@@ -620,6 +651,16 @@ abstract class AttributeDefinition
return $sLabel;
}
/**
* @return bool True if the attribute has a description {@see \AttributeDefinition::GetDescription()}
* @throws \Exception
* @since 3.1.0
*/
public function HasDescription(): bool
{
return utils::IsNotNullOrEmptyString($this->GetDescription());
}
/**
* @param string|null $sDefault
*
@@ -1413,6 +1454,7 @@ class AttributeLinkedSet extends AttributeDefinition
public function __construct($sCode, $aParams)
{
parent::__construct($sCode, $aParams);
$this->aCSSClasses[] = 'attribute-set';
}
public static function ListExpectedParams()
@@ -1426,6 +1468,12 @@ class AttributeLinkedSet extends AttributeDefinition
return "LinkedSet";
}
/** @inheritDoc */
public static function IsBulkModifyCompatible(): bool
{
return false;
}
public function IsWritable()
{
return true;
@@ -1443,7 +1491,26 @@ class AttributeLinkedSet extends AttributeDefinition
public function GetValuesDef()
{
return $this->Get("allowed_values");
$oValSetDef = $this->Get("allowed_values");
if (!$oValSetDef) {
// Let's propose every existing value
$oValSetDef = new ValueSetObjects('SELECT '.LinkSetModel::GetTargetClass($this));
}
return $oValSetDef;
}
public function GetEditValue($value, $oHostObj = null)
{
/** @var ormLinkSet $value * */
if ($value->Count() === 0) {
return '';
}
/** Return linked objects key as string **/
$aValues = $value->GetValues();
return implode(' ', $aValues);
}
public function GetPrerequisiteAttributes($sClass = null)
@@ -1510,11 +1577,42 @@ class AttributeLinkedSet extends AttributeDefinition
return $this->GetOptional('tracking_level', MetaModel::GetConfig()->Get('tracking_level_linked_set_default'));
}
/**
* @return string see LINKSET_EDITMODE_* constants
* @since 3.1.0 N°5563 relations are edited using new attributes in details mode, but as nothing changed in edit mode we are still using edit_mode attribute
*/
public function GetEditMode()
{
return $this->GetOptional('edit_mode', LINKSET_EDITMODE_ACTIONS);
}
/**
* @return string see LINKSET_RELATIONTYPE_* constants
* @since 3.1.0 N°5563
*/
public function GetRelationType()
{
return $this->GetOptional('relation_type', LINKSET_RELATIONTYPE_LINK);
}
/**
* @return string see LINKSET_DISPLAY_STYLE_* constants
* @since 3.1.0 N°3190
*/
public function GetDisplayStyle()
{
return $this->GetOptional('display_style', LINKSET_DISPLAY_STYLE_TAB);
}
/**
* @return boolean
* @since 3.1.0 N°5563
*/
public function GetReadOnly()
{
return $this->GetOptional('read_only', false);
}
public function GetLinkedClass()
{
return $this->Get('linked_class');
@@ -1540,49 +1638,30 @@ class AttributeLinkedSet extends AttributeDefinition
return '';
}
/**
* @param string $sValue
* @param \DBObject $oHostObject
* @param bool $bLocalize
*
* @return string|null
*
* @throws \CoreException
*/
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
/** @inheritDoc * */
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true): string
{
if (is_object($sValue) && ($sValue instanceof ormLinkSet))
{
$sValue->Rewind();
$aItems = array();
while ($oObj = $sValue->Fetch())
{
// Show only relevant information (hide the external key to the current object)
$aAttributes = array();
foreach(MetaModel::ListAttributeDefs($this->GetLinkedClass()) as $sAttCode => $oAttDef)
{
if ($sAttCode == $this->GetExtKeyToMe())
{
continue;
}
if ($oAttDef->IsExternalField())
{
continue;
}
$sAttValue = $oObj->GetAsHTML($sAttCode);
if (strlen($sAttValue) > 0)
{
$aAttributes[] = $sAttValue;
}
}
$sAttributes = implode(', ', $aAttributes);
$aItems[] = $sAttributes;
try {
/** @var ormLinkSet $sValue */
if ($sValue->Count() === 0) {
return '';
}
return implode('<br/>', $aItems);
}
$oLinkSetBlock = new BlockLinksSetDisplayAsProperty($this->GetCode(), $this, $sValue);
return null;
return ConsoleBlockRenderer::RenderBlockTemplates($oLinkSetBlock);
}
catch (Exception $e) {
$sMessage = "Error while displaying attribute {$this->GetCode()}";
IssueLog::Error($sMessage, IssueLog::CHANNEL_DEFAULT, [
'host_object_class' => $this->GetHostClass(),
'host_object_key' => $oHostObject->GetKey(),
'attribute' => $this->GetCode(),
]);
return $sMessage;
}
}
/**
@@ -2289,6 +2368,15 @@ class AttributeLinkedSetIndirect extends AttributeLinkedSet
return $this->GetOptional("duplicates", false);
} // The same object may be linked several times... or not...
/**
* @return boolean
* @since 3.1.0 N°5563
*/
public function GetReadOnly()
{
return $this->GetOptional('read_only', false);
}
public function GetTrackingLevel()
{
return $this->GetOptional('tracking_level',
@@ -2326,6 +2414,13 @@ class AttributeLinkedSetIndirect extends AttributeLinkedSet
return $oRet;
}
/** @inheritDoc */
public static function IsBulkModifyCompatible(): bool
{
return true;
}
}
/**
@@ -6022,7 +6117,9 @@ class AttributeDateTime extends AttributeDBField
public function GetDefaultValue(DBObject $oHostObject = null)
{
// null value will be replaced by the current date, if not already set, in DoComputeValues
if (!$this->IsNullAllowed()) {
return date($this->GetInternalFormat());
}
return $this->GetNullValue();
}
@@ -7812,7 +7909,7 @@ class AttributeBlob extends AttributeDefinition
public function GetDefaultValue(DBObject $oHostObject = null)
{
return "";
return new ormDocument('', '', '');
}
public function IsNullAllowed(DBObject $oHostObject = null)
@@ -8161,6 +8258,11 @@ class AttributeImage extends AttributeBlob
return $oDoc;
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
return new ormDocument('', '', '');
}
/**
* Check that the supplied ormDocument actually contains an image
* {@inheritDoc}
@@ -11352,6 +11454,13 @@ class AttributeTagSet extends AttributeSet
return new ormTagSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()), $this->GetCode(), $this->GetMaxItems());
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
$oTagSet = new ormTagSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()), $this->GetCode(), $this->GetMaxItems());
$oTagSet->SetValues([]);
return $oTagSet;
}
public function IsNull($proposedValue)
{
if (is_null($proposedValue))
@@ -13083,7 +13192,7 @@ class AttributeObsolescenceFlag extends AttributeBoolean
public function GetDefaultValue(DBObject $oHostObject = null)
{
return $this->MakeRealValue("", $oHostObject);
return $this->MakeRealValue(false, $oHostObject);
}
public function IsNullAllowed()

View File

@@ -21,6 +21,7 @@ MetaModel::IncludeModule('application/menunode.class.inc.php');
MetaModel::IncludeModule('application/user.preferences.class.inc.php');
MetaModel::IncludeModule('application/user.dashboard.class.inc.php');
MetaModel::IncludeModule('application/audit.rule.class.inc.php');
MetaModel::IncludeModule('application/audit.domain.class.inc.php');
MetaModel::IncludeModule('application/query.class.inc.php');
MetaModel::IncludeModule('setup/moduleinstallation.class.inc.php');

View File

@@ -11,7 +11,7 @@ define('UTF8_BOM', chr(239).chr(187).chr(191)); // 0xEF, 0xBB, 0xBF
/**
* CellChangeSpec
* A series of classes, keeping the information about a given cell: could it be changed or not (and why)?
* A series of classes, keeping the information about a given cell: could it be changed or not (and why)?
*
* @package iTopORM
*/
@@ -42,6 +42,17 @@ abstract class CellChangeSpec
return $this->m_sOql;
}
/**
* @since 3.1.0 N°5305
*/
public function GetDisplayableValueAndDescription(): string
{
return sprintf("%s%s",
$this->GetDisplayableValue(),
$this->GetDescription()
);
}
abstract public function GetDescription();
}
@@ -86,26 +97,90 @@ class CellStatus_Issue extends CellStatus_Modify
parent::__construct($proposedValue, $previousValue);
}
public function GetDescription()
public function GetDisplayableValue()
{
if (is_null($this->m_proposedValue))
{
return Dict::Format('UI:CSVReport-Value-SetIssue', $this->m_sReason);
return Dict::Format('UI:CSVReport-Value-SetIssue');
}
return Dict::Format('UI:CSVReport-Value-ChangeIssue', $this->m_proposedValue, $this->m_sReason);
return Dict::Format('UI:CSVReport-Value-ChangeIssue', \utils::EscapeHtml($this->m_proposedValue));
}
public function GetDescription()
{
return $this->m_sReason;
}
/*
* @since 3.1.0 N°5305
*/
public function GetDisplayableValueAndDescription(): string
{
return sprintf("%s. %s",
$this->GetDisplayableValue(),
$this->GetDescription()
);
}
}
class CellStatus_SearchIssue extends CellStatus_Issue
{
public function __construct()
/** @var string|null $m_sAllowedValues */
private $m_sAllowedValues;
/**
* @since 3.1.0 N°5305
* @var string $sSerializedSearch
*/
private $sSerializedSearch;
/** @var string|null $m_sTargetClass */
private $m_sTargetClass;
/**
* CellStatus_SearchIssue constructor.
* @since 3.1.0 N°5305
*
* @param string $sOql : main message
* @param string $sReason : main message
* @param null $sClass : used for additional message that provides allowed values for current class $sClass
* @param null $sAllowedValues : used for additional message that provides allowed values $sAllowedValues for current class
*/
public function __construct($sSerializedSearch, $sReason, $sClass=null, $sAllowedValues=null)
{
parent::__construct(null, null, null);
parent::__construct(null, null, $sReason);
$this->sSerializedSearch = $sSerializedSearch;
$this->m_sAllowedValues = $sAllowedValues;
$this->m_sTargetClass = $sClass;
}
public function GetDisplayableValue()
{
if (null === $this->m_sReason) {
return Dict::Format('UI:CSVReport-Value-NoMatch', '');
}
return $this->m_sReason;
}
public function GetDescription()
{
return Dict::S('UI:CSVReport-Value-NoMatch');
if (\utils::IsNullOrEmptyString($this->m_sAllowedValues) ||
\utils::IsNullOrEmptyString($this->m_sTargetClass)) {
return '';
}
return Dict::Format('UI:CSVReport-Value-NoMatch-PossibleValues', $this->m_sTargetClass, $this->m_sAllowedValues);
}
/**
* @since 3.1.0 N°5305
* @return string
*/
public function GetSearchLinkUrl()
{
return sprintf("UI.php?operation=search&filter=%s",
rawurlencode($this->sSerializedSearch)
);
}
}
@@ -126,11 +201,24 @@ class CellStatus_NullIssue extends CellStatus_Issue
class CellStatus_Ambiguous extends CellStatus_Issue
{
protected $m_iCount;
/**
* @since 3.1.0 N°5305
* @var string
*/
protected $sSerializedSearch;
public function __construct($previousValue, $iCount, $sOql)
/**
* @since 3.1.0 N°5305
*
* @param $previousValue
* @param int $iCount
* @param string $sSerializedSearch
*
*/
public function __construct($previousValue, $iCount, $sSerializedSearch)
{
$this->m_iCount = $iCount;
$this->m_sQuery = $sOql;
$this->sSerializedSearch = $sSerializedSearch;
parent::__construct(null, $previousValue, '');
}
@@ -139,12 +227,23 @@ class CellStatus_Ambiguous extends CellStatus_Issue
$sCount = $this->m_iCount;
return Dict::Format('UI:CSVReport-Value-Ambiguous', $sCount);
}
/**
* @since 3.1.0 N°5305
* @return string
*/
public function GetSearchLinkUrl()
{
return sprintf("UI.php?operation=search&filter=%s",
rawurlencode($this->sSerializedSearch)
);
}
}
/**
* RowStatus
* A series of classes, keeping the information about a given row: could it be changed or not (and why)?
* A series of classes, keeping the information about a given row: could it be changed or not (and why)?
*
* @package iTopORM
*/
@@ -211,6 +310,26 @@ class RowStatus_Issue extends RowStatus
}
}
/**
* class dedicated to testability
* not used/ignored in csv imports UI/CLI
* @since 3.1.0 N°5305
*/
class RowStatus_Error extends RowStatus
{
/** @var string */
protected $m_sError;
public function __construct($sError)
{
$this->m_sError = $sError;
}
public function GetDescription()
{
return $this->m_sError;
}
}
/**
* BulkChange
@@ -220,17 +339,35 @@ class RowStatus_Issue extends RowStatus
*/
class BulkChange
{
protected $m_sClass;
/** @var string */
protected $m_sClass;
protected $m_aData; // Note: hereafter, iCol maybe actually be any acceptable key (string)
// #@# todo: rename the variables to sColIndex
protected $m_aAttList; // attcode => iCol
protected $m_aExtKeys; // aExtKeys[sExtKeyAttCode][sExtReconcKeyAttCode] = iCol;
protected $m_aReconcilKeys; // attcode (attcode = 'id' for the pkey)
protected $m_sSynchroScope; // OQL - if specified, then the missing items will be reported
protected $m_aOnDisappear; // array of attcode => value, values to be set when an object gets out of scope (ignored if no scope has been defined)
protected $m_sDateFormat; // Date format specification, see DateTime::createFromFormat
protected $m_bLocalizedValues; // Values in the data set are localized (see AttributeEnum)
protected $m_aExtKeysMappingCache; // Cache for resolving external keys based on the given search criterias
/** @var array<string, string> attcode as key, iCol as value */
protected $m_aAttList;
/** @var array<string, array<string, string>> sExtKeyAttCode as key, array of sExtReconcKeyAttCode/iCol as value */
protected $m_aExtKeys;
/** @var string[] list of attcode (attcode = 'id' for the pkey) */
protected $m_aReconcilKeys;
/** @var string OQL - if specified, then the missing items will be reported */
protected $m_sSynchroScope;
/**
* @var array<string, mixed> attcode as key, attvalue as value. Values to be set when an object gets out of scope
* (ignored if no scope has been defined)
*/
protected $m_aOnDisappear;
/**
* @see DateTime::createFromFormat
* @var string Date format specification
*/
protected $m_sDateFormat;
/**
* @see AttributeEnum
* @var boolean true if Values in the data set are localized
*/
protected $m_bLocalizedValues;
/** @var array Cache for resolving external keys based on the given search criterias */
protected $m_aExtKeysMappingCache;
public function __construct($sClass, $aData, $aAttList, $aExtKeys, $aReconcilKeys, $sSynchroScope = null, $aOnDisappear = null, $sDateFormat = null, $bLocalize = false)
{
@@ -261,30 +398,30 @@ class BulkChange
$this->m_sReportCsvSep = $sSeparator;
$this->m_sReportCsvDelimiter = $sDelimiter;
}
protected function ResolveExternalKey($aRowData, $sAttCode, &$aResults)
{
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
$oReconFilter = new DBObjectSearch($oExtKey->GetTargetClass());
foreach ($this->m_aExtKeys[$sAttCode] as $sForeignAttCode => $iCol)
foreach ($this->m_aExtKeys[$sAttCode] as $sReconKeyAttCode => $iCol)
{
if ($sForeignAttCode == 'id')
if ($sReconKeyAttCode == 'id')
{
$value = (int) $aRowData[$iCol];
}
else
{
// The foreign attribute is one of our reconciliation key
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sForeignAttCode);
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sReconKeyAttCode);
$value = $oForeignAtt->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
}
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
$oReconFilter->AddCondition($sReconKeyAttCode, $value, '=');
$aResults[$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
}
$oExtObjects = new CMDBObjectSet($oReconFilter);
$aKeys = $oExtObjects->ToArray();
return array($oReconFilter->ToOql(), $aKeys);
return array($oReconFilter, $aKeys);
}
// Returns true if the CSV data specifies that the external key must be left undefined
@@ -318,10 +455,10 @@ class BulkChange
{
$aResults = array();
$aErrors = array();
// External keys reconciliation
//
foreach($this->m_aExtKeys as $sAttCode => $aKeyConfig)
foreach($this->m_aExtKeys as $sAttCode => $aReconKeys)
{
// Skip external keys used for the reconciliation process
// if (!array_key_exists($sAttCode, $this->m_aAttList)) continue;
@@ -330,7 +467,7 @@ class BulkChange
if ($this->IsNullExternalKeySpec($aRowData, $sAttCode))
{
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
foreach ($aReconKeys as $sReconKeyAttCode => $iCol)
{
// Default reporting
// $aRowData[$iCol] is always null
@@ -352,25 +489,24 @@ class BulkChange
$oReconFilter = new DBObjectSearch($oExtKey->GetTargetClass());
$aCacheKeys = array();
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
foreach ($aReconKeys as $sReconKeyAttCode => $iCol)
{
// The foreign attribute is one of our reconciliation key
if ($sForeignAttCode == 'id')
if ($sReconKeyAttCode == 'id')
{
$value = $aRowData[$iCol];
}
else
{
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sForeignAttCode);
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sReconKeyAttCode);
$value = $oForeignAtt->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
}
$aCacheKeys[] = $value;
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
$oReconFilter->AddCondition($sReconKeyAttCode, $value, '=');
$aResults[$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
}
$sCacheKey = implode('_|_', $aCacheKeys); // Unique key for this query...
$iForeignKey = null;
$sOQL = '';
// TODO: check if *too long* keys can lead to collisions... and skip the cache in such a case...
if (!array_key_exists($sAttCode, $this->m_aExtKeysMappingCache))
{
@@ -379,9 +515,8 @@ class BulkChange
if (array_key_exists($sCacheKey, $this->m_aExtKeysMappingCache[$sAttCode]))
{
// Cache hit
$iCount = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['c'];
$iObjectFoundCount = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['c'];
$iForeignKey = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['k'];
$sOQL = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['oql'];
// Record the hit
$this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['h']++;
}
@@ -389,34 +524,35 @@ class BulkChange
{
// Cache miss, let's initialize it
$oExtObjects = new CMDBObjectSet($oReconFilter);
$iCount = $oExtObjects->Count();
if ($iCount == 1)
$iObjectFoundCount = $oExtObjects->Count();
if ($iObjectFoundCount == 1)
{
$oForeignObj = $oExtObjects->Fetch();
$iForeignKey = $oForeignObj->GetKey();
}
$this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey] = array(
'c' => $iCount,
'c' => $iObjectFoundCount,
'k' => $iForeignKey,
'oql' => $oReconFilter->ToOql(),
'h' => 0, // number of hits on this cache entry
);
}
switch($iCount)
switch($iObjectFoundCount)
{
case 0:
$aErrors[$sAttCode] = Dict::S('UI:CSVReport-Value-Issue-NotFound');
$aResults[$sAttCode]= new CellStatus_SearchIssue();
break;
$oCellStatus_SearchIssue = $this->GetCellSearchIssue($oReconFilter);
$aResults[$sAttCode] = $oCellStatus_SearchIssue;
$aErrors[$sAttCode] = Dict::S('UI:CSVReport-Value-Issue-NotFound');
break;
case 1:
// Do change the external key attribute
$oTargetObj->Set($sAttCode, $iForeignKey);
break;
// Do change the external key attribute
$oTargetObj->Set($sAttCode, $iForeignKey);
break;
default:
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-FoundMany', $iCount);
$aResults[$sAttCode]= new CellStatus_Ambiguous($oTargetObj->Get($sAttCode), $iCount, $sOQL);
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-FoundMany', $iObjectFoundCount);
$aResults[$sAttCode]= new CellStatus_Ambiguous($oTargetObj->Get($sAttCode), $iObjectFoundCount, $oReconFilter->serialize());
}
}
@@ -433,7 +569,7 @@ class BulkChange
else
{
$aResults[$sAttCode]= new CellStatus_Modify($iForeignObj, $oTargetObj->GetOriginal($sAttCode));
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
foreach ($aReconKeys as $sReconKeyAttCode => $iCol)
{
// Report the change on reconciliation values as well
$aResults[$iCol] = new CellStatus_Modify(utils::HtmlEntities($aRowData[$iCol]));
@@ -446,7 +582,7 @@ class BulkChange
}
}
}
// Set the object attributes
//
foreach ($this->m_aAttList as $sAttCode => $iCol)
@@ -487,7 +623,13 @@ class BulkChange
$value = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
if (is_null($value) && (strlen($aRowData[$iCol]) > 0))
{
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-NoMatch', $sAttCode);
if ($oAttDef instanceof AttributeEnum || $oAttDef instanceof AttributeTagSet){
/** @var AttributeDefinition $oAttributeDefinition */
$oAttributeDefinition = $oAttDef;
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-AllowedValues', $sAttCode, implode(',', $oAttributeDefinition->GetAllowedValues()));
} else {
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-NoMatch', $sAttCode);
}
}
else
{
@@ -504,7 +646,7 @@ class BulkChange
}
}
}
// Reporting on fields
//
$aChangedFields = $oTargetObj->ListChanges();
@@ -556,7 +698,7 @@ class BulkChange
}
}
}
// Checks
//
$res = $oTargetObj->CheckConsistency();
@@ -567,12 +709,101 @@ class BulkChange
}
return $aResults;
}
/**
* search with current permissions did not match
* let's search why and give some more feedbacks to the user through proper labels
*
* @param DBObjectSearch $oDbSearchWithConditions search used to find external key
*
* @return \CellStatus_SearchIssue
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*
* @since 3.1.0 N°5305
*/
protected function GetCellSearchIssue($oDbSearchWithConditions) : CellStatus_SearchIssue {
//current search with current permissions did not match
//let's search why and give some more feedback to the user
$sSerializedSearch = $oDbSearchWithConditions->serialize();
// Count all objects with all permissions without any condition
$oDbSearchWithoutAnyCondition = new DBObjectSearch($oDbSearchWithConditions->GetClass());
$oDbSearchWithoutAnyCondition->AllowAllData(true);
$oExtObjectSet = new CMDBObjectSet($oDbSearchWithoutAnyCondition);
$iAllowAllDataObjectCount = $oExtObjectSet->Count();
if ($iAllowAllDataObjectCount === 0) {
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch-NoObject', $oDbSearchWithConditions->GetClass());
return new CellStatus_SearchIssue($sSerializedSearch, $sReason);
}
// Count all objects with current user permissions
$oDbSearchWithoutAnyCondition->AllowAllData(false);
$oExtObjectSetWithCurrentUserPermissions = new CMDBObjectSet($oDbSearchWithoutAnyCondition);
$iCurrentUserRightsObjectCount = $oExtObjectSetWithCurrentUserPermissions->Count();
if ($iCurrentUserRightsObjectCount === 0){
// No objects visible by current user
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch-NoObject-ForCurrentUser', $oDbSearchWithConditions->GetClass());
return new CellStatus_SearchIssue($sSerializedSearch, $sReason);
}
try{
$aDisplayedAllowedValues = [];
// Possibles values are displayed to UI user. we have to limit the amount of displayed values
$oExtObjectSetWithCurrentUserPermissions->SetLimit(4);
for($i = 0; $i < 3; $i++){
/** @var \DBObject $oVisibleObject */
$oVisibleObject = $oExtObjectSetWithCurrentUserPermissions->Fetch();
if (is_null($oVisibleObject)){
break;
}
$aCurrentAllowedValueFields = [];
foreach ($oDbSearchWithConditions->GetInternalParams() as $sForeignAttCode => $sValue){
$aCurrentAllowedValueFields[] = $oVisibleObject->Get($sForeignAttCode);
}
$aDisplayedAllowedValues[] = implode(" ", $aCurrentAllowedValueFields);
}
$allowedValues = implode(", ", $aDisplayedAllowedValues);
if ($oExtObjectSetWithCurrentUserPermissions->Count() > 3){
$allowedValues .= "...";
}
} catch(Exception $e) {
IssueLog::Error("failure during CSV import when fetching few visible objects: ", null,
[ 'target_class' => $oDbSearchWithConditions->GetClass(), 'criteria' => $oDbSearchWithConditions->GetCriteria(), 'message' => $e->getMessage()]
);
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch-NoObject-ForCurrentUser', $oDbSearchWithConditions->GetClass());
return new CellStatus_SearchIssue($sSerializedSearch, $sReason);
}
if ($iAllowAllDataObjectCount != $iCurrentUserRightsObjectCount) {
// No match and some objects NOT visible by current user. including current search maybe...
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch-SomeObjectNotVisibleForCurrentUser', $oDbSearchWithConditions->GetClass());
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues);
}
// No match. This is not linked to any right issue
// Possible values: DD,DD
$aCurrentValueFields = [];
foreach ($oDbSearchWithConditions->GetInternalParams() as $sValue){
$aCurrentValueFields[] = $sValue;
}
$value =implode(" ", $aCurrentValueFields);
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch', $value);
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues);
}
protected function PrepareMissingObject(&$oTargetObj, &$aErrors)
{
$aResults = array();
$aErrors = array();
// External keys
//
foreach($this->m_aExtKeys as $sAttCode => $aKeyConfig)
@@ -585,7 +816,7 @@ class BulkChange
$aResults[$iCol] = new CellStatus_Void('?');
}
}
// Update attributes
//
foreach($this->m_aOnDisappear as $sAttCode => $value)
@@ -596,7 +827,7 @@ class BulkChange
}
$oTargetObj->Set($sAttCode, $value);
}
// Reporting on fields
//
$aChangedFields = $oTargetObj->ListChanges();
@@ -616,7 +847,7 @@ class BulkChange
$aResults[$iCol]= new CellStatus_Void($oTargetObj->Get($sAttCode));
}
}
// Checks
//
$res = $oTargetObj->CheckConsistency();
@@ -674,14 +905,16 @@ class BulkChange
}
$aResult[$iRow] = $this->PrepareObject($oTargetObj, $aRowData, $aErrors);
if (count($aErrors) > 0)
{
$sErrors = implode(', ', $aErrors);
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
//__ERRORS__ used by tests only
$aResult[$iRow]["__ERRORS__"] = new RowStatus_Error($sErrors);
return $oTargetObj;
}
// Check that any external key will have a value proposed
$aMissingKeys = array();
foreach (MetaModel::GetExternalKeys($this->m_sClass) as $sExtKeyAttCode => $oExtKey)
@@ -689,7 +922,7 @@ class BulkChange
if (!$oExtKey->IsNullAllowed())
{
if (!array_key_exists($sExtKeyAttCode, $this->m_aExtKeys) && !array_key_exists($sExtKeyAttCode, $this->m_aAttList))
{
{
$aMissingKeys[] = $oExtKey->GetLabel();
}
}
@@ -745,14 +978,16 @@ class BulkChange
{
$sErrors = implode(', ', $aErrors);
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
//__ERRORS__ used by tests only
$aResult[$iRow]["__ERRORS__"] = new RowStatus_Error($sErrors);
return;
}
$aChangedFields = $oTargetObj->ListChanges();
if (count($aChangedFields) > 0)
{
$aResult[$iRow]["__STATUS__"] = new RowStatus_Modify(count($aChangedFields));
// Optionaly record the results
//
if ($oChange)
@@ -794,9 +1029,11 @@ class BulkChange
{
$sErrors = implode(', ', $aErrors);
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
//__ERRORS__ used by tests only
$aResult[$iRow]["__ERRORS__"] = new RowStatus_Error($sErrors);
return;
}
$aChangedFields = $oTargetObj->ListChanges();
if (count($aChangedFields) > 0)
{
@@ -821,7 +1058,7 @@ class BulkChange
$aResult[$iRow]["__STATUS__"] = new RowStatus_Disappeared(0);
}
}
public function Process(CMDBChange $oChange = null)
{
if ($oChange)
@@ -866,7 +1103,7 @@ class BulkChange
foreach ($this->m_aAttList as $sAttCode => $iCol)
{
if ($sAttCode == 'id') continue;
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
{
@@ -881,14 +1118,18 @@ class BulkChange
$sFormat = $sDateFormat;
}
$oFormat = new DateTimeFormat($sFormat);
$sDateExample = $oFormat->Format(new DateTime('2022-10-23 16:25:33'));
$sRegExp = $oFormat->ToRegExpr('/');
if (!preg_match($sRegExp, $this->m_aData[$iRow][$iCol]))
$sErrorMsg = Dict::Format('UI:CSVReport-Row-Issue-ExpectedDateFormat', $sDateExample);
if (!preg_match($sRegExp, $sValue))
{
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
$aResult[$iRow][$iCol] = new CellStatus_Issue(utils::HtmlEntities($sValue), null, $sErrorMsg);
}
else
{
$oDate = DateTime::createFromFormat($sFormat, $this->m_aData[$iRow][$iCol]);
$oDate = DateTime::createFromFormat($sFormat, $sValue);
if ($oDate !== false)
{
$sNewDate = $oDate->format($oAttDef->GetInternalFormat());
@@ -898,7 +1139,7 @@ class BulkChange
{
// Leave the cell unchanged
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
$aResult[$iRow][$sAttCode] = new CellStatus_Issue(null, utils::HtmlEntities($this->m_aData[$iRow][$iCol]), Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
$aResult[$iRow][$iCol] = new CellStatus_Issue($sValue, null, $sErrorMsg);
}
}
}
@@ -919,133 +1160,112 @@ class BulkChange
}
$iPreviousTimeLimit = ini_get('max_execution_time');
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
foreach($this->m_aData as $iRow => $aRowData)
{
set_time_limit(intval($iLoopTimeLimit));
if (isset($aResult[$iRow]["__STATUS__"]))
{
// An issue at the earlier steps - skip the rest
continue;
// Avoid too many events
cmdbAbstractObject::SetEventDBLinksChangedBlocked(true);
try {
foreach ($this->m_aData as $iRow => $aRowData) {
set_time_limit(intval($iLoopTimeLimit));
if (isset($aResult[$iRow]["__STATUS__"])) {
// An issue at the earlier steps - skip the rest
continue;
}
try {
$oReconciliationFilter = new DBObjectSearch($this->m_sClass);
$bSkipQuery = false;
foreach ($this->m_aReconcilKeys as $sAttCode) {
$valuecondition = null;
if (array_key_exists($sAttCode, $this->m_aExtKeys)) {
if ($this->IsNullExternalKeySpec($aRowData, $sAttCode)) {
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
if ($oExtKey->IsNullAllowed()) {
$valuecondition = $oExtKey->GetNullValue();
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oExtKey->GetNullValue());
} else {
$aResult[$iRow][$sAttCode] = new CellStatus_NullIssue();
}
} else {
// The value has to be found or verified
/** var DBObjectSearch $oReconFilter */
list($oReconFilter, $aMatches) = $this->ResolveExternalKey($aRowData, $sAttCode, $aResult[$iRow]);
if (count($aMatches) == 1) {
$oRemoteObj = reset($aMatches); // first item
$valuecondition = $oRemoteObj->GetKey();
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oRemoteObj->GetKey());
} elseif (count($aMatches) == 0) {
$oCellStatus_SearchIssue = $this->GetCellSearchIssue($oReconFilter);
$aResult[$iRow][$sAttCode] = $oCellStatus_SearchIssue;
} else {
$aResult[$iRow][$sAttCode] = new CellStatus_Ambiguous(null, count($aMatches), $oReconFilter->serialize());
}
}
} else {
// The value is given in the data row
$iCol = $this->m_aAttList[$sAttCode];
if ($sAttCode == 'id') {
$valuecondition = $aRowData[$iCol];
} else {
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
$valuecondition = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
}
}
if (is_null($valuecondition)) {
$bSkipQuery = true;
} else {
$oReconciliationFilter->AddCondition($sAttCode, $valuecondition, '=');
}
}
if ($bSkipQuery) {
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Reconciliation'));
} else {
$oReconciliationSet = new CMDBObjectSet($oReconciliationFilter);
switch ($oReconciliationSet->Count()) {
case 0:
$oTargetObj = $this->CreateObject($aResult, $iRow, $aRowData, $oChange);
// $aResult[$iRow]["__STATUS__"]=> set in CreateObject
$aVisited[] = $oTargetObj->GetKey();
break;
case 1:
$oTargetObj = $oReconciliationSet->Fetch();
$this->UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $oChange);
// $aResult[$iRow]["__STATUS__"]=> set in UpdateObject
if (!is_null($this->m_sSynchroScope)) {
$aVisited[] = $oTargetObj->GetKey();
}
break;
default:
// Found several matches, ambiguous
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Ambiguous'));
$aResult[$iRow]["id"] = new CellStatus_Ambiguous(0, $oReconciliationSet->Count(), $oReconciliationFilter->serialize());
$aResult[$iRow]["finalclass"] = 'n/a';
}
}
} catch (Exception $e) {
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::Format('UI:CSVReport-Row-Issue-Internal', get_class($e), $e->getMessage()));
}
}
try
{
$oReconciliationFilter = new DBObjectSearch($this->m_sClass);
$bSkipQuery = false;
foreach($this->m_aReconcilKeys as $sAttCode)
{
$valuecondition = null;
if (array_key_exists($sAttCode, $this->m_aExtKeys))
{
if ($this->IsNullExternalKeySpec($aRowData, $sAttCode))
{
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
if ($oExtKey->IsNullAllowed())
{
$valuecondition = $oExtKey->GetNullValue();
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oExtKey->GetNullValue());
}
else
{
$aResult[$iRow][$sAttCode] = new CellStatus_NullIssue();
}
}
else
{
// The value has to be found or verified
list($sQuery, $aMatches) = $this->ResolveExternalKey($aRowData, $sAttCode, $aResult[$iRow]);
if (count($aMatches) == 1)
{
$oRemoteObj = reset($aMatches); // first item
$valuecondition = $oRemoteObj->GetKey();
$aResult[$iRow][$sAttCode] = new CellStatus_Void($oRemoteObj->GetKey());
}
elseif (count($aMatches) == 0)
{
$aResult[$iRow][$sAttCode] = new CellStatus_SearchIssue();
}
else
{
$aResult[$iRow][$sAttCode] = new CellStatus_Ambiguous(null, count($aMatches), $sQuery);
}
}
}
else
{
// The value is given in the data row
$iCol = $this->m_aAttList[$sAttCode];
if ($sAttCode == 'id')
{
$valuecondition = $aRowData[$iCol];
}
else
{
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
$valuecondition = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
}
}
if (is_null($valuecondition))
{
$bSkipQuery = true;
}
else
{
$oReconciliationFilter->AddCondition($sAttCode, $valuecondition, '=');
}
}
if ($bSkipQuery)
{
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Reconciliation'));
}
else
{
$oReconciliationSet = new CMDBObjectSet($oReconciliationFilter);
switch($oReconciliationSet->Count())
{
case 0:
$oTargetObj = $this->CreateObject($aResult, $iRow, $aRowData, $oChange);
// $aResult[$iRow]["__STATUS__"]=> set in CreateObject
$aVisited[] = $oTargetObj->GetKey();
break;
case 1:
$oTargetObj = $oReconciliationSet->Fetch();
$this->UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $oChange);
// $aResult[$iRow]["__STATUS__"]=> set in UpdateObject
if (!is_null($this->m_sSynchroScope))
{
$aVisited[] = $oTargetObj->GetKey();
}
break;
default:
// Found several matches, ambiguous
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Ambiguous'));
$aResult[$iRow]["id"]= new CellStatus_Ambiguous(0, $oReconciliationSet->Count(), $oReconciliationFilter->ToOql());
$aResult[$iRow]["finalclass"]= 'n/a';
if (!is_null($this->m_sSynchroScope)) {
// Compute the delta between the scope and visited objects
$oScopeSearch = DBObjectSearch::FromOQL($this->m_sSynchroScope);
$oScopeSet = new DBObjectSet($oScopeSearch);
while ($oObj = $oScopeSet->Fetch()) {
$iObj = $oObj->GetKey();
if (!in_array($iObj, $aVisited)) {
set_time_limit(intval($iLoopTimeLimit));
$iRow++;
$this->UpdateMissingObject($aResult, $iRow, $oObj, $oChange);
}
}
}
catch (Exception $e)
{
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::Format('UI:CSVReport-Row-Issue-Internal', get_class($e), $e->getMessage()));
}
} finally {
// Send all the retained events for further computations
cmdbAbstractObject::SetEventDBLinksChangedBlocked(false);
cmdbAbstractObject::FireEventDbLinksChangedForAllObjects();
}
if (!is_null($this->m_sSynchroScope))
{
// Compute the delta between the scope and visited objects
$oScopeSearch = DBObjectSearch::FromOQL($this->m_sSynchroScope);
$oScopeSet = new DBObjectSet($oScopeSearch);
while ($oObj = $oScopeSet->Fetch())
{
$iObj = $oObj->GetKey();
if (!in_array($iObj, $aVisited))
{
set_time_limit(intval($iLoopTimeLimit));
$iRow++;
$this->UpdateMissingObject($aResult, $iRow, $oObj, $oChange);
}
}
}
set_time_limit(intval($iPreviousTimeLimit));
// Fill in the blanks - the result matrix is expected to be 100% complete
@@ -1110,7 +1330,7 @@ class BulkChange
}
}
$oBulkChanges->Seek(0);
$aDetails = array();
while ($oChange = $oBulkChanges->Fetch())
{
@@ -1274,7 +1494,7 @@ EOF
$oOldTarget = MetaModel::GetObject($oAttDef->GetTargetClass(), $oOperation->Get('oldvalue'));
$sOldValue = $oOldTarget->GetHyperlink();
}
$sNewValue = Dict::S('UI:UndefinedObject');
if ($oOperation->Get('newvalue') != 0)
{
@@ -1300,11 +1520,11 @@ EOF
}
else
{
$aAttributes[$sAttCode] = 1;
$aAttributes[$sAttCode] = 1;
}
}
}
$aDetails = array();
foreach($aObjects as $iUId => $aObjData)
{
@@ -1356,6 +1576,6 @@ EOF
$aConfig[$sAttCode] = array('label' => MetaModel::GetLabel($sClass, $sAttCode), 'description' => MetaModel::GetDescription($sClass, $sAttCode));
}
$oPage->table($aConfig, $aDetails);
}
}
}

View File

@@ -472,7 +472,7 @@ class CMDBChangeOpSetAttributeBlob extends CMDBChangeOpSetAttribute
$sDisplayLabel = Dict::S('UI:OpenDocumentInNewWindow_');
$sDisplayUrl = $oPrevDoc->GetDisplayURL(get_class($this), $this->GetKey(), 'prevdata');
$sDownloadLabel = Dict::Format('UI:DownloadDocument_');
$sDownloadLabel = Dict::S('UI:DownloadDocument_');
$sDownloadUrl = $oPrevDoc->GetDownloadURL(get_class($this), $this->GetKey(), 'prevdata');
$sDocView = <<<HTML

View File

@@ -499,7 +499,7 @@ abstract class CMDBObject extends DBObject
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
$oMyChangeOp->Set("oldvalue", $original);
$oMyChangeOp->Set("newvalue", $value[$sAttCode]);
$oMyChangeOp->Set("newvalue", $value);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeCustomFields)
@@ -640,20 +640,6 @@ abstract class CMDBObject extends DBObject
return $newKey;
}
public function DBUpdate()
{
// Copy the changes list before the update (the list should be reset afterwards)
$aChanges = $this->ListChanges();
if (count($aChanges) == 0)
{
return;
}
$ret = parent::DBUpdate();
return $ret;
}
/**
* @param null $oDeletionPlan
*

View File

@@ -1611,4 +1611,22 @@ class CMDBSource
return 'ALTER DATABASE'.CMDBSource::GetSqlStringColumnDefinition().';';
}
/**
* Check which mysql client option (--ssl or --ssl-mode) to be used for encrypted connection
*
* @return bool true if --ssl-mode should be used, false otherwise
* @throws \MySQLException
*
* @link https://dev.mysql.com/doc/refman/5.7/en/connection-options.html#encrypted-connection-options "Command Options for Encrypted Connections"
*/
public static function IsSslModeDBVersion()
{
if (static::GetDBVendor() === static::ENUM_DB_VENDOR_MYSQL)
{
//Mysql 5.7.0 and upper deprecated --ssl and uses --ssl-mode instead
return version_compare(static::GetDBVersion(), '5.7.11', '>=');
}
return false;
}
}

View File

@@ -129,6 +129,22 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'event_service.debug.filter_events' => [
'type' => 'array',
'description' => 'List of events name to filter Event Service debug messages',
'default' => [],
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'event_service.debug.filter_sources' => [
'type' => 'array',
'description' => 'List of event sources to filter Event Service debug messages',
'default' => '',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'app_env_label' => [
'type' => 'string',
'description' => 'Label displayed to describe the current application environment, defaults to the environment name (e.g. "production")',
@@ -326,11 +342,11 @@ class Config
'show_in_conf_sample' => false,
],
'allow_menu_on_linkset' => [
'type' => 'bool',
'description' => 'Display Action menus in view mode on any LinkedSet with edit_mode != none',
'default' => false,
'value' => false,
'source_of_value' => '',
'type' => 'bool',
'description' => 'Display Action menus in view mode on any LinkedSet with edit_mode != none',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'allow_target_creation' => [
@@ -576,13 +592,29 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'email_css' => [
'type' => 'string',
'description' => 'CSS that will override the standard stylesheet used for the notifications',
'default' => "",
'value' => "",
'email_transport_smtp.allow_self_signed' => array(
'type' => 'bool',
'description' => 'Allow self signed peer certificates',
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'email_transport_smtp.verify_peer' => array(
'type' => 'bool',
'description' => 'Verify peer certificate',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'email_css' => [
'type' => 'string',
'description' => 'CSS that will override the standard stylesheet used for the notifications',
'default' => "",
'value' => "",
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'email_default_sender_address' => [
'type' => 'string',
@@ -1238,6 +1270,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'navigation_menu.show_organization_filter' => [
'type' => 'bool',
'description' => 'Display organization filter in menu',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'quick_create.enabled' => [
'type' => 'bool',
'description' => 'Whether or not the quick create is enabled',
@@ -1505,11 +1545,11 @@ class Config
'show_in_conf_sample' => false,
],
'security.hide_administrators' => [
'type' => 'bool',
'description' => 'If true, non-administrator users will not be able to see the administrator accounts, the Administrator profile and the links between the administrator accounts and their profiles.',
'default' => false,
'value' => false,
'source_of_value' => '',
'type' => 'bool',
'description' => 'If true, non-administrator users will not be able to see the administrator accounts, the Administrator profile and the links between the administrator accounts and their profiles.',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'behind_reverse_proxy' => [
@@ -1552,6 +1592,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'audit.enable_selection_landing_page' => [
'type' => 'bool',
'description' => 'If true audit categories must be selected before results are computed (use this setting in case of a lot of audit categories)',
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
];
public function IsProperty($sPropCode)
@@ -1869,7 +1917,7 @@ class Config
}
if (strlen($sNoise) > 0)
{
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
throw new ConfigException('Syntax error in configuration file',
array('file' => $sConfigFile, 'error' => '<tt>'.utils::EscapeHtml($sNoise, ENT_QUOTES).'</tt>'));
}
@@ -2254,7 +2302,7 @@ class Config
$oHandle = null;
$sConfig = null;
if (is_file($this->m_sFile))
if ($this->m_sFile !== null && is_file($this->m_sFile))
{
$oHandle = fopen($this->m_sFile, 'r');
$index = 0;
@@ -2429,10 +2477,15 @@ class Config
/**
* Helper function to initialize a configuration from the page arguments
*
* @see \Parameters::GetParamForConfigArray() to get aParamValues from {@see Parameters} object hierarchy in setup
* @see \WizardController::GetParamForConfigArray() to get aParamValues from {@see \WizardController} object hierarchy in setup
*
* @param array $aParamValues
* @param string|null $sModulesDir
* @param ?string $sModulesDir
* @param bool $bPreserveModuleSettings
*
* @return void The current object is modified directly
*
* @throws \Exception
* @throws \CoreException
*/
@@ -2679,7 +2732,7 @@ class ConfigPlaceholdersResolver
}
$sPattern = '/\%(env|server)\((\w+)\)(?:\?:(\w*))?\%/'; //3 capturing groups, ie `%env(HTTP_PORT)?:8080%` produce: `env` `HTTP_PORT` and `8080`.
if (! preg_match_all($sPattern, $rawValue, $aMatchesCollection, PREG_SET_ORDER))
{
return $rawValue;

View File

@@ -52,17 +52,23 @@
*/
class ContextTag
{
const TAG_PORTAL = 'GUI:Portal';
const TAG_CRON = 'CRON';
const TAG_CONSOLE = 'GUI:Console';
const TAG_SETUP = 'Setup';
const TAG_SYNCHRO = 'Synchro';
const TAG_REST = 'REST/JSON';
public const TAG_PORTAL = 'GUI:Portal';
public const TAG_CRON = 'CRON';
public const TAG_CONSOLE = 'GUI:Console';
public const TAG_SETUP = 'Setup';
public const TAG_SYNCHRO = 'Synchro';
public const TAG_REST = 'REST/JSON';
/**
* @var string
* @since 3.1.0 N°3200
*/
public const TAG_OBJECT_SEARCH = 'ObjectSearch';
protected static $aStack = array();
/**
* Store a context tag on the stack
*
* @param string $sTag
*/
public function __construct($sTag)

View File

@@ -270,4 +270,4 @@
</class>
</classes>
</meta>
</itop_design>
</itop_design>

File diff suppressed because it is too large Load Diff

View File

@@ -53,6 +53,18 @@ class DBObjectSet implements iDBObjectSetIterator
* @var array
*/
protected $m_aAttToLoad;
/**
* @var null|array
*/
protected $m_aExtendedDataSpec;
/**
* @var int Maximum number of elements to retrieve
*/
protected $m_iLimitCount;
/**
* @var int Offset from which elements should be retrieved
*/
protected $m_iLimitStart;
/**
* @var array
*/
@@ -141,7 +153,7 @@ class DBObjectSet implements iDBObjectSetIterator
{
$sRet = '';
$this->Rewind();
$sRet .= "Set (".$this->m_oFilter->ToOQL().")<br/>\n";
$sRet .= "Set (".$this->m_oFilter->ToOQL(true).")<br/>\n";
$sRet .= "Query: <pre style=\"font-size: smaller; display:inline;\">".$this->m_oFilter->MakeSelectQuery().")</pre>\n";
$sRet .= $this->Count()." records<br/>\n";
@@ -154,6 +166,7 @@ class DBObjectSet implements iDBObjectSetIterator
}
$sRet .= "</ul>\n";
}
$this->Rewind();
return $sRet;
}

View File

@@ -773,14 +773,14 @@ abstract class DBSearch
* @see DBSearch::ToOQL()
*
* @param string $sQuery The OQL to convert to a DBSearch
* @param mixed[string] $aParams array of <mixed> params index by <string> name
* @param array $aParams array of <mixed> params index by <string> name
* @param ModelReflection|null $oMetaModel The MetaModel to use when checking the consistency of the OQL
*
* @return DBObjectSearch|DBUnionSearch
*
* @throws OQLException
*/
static public function FromOQL($sQuery, $aParams = null, ModelReflection $oMetaModel=null)
public static function FromOQL($sQuery, $aParams = null, ModelReflection $oMetaModel=null)
{
if (empty($sQuery))
{
@@ -1717,6 +1717,6 @@ abstract class DBSearch
*/
public function __toString()
{
return $this->ToOQL();
return $this->ToOQL(true);
}
}

View File

@@ -30,6 +30,7 @@ use DOMDocument;
use DOMFormatException;
use IssueLog;
use LogAPI;
use utils;
/**
* Class \Combodo\iTop\DesignDocument
@@ -182,6 +183,26 @@ class DesignElement extends \DOMElement
return $this->ownerDocument->GetNodes($sXPath, $this);
}
public static function ToArray(DesignElement $oNode)
{
$aRes = [];
if ($oNode->GetNodes('./*')->length == 0) {
return $oNode->GetText('');
}
foreach ($oNode->GetNodes('./*') as $oSubNode) {
/** @var \Combodo\iTop\DesignElement $oSubNode */
$aSubArray = DesignElement::ToArray($oSubNode);
if ($oSubNode->hasAttribute('id')) {
$aRes[$oSubNode->getAttribute('id')] = $aSubArray;
} else {
$aRes[$oSubNode->tagName] = $aSubArray;
}
}
return $aRes;
}
/**
* Create an HTML representation of the DOM, for debugging purposes
*

View File

@@ -110,7 +110,7 @@ class Dict
*
* @param string $sStringCode The code identifying the dictionary entry
* @param string $sDefault Default value if there is no match in the dictionary
* @param bool $bUserLanguageOnly True to allow the use of the default language as a fallback, false otherwise
* @param bool $bUserLanguageOnly False to allow the use of the default language as a fallback, true otherwise
*
* @return string
*/
@@ -174,7 +174,7 @@ class Dict
*
* @return string
*/
public static function Format($sFormatCode /*, ... arguments ....*/)
public static function Format($sFormatCode /*, ... arguments ... */)
{
$sLocalizedFormat = self::S($sFormatCode);
$aArguments = func_get_args();

View File

@@ -77,7 +77,7 @@ class EMail implements iEMail
* @return void
* @throws \ConfigException
* @throws \CoreException
* @since 2.7.>8 3.0.3 3.1.0 N°4947 Method creation, to factorize same code in children classes
* @since 2.7.8 3.0.3 3.1.0 N°4947 Method creation, to factorize same code in children classes
*/
protected function InitRecipientFrom()
{

View File

@@ -134,7 +134,7 @@ abstract class DOMSanitizer extends HTMLSanitizer
public function DoSanitize($sHTML)
{
$this->oDoc = new DOMDocument();
$this->oDoc->preserveWhitespace = true;
$this->oDoc->preserveWhiteSpace = true;
// MS outlook implements empty lines by the mean of <p><o:p></o:p></p>
// We have to transform that into <p><br></p> (which is how Thunderbird implements empty lines)
@@ -286,8 +286,8 @@ class HTMLDOMSanitizer extends DOMSanitizer
'strong' => array(),
'img' => array('src', 'style', 'alt', 'title'),
'ul' => array('style'),
'ol' => array('style'),
'li' => array('style'),
'ol' => array('reversed', 'start', 'style', 'type'),
'li' => array('style', 'value'),
'h1' => array('style'),
'h2' => array('style'),
'h3' => array('style'),
@@ -402,7 +402,7 @@ class HTMLDOMSanitizer extends DOMSanitizer
public function LoadDoc($sHTML)
{
@$this->oDoc->loadHTML('<?xml encoding="UTF-8"?>'.$sHTML); // For loading HTML chunks where the character set is not specified
$this->oDoc->preserveWhitespace = true;
$this->oDoc->preserveWhiteSpace = true;
}
public function PrintDoc()

View File

@@ -299,7 +299,7 @@ class ExecutionKPI
*/
private static function Push(ExecutionKPI $oExecutionKPI)
{
array_push(self::$m_aExecutionStack, $oExecutionKPI);
self::$m_aExecutionStack[] = $oExecutionKPI;
}
/**
@@ -449,4 +449,3 @@ class ExecutionKPI
return 0;
}
}

View File

@@ -551,6 +551,15 @@ class LogChannels
*/
public const NOTIFICATIONS = 'notifications';
/**
* @var string Everything related to the backup / restore
* @since 3.1.0
*/
public const BACKUP = 'backup';
/**
* @since 3.0.0
*/
public const CLI = 'CLI';
/**
@@ -560,15 +569,36 @@ class LogChannels
*/
public const CMDB_SOURCE = 'cmdbsource';
public const CONSOLE = 'console';
/**
* @since 3.0.0
*/
public const CONSOLE = 'console';
public const CORE = 'core';
public const CORE = 'core';
public const DEADLOCK = 'DeadLock';
/**
* @var string Everything related to the datatable component
* @since 3.1.0
*/
public const DATATABLE = 'Datatable';
public const DEADLOCK = 'DeadLock';
public const INLINE_IMAGE = 'InlineImage';
public const PORTAL = 'portal';
/**
* @var string Everything related to the event service
* @since 3.1.0
*/
public const EVENT_SERVICE = 'EventService';
/**
* @var string Everything related to the datamodel CRUD
* @since 3.1.0
*/
public const DM_CRUD = 'DMCRUD';
}
@@ -1021,6 +1051,7 @@ class DeprecatedCallsLog extends LogAPI
public const ENUM_CHANNEL_FILE = 'deprecated-file';
public const CHANNEL_DEFAULT = self::ENUM_CHANNEL_PHP_METHOD;
/** @var string Warning this constant won't be used directly ! To see the real default level check {@see GetLevelDefault()} */
public const LEVEL_DEFAULT = self::LEVEL_ERROR;
/** @var \FileLog we want our own instance ! */
@@ -1095,7 +1126,12 @@ class DeprecatedCallsLog extends LogAPI
}
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 4);
$iStackDeprecatedMethodLevel = 2; // level 0 = current method, level 1 = @trigger_error, level 2 = method containing the `trigger_error` call
$iStackDeprecatedMethodLevel = 2; // level 0 = current method, level 1 = @trigger_error, level 2 = method containing the `trigger_error` call (can be either 'trigger_deprecation' or the faulty method), level 3 = In some cases, method containing the 'trigger_deprecation' call
// In case current level is actually a 'trigger_deprecation' call, try to go one level further to get the real deprecated method
if (array_key_exists($iStackDeprecatedMethodLevel, $aStack) && ($aStack[$iStackDeprecatedMethodLevel]['function'] === 'trigger_deprecation') && array_key_exists($iStackDeprecatedMethodLevel + 1, $aStack)) {
$iStackDeprecatedMethodLevel++;
}
$sDeprecatedObject = $aStack[$iStackDeprecatedMethodLevel]['class'];
$sDeprecatedMethod = $aStack[$iStackDeprecatedMethodLevel]['function'];
if (($sDeprecatedObject === __CLASS__) && ($sDeprecatedMethod === 'Log')) {
@@ -1140,7 +1176,6 @@ class DeprecatedCallsLog extends LogAPI
* - else call parent method
*
* In other words, when in dev mode all deprecated calls will be logged to file
*
*/
protected static function GetLevelDefault(string $sConfigKey)
{

View File

@@ -128,6 +128,18 @@ abstract class MetaModel
/** @var string */
protected static $m_sEnvironment = 'production';
/**
* Objects currently created/updated.
*
* if an object is already being updated, then this method will return this object instead of recreating a new one.
* At this point the method DBUpdate of a new object with the same class and id won't do anything due to reentrance protection,
* so to ensure that the potential modifications are correctly saved, the object currently being updated is returned.
* DBUpdate() method then will take care that all the modifications will be saved.
*
* [class][key] -> object
*/
protected static array $m_aReentranceProtection = [];
/**
* MetaModel constructor.
*/
@@ -338,7 +350,7 @@ abstract class MetaModel
{
self::_check_subclass($sClass);
return $sClass::GetClassName($sClass);
return call_user_func([$sClass, 'GetClassName'], $sClass);
}
/**
@@ -422,7 +434,7 @@ abstract class MetaModel
{
self::_check_subclass($sClass);
return $sClass::GetClassDescription($sClass);
return call_user_func([$sClass, 'GetClassDescription'], $sClass);
}
/**
@@ -2060,6 +2072,43 @@ abstract class MetaModel
return $aAttCodesToPrint;
}
/**
* @param string $sObjectClass class of the object containing the AttributeLinkedSetIndirect (eg: Team)
* @param string $sObjectLinkedSetIndirectAttCode code of the AttributeLinkedSetIndirect in the sObjectClass (eg: persons_list in the Team class, pointing to lnkPersonToTeam lnk class)
* @param string $sRemoteClass remote class pointed by the lnk class (eg: Person pointed by lnkPersonToTeam)
* @param string $sLnkExternalKeyToRemoteClassAttCode in the lnk class, external key to the remote class (eg: person_id in lnkPersonToTeam, pointing to a Person instance)
*
* @return string[] attcodes to display, containing aliases
* @throws \CoreException
*
* @since 3.0.0 N°2334 added code for n-n relations in {@see BlockIndirectLinksViewTable::GetAttCodesToDisplay}
* @since 3.1.0 N°3200 method creation so that it can be used elsewhere
*/
public static function GetAttributeLinkedSetIndirectDatatableAttCodesToDisplay(string $sObjectClass, string $sObjectLinkedSetIndirectAttCode, string $sRemoteClass, string $sLnkExternalKeyToRemoteClassAttCode):array
{
$aLnkAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectLinkClass($sObjectClass, $sObjectLinkedSetIndirectAttCode);
$aRemoteAttDefsToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectRemoteClass($sRemoteClass);
$aLnkAttCodesToDisplay = array_map(
function ($oLnkAttDef) {
return ormLinkSet::LINK_ALIAS.'.'.$oLnkAttDef->GetCode();
},
$aLnkAttDefsToDisplay
);
if (!in_array(ormLinkSet::LINK_ALIAS.'.'.$sLnkExternalKeyToRemoteClassAttCode, $aLnkAttCodesToDisplay)) {
// we need to display a link to the remote class instance !
$aLnkAttCodesToDisplay[] = ormLinkSet::LINK_ALIAS.'.'.$sLnkExternalKeyToRemoteClassAttCode;
}
$aRemoteAttCodesToDisplay = array_map(
function ($oRemoteAttDef) {
return ormLinkSet::REMOTE_ALIAS.'.'.$oRemoteAttDef->GetCode();
},
$aRemoteAttDefsToDisplay
);
$aAttCodesToDisplay = array_merge($aLnkAttCodesToDisplay, $aRemoteAttCodesToDisplay);
return $aAttCodesToDisplay;
}
/**
* @param string $sClass
* @param string $sListCode
@@ -5077,9 +5126,9 @@ abstract class MetaModel
if (!empty(self::$m_sTablePrefix))
{
foreach(CMDBSource::EnumTables() as $sTable)
foreach(self::DBEnumTables() as $sTable)
{
// perform a case insensitive test because on Windows the table names become lowercase :-(
// perform a case-insensitive test because on Windows the table names become lowercase :-(
if (strtolower(substr($sTable, 0, strlen(self::$m_sTablePrefix))) == strtolower(self::$m_sTablePrefix))
{
CMDBSource::DropTable($sTable);
@@ -6785,6 +6834,19 @@ abstract class MetaModel
}
$sClass = $aRow[$sClassAlias."finalclass"];
}
// if an object is already being updated, then this method will return this object instead of recreating a new one.
// At this point the method DBUpdate of a new object with the same class and id won't do anything due to reentrance protection,
// so to ensure that the potential modifications are correctly saved, the object currently being updated is returned.
// DBUpdate() method then will take care that all the modifications will be saved.
if (array_key_exists($sClassAlias.'id', $aRow)) {
$iKey = $aRow[$sClassAlias."id"];
$oObject = self::GetReentranceObject($sClass, $iKey);
if ($oObject !== false) {
return $oObject;
}
}
return new $sClass($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec);
}
@@ -7543,6 +7605,35 @@ abstract class MetaModel
/** @var AttributeEnum $oAttDef */
return $oAttDef->GetStyle($sValue);
}
protected static function GetReentranceObject($sClass, $sKey)
{
if (isset(self::$m_aReentranceProtection[$sClass][$sKey])) {
return self::$m_aReentranceProtection[$sClass][$sKey];
}
return false;
}
/**
* @param \DBObject $oObject
*
* @return bool true if reentry possible
*/
public static function StartReentranceProtection(DBObject $oObject)
{
if (isset(self::$m_aReentranceProtection[get_class($oObject)][$oObject->GetKey()])) {
return false;
}
self::$m_aReentranceProtection[get_class($oObject)][$oObject->GetKey()] = $oObject;
return true;
}
public static function StopReentranceProtection(DBObject $oObject)
{
if (isset(self::$m_aReentranceProtection[get_class($oObject)][$oObject->GetKey()])) {
unset(self::$m_aReentranceProtection[get_class($oObject)][$oObject->GetKey()]);
}
}
}

View File

@@ -2860,9 +2860,26 @@ class FunctionExpression extends Expression
{
throw new \Exception("Function {$this->m_sVerb} requires 1 argument");
}
// N°5985 - Since PHP 8.1+, a bug fix on \DateTimeInterval for a date anterior to "1937-05-23" now returns a different number of days. The workaround below aim at making the code work with PHP 7.4 => 8.2+
//
// $oDate = new DateTimeImmutable('2020-01-02');
// $oZero = new DateTimeImmutable('1937-05-22');
// $iRet = (int) $oDate->diff($oZero)->format('%a');
// echo $iRet."\n"; // 30174 (PHP 8.0) vs 30175 (PHP 8.1+)
//
// $oDate = new DateTimeImmutable('2020-01-02');
// $oZero = new DateTimeImmutable('1937-05-23');
// $iRet = (int) $oDate->diff($oZero)->format('%a');
// echo $iRet."\n"; // 30174 (PHP 8.0) vs 30174 (PHP 8.1+)
//
// To work around that we take 1970-01-01 as "zero date" and we offset it with the number of days between 1582-01-01 and 1970-01-01.
// Note that as the "target" date could be between 1582-01-01 and 1970-01-01 we have to format the interval with the "-"/"+" sign in order to correct the number of days.
$oDate = new DateTime($this->m_aArgs[0]->Evaluate($aArgs));
$oZero = new DateTime('1582-01-01');
$iRet = (int) $oDate->diff($oZero)->format('%a') + 577815;
$oZero = new DateTime('1970-01-01');
$iDaysBetween19700101And15800101 = 141713;
$iRet = (int) $oZero->diff($oDate)->format('%R%a') + 577815 + $iDaysBetween19700101And15800101;
return $iRet;
case 'FROM_DAYS':

View File

@@ -26,6 +26,37 @@
class OQLException extends CoreException
{
/**
* @var string
* @since 3.1.0
*/
protected $m_MyIssue;
/**
* @var string
* @since 3.1.0
*/
protected $m_sInput;
/**
* @var int
* @since 3.1.0
*/
protected $m_iLine;
/**
* @var int
* @since 3.1.0
*/
protected $m_iCol;
/**
* @var string
* @since 3.1.0
*/
protected $m_sUnexpected;
/**
* @var array|null string
* @since 3.1.0
*/
protected $m_aExpecting;
public function __construct($sIssue, $sInput, $iLine, $iCol, $sUnexpected, $aExpecting = null)
{
$this->m_MyIssue = $sIssue;

View File

@@ -25,6 +25,9 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Service\Events\EventData;
use Combodo\iTop\Service\Events\EventService;
/**
* ormDocument
@@ -193,7 +196,6 @@ class ormDocument
* @param string $sContentDisposition Either 'inline' or 'attachment'
* @param string $sSecretField The attcode of the field containing a "secret" to be provided in order to retrieve the file
* @param string $sSecretValue The value of the secret to be compared with the value of the attribute $sSecretField
* @return none
*/
public static function DownloadDocument(WebPage $oPage, $sClass, $id, $sAttCode, $sContentDisposition = 'attachment', $sSecretField = null, $sSecretValue = null)
{
@@ -212,6 +214,12 @@ class ormDocument
$oDocument = $oObj->Get($sAttCode);
if (is_object($oDocument))
{
$aEventData = array(
'debug_info' => $oDocument->GetFileName(),
'object' => $oObj,
'document' => $oDocument,
);
EventService::FireEvent(new EventData(EVENT_DOWNLOAD_DOCUMENT, $sClass, $aEventData));
$oPage->TrashUnexpectedOutput();
$oPage->SetContentType($oDocument->GetMimeType());
$oPage->SetContentDisposition($sContentDisposition,$oDocument->GetFileName());

View File

@@ -383,9 +383,8 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
$iPreservedCount = count($this->aPreserved);
if ($this->iCursor < $iPreservedCount)
{
$iRet = current($this->aPreserved);
$this->oOriginalSet->Seek($iRet);
$oRet = $this->oOriginalSet->Fetch();
$sId = key($this->aPreserved);
$oRet = MetaModel::GetObject($this->sClass, $sId);
}
else
{
@@ -736,6 +735,7 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
$oLink->DBClone();
}
}
$oLink->SetLinkHostObject($oHostObject);
$oLink->DBWrite();
$this->aPreserved[$oLink->GetKey()] = $oLink;
@@ -847,11 +847,30 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator
}
$oLinkSet = new DBObjectSet($oLinkSearch);
$oLinkSet->SetShowObsoleteData($bShowObsolete);
if ($this->HasDelta())
{
if ($this->HasDelta()) {
$oLinkSet->AddObjectArray($this->aAdded);
}
return $oLinkSet;
}
/**
* GetValues.
*
* @return array of tag codes
*/
public function GetValues()
{
$aValues = array();
foreach ($this->aPreserved as $sTagCode => $oTag) {
$aValues[] = $sTagCode;
}
foreach ($this->aAdded as $sTagCode => $oTag) {
$aValues[] = $sTagCode;
}
sort($aValues);
return $aValues;
}
}

View File

@@ -499,9 +499,9 @@ final class ormTagSet extends ormSet
}
/**
* @param $sTagCode
* @param $sTagLabel
*
* @return DBObject tag
* @return string Tag code
* @throws \CoreUnexpectedValue
* @throws \CoreException
*/

View File

@@ -21,6 +21,19 @@ use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\MultiColumnUIBlockFactor
*/
class PDFBulkExport extends HTMLBulkExport
{
/**
* @var string For sample purposes
* @internal
* @since 2.7.8
*/
const ENUM_OUTPUT_TYPE_SAMPLE = 'sample';
/**
* @var string For the real export
* @internal
* @since 2.7.8
*/
const ENUM_OUTPUT_TYPE_REAL = 'real';
public function DisplayUsage(Page $oP)
{
$oP->p(" * pdf format options:");
@@ -197,6 +210,25 @@ EOF
return $sPDF;
}
/**
* @inheritDoc
* @since 2.7.8
*/
protected function GetSampleData($oObj, $sAttCode)
{
if ($sAttCode !== 'id')
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
// As sample data will be displayed in the web browser, AttributeImage needs to be rendered with a regular HTML format, meaning its "src" looking like "data:image/png;base64,iVBORw0KGgoAAAANSUh..."
// Whereas for the PDF generation it needs to be rendered with a TCPPDF-compatible format, meaning its "src" looking like "@iVBORw0KGgoAAAANSUh..."
if ($oAttDef instanceof AttributeImage) {
return $this->GetAttributeImageValue($oAttDef, $oObj->Get($sAttCode), static::ENUM_OUTPUT_TYPE_SAMPLE);
}
}
return parent::GetSampleData($oObj, $sAttCode);
}
protected function GetValue($oObj, $sAttCode)
{
switch($sAttCode)
@@ -212,31 +244,7 @@ EOF
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
if ($oAttDef instanceof AttributeImage)
{
// To limit the image size in the PDF output, we have to enforce the size as height/width because max-width/max-height have no effect
//
$iDefaultMaxWidthPx = 48;
$iDefaultMaxHeightPx = 48;
if ($value->IsEmpty())
{
$iNewWidth = $iDefaultMaxWidthPx;
$iNewHeight = $iDefaultMaxHeightPx;
$sUrl = $oAttDef->Get('default_image');
}
else
{
list($iWidth, $iHeight) = utils::GetImageSize($value->GetData());
$iMaxWidthPx = min($iDefaultMaxWidthPx, $oAttDef->Get('display_max_width'));
$iMaxHeightPx = min($iDefaultMaxHeightPx, $oAttDef->Get('display_max_height'));
$fScale = min($iMaxWidthPx / $iWidth, $iMaxHeightPx / $iHeight);
$iNewWidth = $iWidth * $fScale;
$iNewHeight = $iHeight * $fScale;
$sUrl = 'data:'.$value->GetMimeType().';base64,'.base64_encode($value->GetData());
}
$sRet = ($sUrl !== null) ? '<img src="'.$sUrl.'" style="width: '.$iNewWidth.'px; height: '.$iNewHeight.'px">' : '';
$sRet = '<div class="ibo-input-image--image-view">'.$sRet.'</div>';
$sRet = $this->GetAttributeImageValue($oAttDef, $value, static::ENUM_OUTPUT_TYPE_REAL);
}
else
{
@@ -251,6 +259,55 @@ EOF
return $sRet;
}
/**
* @param \AttributeImage $oAttDef Instance of image attribute
* @param \ormDocument $oValue Value of image attribute
* @param string $sOutputType {@see \PDFBulkExport::ENUM_OUTPUT_TYPE_SAMPLE}, {@see \PDFBulkExport::ENUM_OUTPUT_TYPE_REAL}
*
* @return string Rendered value of $oAttDef / $oValue according to the desired $sOutputType
* @since 2.7.8
*/
protected function GetAttributeImageValue(AttributeImage $oAttDef, ormDocument $oValue, string $sOutputType)
{
// To limit the image size in the PDF output, we have to enforce the size as height/width because max-width/max-height have no effect
//
$iDefaultMaxWidthPx = 48;
$iDefaultMaxHeightPx = 48;
if ($oValue->IsEmpty()) {
$iNewWidth = $iDefaultMaxWidthPx;
$iNewHeight = $iDefaultMaxHeightPx;
$sUrl = $oAttDef->Get('default_image');
} else {
list($iWidth, $iHeight) = utils::GetImageSize($oValue->GetData());
$iMaxWidthPx = min($iDefaultMaxWidthPx, $oAttDef->Get('display_max_width'));
$iMaxHeightPx = min($iDefaultMaxHeightPx, $oAttDef->Get('display_max_height'));
$fScale = min($iMaxWidthPx / $iWidth, $iMaxHeightPx / $iHeight);
$iNewWidth = $iWidth * $fScale;
$iNewHeight = $iHeight * $fScale;
$sValueAsBase64 = base64_encode($oValue->GetData());
switch ($sOutputType) {
case static::ENUM_OUTPUT_TYPE_SAMPLE:
$sUrl = 'data:'.$oValue->GetMimeType().';base64,'.$sValueAsBase64;
break;
case static::ENUM_OUTPUT_TYPE_REAL:
default:
// TCPDF requires base64-encoded images to be rendered without the usual "data:<MIMETYPE>;base64" header but with an "@"
// @link https://tcpdf.org/examples/example_009/
$sUrl = '@'.$sValueAsBase64;
break;
}
}
$sRet = ($sUrl !== null) ? '<img src="'.$sUrl.'" style="width: '.$iNewWidth.'px; height: '.$iNewHeight.'px;">' : '';
$sRet = '<div class="ibo-input-image--image-view">'.$sRet.'</div>';
return $sRet;
}
public function GetSupportedFormats()
{
return array('pdf' => Dict::S('Core:BulkExport:PDFFormat'));

View File

@@ -30,18 +30,40 @@
/**
* Element of the response formed by RestResultWithObjects
*
* @package REST Services
* @package RESTAPI
* @api
*/
class ObjectResult
{
/**
* @var int
* @api
*/
public $code;
/**
* @var string
* @api
*/
public $message;
/**
* @var mixed|null
* @api
*/
public $class;
/**
* @var mixed|null
* @api
*/
public $key;
/**
* @var array
* @api
*/
public $fields;
/**
* Default constructor
* @api
*/
public function __construct($sClass = null, $iId = null)
{
@@ -54,11 +76,17 @@ class ObjectResult
/**
* Helper to make an output value for a given attribute
*
*
* @api
* @param DBObject $oObject The object being reported
* @param string $sAttCode The attribute code (must be valid)
* @param boolean $bExtendedOutput Output all of the link set attributes ?
*
* @return string A scalar representation of the value
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
protected function MakeResultValue(DBObject $oObject, $sAttCode, $bExtendedOutput = false)
{
@@ -112,11 +140,17 @@ class ObjectResult
/**
* Report the value for the given object attribute
*
*
* @api
* @param DBObject $oObject The object being reported
* @param string $sAttCode The attribute code (must be valid)
* @param boolean $bExtendedOutput Output all of the link set attributes ?
*
* @return void
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public function AddField(DBObject $oObject, $sAttCode, $bExtendedOutput = false)
{
@@ -129,8 +163,7 @@ class ObjectResult
/**
* REST response for services managing objects. Derive this structure to add information and/or constants
*
* @package Extensibility
* @package REST Services
* @package RESTAPI
* @api
*/
class RestResultWithObjects extends RestResult
@@ -140,13 +173,19 @@ class RestResultWithObjects extends RestResult
/**
* Report the given object
*
* @param int An error code (RestResult::OK is no issue has been found)
*
* @api
* @param int $iCode An error code (RestResult::OK is no issue has been found)
* @param string $sMessage Description of the error if any, an empty string otherwise
* @param DBObject $oObject The object being reported
* @param array $aFieldSpec An array of class => attribute codes (Cf. RestUtils::GetFieldList). List of the attributes to be reported.
* @param array|null $aFieldSpec An array of class => attribute codes (Cf. RestUtils::GetFieldList). List of the attributes to be reported.
* @param boolean $bExtendedOutput Output all of the link set attributes ?
*
* @return void
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
*/
public function AddObject($iCode, $sMessage, $oObject, $aFieldSpec = null, $bExtendedOutput = false)
{
@@ -184,16 +223,30 @@ class RestResultWithObjects extends RestResult
}
}
/**
* @package RESTAPI
* @api
*/
class RestResultWithRelations extends RestResultWithObjects
{
public $relations;
/**
* @api
*/
public function __construct()
{
parent::__construct();
$this->relations = array();
}
/**
* @param $sSrcKey
* @param $sDestKey
*
* @return void
* @api
*/
public function AddRelation($sSrcKey, $sDestKey)
{
if (!array_key_exists($sSrcKey, $this->relations))
@@ -207,7 +260,7 @@ class RestResultWithRelations extends RestResultWithObjects
/**
* Deletion result codes for a target object (either deleted or updated)
*
* @package Extensibility
* @package RESTAPI
* @api
* @since 2.0.1
*/
@@ -215,30 +268,37 @@ class RestDelete
{
/**
* Result: Object deleted as per the initial request
* @api
*/
const OK = 0;
/**
* Result: general issue (user rights or ... ?)
* Result: general issue (user rights or ... ?)
* @api
*/
const ISSUE = 1;
/**
* Result: Must be deleted to preserve database integrity
* Result: Must be deleted to preserve database integrity
* @api
*/
const AUTO_DELETE = 2;
/**
* Result: Must be deleted to preserve database integrity, but that is NOT possible
* Result: Must be deleted to preserve database integrity, but that is NOT possible
* @api
*/
const AUTO_DELETE_ISSUE = 3;
/**
* Result: Must be deleted to preserve database integrity, but this must be requested explicitely
* Result: Must be deleted to preserve database integrity, but this must be requested explicitly
* @api
*/
const REQUEST_EXPLICITELY = 4;
/**
* Result: Must be updated to preserve database integrity
* @api
*/
const AUTO_UPDATE = 5;
/**
* Result: Must be updated to preserve database integrity, but that is NOT possible
* @api
*/
const AUTO_UPDATE_ISSUE = 6;
}
@@ -575,7 +635,7 @@ class CoreServices implements iRestServiceProvider
$oObject = $oElement->GetProperty('object');
if ($oObject)
{
if ($bEnableRedundancy)
if ($bEnableRedundancy && $sDirection == 'down')
{
// Add only the "reached" objects
if ($oElement->GetProperty('is_reached'))

View File

@@ -42,7 +42,7 @@ class TemplateStringPlaceholder
public function __construct($sToken)
{
$this->sToken = $sToken;
$this->sAttcode = '';
$this->sAttCode = '';
$this->sFunction = '';
$this->sParamName = '';
$this->bIsValid = false; // Validity may be false in general, but it can work anyway (thanks to specialization) when rendering
@@ -90,9 +90,9 @@ class TemplateString
$oPlaceholder->sFunction = '';
$oPlaceholder->sParamName = $sParamName;
$sAttcode = substr($sPlaceholder, strlen($sParamPrefix));
$oPlaceholder->sAttcode = $sAttcode;
$oPlaceholder->bIsValid = MetaModel::IsValidAttCode($sClass, $sAttcode, true /* extended */);
$sAttCode = substr($sPlaceholder, strlen($sParamPrefix));
$oPlaceholder->sAttCode = $sAttCode;
$oPlaceholder->bIsValid = MetaModel::IsValidAttCode($sClass, $sAttCode, true /* extended */);
}
}
@@ -164,7 +164,7 @@ class TemplateString
$oRef = $aParamValues[$oPlaceholder->sParamName];
try
{
$value = $oRef->Get($oPlaceholder->sAttcode);
$value = $oRef->Get($oPlaceholder->sAttCode);
$aSearch[] = '$'.$oPlaceholder->sToken.'$';
$aReplace[] = $value;
$oPlaceholder->bIsValid = true;

View File

@@ -33,20 +33,22 @@ abstract class Trigger extends cmdbAbstractObject
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "description",
"state_attcode" => "",
"reconc_keys" => array('description'),
"db_table" => "priv_trigger",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-conflict.svg'),
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => array('finalclass'),
"state_attcode" => "",
"reconc_keys" => array('description'),
"db_table" => "priv_trigger",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-conflict.svg'),
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("action_list", array("linked_class" => "lnkTriggerAction", "ext_key_to_me" => "trigger_id", "ext_key_to_remote" => "action_id", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("action_list",
array("linked_class" => "lnkTriggerAction", "ext_key_to_me" => "trigger_id", "ext_key_to_remote" => "action_id", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array(), "display_style" => 'property')));
$aTags = ContextTag::GetTags();
MetaModel::Init_AddAttribute( new AttributeEnumSet("context", array("allowed_values" => null, "possible_values" => new ValueSetEnumPadded($aTags), "sql" => "context", "depends_on" => array(), "is_null_allowed" => true, "max_items" => 12)));

View File

@@ -348,7 +348,7 @@ abstract class User extends cmdbAbstractObject
$aChanges = $this->ListChanges();
if (array_key_exists('login', $aChanges)) {
// Check login uniqueness
if (strcasecmp($this->Get('login'), $this->GetOriginal('login')) !== 0) {
if ( $this->GetOriginal('login') === null || strcasecmp($this->Get('login'), $this->GetOriginal('login')) !== 0) {
$sNewLogin = $aChanges['login'];
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT User WHERE login = :newlogin");
if (!$this->IsNew()) {
@@ -1936,7 +1936,7 @@ class UserRights
// The bug has been fixed in PHP 7.2, but in case session_regenerate_id()
// fails we just silently ignore the error and keep the same session id...
$old_error_handler = set_error_handler(array(__CLASS__, 'VoidErrorHandler'));
session_regenerate_id(true);
Session::RegenerateId(true);
if ($old_error_handler !== null) {
set_error_handler($old_error_handler);
}

View File

@@ -43,3 +43,9 @@
.ibo-svg-illustration--container > svg *[fill="#6c63ff"]{
fill: $ibo-svg-illustration--fill;
}
// Workaround for a Safari (unsupported browser) issue: https://sourceforge.net/p/itop/discussion/itop-hub/thread/fc97ec4a10/?limit=25#1cc0/9930
// Fix found here: https://stackoverflow.com/questions/40895387/z-index-not-working-on-safari-fine-on-firefox-and-chrome
.ibo-navigation-menu.ibo-is-active .ibo-navigation-menu--drawer{
transform: translate3d(0,0,0);
}

View File

@@ -3,5 +3,7 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
@import "bulk/all";
@import "display-block/all";
@import "linked-set/all";
@import "tabular-fields/all";

View File

@@ -0,0 +1,6 @@
/*
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@import "bulk-modify";

View File

@@ -0,0 +1,14 @@
/*
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
.ibo-bulk--bulk-modify--incompatible-attribute {
&:before{
margin-right: $ibo-vendors-selectize--item--icon--margin-right;
@extend %fa-solid-base;
content: '\f05a';
color: $ibo-color-information-500;
}
}

View File

@@ -0,0 +1,6 @@
/*
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@import "linked-set-selector";

View File

@@ -0,0 +1,14 @@
/*
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
.ibo-linked-set--bulk-tooltip-info {
font-size: $ibo-font-size-100;
&:before{
margin-right: $ibo-vendors-selectize--item--icon--margin-right;
@extend %fa-solid-base;
content: '\f05a';
color: $ibo-color-information-500;
}
}

View File

@@ -12,17 +12,3 @@ select + label, label + select, label > select,
input + label, label + input, label > input {
margin-left: $ibo-input--spacing-left--with-label;
}
.ibo-input-with-label--label {
&.ibo-has-description {
&::after {
content: $ibo-field--label--description--content;
padding-left: $ibo-field--label--description--padding-left;
vertical-align: top;
cursor: pointer;
color: $ibo-field--label--description--color;
@extend %ibo-font-ral-bol-50;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -9,9 +9,6 @@ $ibo-alert--padding-x: 20px !default;
$ibo-alert--min-height: 30px !default;
$ibo-alert--border-radius: $ibo-border-radius-300 !default;
$ibo-alert--title--highlight--width: 4px !default;
$ibo-alert--title--highlight--height: 100% !default;
$ibo-alert--body--margin-top: $ibo-spacing-200 !default;
$ibo-alert-minimized--padding-y: 5px !default;
@@ -68,14 +65,7 @@ $ibo-alert-colors: (
@extend %ibo-font-size-150;
&::before {
display: block;
position: absolute;
top: 0;
left: 0;
content: '';
width: $ibo-alert--title--highlight--width;
height: $ibo-alert--title--highlight--height;
@include ibo-vertical-highlight;
}
.ibo-alert--title {

View File

@@ -1,5 +1,5 @@
/*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -21,7 +21,7 @@
@import "prop";
@import "title";
@import "datatable";
@import "datatable/all";
@import "form";
@import "fieldset";
@import "field";

View File

@@ -6,10 +6,6 @@
/* SCSS variables */
$ibo-field--value--color: $ibo-color-grey-800 !default;
$ibo-field--label--description--content: "?" !default;
$ibo-field--label--description--padding-left: $ibo-spacing-200 !default;
$ibo-field--label--description--color: $ibo-color-grey-600 !default;
$ibo-field--background-color--is-fullscreen: $ibo-color-white-100 !default;
$ibo-field--label--width--is-fullscreen: 100% !default;
@@ -162,18 +158,6 @@ $ibo-field--enable-bulk--checkbox--margin-left: $ibo-spacing-300 !default;
width: 30%;
word-break: break-word; /* We want labels to wrap if it is very long if it has no spaces */
@extend %ibo-font-weight-600;
> .ibo-has-description {
&::after {
content: $ibo-field--label--description--content;
padding-left: $ibo-field--label--description--padding-left;
vertical-align: top;
cursor: pointer;
color: $ibo-field--label--description--color;
@extend %ibo-font-ral-bol-50;
}
}
}
.ibo-field--label-small .ibo-field--label{
width: 20em;

View File

@@ -1,4 +1,48 @@
/*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
*/
/* SCSS variables */
$ibo-modal-option--do-not-show-again--margin-top: $ibo-spacing-500 !default;
$ibo-modal--is-informative--min-width: $ibo-size-700 !default;
$ibo-modal--is-informative--min-height: $ibo-size-300 !default;
$ibo-modal--is-informative--is-error--highlight--background-color: $ibo-color-red-600 !default;
$ibo-modal--is-informative--is-warning--highlight--background-color: $ibo-color-orange-600 !default;
$ibo-modal--is-informative--is-information--highlight--background-color: $ibo-color-blue-600 !default;
$ibo-modal--is-informative--is-success--highlight--background-color: $ibo-color-green-600 !default;
// Modal Option - Do not show again
.ibo-modal-option--do-not-show-again{
margin-top: $ibo-modal-option--do-not-show-again--margin-top;
.ibo-modal-option--do-not-show-again--checkbox{
height: auto;
display: inline-block;
width: auto;
}
}
.ibo-modal.ibo-is-informative{
display: flex;
align-items: center;
min-width: $ibo-modal--is-informative--min-width;
min-height: $ibo-modal--is-informative--min-height !important; // !important in order to overload jQueryUI CSS rule that's put directly on the element
&::before {
@include ibo-vertical-highlight;
}
&.ibo-is-error::before {
background-color: $ibo-modal--is-informative--is-error--highlight--background-color;
}
&.ibo-is-warning::before {
background-color: $ibo-modal--is-informative--is-warning--highlight--background-color;
}
&.ibo-is-information::before {
background-color: $ibo-modal--is-informative--is-information--highlight--background-color;
}
&.ibo-is-success::before {
background-color: $ibo-modal--is-informative--is-success--highlight--background-color;
}
}

View File

@@ -86,28 +86,27 @@ $ibo-panel--collapsible-toggler--font-size: $ibo-font-size-250 !default;
$ibo-panel--collapsible-toggler--color: $ibo-color-grey-700 !default;
/* Rules */
.ibo-panel {
--ibo-main-color: #{map-get($ibo-panel-colors, 'neutral')}; /* --ibo-main-color is to allow overload from custom dynamic value from the DM. The overload will be done through an additional CSS class of a particular DM class or DM attribute */
position: relative;
&.ibo-has-icon {
.ibo-panel--titles {
padding-left: $ibo-panel--icon--spacing;
// Note: Direct child selector is mandatory, otherwise a panel within a panel could be affected too when it shouldn't (eg. dashboard in an object, n:n panel)
> .ibo-panel--header {
.ibo-panel--titles {
padding-left: $ibo-panel--icon--spacing;
}
}
&.ibo-has-medallion-icon {
.ibo-panel--header-left {
position: relative;
z-index: 1;
margin-left: $ibo-panel--icon--spacing--as-medallion;
}
// Note: Direct child selector is mandatory, otherwise a panel within a panel could be affected too when it shouldn't (eg. dashboard in an object, n:n panel)
> .ibo-panel--header {
.ibo-panel--header-left {
position: relative;
z-index: 1;
margin-left: $ibo-panel--icon--spacing--as-medallion;
.ibo-panel--icon {
position: absolute;
bottom: $ibo-panel--icon--bottom--as-medallion;

View File

@@ -0,0 +1,7 @@
/*
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@import "datatable";
@import "datatableconfig";

View File

@@ -1,5 +1,5 @@
/*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -119,6 +119,10 @@ $ibo-fieldsorter--selected--background-color: $ibo-color-blue-200 !default;
&.selected {
background-color: $ibo-datatable--row--background-color--is-selected;
}
.ibo-datatable--row-actions-toolbar{
justify-content: end;
}
}
}
@@ -144,4 +148,6 @@ $ibo-fieldsorter--selected--background-color: $ibo-color-blue-200 !default;
@extend %ibo-font-ral-sembol-100;
border-bottom: $ibo-vendors-datatables--columns-header--border-bottom;
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
$ibo-datatableconfig--attributes-panel--first-column--margin-x: $ibo-spacing-0 !default;
$ibo-datatableconfig--attributes-panel--first-column--margin-y: $ibo-spacing-300 !default;
$ibo-datatableconfig--attributes-panel--first-column--max-height: 150px !default;
$ibo-datatableconfig--attributes-panel--per-page--input--margin-x: $ibo-spacing-200 !default;
$ibo-datatableconfig--attributes-panel--per-page--input--margin-y: $ibo-spacing-0 !default;
$ibo-datatableconfig--settings-panel--option--margin-right: $ibo-spacing-200 !default;
.ibo-datatableconfig--attributes-panel .ibo-panel--body{
@extend %ibo-font-ral-nor-100;
}
.ibo-datatableconfig--attributes-panel .ibo-multi-column .ibo-column:first-child{
margin: $ibo-datatableconfig--attributes-panel--first-column--margin-y $ibo-datatableconfig--attributes-panel--first-column--margin-x;
max-height: $ibo-datatableconfig--attributes-panel--first-column--max-height;
overflow-y: scroll;
}
.ibo-datatableconfig--attributes-panel--per-page--input{
margin: $ibo-datatableconfig--attributes-panel--per-page--input--margin-y $ibo-datatableconfig--attributes-panel--per-page--input--margin-x;
}
.ibo-datatableconfig--settings-panel .ibo-panel--body{
display: flex;
flex-direction: row;
}
.ibo-datatableconfig--settings-panel--options-container{
flex-grow: 1;
}
.ibo-datatableconfig--settings-panel--option{
display: flex;
flex-direction: row;
align-items: first baseline;
margin-right: $ibo-datatableconfig--settings-panel--option--margin-right;
}

View File

@@ -4,18 +4,19 @@
*/
$ibo-input-set--padding-x: $ibo-spacing-300 !default;
$ibo-input-set--padding-y: 5px !default;
$ibo-input-set--padding-y: $ibo-spacing-0 !default;
$ibo-input-set--input--height: auto !default;
$ibo-input-set--input--min-height: $ibo-input--height !default;
$ibo-input-set--input--height: 100% !default;
$ibo-input-set--remove--padding-top: 0.15em !default;
$ibo-input-set--remove--border-left: none !default;
$ibo-input-set--has-items--after--right: 8px !default;
$ibo-input-set--has-items--after--top: 5px !default;
$ibo-input-set--item--siblings-spacing: 3px !default;
$ibo-input-set--item--margin-y: $ibo-spacing-100 !default;
$ibo-input-set--item--margin-y: 1px !default;
$ibo-input-set--item--padding-x: 6px !default;
$ibo-input-set--item--padding-y: $ibo-spacing-200 !default;
$ibo-input-set--item--max-width: 120px !default;
@@ -26,6 +27,10 @@ $ibo-input-set--item--box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15), 0 0 1px 1px rgb
.ibo-input-set {
flex-wrap: wrap;
height: $ibo-input-set--input--height;
min-height: $ibo-input-set--input--min-height;
> input {
height: $ibo-input-set--input--height;
}
@@ -36,17 +41,7 @@ $ibo-input-set--item--box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15), 0 0 1px 1px rgb
border-left: $ibo-input-set--remove--border-left;
}
&.has-items::after {
content: "\f067";
font-family: "Font Awesome 5 Free";
font-weight: 600;
font-size: 10px;
position: absolute;
right: $ibo-input-set--has-items--after--right;
top: $ibo-input-set--has-items--after--top;
}
}
/* CSS rules from the old style, retrieved almost as is. Do not change the classes names in the markup without second thought as they are used by the portal as well */

View File

@@ -477,10 +477,13 @@ $ibo-navigation-menu--user-info--height--is-expanded: 100% !default;
transition-duration: 0.1s;
transition-timing-function: linear;
> .ibo-navigation-menu--menu-group-icon,
> .ibo-navigation-menu--menu-group-title{
> .ibo-navigation-menu--menu-group-icon{
display: flex; /* To avoid end space due to display:inline-block */
}
.ibo-navigation-menu--menu-group-title{
flex-grow: 1;
/* Note that display property is not set to flex on purpose so its text has an ellipsis on overflow */
}
&:not(:last-child){
margin-bottom: $ibo-navigation-menu--middle-part--elements-spacing;
@@ -661,8 +664,8 @@ $ibo-navigation-menu--user-info--height--is-expanded: 100% !default;
.ibo-navigation-menu--menu-nodes-title{
margin-top: $ibo-navigation-menu--menu-nodes-title--margin-top;
margin-bottom: $ibo-navigation-menu--menu-nodes-title--margin-bottom;
word-break: break-word; /* Ensure that long words wrap instead of being clipped */
@extend %ibo-font-ral-nor-350;
@extend %ibo-text-truncated-with-ellipsis;
}
/* - Menu node */
.ibo-navigation-menu--menu-node-title{

View File

@@ -35,6 +35,8 @@ $ibo-tab-container--extra-tab-toggler--text-color: $ibo-color-grey-700 !default;
$ibo-tab-container--extra-tab-toggler--text-color--on-hover: $ibo-color-blue-800 !default;
$ibo-tab-container--extra-tab-toggler--background-color--on-hover: $ibo-color-grey-200 !default;
$ibo-tab-container--extra-tab-toggler--tooltip-title--margin-bottom: $ibo-spacing-500 !default;
$ibo-tab-container--tab-container--label--text-color: $ibo-color-grey-600 !default;
$ibo-tab-container--tab-container--label--spacing: 20px !default;
$ibo-tab-container--tab-container--label--margin-bottom: 20px !default;
@@ -148,19 +150,24 @@ $ibo-tab-container--tab-container--last--min-height: 60vh !default;
}
}
.ibo-tab-container--extra-tab-toggler--tooltip-title {
@extend %ibo-font-weight-600;
margin-bottom: $ibo-tab-container--extra-tab-toggler--tooltip-title--margin-bottom;
}
.ibo-tab-container--tab-container {
padding: $ibo-tab-container--tab-container--padding-y $ibo-tab-container--tab-container--padding-x;
overflow-x: auto;
}
.ibo-tab-container--tab-container-list.ibo-is-scrollable {
.ibo-tab-container--tab-container--label {
.ibo-tab-container--tab-container:not(:first-child:nth-last-child(2)) .ibo-tab-container--tab-container--label {
display: block;
}
.ibo-tab-container--tab-container {
min-height: $ibo-tab-container--tab-container--min-height;
}
.ibo-tab-container--tab-container:last-child {
.ibo-tab-container--tab-container:last-child:not(:only-child) {
min-height: $ibo-tab-container--tab-container--last--min-height;
}
}

View File

@@ -27,11 +27,6 @@ tr.ibo-csv-import--row-unchanged td {
border-bottom: 1px $ibo-color-grey-400 solid;
}
.wizContainer table tr.ibo-csv-import--row-error td {
border-bottom: 1px $ibo-color-grey-400 solid;
background-color: $ibo-color-red-200;
}
tr.ibo-csv-import--row-modified td {
border-bottom: 1px $ibo-color-grey-400 solid;
}
@@ -44,4 +39,4 @@ tr.ibo-csv-import--row-added td {
font-size: 4em;
color: $ibo-color-primary-400;
margin: 20px;
}
}

View File

@@ -5,9 +5,14 @@
@import "border-radius";
@import "color";
@import "class-icon";
@import "depression";
@import "elevation";
@import "font-icon";
@import "fullscreen";
@import "sticky-header";
@import "text-decoration";
@import "text-position";
@import "typography";
@import "misc";
@import "class-icon";
@import "visibility";

View File

@@ -0,0 +1,36 @@
/*!
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/* Used on all ancestors when an element needs to be fullscreen (see .ibo-is-fullscreen) */
html.ibo-has-fullscreen-descendant {
position: fixed !important;
width: 0 !important;
height: 0 !important;
}
body.ibo-has-fullscreen-descendant {
width: 0 !important;
height: 0 !important;
overflow: hidden !important;
}
.ibo-has-fullscreen-descendant {
position: static !important;
overflow: visible !important;
z-index: 1050 !important;
}
/* Used on a fullscreen element (see .ibo-has-fullscreen-descendant) */
.ibo-is-fullscreen {
position: absolute;
top: 0 !important;
left: 0 !important;
margin: 0 !important;
padding: 0 !important;
width: 100vw;
height: 100vh;
overflow: auto;
z-index: 1050;
}

View File

@@ -3,191 +3,9 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
/* SCSS variables */
$ibo-is-code--background-color: $ibo-color-white-200 !default;
$ibo-is-code--padding: 1.25rem 1.5rem !default;
$ibo-sticky-sentinel--left: 0 !default;
$ibo-sticky-sentinel--right: 0 !default;
$ibo-sticky-sentinel--height: 0 !default;
$ibo-sticky-sentinel-top--top: 0 !default;
$ibo-sticky-sentinel-top--height: $ibo-sticky-sentinel--height !default;
$ibo-sticky-sentinel-bottom--bottom: 0 !default;
$ibo-sticky-sentinel-bottom--height: $ibo-sticky-sentinel--height !default;
/**************/
/* Visibility */
/**************/
.ibo-is-visible {
display: inherit !important; /* Note: !important is necessary as it needs to overload any standard rules */
visibility: visible !important;
}
.ibo-is-hidden {
display: none !important; /* Note: !important is necessary as it needs to overload any standard rules */
}
.ibo-is-transparent {
opacity: 0 !important; /* Note: !important is necessary as it needs to overload any standard rules */
}
.ibo-is-opaque {
opacity: 1 !important; /* Note: !important is necessary as it needs to overload any standard rules */
}
/****************************/
/* Disposition / alignement */
/****************************/
.ibo-is-fullwidth {
width: 100%;
}
%ibo-fully-centered-content {
display: flex;
justify-content: center;
align-items: center;
}
%ibo-vertically-centered-content {
display: flex;
align-items: center;
}
/* Typically to align icons and text as it is a good practice to align them on the baseline and not the "middle" */
%ibo-baseline-centered-content {
display: flex;
align-items: baseline;
}
/* Note: This might not be named correctly. The intention is to make an element occupy the full height of its parent and to be centered in it */
%ibo-full-height-content {
display: flex;
align-items: stretch;
}
/**************/
/* Fullscreen */
/**************/
/* Used on all ancestors when an element needs to be fullscreen (see .ibo-is-fullscreen) */
html.ibo-has-fullscreen-descendant {
position: fixed !important;
width: 0 !important;
height: 0 !important;
}
body.ibo-has-fullscreen-descendant {
width: 0 !important;
height: 0 !important;
overflow: hidden !important;
}
.ibo-has-fullscreen-descendant {
position: static !important;
overflow: visible !important;
z-index: 1050 !important;
}
/* Used on a fullscreen element (see .ibo-has-fullscreen-descendant) */
.ibo-is-fullscreen {
position: absolute;
top: 0 !important;
left: 0 !important;
margin: 0 !important;
padding: 0 !important;
width: 100vw;
height: 100vh;
overflow: auto;
z-index: 1050;
}
/****************/
/* Text helpers */
/****************/
%ibo-text-truncated-with-ellipsis {
white-space: nowrap;
overflow-x: hidden;
text-overflow: ellipsis;
}
.ibo-text-truncated-with-ellipsis {
@extend %ibo-text-truncated-with-ellipsis;
}
/* Use this when you want the hyperlink to be of the color of its container's text instead of the global hyperlink color */
%ibo-hyperlink-inherited-colors {
color: inherit;
&:hover,
&:active {
color: inherit;
}
}
.ibo-is-broken-hyperlink {
text-decoration: line-through;
cursor: help;
}
.ibo-is-code {
background-color: $ibo-is-code--background-color;
padding: $ibo-is-code--padding;
@extend %ibo-font-code-150;
}
.ibo-add-margin-top-250{
margin-top: $ibo-spacing-400;
}
/*
* A single class to handle WYSIWYG generated content, where only HTML tags are available
* See https://bulma.io/documentation/elements/content/
*/
.ibo-is-html-content {
@extend .content;
/* Force user-generated tables to fit within the container as they often have an hard-coded width */
table {
width: unset !important;
max-width: max-content;
}
/* For table to render like in CKEditor, works with bulma lib. overload see:
* - ../../vendors/_bulma-variables-overload.scss)
* - ../../_shame.scss
*/
table {
border-collapse: separate;
border-spacing: 2px;
}
/* Preserve original text color in code blocks, except for the Highlight.js blocks which have their own colors */
& > code,
code:not(.hljs) {
color: inherit;
}
}
/***********************************************************************/
/* Sticky headers */
/* */
/* Used as a trigger to make an element stick to another during scroll */
/***********************************************************************/
.ibo-sticky-sentinel {
position: absolute;
left: $ibo-sticky-sentinel--left;
right: $ibo-sticky-sentinel--right;
visibility: hidden;
}
.ibo-sticky-sentinel-top {
top: $ibo-sticky-sentinel-top--top;
height: $ibo-sticky-sentinel-top--height; /* To be overloaded by use cases */
}
.ibo-sticky-sentinel-bottom {
bottom: $ibo-sticky-sentinel-bottom--bottom;
height: $ibo-sticky-sentinel-bottom--height; /* To be overloaded by use cases */
}
%ibo-medallion {
position: relative;

View File

@@ -0,0 +1,35 @@
/*!
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/***********************************************************************/
/* Sticky headers */
/* */
/* Used as a trigger to make an element stick to another during scroll */
/***********************************************************************/
/* SCSS variables */
$ibo-sticky-sentinel--left: 0 !default;
$ibo-sticky-sentinel--right: 0 !default;
$ibo-sticky-sentinel--height: 0 !default;
$ibo-sticky-sentinel-top--top: 0 !default;
$ibo-sticky-sentinel-top--height: $ibo-sticky-sentinel--height !default;
$ibo-sticky-sentinel-bottom--bottom: 0 !default;
$ibo-sticky-sentinel-bottom--height: $ibo-sticky-sentinel--height !default;
/* Rules */
.ibo-sticky-sentinel {
position: absolute;
left: $ibo-sticky-sentinel--left;
right: $ibo-sticky-sentinel--right;
visibility: hidden;
}
.ibo-sticky-sentinel-top {
top: $ibo-sticky-sentinel-top--top;
height: $ibo-sticky-sentinel-top--height; /* To be overloaded by use cases */
}
.ibo-sticky-sentinel-bottom {
bottom: $ibo-sticky-sentinel-bottom--bottom;
height: $ibo-sticky-sentinel-bottom--height; /* To be overloaded by use cases */
}

View File

@@ -0,0 +1,90 @@
/*!
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/* SCSS variables */
$ibo-has-description--content: "?" !default;
$ibo-has-description--padding-left: $ibo-spacing-200 !default;
$ibo-has-description--color: $ibo-color-grey-600 !default;
$ibo-has-description--font-size: 0.7em !default; /* Font size is em on purpose as we want it to be proportional to its context */
$ibo-is-code--background-color: $ibo-color-white-200 !default;
$ibo-is-code--padding: 1.25rem 1.5rem !default;
/* Rules */
%ibo-text-truncated-with-ellipsis {
white-space: nowrap;
overflow-x: hidden;
text-overflow: ellipsis;
}
.ibo-text-truncated-with-ellipsis {
@extend %ibo-text-truncated-with-ellipsis;
}
/* Use this when you want the hyperlink to be of the color of its container's text instead of the global hyperlink color */
%ibo-hyperlink-inherited-colors {
color: inherit;
&:hover,
&:active {
color: inherit;
}
}
.ibo-is-broken-hyperlink {
text-decoration: line-through;
cursor: help;
}
/* Class to display a hint on elements that have a tooltip for further description */
.ibo-has-description {
&::after {
content: $ibo-has-description--content;
padding-left: $ibo-has-description--padding-left;
vertical-align: top;
cursor: pointer;
color: $ibo-has-description--color;
/* We don't use a %ibo-font-ral-xxx-yyy as we need a specific size */
@extend %ibo-font-weight-700;
font-size: $ibo-has-description--font-size;
}
}
.ibo-is-code {
background-color: $ibo-is-code--background-color;
padding: $ibo-is-code--padding;
@extend %ibo-font-code-150;
}
/*
* A single class to handle WYSIWYG generated content, where only HTML tags are available
* See https://bulma.io/documentation/elements/content/
*/
.ibo-is-html-content {
@extend .content;
/* Force user-generated tables to fit within the container as they often have an hard-coded width */
table {
width: unset !important;
max-width: max-content;
}
/* For table to render like in CKEditor, works with bulma lib. overload see:
* - ../../vendors/_bulma-variables-overload.scss)
* - ../../_shame.scss
*/
table {
border-collapse: separate;
border-spacing: 2px;
}
/* Preserve original text color in code blocks, except for the Highlight.js blocks which have their own colors */
& > code,
code:not(.hljs) {
color: inherit;
}
}

View File

@@ -0,0 +1,34 @@
/*!
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/****************************/
/* Disposition / alignement */
/****************************/
.ibo-is-fullwidth {
width: 100%;
}
%ibo-fully-centered-content {
display: flex;
justify-content: center;
align-items: center;
}
%ibo-vertically-centered-content {
display: flex;
align-items: center;
}
/* Typically to align icons and text as it is a good practice to align them on the baseline and not the "middle" */
%ibo-baseline-centered-content {
display: flex;
align-items: baseline;
}
/* Note: This might not be named correctly. The intention is to make an element occupy the full height of its parent and to be centered in it */
%ibo-full-height-content {
display: flex;
align-items: stretch;
}

View File

@@ -0,0 +1,21 @@
/*!
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
.ibo-is-visible {
display: inherit !important; /* Note: !important is necessary as it needs to overload any standard rules */
visibility: visible !important;
}
.ibo-is-hidden {
display: none !important; /* Note: !important is necessary as it needs to overload any standard rules */
}
.ibo-is-transparent {
opacity: 0 !important; /* Note: !important is necessary as it needs to overload any standard rules */
}
.ibo-is-opaque {
opacity: 1 !important; /* Note: !important is necessary as it needs to overload any standard rules */
}

View File

@@ -1,5 +1,6 @@
/*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@import "highlight";

View File

@@ -0,0 +1,17 @@
/*
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
$ibo-vertical-highlight--width: $ibo-size-100;
$ibo-vertical-highlight--height: 100%;
@mixin ibo-vertical-highlight {
display: block;
position: absolute;
top: 0;
left: 0;
content: "";
width: $ibo-vertical-highlight--width;
height: $ibo-vertical-highlight--height;
}

View File

@@ -4,10 +4,95 @@
*/
$ibo-vendors-selectize-input--color: $ibo-color-grey-900 !default;
$ibo-vendors-selectize-control--plugin-add-button--add-option--right: $ibo-spacing-0 !default;
$ibo-vendors-selectize-control--plugin-add-button--add-option--height: 100% !default;
$ibo-vendors-selectize-control--plugin-add-button--add-option--width: $ibo-size-350 !default;
$ibo-vendors-selectize-control--plugin-add-button--add-option--color: $ibo-color-grey-900 !default;
$ibo-vendors-selectize--item--icon--margin-right: $ibo-spacing-200 !default;
$ibo-vendors-selectize--item--add--background-color: $ibo-color-green-100 !default;
$ibo-vendors-selectize--item--add--icon--color: $ibo-color-green-900 !default;
$ibo-vendors-selectize--item--remove--background-color: $ibo-color-red-100 !default;
$ibo-vendors-selectize--item--remove--icon--color: $ibo-color-red-800 !default;
$ibo-vendors-selectize--item--ignore-partial--background-color: $ibo-color-grey-200 !default;
$ibo-vendors-selectize--input-error--border: 1px solid $ibo-color-red-600 !default;
.selectize-dropdown-content {
max-height: unset; /* Overloaded as it will be handled by the _input-select.scss partial */
}
.selectize-input input{
color: $ibo-vendors-selectize-input--color;
}
.selectize-control.plugin-combodo_add_button{
display: flex;
.selectize-add-option {
position: absolute;
right: $ibo-vendors-selectize-control--plugin-add-button--add-option--right;
display: inline-flex;
justify-content: center;
align-items: center;
height: $ibo-vendors-selectize-control--plugin-add-button--add-option--height;
width: $ibo-vendors-selectize-control--plugin-add-button--add-option--width;
z-index: 1;
color: $ibo-vendors-selectize-control--plugin-add-button--add-option--color;
}
}
// Simple options renderer
.simple-option-renderer--container {
display: flex;
align-items: center;
}
.simple-option-renderer--container--icon {
width: 25px;
text-align: center;
}
.simple-option-renderer--container--label {
margin-left: 3px;
flex-grow: 1;
}
.selectize-input{
.attribute-set-item{
>* {
display: inline;
}
&.item-add::before,&.item-remove::before{
@extend %fa-solid-base;
margin-right: $ibo-vendors-selectize--item--icon--margin-right;
}
&.item-add{
background-color: $ibo-vendors-selectize--item--add--background-color !important;
&::before{
color: $ibo-vendors-selectize--item--add--icon--color;
content: '\f067';
}
}
&.item-remove{
background-color: $ibo-vendors-selectize--item--remove--background-color !important;
&::before{
color: $ibo-vendors-selectize--item--remove--icon--color;
content: '\f1f8';
}
}
&.item-ignore-partial{
background-color: $ibo-vendors-selectize--item--ignore-partial--background-color !important;
}
}
&.selectize-input-error{
border: $ibo-vendors-selectize--input-error--border;
}
}

View File

@@ -7,6 +7,16 @@
*
* Other modification done : replaced the `Alpha(` by `alpha(` to avoid warnings generated by SCSSPHP
*/
/********************************************************************************/
/* */
/* @deprecated 3.0.0 N°3558 now we are using /css/backoffice/vendors/_jquery* */
/* */
/********************************************************************************/
.ui-draggable-handle {
-ms-touch-action: none;
touch-action: none;

View File

@@ -0,0 +1,10 @@
{
"config" : {
"classmap-authoritative" : true
},
"autoload" : {
"psr-4" : {
"Combodo\\iTop\\Cas\\" : "src"
}
}
}

18
datamodels/2.x/authent-cas/composer.lock generated Normal file
View File

@@ -0,0 +1,18 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "d751713988987e9331980363e24189ce",
"packages": [],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.1.0"
}

View File

@@ -6,7 +6,7 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'CAS:Error:UserNotAllowed' => 'User not allowed~~',
'CAS:Login:SignIn' => 'Sign in with CAS~~',
'CAS:Login:SignInTooltip' => 'Click here to authenticate yourself with the CAS server~~',
'CAS:Error:UserNotAllowed' => 'Nem engedélyezett felhasználó',
'CAS:Login:SignIn' => 'Bejelentkezés CAS szerverrel',
'CAS:Login:SignInTooltip' => 'Kattintson ide az azonosításhoz a CAS szerveren',
));

View File

@@ -1,3 +0,0 @@
<?php
require_once __DIR__.'/src/Config.php';
require_once __DIR__.'/src/CASLoginExtension.php';

View File

@@ -24,7 +24,8 @@ SetupWebPage::AddModule(
//
'datamodel' => array(
'model.authent-cas.php',
'main.php'
'vendor/autoload.php',
'src/CASLoginExtension.php',
),
'webservice' => array(
@@ -50,6 +51,7 @@ SetupWebPage::AddModule(
'cas_port' => '',
'cas_context' => '',
'cas_version' => '',
'service_base_url' => '',
),
)
);

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