Compare commits

...

949 Commits

Author SHA1 Message Date
Romain Quetiez
bbf7a8cc42 Ease toggling between optimization and legacy algorithms 2025-07-11 15:24:14 +02:00
Romain Quetiez
726542dd66 Update todo list 2025-07-04 15:38:14 +02:00
Romain Quetiez
8e65a65f6b Optimize the build of the graph (POC, somewhat hardcoded) 2025-07-04 15:36:03 +02:00
Romain Quetiez
f8ae1fd2d8 Measure operations duration 2025-07-04 14:31:40 +02:00
bdalsass
57d610fc16 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-07-03 13:41:47 +02:00
bdalsass
740f83f43e N°8395 - php8.1 compatibility in class extended Exception 2025-07-03 13:39:39 +02:00
bdalsass
d2d422afcf Merge remote-tracking branch 'origin/support/3.2' into develop 2025-07-01 16:11:20 +02:00
bdalsass
14791bf6b4 N°8148 - CAS problem when sending a link ending in &
- ApplicationContextTest mocking service
2025-07-01 15:57:45 +02:00
Benjamin Dalsass
062d543b26 N°8148 - CAS problem when sending a link ending in & (#722)
N°8148 - CAS problem when sending a link ending in &
2025-06-30 14:19:33 +02:00
bdalsass
278ac66637 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-06-30 10:08:47 +02:00
bdalsass
8cece0f0fd N°8395 - php8.1 compatibility in class extended Exception 2025-06-30 10:01:00 +02:00
odain
87db141c3c Merge branch 'support/3.2' into develop 2025-06-19 11:32:58 +02:00
odain
18871566d2 N°8286 - fix call to CompleteSessionData in case of no proper session content 2025-06-19 11:29:48 +02:00
odain
b565b700a9 Merge branch 'feature/7398-sessiontracker-extensibility' into support/3.2 2025-06-19 10:39:35 +02:00
odain
3846087dc1 N°7398 - regenerate autoload 2025-06-19 10:39:09 +02:00
odain
4f96246cbe N°7398-log level with invalid json 2025-06-19 10:38:01 +02:00
odain
ddd3dcdaf2 N°8286 - Be able to add additional data in iTop session files via custom classes 2025-06-19 10:38:01 +02:00
odain
82ad9d4317 N°7398 - be able to add custom data in session files generated by SessionHandler 2025-06-19 10:38:00 +02:00
Anne-Cath
6dc48ad36d N°8351 - [Export Impact Analysis] Field to display for the CSV export - Add the friendlyname to each list 2025-06-17 10:22:53 +02:00
jf-cbd
f062f994f0 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-06-16 17:01:49 +02:00
Björn Rudner
84476a869f 🎨 remove outdated or duplicated elemets (#726) 2025-06-16 16:25:22 +02:00
jf-cbd
684f829581 Merge commit from fork 2025-06-16 15:06:45 +02:00
jf-cbd
fb81e54ed9 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	core/kpi.class.inc.php
#	datamodels/2.x/itop-profiles-itil/datamodel.itop-profiles-itil.xml
2025-06-16 15:03:36 +02:00
Björn Rudner
d1e6334629 N°8456 - 🐛 add missing element attributes (#725) 2025-06-13 16:50:56 +02:00
Eric Espie
047ba8c6c7 Better KPI log performances 2025-06-13 15:31:30 +02:00
Eric Espie
916467ea61 Better perf for KPI logging 2025-06-13 15:26:43 +02:00
Romain Quetiez
8a4ad87b06 N°8440 Allow icon file ids to contain quotes, by correctly escaping language literals (XPath or PHP) 2025-06-13 09:58:59 +02:00
Romain Quetiez
2086052d60 N°8440 Allow icon file ids to contain quotes, by correctly escaping language literals (XPath or PHP) 2025-06-13 09:55:25 +02:00
v-dumas
f609010d7f Fix typo in SynchroLog class name 2025-06-11 17:49:28 +02:00
v-dumas
9330b5ec9a N°8377 - Fix confusing duplicated stimulus label on Change 2025-06-11 17:42:04 +02:00
v-dumas
18b85d4080 N°8377 - Missing rights on change ITIL for SuperUser 2025-06-11 17:41:01 +02:00
v-dumas
7214e5b43a Fix typo in SynchroLog class name 2025-06-11 17:14:15 +02:00
v-dumas
3689a83953 N°2583 - Control rights per class (bulk read on business, bulk write on others) 2025-06-11 17:13:25 +02:00
v-dumas
a715ce1292 Fix typo in SynchroLog class name 2025-06-11 17:11:43 +02:00
v-dumas
17d85fbb3f N°2583 - Advanced mode on CSV import add technical classes and class codes 2025-06-11 16:50:38 +02:00
v-dumas
5e59aff74f N°2583 - Filter Universal Search based on user rights 2025-06-11 16:48:28 +02:00
v-dumas
9e1e81ccc1 N°2583 - Audit, User, Query classes available for universal Search without the "Administrator" profile 2025-06-11 11:17:27 +02:00
v-dumas
c57c8d2af3 N°8406 - Fix FR translations 2025-06-11 10:16:50 +02:00
bdalsass
8a436f9760 N°8251 - Deprecated function when bulk assign
- Deprecated on strlen
2025-06-06 15:26:05 +02:00
bdalsass
2e77713772 N°8399 - Portal: unnecessary screen is displayed during a transition with no field to display 2025-06-06 15:04:21 +02:00
Anne-Cath
0a4f9e95c5 N°7216 - CSV Import failling with cryptic message when some lines have a missing separator - fix test 2025-06-06 10:29:26 +02:00
bdalsass
975c554e91 N°8251 - Deprecated function when bulk assign 2025-06-06 09:02:29 +02:00
bdalsass
ac6f642052 N°8232 - CKEditor: Unexpected lines repeatedly added after blocks 2025-06-06 08:54:23 +02:00
bdalsass
cf9ab2a83c N°8436 - CreateBrick fails when used with an abstract class 2025-06-04 16:22:41 +02:00
Anne-Cath
282fb13973 N°7216 - CSV Import failling with cryptic message when some lines have a missing separator - fix import with ignored values 2025-06-03 13:32:49 +02:00
bdalsass
0b92535fda Merge remote-tracking branch 'origin/support/3.2' into develop 2025-06-02 17:18:34 +02:00
bdalsass
3ff3dcba54 N°8308 - Broken URL in sending mail 2025-06-02 17:15:51 +02:00
jf-cbd
657fc912bf N°8198 - ModuleInstallation now extends DBObject + better exception message 2025-06-02 16:09:29 +02:00
Stephen Abello
a0f4c62bcc Fix browse brick items color not being overloadable 2025-05-28 16:35:12 +02:00
Anne-Catherine
5ae5221f6f N°6925 - Enable on any search result edition of OQL (#711) 2025-05-28 10:59:50 +02:00
jf-cbd
b15ca2fbc9 N°8260 - Change format of REST logs when they are close to the SQL field size limit 2025-05-27 10:41:05 +02:00
Stephen Abello
cb382eab4e Fix CI following dict behavior change 2025-05-26 16:23:38 +02:00
Stephen Abello
9723cde24c N°7960 - Add a wrapper to vsprintf to handle argument number disparities (#717) 2025-05-26 15:41:25 +02:00
Stephen Abello
7ae49e2cf4 N°8076 - Fix cancel and close buttons in a modal blocking all buttons for the underlying object 2025-05-26 11:25:08 +02:00
v-dumas
cb13a7a5b4 N°2583 - Audit, User, Query classes can be Import with bulk_modify right 2025-05-23 17:37:47 +02:00
bdalsass
cd4e2608ee Merge remote-tracking branch 'origin/support/3.2' into develop 2025-05-23 10:19:16 +02:00
bdalsass
9618e47045 N°8201 - [CVE_Request]_Cross-Site-Script Reflected(XSS Reflected at the name="attr_installed" (Low or Medium) 2025-05-23 10:16:22 +02:00
bdalsass
d84506ea9e Merge remote-tracking branch 'origin/support/2.7' into support/3.2
# Conflicts:
#	pages/UI.php
2025-05-23 10:14:50 +02:00
bdalsass
13239c2751 N°8201 - [CVE_Request]_Cross-Site-Script Reflected(XSS Reflected at the name="attr_installed" (Low or Medium) 2025-05-23 10:06:01 +02:00
bdalsass
d6c9a6d318 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-05-23 09:14:26 +02:00
bdalsass
8b30e36dd1 N°8168 - Stored XSS in portals lnk 2025-05-23 08:52:18 +02:00
bdalsass
80b290ab88 Merge remote-tracking branch 'origin/support/2.7' into support/3.2
# Conflicts:
#	sources/renderer/bootstrap/fieldrenderer/bslinkedsetfieldrenderer.class.inc.php
2025-05-23 08:49:52 +02:00
bdalsass
81b20ee583 N°8168 - Stored XSS in portals lnk 2025-05-23 08:42:56 +02:00
Pavel Stetina
ca937b829b Typo correction (#718) 2025-05-22 14:43:31 +02:00
Stephen Abello
e0b8ee2143 Merge branch 'support/3.2' into develop 2025-05-20 09:50:11 +02:00
Stephen Abello
a5545b0084 N°8205 - Security hardening 2025-05-20 09:48:00 +02:00
Stephen Abello
d72e861dfe N°8315 - Security hardening 2025-05-19 14:51:12 +02:00
Stephen Abello
92385273ff N°8315 - Security hardening 2025-05-19 14:45:24 +02:00
bdalsass
61c25f85e7 N°8313 - edit dashboard (fix broken test 3.2) 2025-05-19 13:12:02 +02:00
bdalsass
20a7a945fc Merge remote-tracking branch 'origin/support/3.2' into develop 2025-05-16 14:36:36 +02:00
bdalsass
b1cf2ec137 N°8313 - edit dashboard (fix broken test 3.2) 2025-05-16 14:35:39 +02:00
bdalsass
0640c1e9b6 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	application/utils.inc.php
2025-05-16 14:22:12 +02:00
bdalsass
7549ded51d Merge remote-tracking branch 'origin/support/2.7' into support/3.2
# Conflicts:
#	application/dashboard.class.inc.php
#	setup/setuputils.class.inc.php
#	tests/php-unit-tests/unitary-tests/application/utilsTest.php
2025-05-16 14:19:00 +02:00
bdalsass
38683c20b1 N°8313 - edit dashboard (fix broken test) 2025-05-16 14:05:55 +02:00
bdalsass
81791dd253 N°8313 - edit dashboard 2025-05-16 14:05:55 +02:00
bdalsass
e77e0eec9f N°8355 - render_dashboard 2025-05-16 14:05:55 +02:00
Guy Couronné
cad40ed758 N°7799 - Allow AttributeSet in XML sample file (#654)
*  Allow AttributeSet in XML sample file

In XML Data Loader, manage AttributeSet class and its children to be imported , in sample XML files.
2025-05-15 16:00:50 +02:00
Stephen Abello
11d23ac99b N°8274 - Make linkset toggler follow primary colors 2025-05-15 11:04:57 +02:00
Stephen Abello
1eeb98d2e2 N°8274 - Make hardcoded string a dict entry 2025-05-15 11:04:57 +02:00
Stephen Abello
92e2d22248 N°8274 - Change ui version 2025-05-15 11:04:57 +02:00
jf-cbd
5811dc06e3 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-05-15 09:48:22 +02:00
jf-cbd
f5ddbbbe0e Rollback typing parameter 2025-05-13 16:59:43 +02:00
jf-cbd
6811a82e1a Merge remote-tracking branch 'origin/support/2.7' into support/3.2
# Conflicts:
#	datamodels/2.x/itop-backup/status.php
#	setup/setuputils.class.inc.php
2025-05-13 16:40:11 +02:00
jf-cbd
960133c0df N°8379 - fix backup issue 2025-05-13 16:27:59 +02:00
jf-cbd
544c4ae888 N°8379 - fix backup issue 2025-05-13 16:05:39 +02:00
Stephen Abello
6bd11fb9bf N°8361 - Make vertical the default value for portal's menu 2025-05-06 10:43:34 +02:00
jbostoen
9ee18c2f36 Add helper method to create an ObjectResult from a DBObject. (#706)
Author: Jeffrey Bostoen <support@jeffreybostoen.be>
2025-04-25 14:46:33 +02:00
jf-cbd
624573d088 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	tests/php-unit-tests/unitary-tests/core/DBObject/DBObjectTest.php
2025-04-22 10:22:25 +02:00
jf-cbd
43a10e6944 Revert "N°8259 - Problem with GetMaxSize on AttributeText"
This reverts commit 29c75f626b.
2025-04-22 09:37:54 +02:00
Anne-Catherine
b21a02dcb8 N°7385 - Trigger on mention executed even if it's not a mention with @ (#638) 2025-04-18 14:52:23 +02:00
Anne-Catherine
3b60e63c97 N°6663 - Request template fields do not apply portal silos (#684) 2025-04-18 13:01:19 +02:00
Benjamin Dalsass
adc8b47945 N°8318 - Managing menu in itop portal new look (#710) 2025-04-14 16:38:36 +02:00
Stephen Abello
5e6d96019d N°8274 - Move mixins declarations to utils, move their usage to themes/ to be able to have their variables overloaded 2025-04-11 10:43:03 +02:00
Stephen Abello
06cc64f6d6 N°8274 - Move CSS color declaration to themes/ to avoid them being redeclared with every file importing utils 2025-04-11 10:09:18 +02:00
Stephen Abello
a5c14c3a48 N°8274 - Move body colors from legacy file to current theme files 2025-04-11 10:08:17 +02:00
Stephen Abello
8f6f243716 N°8274 - Fix typo 2025-04-11 09:47:50 +02:00
Stephen Abello
3c574819b2 N°8274 - Update precompiled stylesheets 2025-04-10 10:35:01 +02:00
Stephen Abello
5b1ea20407 N°8274 - Move every color style to themes/ to ease color customization, add illustration to export popup 2025-04-10 10:33:34 +02:00
Ben
4d88ef6ed4 Update contributors 2025-04-08 09:55:20 +02:00
Stephen Abello
5cb1102e6e N°8274 - Update precompiled stylesheets 2025-04-03 15:51:44 +02:00
Stephen Abello
bf23e4d98d N°8274 - Fix portal additional user profile tabs display 2025-04-03 15:51:36 +02:00
Stephen Abello
8645b28baa N°8274 - Update precompiled stylesheets 2025-04-02 15:50:29 +02:00
Stephen Abello
eff6fcff49 N°8274 - Fix input followed by input control (such as date time picker) display 2025-04-02 15:48:42 +02:00
Stephen Abello
994b08f94a N°8274 - Add no item illustration to browse brick 2025-04-02 15:45:28 +02:00
Stephen Abello
19495d8acf N°8274 - Update precompiled stylesheets 2025-04-02 14:32:38 +02:00
Stephen Abello
48a3ea1945 N°8274 - Replace color for selected rows in datatables 2025-04-02 14:31:33 +02:00
Stephen Abello
a11b2845f7 N°8274 - Correctly vertical align following dot in modal title 2025-04-02 12:07:21 +02:00
Stephen Abello
ee72325451 N°8274 - Make color slightly different for nested fieldset 2025-04-02 12:07:04 +02:00
Stephen Abello
ebb59d3ddc N°8274 - Align modal header and footer padding 2025-04-02 11:35:23 +02:00
Stephen Abello
0193db609d N°8274 - Add illustration on empty manage brick 2025-04-02 11:30:14 +02:00
Stephen Abello
ea2caed98a N°8274 - Correctly vertical align following dot 2025-04-02 11:29:42 +02:00
Stephen Abello
09c092782c N°8274 - Add approot to portal's Symfony variables. Allow portal's twig to display images located in /images in twig 2025-04-02 11:15:45 +02:00
Stephen Abello
a4166f874e N°8274 - Allow to recolor illustrations in portal 2025-04-01 16:28:40 +02:00
Stephen Abello
96382377ee N°8274 - Polishing new look portal (#707)
* Remove precompiled CSS files

* Fix mosaic design, polish mosaic and treeview hover colors

* Fix modal close button not being visible, clean modal code

* Fix mandatory and tooltip indicator on fields

* Fix treeview filter data display

* N°8274 - Improve profile brick display

* N°8274 - Use ipb alert instead of bootstrap alerts

* N°8274 - Make sure dropdown items use all available space

* N°8274 - Remove class name from the object brick title and add it to a separate title complement

* N°8274 - Polishing new look portal
- La police des informations dans l'annonce est trop petite (Quitte a agrandir le bloc information pour ne pas perdre de ligne d'affichage)

* N°8274 - Add visual indicator to selected tab

* N°8274 - Set the icon url to home page if it's not customized

* N°8274 - Polishing new look portal
- fix manage bricks pills click event

* N°8274 - Make panel title and tab text bigger

* N°8274 - Make datatables more similar to backoffice

* Add precompiled stylesheets

---------

Co-authored-by: Benjamin <benjamin.dalsass@combodo.com>
2025-03-31 15:41:55 +02:00
Stephen Abello
84708fb327 Merge branch 'support/3.2' into develop 2025-03-26 09:31:38 +01:00
Stephen Abello
bf8269fee1 N°8293 - Make multiple modal in portal correctly stack the backdrops 2025-03-26 09:26:04 +01:00
Romain Quetiez
02adca0a1f 🎨 N°8231 Code format and comments 2025-03-25 11:48:23 +01:00
jf-cbd
90370fce3b Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/composer.json
2025-03-24 11:45:49 +01:00
jf-cbd
c99995563e Merge remote-tracking branch 'origin/support/3.2.1' into support/3.2
# Conflicts:
#	lib/composer/autoload_classmap.php
#	lib/composer/autoload_static.php
2025-03-24 11:31:25 +01:00
jf-cbd
7af1cdebfa Workaround for N°4459 doesn't work on 3.2 2025-03-24 11:21:41 +01:00
jf-cbd
a9fb5cd182 N°7870 - Tests: access the symfony service container to instantiate services 2025-03-24 11:02:34 +01:00
jf-cbd
9010504902 Fix tests 2025-03-24 10:53:18 +01:00
jf-cbd
2625b1ab54 Update tests for 3.1 2025-03-24 10:52:47 +01:00
Molkobain
b967338c48 🐛 N°8278 - Avoid autoloaders class name conflict by ensuring all existing have a unique name (#704)
* 🐛 Avoid autoloaders class name conflict by ensuring all existing have a unique name

* 🐛 Redump itop-attachments autoloaders for correct suffix
2025-03-24 10:15:09 +01:00
jf-cbd
10fbea5f4d Fix merge conflict 2025-03-24 10:09:33 +01:00
jf-cbd
e49b8fc480 Merge remote-tracking branch 'origin/support/2.7' into support/3.2.1
# Conflicts:
#	core/restservices.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php
#	lib/composer/autoload_classmap.php
#	lib/composer/autoload_static.php
2025-03-24 10:07:36 +01:00
jf-cbd
4a415fdb59 N°8215 - When PHP warning are enabled, Global Request doesn't work 2025-03-21 14:44:18 +01:00
jf-cbd
5232694c04 N°8281 - Unable to click search button in multiple request template 2025-03-21 11:20:37 +01:00
Eric Espie
2a87d0aa15 N°8242 - When editing a dashboard the parameters do not refresh as expected 2025-03-18 17:40:10 +01:00
Anne-Cath
a103c458cb N°8272 - Portal : Unable to save a date in French format - rollback removed code for N°7145 2025-03-18 16:58:50 +01:00
jf-cbd
fe913524fd Merge remote-tracking branch 'origin/support/3.2' into develop 2025-03-13 16:04:19 +01:00
Tommaso Rossi
9a895a7fbd 🐛 N°8115 - Unattended Install: unable to connect to MySQL with TLS (#694)
🐛 Support TLS connections to MySQL in unattended-install process
2025-03-13 15:59:04 +01:00
jf-cbd
9f2375999a Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	tests/php-unit-tests/unitary-tests/core/DBObject/DBObjectTest.php
2025-03-13 15:57:15 +01:00
jf-cbd
f5011bb200 N°8260 - Change format of REST logs when they are close to the SQL field size limit 2025-03-13 15:39:00 +01:00
jf-cbd
29c75f626b N°8259 - Problem with GetMaxSize on AttributeText 2025-03-13 15:29:00 +01:00
jf-cbd
4b86639d71 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-03-12 11:55:15 +01:00
jf-cbd
0562563cbb Dump autoloader 2025-03-12 11:51:59 +01:00
jf-cbd
40068bd913 Merge remote-tracking branch 'origin/support/2.7' into support/3.2
# Conflicts:
#	lib/composer/autoload_classmap.php
#	lib/composer/autoload_static.php
2025-03-12 11:47:33 +01:00
jf-cbd
874a5fd2ce Dump autoloader 2025-03-12 11:36:06 +01:00
Stephen Abello
911a204f37 N°4685 - Fix padding in browse brick's treeview toolbar 2025-03-07 10:39:42 +01:00
jf-cbd
69f9a0b369 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-03-06 16:25:43 +01:00
jf-cbd
1ec139782e Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2025-03-06 16:24:40 +01:00
jf-cbd
056dce4d78 Merge remote-tracking branch 'origin/support/2.7' into support/3.1
# Conflicts:
#	webservices/rest.php
2025-03-06 16:12:15 +01:00
jf-cbd
063bb9680e N°8231 - Better variable fallback 2025-03-06 16:09:51 +01:00
jf-cbd
25e8ec38d6 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-03-06 15:31:19 +01:00
jf-cbd
9b1395db03 Workaround for N°4459 doesn't work on 3.2 2025-03-06 15:23:38 +01:00
jf-cbd
8fd9eb6a84 Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2025-03-06 14:58:24 +01:00
jf-cbd
1142bf327c Fix tests 2025-03-06 14:54:18 +01:00
jf-cbd
278496eaf6 Update tests for 3.1 2025-03-06 14:40:39 +01:00
jf-cbd
77ba0b398f Fix merge conflict 2025-03-06 12:11:20 +01:00
jf-cbd
04ca7bf603 Merge remote-tracking branch 'origin/support/2.7' into support/3.1
# Conflicts:
#	core/restservices.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php
#	tests/php-unit-tests/unitary-tests/core/Delta/delta_test_sanitize_output.xml
#	tests/php-unit-tests/unitary-tests/core/RestServicesSanitizeOutputTest.php
#	tests/php-unit-tests/unitary-tests/core/RestServicesTest.php
#	webservices/rest.php
2025-03-06 12:10:00 +01:00
jf-cbd
8f8ac46f55 N°8215 - When PHP warning are enabled, Global Request doesn't work 2025-03-06 11:59:08 +01:00
denis.flaven@combodo.com
07b904ee1b N°8231 - making rest api logs more readable 2025-03-06 11:59:08 +01:00
Stephen Abello
1574993a28 N°4685 - Update precompiled stylesheets 2025-03-06 10:35:51 +01:00
Stephen Abello
43120bfb60 N°4685 - Make user card display nicer when user's name is short 2025-03-06 10:31:11 +01:00
Stephen Abello
9c1a1d08a1 N°4685 - Fix navigation menu hover having the same color as regular background 2025-03-06 10:30:53 +01:00
Stephen Abello
f095f93326 N°4684 - Align portal default theme with backoffice one (#702)
* Prepare SCSS files and move nav menu / main wrapper from Bootstrap positioning

* Small work on navigation menu

* Split bootstrap theme file into multiple files inside themes/

* Fix unit test missing css/ import path

* Better display for usercard

* Upload precompiled portal stylesheets to fix unit test based on portal.css

* Polish menu

* Stylize home tiles

* Stylize home tiles and layout pages

* Stylize home tiles and layout pages

* Stylize home tiles and layout pages

* Stylize home tiles and layout pages

* Define default font in a more elegant way

* Small implementation for open/close navigation_menu

* Fix navigation menu dropdown menu not working

* Fix menu colors

* Set  <html> lang attribute

* Add accessibility attributes to menu toggler

* Fix bricks / page title dot spacing

* new look adaptation

* Fix padding in manage brick

* Fix menu entries font size and color

* Change manage export color

* Fix icon size in tiles

* Add style to manage brick panels

* Redesign browse brick mosaic view

* Fix variable name collision

* - Set templates cache in dev mode to 1s
- Implements components classes JS
- Move navigations layouts outside the global layout
- Update tiles

* Fix tile description font size

* Redesign browse brick tree mode

* Tweak navigation menu css

* - use custom elements for js components
- adjust layouts

* Modify forms/modals

* Modify method name following code review

* Add a dropdown element to replace bootstrap one

* improvement to dropdown

* datatable prevent column sort icon to wrap

* update composer json file.
Without classmap-authoritative flag, classmap are not generated
There is no test folder

* remove colored circle in manage brick tile titles

* remove white span between title and title additional part in brick layout

* convert navigation menu js to custom element

* navigation menu (WIP)

* Improvement to dropdown

* Fix some caselog classes

* Improvement to dropdown

* Improvement to dropdown

* navigation menu (WIP)

* portal ui version  2025

* datatable sort icon issue on link sets

* portal ui settings

* Fix dropdown for browse brick

* add portal scss colors

* add alerts scss=

* Buttons improvement

* Correction list table action issue

* responsive adjustments

* restore ben-j erased stephen

* ipb-button integration

* remove table header bottom border

* remove brick page ipb-page--main-header duplicate

* Adjust button styel

* Fix browse brick buttons

* Correctly overload approot and fix scsss imports

* Fix treeview expand buttons, fix mosaic first display glitch and add animation to each mosaic tile drawn

* Fix treeview toolbar

* remove wrap from ipb-button-groups

* Fix hover and clickable space in mosaics

* Clean dropdown css

* Fix dropdown menu content for a better display

* Align pagination buttons with theme buttons

* Align pagination buttons with theme buttons

* Fix drowdown

* Add precompiled stylesheets for merge to main branch

---------

Co-authored-by: Benjamin Dalsass <95754414+bdalsass@users.noreply.github.com>
2025-03-05 14:31:00 +01:00
Eric Espie
c9c3b6c108 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-03-05 11:15:54 +01:00
Eric Espie
5aee7f4722 N°8242 - When editing a dashboard the parameters do not refresh as expected 2025-03-05 11:02:19 +01:00
Eric Espie
292701b71c N°8242 - When editing a dashboard the parameters do not refresh as expected 2025-03-04 16:51:58 +01:00
jf-cbd
5caddf81b8 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-03-03 17:08:14 +01:00
jf-cbd
533b57ab99 Merge branch 'support/3.1' into support/3.2 2025-03-03 17:07:24 +01:00
jf-cbd
be8d348b25 N°8231 - Update tests 2025-03-03 16:49:27 +01:00
jf-cbd
6c7a98fe3d Merge remote-tracking branch 'origin/support/2.7' into support/3.1 2025-03-03 16:38:32 +01:00
denis.flaven@combodo.com
ec2203229b N°8231 - making rest api log more readable 2025-03-03 16:23:25 +01:00
jf-cbd
73fe07a745 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-03-03 14:38:35 +01:00
jf-cbd
2f699d355a Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2025-03-03 14:37:57 +01:00
jf-cbd
da4457f5b4 Merge remote-tracking branch 'origin/support/2.7' into support/3.1 2025-03-03 14:36:50 +01:00
denis.flaven@combodo.com
97848cea4f N°8231 - making rest api log more readable 2025-03-03 11:38:52 +01:00
jf-cbd
1760727879 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-02-25 11:40:02 +01:00
jf-cbd
2ccd883c9a 👥 Update contributor list 2025-02-25 11:38:54 +01:00
jf-cbd
eabd68ff77 👥 Update contributor list 2025-02-25 11:38:14 +01:00
jf-cbd
d982652228 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-02-25 09:22:04 +01:00
jf-cbd
2b493787b1 Update itop-version-history.md with 2.7.12, 3.1.3 and 3.2.1 2025-02-25 09:19:47 +01:00
Timothee
1d26cac8e3 Fix translations 2025-02-21 18:23:31 +01:00
Timothee
cc47d27fd8 Merge branch 'support/3.2' into develop
# Conflicts:
#	datamodels/2.x/itop-attachments/dictionaries/cs.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/dictionaries/da.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/dictionaries/de.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/dictionaries/es_cr.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/dictionaries/fr.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/dictionaries/hu.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/dictionaries/it.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/dictionaries/ja.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/dictionaries/nl.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/dictionaries/pl.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/dictionaries/pt_br.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/dictionaries/ru.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/dictionaries/sk.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/dictionaries/tr.dict.itop-attachments.php
#	datamodels/2.x/itop-attachments/dictionaries/zh_cn.dict.itop-attachments.php
#	datamodels/2.x/itop-config-mgmt/dictionaries/it.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config/dictionaries/it.dict.itop-config.php
#	datamodels/2.x/itop-incident-mgmt-itil/dictionaries/it.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/dictionaries/it.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-portal/dictionaries/it.dict.itop-portal.php
#	datamodels/2.x/itop-problem-mgmt/dictionaries/it.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-request-mgmt-itil/dictionaries/it.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/dictionaries/it.dict.itop-request-mgmt.php
#	dictionaries/cs.dictionary.itop.core.php
#	dictionaries/da.dictionary.itop.core.php
#	dictionaries/de.dictionary.itop.core.php
#	dictionaries/es_cr.dictionary.itop.core.php
#	dictionaries/fr.dictionary.itop.core.php
#	dictionaries/fr.dictionary.itop.ui.php
#	dictionaries/hu.dictionary.itop.core.php
#	dictionaries/it.dictionary.itop.core.php
#	dictionaries/it.dictionary.itop.ui.php
#	dictionaries/ja.dictionary.itop.core.php
#	dictionaries/nl.dictionary.itop.core.php
#	dictionaries/pl.dictionary.itop.core.php
#	dictionaries/pt_br.dictionary.itop.core.php
#	dictionaries/ru.dictionary.itop.core.php
#	dictionaries/sk.dictionary.itop.core.php
#	dictionaries/tr.dictionary.itop.core.php
#	dictionaries/ui/components/quick-create/cs.dictionary.itop.quick-create.php
#	dictionaries/ui/components/quick-create/da.dictionary.itop.quick-create.php
#	dictionaries/ui/components/quick-create/de.dictionary.itop.quick-create.php
#	dictionaries/ui/components/quick-create/es_cr.dictionary.itop.quick-create.php
#	dictionaries/ui/components/quick-create/fr.dictionary.itop.quick-create.php
#	dictionaries/ui/components/quick-create/hu.dictionary.itop.quick-create.php
#	dictionaries/ui/components/quick-create/it.dictionary.itop.quick-create.php
#	dictionaries/ui/components/quick-create/ja.dictionary.itop.quick-create.php
#	dictionaries/ui/components/quick-create/nl.dictionary.itop.quick-create.php
#	dictionaries/ui/components/quick-create/pl.dictionary.itop.quick-create.php
#	dictionaries/ui/components/quick-create/pt_br.dictionary.itop.quick-create.php
#	dictionaries/ui/components/quick-create/ru.dictionary.itop.quick-create.php
#	dictionaries/ui/components/quick-create/sk.dictionary.itop.quick-create.php
#	dictionaries/ui/components/quick-create/tr.dictionary.itop.quick-create.php
2025-02-21 17:58:13 +01:00
jf-cbd
d74c850621 N°7870 - Tests: access the symfony service container to instantiate services 2025-02-21 15:28:38 +01:00
jf-cbd
4e575e17a3 Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2025-02-21 13:23:57 +01:00
jf-cbd
94d6eca0c1 N°8215 - When PHP warning are enabled, Global Request doesn't work 2025-02-21 13:20:27 +01:00
jf-cbd
355da8ec0a Merge remote-tracking branch 'origin/support/2.7' into support/3.1
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php
2025-02-21 13:18:19 +01:00
jf-cbd
5f006c45db N°8215 - When PHP warning are enabled, Global Request doesn't work 2025-02-21 13:16:07 +01:00
jf-cbd
1e5334ab2b Update comment on logout.html.twig 2025-02-11 09:43:33 +01:00
Anne-Cath
05419f3d7f N°7216 import improves error handling missing or null data - fix test 2025-02-07 22:59:59 +01:00
Eric Espie
a9bc4973b4 N°8139 - Fix ApplyStimulus during post update 2025-02-07 17:24:43 +01:00
Timothee
e93d909a38 🌐 Dictionaries update 2025-02-07 15:36:58 +01:00
jf-cbd
534b087a2c N°8163 - Increase page redirection timeout on logout 2025-02-07 14:36:01 +01:00
Eric Espie
14346e0895 N°8139 - Avoid double writing in lifecycle action 2025-02-07 11:10:14 +01:00
Eric Espie
6985e366c2 N°8139 - Avoid double writing in lifecycle action 2025-02-07 10:58:13 +01:00
denis.flaven@combodo.com
e85ec6bb47 Merge branch 'support/3.2' into develop 2025-02-07 10:36:32 +01:00
denis.flaven@combodo.com
cf57549370 Merge branch 'support/3.1' into support/3.2 2025-02-07 10:29:24 +01:00
denis.flaven@combodo.com
46aaeb4301 Merge branch 'support/2.7' into support/3.1 2025-02-07 10:24:18 +01:00
Eric Espie
7ddc593869 🔊 enhance crud logs 2025-02-07 10:14:39 +01:00
denis.flaven@combodo.com
affed69999 Version number bump. 2025-02-07 10:09:48 +01:00
Anne-Cath
533fe0ae01 N°7216 import improves error handling missing or null data - case if an ext key use more than 1 field for reconciliation add test 2025-02-06 17:43:18 +01:00
Anne-Cath
efd117eac7 N°7216 import improves error handling missing or null data - case if an ext key use more than 1 field for reconciliation 2025-02-06 17:28:47 +01:00
jf-cbd
01a17b5f9b Merge branch 'support/3.2' into develop 2025-02-06 17:13:58 +01:00
jf-cbd
695f357054 N°8170 - Issue with Ckeditor and precanned reply
N°8152 - CKEditor : Style and list button are the same
2025-02-06 17:11:53 +01:00
Eric Espie
0088580798 Better specific delta detection for tests 2025-02-05 17:40:57 +01:00
Ben
92d49c6c47 Adding new modules 2025-02-04 11:56:57 +01:00
Ben
f6298370a7 Adding new modules 2025-02-04 11:56:32 +01:00
jf-cbd
623d2823ff Fix merge conflict 2025-02-04 11:29:19 +01:00
jf-cbd
5d5849b724 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php
2025-02-04 10:52:18 +01:00
jf-cbd
bd6ccc55f8 N°8150 - Better picture checking 2025-02-04 10:49:54 +01:00
XGUI
3a497524dc N°8129 - Fix unexpected identifier self 2025-02-04 10:48:28 +01:00
XGUI
902e2259a9 N°8129 - Remove duplicated test method 2025-02-04 10:33:35 +01:00
XGUI
b87fb65ac4 Merge branch 'support/3.2' into develop
# Conflicts:
#	core/attributedef.class.inc.php
#	datamodels/2.x/version.xml
#	tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php
#	tests/php-unit-tests/unitary-tests/core/AttributeDefinitionTest.php
2025-02-04 10:14:41 +01:00
denis.flaven@combodo.com
4c1da328ce Merge support/3.1 into support/3.2 2025-01-31 17:20:33 +01:00
denis.flaven@combodo.com
64a216e0f6 Merge branch 'support/2.7' into support/3.1 2025-01-31 17:13:09 +01:00
denis.flaven@combodo.com
d5754fc568 N°8135 - Bump datamodel version. 2025-01-31 17:04:56 +01:00
Tommaso Rossi
ab929571c7 🐛 N°8115 - Unattended Install: unable to connect to MySQL with TLS (#694)
🐛 Support TLS connections to MySQL in unattended-install process
2025-01-31 14:45:52 +01:00
Anne-Catherine
8e0e01ad40 N°7216 import improves error handling missing or null data (#612)
Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>
2025-01-30 16:14:14 +01:00
Anne-Catherine
8a604c643e N°7371 - Remembers the choice done in the popup that appears when saving public and private logs at the same time (#636) 2025-01-30 15:55:34 +01:00
XGUI
5851b2e1ff N°8129 - Fix missing namespace backslash 2025-01-30 15:06:00 +01:00
XGUI
803ebfbe33 N°8129 - Dont crash if date/time default value has a bad format 2025-01-30 14:14:31 +01:00
odain
d12c8a183c N°8143 - Setup page in error in support.combodo.com 2025-01-30 11:16:07 +01:00
Anne-Cath
7bbd4b4726 N°8144 - Issue using Organization filtering box 2025-01-30 10:10:36 +01:00
odain
c8754725b0 N°8143 - Setup page in error in support.combodo.com 2025-01-29 16:42:43 +01:00
XGUI
f1594ad974 Merge remote-tracking branch 'origin/develop' into develop 2025-01-29 15:58:06 +01:00
XGUI
805e208e32 Merge branch 'issue/8129-dont_crash_if_datetime_default_value_has_bad_format' into develop 2025-01-29 15:56:50 +01:00
XGUI
9e3f99a150 N°8129 - Dont crash if date/time default value has a bad format 2025-01-29 15:55:20 +01:00
XGUI
e9d21bca39 N°8129 Handle badly formatted default date (e.g. "27/01/2025") 2025-01-29 15:35:26 +01:00
XGUI
807283505d N°8129 - Dont crash if date/time default value has a bad format 2025-01-29 11:08:21 +01:00
Eric Espie
71244e5c15 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-01-29 09:37:10 +01:00
Eric Espie
a3c911e93e N°8139 - Avoid double writing in lifecycle action 2025-01-29 09:32:21 +01:00
Eric Espie
72e0750c1b Merge remote-tracking branch 'origin/support/3.2' into develop 2025-01-28 11:13:05 +01:00
Eric Espie
38e0fb27f1 N°8131 - Issue on DBlinkchange event when object is deleted 2025-01-28 11:12:11 +01:00
Eric Espie
4b4197d910 N°8131 - Issue on DBlinkchange event when object is deleted 2025-01-28 11:09:31 +01:00
jf-cbd
ac8004561b Merge remote-tracking branch 'origin/support/3.2' into develop 2025-01-28 10:53:15 +01:00
jf-cbd
c5189b7d33 N°8134 - Fix import after merge 2025-01-28 10:52:29 +01:00
jf-cbd
b0288bc08e Merge remote-tracking branch 'origin/support/3.1' into support/3.2
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/src/Controller/UserProfileBrickController.php
2025-01-28 10:44:35 +01:00
Eric Espie
52097f5e12 N°8131 - Issue on DBlinkchange event when object is deleted 2025-01-28 10:33:11 +01:00
jf-cbd
ccb1ca9d79 Merge remote-tracking branch 'origin/support/2.7' into support/3.1 2025-01-28 10:32:18 +01:00
jf-cbd
44290db312 N°8134 - Portal user profile is broken, regression from 7776 2025-01-28 10:23:44 +01:00
Eric Espie
65e49e2139 N°8139 - Avoid double writing in lifecycle action 2025-01-27 17:34:46 +01:00
Eric Espie
b1bf89807d Add CRUD logs 2025-01-27 15:06:11 +01:00
Eric Espie
766d3b51ad Merge remote-tracking branch 'origin/support/3.2' into develop 2025-01-24 14:36:14 +01:00
Eric Espie
39c59f46fd Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2025-01-24 14:35:48 +01:00
Eric Espie
025af923ea N°8131 - Issue on DBlinkchange event when object is delete 2025-01-24 14:23:50 +01:00
Eric Espie
3215bffbcb Merge remote-tracking branch 'origin/support/3.2' into develop 2025-01-24 12:07:13 +01:00
Eric Espie
c2aecad9d2 Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2025-01-24 12:06:49 +01:00
v-dumas
986e900a99 N°7145 - Inline & Attachment add default expire for MailToTicket 2025-01-24 12:03:16 +01:00
Eric Espie
858b12abaa N°8131 - Issue on DBlinkchange event when object is delete 2025-01-24 11:59:51 +01:00
Stephen Abello
bd93879ac4 Add yellow and purple SCSS variables 2025-01-23 11:27:36 +01:00
Timmy38
133a4a8533 N°8113 Fix global organization filter (#699) 2025-01-23 10:04:02 +01:00
Eric Espie
51bb0add8e Merge remote-tracking branch 'origin/support/3.2' into develop 2025-01-22 17:11:13 +01:00
Eric Espie
1163a20cdd Merge remote-tracking branch 'origin/support/3.1' into support/3.2
# Conflicts:
#	application/cmdbabstract.class.inc.php
2025-01-22 16:44:12 +01:00
Eric Espie
a7bc4bd411 Add new measure points for KPI logger 2025-01-22 16:34:31 +01:00
Benjamin Dalsass
6f3fddf9bf N°8122 - View or edit an object in portal in a new tab no more possible 2025-01-22 14:15:23 +01:00
jf-cbd
4c8392b332 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-01-21 17:43:35 +01:00
jf-cbd
470f145747 N°6284 - fix regex 2025-01-21 17:41:53 +01:00
Timmy38
2852d8ce49 N°7662 Allowing indentation and de-indentation (#698) 2025-01-21 17:12:43 +01:00
jf-cbd
fb180e8370 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-01-21 17:03:52 +01:00
jf-cbd
7e9a95fe93 Merge remote-tracking branch 'origin/support/3.1' into support/3.2
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/src/Controller/UserProfileBrickController.php
2025-01-21 16:55:04 +01:00
jf-cbd
a07f66c061 Merge remote-tracking branch 'origin/support/2.7' into support/3.1 2025-01-21 16:48:33 +01:00
jf-cbd
c49ceae75e Fix HandleForm call 2025-01-21 16:46:15 +01:00
jf-cbd
2dd1224219 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-01-21 13:50:19 +01:00
jf-cbd
80244e2797 Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2025-01-21 13:48:12 +01:00
jf-cbd
4da975cb64 Merge remote-tracking branch 'origin/support/2.7' into support/3.1 2025-01-21 12:15:06 +01:00
jf-cbd
8980f627e9 Fix format 2025-01-21 12:09:06 +01:00
jf-cbd
5ce1788d4a N°7776 remove twig from ajax calls, 3.2 edition 2025-01-20 16:34:46 +01:00
jf-cbd
38fe31c9fc Merge remote-tracking branch 'origin/support/3.1' into support/3.2
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/src/Controller/UserProfileBrickController.php
#	datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php
2025-01-20 16:33:35 +01:00
jf-cbd
ec61b52238 N°7776 remove twig from ajax calls, 3.1 edition 2025-01-20 16:02:26 +01:00
jf-cbd
072596a53b Merge remote-tracking branch 'origin/support/2.7' into support/3.1
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php
2025-01-20 15:53:34 +01:00
jf-cbd
160bfd714b N°7776 remove twig from ajax calls 2025-01-20 15:41:22 +01:00
Eric Espie
3b51124f1c N°7927 - No context tag available on designer MTP 2025-01-17 10:06:20 +01:00
jf-cbd
e7b87128b1 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-01-16 17:18:02 +01:00
jf-cbd
19fa836758 Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2025-01-16 17:16:31 +01:00
jf-cbd
1c5cb1547f Merge remote-tracking branch 'origin/support/2.7' into support/3.1 2025-01-16 17:15:12 +01:00
jf-cbd
8d58372074 Update unattended installation script documentation 2025-01-16 17:13:26 +01:00
Eric Espie
e98c6637ac N°8108 - EVENT_DB_AFTER_WRITE: $oEventData->Get('changes') missing previous values 2025-01-16 09:31:12 +01:00
odain
7d2a9d0bfc N°8066 - Polishing Webhook Oauth 2025-01-15 17:26:12 +01:00
odain
762d1162ca N°8066 - Polishing Webhook Oauth 2025-01-15 16:59:51 +01:00
odain
d0bdde30ad Merge branch 'support/3.2' into develop 2025-01-14 08:34:41 +01:00
odain
95dbe4c859 N°7873 - ci fix remove useless require_once 2025-01-14 07:52:07 +01:00
Benjamin Dalsass
3fe8b6c696 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-01-13 15:01:55 +01:00
Benjamin Dalsass
ec6adee9c1 Remove useless require_once and replace it with inheritance 2025-01-13 14:32:06 +01:00
Benjamin Dalsass
baa8bba926 N°8031 - Make all portal bricks use custom templates for all templates
add missing templates
2025-01-13 12:50:58 +01:00
v-dumas
8dc5411717 N°7906 - User Preference: friendlyname, list, search criteria and reconciliation 2025-01-13 12:08:14 +01:00
Tommaso Rossi
6539687f16 🌐 Italian translation enhancements (#697) 2025-01-13 12:07:33 +01:00
Benjamin Dalsass
f1d448fd78 N°8031 - Make all portal bricks use custom templates for all templates
add missing templates
2025-01-13 11:53:44 +01:00
odain
a4490e2b5f N°7873 - Portal: Brick visible despite XML security tag allowed profiles 2025-01-13 11:06:27 +01:00
Eric Espie
e0b12144cb Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	sources/Application/UI/Base/Layout/ActivityPanel/ActivityPanel.php
2025-01-13 09:18:00 +01:00
odain
efb7831a5a N°7111 - False link at the end of the setup 2025-01-13 08:30:23 +01:00
Benjamin Dalsass
9fadbb5eb1 N°8031 - Make all portal bricks use custom templates for all templates (#696)
Service implementation
2025-01-13 08:04:49 +01:00
Romain Quetiez
93dba0644d N°5791 - Handle NOT IN and NOT LIKE too 2025-01-10 16:21:12 +01:00
XGUI
ec324bb28e N°7746 - Caselog edition button active even if the user has a read-only access to the object 2025-01-10 11:12:03 +01:00
Eric Espie
dc8f521b12 N°7145 - UI - Init Value DateTime and Date with Day Time (Add default values on mandatory dates) 2025-01-09 11:56:55 +01:00
Eric Espie
bf6277fcd2 N°7145 - UI - Init Value DateTime and Date with Day Time (support raw date values) 2025-01-09 11:22:28 +01:00
Eric Espie
886db5d6ad N°7145 - UI - Init Value DateTime and Date with Day Time (use OQL syntax for default value) 2025-01-09 10:49:35 +01:00
Eric Espie
79a46581ef Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	tests/php-unit-tests/src/BaseTestCase/ItopDataTestCase.php
2025-01-07 13:31:49 +01:00
Eric Espie
0e8ddf990c N°7145 - UI - Init Value DateTime and Date with Day Time (Fix Event class) 2025-01-07 13:21:13 +01:00
Eric Espie
346a8eadec N°5791 - Obsolescence condition containing IN makes object creation fail 2025-01-07 11:29:33 +01:00
Eric Espie
6948c594c2 N°7145 - UI - Init Value DateTime and Date with Day Time 2025-01-07 10:18:23 +01:00
Eric Espie
301a7a92a0 N°7145 - UI - Init Value DateTime and Date with Day Time 2025-01-06 16:52:32 +01:00
Eric Espie
73bb80ebea N°7206 - Force DBUpdate() when a transition is asked, and it leads to the same state 2025-01-06 11:31:36 +01:00
Timmy38
1e674d7bdc N°7383 Show data table even when there is no data after filtering (#695) 2025-01-06 11:06:18 +01:00
v-dumas
1225ee1e78 improve test 2025-01-03 17:18:24 +01:00
v-dumas
a91de9fb36 Add helpers for stopwatches manipulation 2025-01-03 17:04:41 +01:00
v-dumas
71b3a415a6 N°7206 - Add tests 2025-01-03 16:17:51 +01:00
Romain Quetiez
b97c7433c8 🎨 N°7633 Cosmetics on unit tests 2025-01-03 11:57:51 +01:00
jf-cbd
5a59d16c99 Merge remote-tracking branch 'origin/support/3.2' into develop 2025-01-02 09:34:32 +01:00
jf-cbd
11fc958a7b 🐛 N°6284 - Add data-object-key 2024-12-31 15:01:36 +01:00
odain
a63a3d3d9c Merge branch 'support/3.2' into develop 2024-12-27 11:14:21 +01:00
odain
a151e40b75 Merge branch 'support/3.1' into support/3.2 2024-12-27 11:13:50 +01:00
odain
5780f26817 N°7810: fix merge 2024-12-27 11:13:31 +01:00
odain
ed6e4f612a Merge branch 'support/3.2' into develop 2024-12-27 09:24:49 +01:00
odain
9a690861a3 Merge branch 'support/3.1' into support/3.2 2024-12-27 09:24:33 +01:00
odain
343f3286b8 Merge branch 'support/2.7' into support/3.1 2024-12-27 09:08:47 +01:00
Eric Espie
37fc1a5723 N°7810 - security hardening 2024-12-27 09:04:28 +01:00
Eric Espie
16279233e0 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-12-26 14:54:14 +01:00
Eric Espie
7a09b3effc 🐛 Don't display create mailbox if the corresponding class does not exist 2024-12-24 15:05:48 +01:00
Romain Quetiez
4f6d514694 N°8001 - Refactoring: instantiation of an ormDocument moved to... ormDocument 2024-12-20 10:36:25 +01:00
Stephen Abello
07964cc404 Merge branch 'support/3.2' into develop 2024-12-20 10:16:11 +01:00
Stephen Abello
8f8b65a71d N°8031 - Add robustness if portal configuration doesn't go through 2024-12-20 10:15:22 +01:00
Timothee
49e72e83fe N°6282 Fix XSS vulnerability in wsdl 2024-12-19 15:34:43 +01:00
Stephen Abello
ea5473ad77 Merge branch 'support/3.2' into develop 2024-12-19 15:16:51 +01:00
Stephen Abello
f0685e33e1 N°8031 - Add robustness and fix SF parameter binding being ambiguous in extensions bricks 2024-12-19 15:14:46 +01:00
Tommaso Rossi
2a888cf5af 🌐 Update IT IT dictionaries (#693) 2024-12-19 14:13:13 +01:00
Timmy38
42f391472b N°6282 Fix XSS vulnerability in soap (#690)
* N°6282 Fix XSS vulnerability in soap
2024-12-19 11:39:00 +01:00
Eric Espie
30ef2735b6 N°8001 - TriggerOnObjectMention : load of the icon failed 2024-12-18 16:03:39 +01:00
Stephen Abello
548131482a Merge branch 'support/3.2' into develop 2024-12-18 11:10:43 +01:00
Stephen Abello
bbff0b72d3 N°8031 - Make all portal bricks use custom templates for all templates (#691)
* N°8031 - Make all portal bricks use custom templates for all templates

* Rename parameter following code review

* Add const variables following code review

* Modify method name following code review
2024-12-18 11:09:39 +01:00
Stephen Abello
5ac6c84813 N°8031 - Make all portal bricks use custom templates for all templates (#691)
* N°8031 - Make all portal bricks use custom templates for all templates

* Rename parameter following code review

* Add const variables following code review

* Modify method name following code review
2024-12-18 11:03:52 +01:00
jf-cbd
0c3a35fc43 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-12-18 10:49:22 +01:00
jf-cbd
2dffab9ca0 🌐 Update ZH CN dictionaries 2024-12-18 10:39:25 +01:00
jf-cbd
881dbd3c01 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-12-18 10:12:13 +01:00
jf-cbd
1cbfa4f1b1 🌐 Update ZH CN dictionaries 2024-12-18 10:11:52 +01:00
jf-cbd
81887f480a 🌐 Update ZH CN dictionaries 2024-12-18 10:08:55 +01:00
odain
d25bf4ef66 Merge branch 'support/3.2' into develop 2024-12-17 17:32:17 +01:00
odain-cbd
5f85757630 N°7633 - Reloads the same user multiple times if it no longer exists (#692)
* N°7633 - Reloads the same user multiple times if it no longer exists

* Update core/userrights.class.inc.php

good catch

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

* Update core/userrights.class.inc.php

good catch (again)

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

* Update core/userrights.class.inc.php

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

* PR feedbacks from Romain

* ci: rename user logins

---------

Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>
2024-12-17 17:27:49 +01:00
jf-cbd
90f58721b1 🌐 Update ZH CN dictionaries 2024-12-17 17:21:55 +01:00
v-dumas
56d3ac668c N°8047 - SuperUser profile moved under iTop Community 3.2.1 2024-12-17 16:31:59 +01:00
Eric Espie
b20b4b7e82 Revert GetDataPath() usage 2024-12-17 15:53:57 +01:00
jf-cbd
eeabad895d 🌐 Update EN dic 2024-12-16 18:48:48 +01:00
jf-cbd
9fdf183851 Merge branch 'support/3.2' into develop
# Conflicts:
#	dictionaries/ui/components/quick-create/zh_cn.dictionary.itop.quick-create.php
#	dictionaries/zh_cn.dictionary.itop.core.php
#	setup/runtimeenv.class.inc.php
2024-12-16 18:38:07 +01:00
jf-cbd
0a3b02bf45 🌐 Update ZH CN dictionaries 2024-12-16 18:30:22 +01:00
jf-cbd
74ebbc5fa4 🌐 Update ZH CN dictionaries 2024-12-16 18:19:17 +01:00
Eric Espie
a762b6a2bb Fix failing directory removal 2024-12-16 17:04:33 +01:00
Eric Espie
32ef639ce1 N°7803 - MTP from itophub/designer failing in itop 3.2.0 2024-12-16 16:44:59 +01:00
Purple Grape
8647a76dbf 🌐 Translations - improved translation for zh cn 20241108 (#679)
* improved translation for zh cn
2024-12-16 11:39:26 +01:00
jf-cbd
96414dcc98 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-12-16 11:07:32 +01:00
jf-cbd
47cd8bce31 Security hardening 2024-12-16 11:05:16 +01:00
jf-cbd
86c677b2ca Merge remote-tracking branch 'origin/support/3.1' into support/3.2
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/src/Controller/ObjectController.php
2024-12-16 11:02:37 +01:00
Eric Espie
960129316d N°8050 - Fix regression due to directories filter changes 2024-12-16 10:56:06 +01:00
jf-cbd
1fa50f695d Security hardening 2024-12-16 10:47:06 +01:00
Anne-Cath
6ab19d29b9 fix tests in all iTop configurations 2024-12-16 10:38:44 +01:00
jf-cbd
692cf4f635 Merge branch 'support/2.7' into support/3.1 2024-12-16 10:27:00 +01:00
jf-cbd
95aa444ee6 Security hardening 2024-12-13 16:48:13 +01:00
jf-cbd
f5de808c7c Security hardening (#685)
* security hardening
2024-12-13 15:09:18 +01:00
Eric Espie
5c5c5d769f Merge remote-tracking branch 'origin/support/3.2' into develop 2024-12-13 10:17:40 +01:00
Eric Espie
fc388313d7 N°8050 - Problems with interface discovery from itop 2024-12-13 10:14:17 +01:00
odain
d544ee5498 N°7750 - ci enhance test 2024-12-13 09:35:35 +01:00
Anne-Cath
ecce0a3376 fix tests 2024-12-13 09:20:19 +01:00
odain
ce187550f6 N°7750 - Fix display regression 2024-12-12 12:21:57 +01:00
Anne-Cath
f8e761abe0 fix tests 2024-12-12 11:45:09 +01:00
Eric Espie
d3a9e30178 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-12-12 09:55:58 +01:00
Eric Espie
5d15a08824 🥅 Fast setup error display 2024-12-12 09:55:16 +01:00
Eric Espie
0fd2cf85a2 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-12-12 08:44:38 +01:00
odain
8135316119 N°7750 - Bug with OQL set as Shortcut 2024-12-11 16:05:15 +01:00
Eric Espie
868c0ae836 N°7997 - Sharing Base incompatible with iTop >= 3.1 (code review) 2024-12-11 15:51:36 +01:00
Eric Espie
d03d4fce5f N°7997 - Sharing Base incompatible with iTop >= 3.1 2024-12-11 13:35:15 +01:00
Eric Espie
aa55c2b30f N°7997 - Sharing Base incompatible with iTop >= 3.1 2024-12-11 13:28:37 +01:00
Eric Espie
9d3b46b919 🥅 Fast setup error display 2024-12-11 11:40:03 +01:00
Eric Espie
01b4dbba71 🥅 Fast setup error display 2024-12-11 11:20:32 +01:00
Eric Espie
72ac4096c1 N°7854 - ⬆️ Bump twig version 2024-12-10 17:08:48 +01:00
Eric Espie
19559b08a7 N°7854 - ⬆️ Bump twig version 2024-12-10 10:52:30 +01:00
Eric Espie
346564ca0e N°7854 - ⬆️ Bump twig version 2024-12-10 10:11:28 +01:00
Anne-Cath
a64fed66a3 N°1681 - Add/fix @since 2024-12-10 09:48:25 +01:00
Anne-Catherine
261106fa9d N°1681 - Add new triggers for attachment creation and removal (#534) 2024-12-10 09:33:20 +01:00
Eric Espie
1556b95653 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-12-09 17:06:40 +01:00
Eric Espie
1bf53bae2a N°7871 - unable to migrate to itop3.2 if a trigger on object mention without "mentioned filter" exists 2024-12-09 17:05:07 +01:00
Anne-Catherine
324cb5eb6c N°7636 - Too many OQL requests executed in order to display a lnk (#661) 2024-12-09 15:15:33 +01:00
Timmy38
eedbf3d266 N°6617 Fix issue when providing invalid dashboard layout class (#689)
N°6617 Fix issue when providing invalid dashboard layout class
2024-12-09 14:10:03 +01:00
Eric Espie
3b197692ec Merge remote-tracking branch 'origin/support/3.2' into develop 2024-12-09 10:40:06 +01:00
Eric Espie
29ce042916 N°7867 - Issue with MTP offline (reset cache in metamodel) 2024-12-09 10:38:50 +01:00
odain
83539d6d4c N°7446 - Make ItopCustomDatamodelTestCase work with modules in production-modules 2024-12-05 11:29:05 +01:00
Eric Espie
e6a7b926f6 N°7852 - Issue on Class groups definition when checking EventNotification class 2024-12-05 11:14:19 +01:00
Eric Espie
b30e053236 N°8020 - Bugs MFA and designer incompatibility 2024-12-03 16:42:27 +01:00
Stephen Abello
ecfc26f1ec Merge branch 'support/3.2' into develop 2024-12-03 10:47:42 +01:00
Stephen Abello
afd96a0f49 N°7995 - Allow to redefine portal twig template for all bricks in a portal (#686)
* N°7995 - Allow to redefine portal twig template for all bricks in a portal

* Apply modifications from code review

* Fix variable name

* Apply changes from code review
2024-12-03 10:44:37 +01:00
Stephen Abello
a34baf840a N°7995 - Allow to redefine portal twig template for all bricks in a portal (#686)
* N°7995 - Allow to redefine portal twig template for all bricks in a portal

* Apply modifications from code review

* Fix variable name

* Apply changes from code review
2024-12-03 10:41:25 +01:00
Eric Espie
6f9bd9bae5 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-12-03 10:31:35 +01:00
Eric Espie
a77765ec7b N°8019 - Enrich event with transition information 2024-12-03 10:28:13 +01:00
jf-cbd
64b4b03ea9 Security hardening 2024-12-03 10:26:11 +01:00
XGUI
c32b2f9dfe Issue/7984 (#688)
* N°7984 - Change ? for field description (tooltip) by 🛈 in console and portal
2024-12-03 10:20:47 +01:00
jf-cbd
a797878b17 Security hardening 2024-12-03 09:54:31 +01:00
jf-cbd
a1860c82fb Merge branch 'support/3.2' into develop 2024-12-02 17:47:10 +01:00
jf-cbd
1fa0f7bdd9 N°8007 - Security hardening 2024-12-02 17:45:39 +01:00
Eric Espie
f718b4173d N°7206 - TriggerOnStateEnter not called when using reassign transition (after review) 2024-12-02 17:28:15 +01:00
Eric Espie
e057c0f081 N°7777 - Click on tab "Last executions" during action creation crashs the form 2024-12-02 17:12:32 +01:00
Eric Espie
5a49fc7654 N°7206 - TriggerOnStateEnter not called when using reassign transition 2024-12-02 14:49:28 +01:00
Stephen Abello
6fca659c9d Fix portal not being able to use $common-* SCSS variables when recompiling stylesheets 2024-12-02 14:06:53 +01:00
Stephen Abello
eacd08f31e Fix SCSS comments type 2024-12-02 14:03:39 +01:00
jf-cbd
d47d65df10 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-11-29 16:49:57 +01:00
jf-cbd
5f7d8f6cc0 Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2024-11-29 16:43:13 +01:00
jf-cbd
cbb4281a37 N°7980 - security hardening 2024-11-29 16:40:34 +01:00
XGUI
b42297ad84 Merge branch 'issue/7726' into develop
# Conflicts:
#	core/datamodel.core.xml
2024-11-29 16:28:50 +01:00
XavierGR
6cb1cf7b7e N°7726 - Fatal error if a newsroom is sent without any message 2024-11-29 16:26:32 +01:00
odain
d7a8d335d5 N°7446 - Fix in CI environment when class already loaded 2024-11-29 12:22:38 +01:00
odain
a65dd1c27c Merge branch 'support/3.2' into develop 2024-11-29 12:09:08 +01:00
odain
bd1d447677 N°7446 - Fix in CI environment 2024-11-29 12:08:16 +01:00
XGUI
d85d611642 Merge branch 'feature/5874' into develop 2024-11-29 08:38:52 +01:00
XavierGR
6a00786535 N° 5874 - Quick create: improvement for newcomer 2024-11-29 08:37:11 +01:00
XGUI
6490fe93a1 Merge branch 'issue/7746' into develop 2024-11-29 08:24:27 +01:00
xavier.guiboud-ribaud@combodo.com
642a13ad0d N°7984 - Change ? for field description (tooltip) by 🛈 in console and portal 2024-11-29 08:23:40 +01:00
xavier.guiboud-ribaud@combodo.com
e1bfe9a3b6 * N°7746 - Caselog edition button active even if the user has a read-only access to the object 2024-11-29 08:23:32 +01:00
XGUI
af8ff9b29f Issue/7707 (#687)
* N°7707 - Newsroom user preference display counter not changeable
2024-11-28 16:02:35 +01:00
Eric Espie
bb405d5173 N°7429 - Create an MFA extension for iTop - Add support for AttributeClassSet 2024-11-27 14:57:51 +01:00
Benjamin Dalsass
fcfdac2844 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-11-27 09:56:35 +01:00
Benjamin Dalsass
4723fc885c Merge remote-tracking branch 'origin/support/3.1' into support/3.2
# Conflicts:
#	application/dashboard.class.inc.php
2024-11-27 09:55:15 +01:00
Benjamin Dalsass
06dcae1dd1 Merge remote-tracking branch 'origin/support/2.7' into support/3.1 2024-11-27 09:50:57 +01:00
Benjamin Dalsass
e03033ce52 N°7219 - Fatal error following dashboard modification when dashboard title contains an é 2024-11-27 09:40:22 +01:00
Timothee
19eae916f0 N°7792 Do not initialize CAS if already started 2024-11-22 09:59:41 +01:00
Romain Quetiez
ce68e270c3 N°7985 Remove OQL grammar ambiguity leading to scary warnings when generating the parser (4 parsing conflicts) 2024-11-21 10:29:45 +01:00
jf-cbd
0d8a20c35e Merge remote-tracking branch 'origin/support/3.2' into develop 2024-11-19 08:50:29 +01:00
Lars Kaltefleiter
596e26a96f 🌐 N°7932 Translations - British English (#677)
* Translation into EN GB
2024-11-18 14:38:53 +01:00
Thomas Casteleyn
1f3780f338 Correctly identify as iTop in cURL requests (#652)
* Correctly identify as iTop in cURL requests (with configuration option)
2024-11-14 15:18:47 +01:00
Eric Espie
85f6195a51 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-11-14 14:34:05 +01:00
Thomas Casteleyn
7e1b1779a9 ️ Add faster way to rename column on the same table (#676)
* Add faster way to rename column on the same table
2024-11-14 14:24:45 +01:00
Eric Espie
ef42a49009 Allow extensions to add extra datamodel for tests 2024-11-14 09:27:57 +01:00
Romain Quetiez
51e5f1e7de N°7962 Suppress warnings emitted by the parser/lexer generators and the runtime OQL lexer, with PHP 8.1+ 2024-11-13 15:28:29 +01:00
Timothee
9cffd17e19 N°7704 Fix missing namespace for constant EVENT_DOWNLOAD_DOCUMENT 2024-11-13 14:39:23 +01:00
Benjamin Dalsass
d95e7168aa Merge branch 'support/3.2' into develop 2024-11-13 13:32:53 +01:00
Benjamin Dalsass
e9f16935b6 N°6613 - Fix user picture pushing invalid data 2024-11-13 13:20:27 +01:00
Timothee
e7488b2c89 N°7721 default configuration parameter 2024-11-12 14:48:58 +01:00
Stephen Abello
8ac4086e71 N°7793 Fix primary color being set to blue due to a mistake 2024-11-12 13:52:03 +01:00
Benjamin Dalsass
6b5273fa1c N°1000 - Portal: filter BrowseBrick result varies if in list or tree (#673)
* N°1000 - Portal: filter BrowseBrick result varies if in list or tree

* N°1000 - Portal: filter BrowseBrick result varies if in list or tree
- replace html separators by css class

* N°1000 - Portal: filter BrowseBrick result varies if in list or tree
- replace html separators by css class

* N°1000 - Portal: filter BrowseBrick result varies if in list or tree
- add alteration comment to Tree List Filter jQuery plugin 1.0
- remove display none to tree-item-filter-data
2024-11-12 11:02:43 +01:00
jf-cbd
a7c22c06af Merge remote-tracking branch 'origin/support/3.2' into develop 2024-11-12 09:37:41 +01:00
jf-cbd
9b1e854bf7 Update translations 2024-11-12 09:27:35 +01:00
denis.flaven@combodo.com
a72d1ca1b3 🔨 Replace gitmojis by their UTF-8 equivalent in output. 2024-11-08 16:08:06 +01:00
Lars Kaltefleiter
926700856d Fix some typos in en translations (#678) 2024-11-08 10:56:10 +01:00
Stephen Abello
be5e4458ba N°7793 Add breaking changes introduced by adding common SCSS variables between backoffice and end-user portal (#675)
* N°7793 Add breaking changes induce by adding common SCSS variables between backoffice and end-user portal

* N°7793 Handle breaking changes in darkmoon theme
2024-11-08 10:04:56 +01:00
Stephen Abello
317cd585b2 Merge branch 'support/3.2' into develop 2024-11-08 09:45:05 +01:00
Stephen Abello
ab93d59a77 Merge branch 'support/3.1' into support/3.2
# Conflicts:
#	sources/Core/Email/EmailLaminas.php
2024-11-08 09:41:48 +01:00
Karel Vlk
c70d62a51e 🐛 N°7916 SF#2274 EmailLaminas.php: Keep charset with part header in multipart email (#672)
* 🐛 N°2274 EmailLaminas.php: Keep charset with part header in multipart email

* Add a unit test

---------

Co-authored-by: Stephen Abello <stephen.abello@combodo.com>
2024-11-08 09:38:57 +01:00
jf-cbd
5d8db176f4 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	dictionaries/es_cr.dictionary.itop.core.php
2024-11-07 14:57:54 +01:00
jf-cbd
82bc2f33f2 Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2024-11-07 14:53:48 +01:00
jf-cbd
0d5ff261fe Merge remote-tracking branch 'origin/support/2.7' into support/3.1 2024-11-07 14:51:32 +01:00
jf-cbd
374b35f78a 🚀 Fix GitHub action 2024-11-07 14:50:46 +01:00
Eric Espie
f5c29edee4 N°7874 - Inline image deleted by garbage collector 2024-11-07 09:24:33 +01:00
Timothee
8fb450a6d4 🌐 ES localization, Fix encoding 2024-11-06 17:37:37 +01:00
Timothee
311c5d0d51 🌐 ES localization 2024-11-06 17:12:47 +01:00
Stephen Abello
c6039f4b51 Merge branch 'support/3.2' into develop 2024-11-06 09:57:03 +01:00
Stephen Abello
f90bd81e15 N°7793 Add common SCSS variables between backoffice and end-user portal (#674)
* N°7793 Add common SCSS variables between backoffice and end-user portal

* Add shame & readme

* Move font face to common

* Inherit color functions

* Move font icon to common

* FIx breaking change introduced in lifecycle palette

* FIx breaking change introduced in base

* Move approot url to common

* Make highlightjs common variables more coherent with 3.2.1 commonization approach

* Deprecated and migrate the usage of ibo-adjust-alpha and ibo-adjust-lightness
2024-11-06 09:52:54 +01:00
Stephen Abello
a10e547420 N°7898 CKEditor content should be inserted at the cursor position. Thanks to @jbostoen 2024-11-04 15:57:44 +01:00
Stephen Abello
d0f9e57bf1 Merge branch 'support/3.2' into develop 2024-11-04 14:40:52 +01:00
Stephen Abello
2519456c98 Merge branch 'support/3.1' into support/3.2 2024-11-04 14:39:37 +01:00
Stephen Abello
9371bc6d7b N°7925 Fix incorrectly formatted In-Reply-To email header 2024-11-04 14:38:31 +01:00
Karel Vlk
58e964fb8c 🐛 N°7917 SF#2272 EmailLaminas.php: Fix Message-ID format (#671)
* 🐛 N°2272 EmailLaminas.php: Fix Message-ID format

* EmailLaminas.php: Add MessageId import

as suggested by @steffunky in PR 671
2024-11-04 14:37:15 +01:00
v-dumas
57760528c9 N°7759 - Improve Delivery Model Tooltips 2024-11-04 12:21:17 +01:00
jf-cbd
385c60e993 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-10-31 09:41:38 +01:00
jf-cbd
561baf5771 update tests to comply with expected behavior 2024-10-31 09:32:58 +01:00
jf-cbd
f281fa7b14 Remove unnecessary reload 2024-10-29 11:21:04 +01:00
Eric Espie
58c68bade9 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-10-29 11:18:45 +01:00
Eric Espie
6ae8a72a35 Add Fast setup for developers 2024-10-29 10:51:16 +01:00
Anne-Cath
eb992c7b45 N°7915 - declaration of DisplayBareProperties function in DocumentFile class has wrong id 2024-10-25 15:40:43 +02:00
Anne-Catherine
0688405f86 N°7348 - Enable to param maximum depth of impact analysis in function Ticket::UpdateImpactedItems (#633) 2024-10-25 15:29:09 +02:00
Romain Quetiez
694711b3f4 📝 The OQL parser builder does not work for PHP8+ 2024-10-25 15:07:11 +02:00
Romain Quetiez
01d3f707e4 N°4517 Retrofit PHP 8.1 compatibility changes to the template so that the changes will be preserved next time the parser is rebuilt (and the parser has been rebuilt, resulting in different line numbers in the generated comments) 2024-10-25 15:03:44 +02:00
odain
4e28856015 ci: enhance DB cleanup in tearDown 2024-10-25 12:02:21 +02:00
jf-cbd
dd34fda42e Merge remote-tracking branch 'origin/support/3.2' into develop 2024-10-23 17:56:09 +02:00
jf-cbd
8bb456df53 Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2024-10-23 17:55:12 +02:00
jf-cbd
4886e2a7dc 📝 Update itop-version-history.md 2024-10-23 17:54:26 +02:00
Romain Quetiez
674dfebb0d N°7803 Skip test if itop-hub-connector is not installed (new helper to handle this in any other tests) 2024-10-23 15:46:44 +02:00
jf-cbd
e8ecc85828 Merge remote-tracking branch 'origin/support/2.7' into support/3.1 2024-10-22 16:09:02 +02:00
jf-cbd
04bd8cc5ce 🚀 Update GitHub actions to improve PR classification 2024-10-22 16:07:47 +02:00
jf-cbd
6becd73ac2 Update tests to be minimal and removed hard-coded ITOP_DESIGN_LATEST_VERSION VERSION 2024-10-22 11:47:25 +02:00
jf-cbd
72d6e251b8 🔖 Update tests for XML 3.3.0 version 2024-10-21 18:39:39 +02:00
jf-cbd
9491c9102c 🔖 Update tests for XML 3.3.0 version 2024-10-21 18:28:50 +02:00
jf-cbd
a728cf312d 🔖 Update XML 3.3.0 version 2024-10-21 18:21:37 +02:00
jf-cbd
e321e27899 🔖 Prepare 3.3.0 version 2024-10-21 18:07:54 +02:00
jf-cbd
96e8467e13 Temporary rollback : waiting for datamodel upgrade 2024-10-21 18:01:58 +02:00
jf-cbd
508918a684 Temporary rollback : waiting for datamodel upgrade 2024-10-21 17:56:43 +02:00
jf-cbd
8adef26d18 🔖 Prepare 3.3.0 version 2024-10-21 17:36:45 +02:00
jf-cbd
2d45abd12b 🔖 Prepare 3.3.0 version 2024-10-21 17:34:30 +02:00
jf-cbd
bd5e55aad9 🔖 Prepare 3.3.0 version 2024-10-21 17:26:15 +02:00
Romain Quetiez
764eddd9f8 N°7803 Fix test not working on a clean installation 2024-10-21 10:45:26 +02:00
Romain Quetiez
015ff8f179 A better illustration to show that the impact is propagated in one way 2024-10-18 17:29:50 +02:00
Romain Quetiez
9b651c2451 N°7803 Test iTop Hub move to production (compilation phase) 2024-10-18 17:23:46 +02:00
v-dumas
bb16f4ad57 N°7903 - On-going demand brick broken when adding Global Demand 1.5.0 (2) 2024-10-18 11:10:27 +02:00
v-dumas
7b3023ccce N°7820 - Tooltip Newsroom 2024-10-17 18:24:08 +02:00
v-dumas
e6a3a95ff9 N°7859 - French translation missing on newsroom 2024-10-17 18:16:48 +02:00
v-dumas
80e413c370 N°7906 - User Preference: missing list, search criteria and reconciliation 2024-10-17 17:43:18 +02:00
v-dumas
69fd9048fd N°7903 - On-going demand brick broken when adding Global Demand 1.5.0 2024-10-17 17:14:30 +02:00
odain-cbd
7254bb7a2f N°7433 - Allow MFA configuration in the portal (#670)
* N°7433 - Extension for MFA

* Portal user profile brick

* fix mfa tab display

* Portal user profile brick

* Portal user profile brick

* mfa portal action

* extensibility on portal to make mfa recovery mode work

* Portal user profile brick extension

* Portal user profile brick extension

* display alert section in user portal profile layout

* protect mfa via transaction_id mecanism

* Fix transaction ids

* Fix when no MFA

* N°7433 - Allow MFA configuration in the portal (refactor interfaces)

* N°7433 - Allow MFA configuration in the portal (revert unnecessary changes)

* N°7433 - Allow MFA configuration in the portal (finer style)

* N°7433 - Allow MFA configuration in the portal (PHPDoc update)

* N°7433 - PR review

---------

Co-authored-by: Eric Espie <eric.espie@combodo.com>
2024-10-16 16:41:58 +02:00
Romain Quetiez
7a4d29d561 N°7870 Prerequisites to test portal services (instantiate a symfony service) 2024-10-15 15:11:16 +02:00
Eric Espie
c54909f2a3 Fix comments for doc 2024-10-15 14:37:42 +02:00
Eric Espie
b57e4fce07 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-10-14 09:27:25 +02:00
Eric Espie
23af649d7f Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2024-10-14 09:26:25 +02:00
Eric Espie
88756a443a Fix event listeners display when the listener is a static method 2024-10-14 09:25:05 +02:00
Eric Espie
4a78f8fb43 N°7847 - Extensions via interface not recognized
N°7803 - MTP from itophub/designer failing in itop 3.2.0
Fix setup
2024-10-11 11:01:02 +02:00
Eric Espie
6c8388ea5e N°7847 - Extensions via interface not recognized
N°7803 - MTP from itophub/designer failing in itop 3.2.0
2024-10-11 10:02:03 +02:00
Eric Espie
75520bfaf9 N°7847 - Extensions via interface not recognized
N°7803 - MTP from itophub/designer failing in itop 3.2.0
2024-10-10 17:59:50 +02:00
v-dumas
3a4d9e64f8 N°7886 - Remove deprecated datamodel methods (2) 2024-10-10 16:59:20 +02:00
denis.flaven@combodo.com
209e30f7ef Merge 'origin/support/3.1' into support/3.2 2024-10-03 12:43:44 +02:00
denis.flaven@combodo.com
052e2a1a42 🔖 Prepare 3.1.2 version 2024-10-03 12:14:32 +02:00
jf-cbd
b460705831 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	.doc/itop-version-history.md
2024-09-30 11:46:20 +02:00
jf-cbd
3aadccc2e8 📝 Update itop-version-history.md 2024-09-30 11:44:25 +02:00
Romain Quetiez
5ae2fdee94 N°7858 Typo in backup config causing wrong scheduling of first occurrence of weekly backup (if on the same day as first execution) 2024-09-27 16:47:32 +02:00
Romain Quetiez
380512dcbe N°7845 Suppress risky syntax with DateTime::modify (at best, just a bad example, at worst, just a result that will vary wether PHP > 8.2 or not) 2024-09-26 17:51:52 +02:00
Eric Espie
58c47f4c0a Merge remote-tracking branch 'origin/support/3.2' into develop 2024-09-26 17:41:05 +02:00
Eric Espie
8e2a68887c Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2024-09-26 17:40:24 +02:00
Eric Espie
eb1ecff7d8 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-09-26 17:37:43 +02:00
Eric Espie
8141723869 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	approot.inc.php
#	css/css-variables.scss
#	datamodels/2.x/authent-cas/module.authent-cas.php
#	datamodels/2.x/authent-external/module.authent-external.php
#	datamodels/2.x/authent-ldap/module.authent-ldap.php
#	datamodels/2.x/authent-local/module.authent-local.php
#	datamodels/2.x/combodo-db-tools/module.combodo-db-tools.php
#	datamodels/2.x/itop-attachments/module.itop-attachments.php
#	datamodels/2.x/itop-backup/module.itop-backup.php
#	datamodels/2.x/itop-bridge-virtualization-storage/module.itop-bridge-virtualization-storage.php
#	datamodels/2.x/itop-change-mgmt-itil/module.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/module.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php
#	datamodels/2.x/itop-config/module.itop-config.php
#	datamodels/2.x/itop-core-update/module.itop-core-update.php
#	datamodels/2.x/itop-datacenter-mgmt/module.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-endusers-devices/module.itop-endusers-devices.php
#	datamodels/2.x/itop-files-information/module.itop-files-information.php
#	datamodels/2.x/itop-full-itil/module.itop-full-itil.php
#	datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/module.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-oauth-client/module.itop-oauth-client.php
#	datamodels/2.x/itop-portal-base/module.itop-portal-base.php
#	datamodels/2.x/itop-portal/module.itop-portal.php
#	datamodels/2.x/itop-problem-mgmt/module.itop-problem-mgmt.php
#	datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/module.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/module.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/module.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/module.itop-service-mgmt.php
#	datamodels/2.x/itop-sla-computation/module.itop-sla-computation.php
#	datamodels/2.x/itop-storage-mgmt/module.itop-storage-mgmt.php
#	datamodels/2.x/itop-tickets/module.itop-tickets.php
#	datamodels/2.x/itop-virtualization-mgmt/module.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-welcome-itil/module.itop-welcome-itil.php
#	datamodels/2.x/version.xml
2024-09-26 17:37:07 +02:00
denis.flaven@combodo.com
8cb701bda3 🔖 Prepare 2.7.11 version 2024-09-26 16:53:24 +02:00
vdumas
a8f3c3054b N°7774 - Tooltip unscribe from News center to be improved 2024-09-23 17:39:10 +02:00
vdumas
c5fb4227bf N°7774 - Tooltip unscribe from News center to be improved 2024-09-23 17:27:55 +02:00
jf-cbd
1b29746806 Rename github token 2024-09-23 17:14:41 +02:00
vdumas
f98ad6ccab N°7658 - Issue with Date widget display on object search 2024-09-23 14:54:07 +02:00
jf-cbd
fb9c317256 Add an action in the workflow to automatically add pull requests to the Combodo PRs dashboard 2024-09-23 14:43:33 +02:00
Eric Espie
0f1f2875ff Allow searching unknown class 2024-09-20 14:37:29 +02:00
Timothee
a88b4ad06b N°5079 - Fix misleading "leave confirmation" alert after closing some custom forms in portal 2024-09-20 11:31:42 +02:00
Anne-Catherine
f3a6b064c8 N°824 - Allow OQL JOIN on object ON EventNotification (#642) 2024-09-19 08:58:27 +02:00
Anne-Cath
5e6fb33ed7 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-09-19 08:11:56 +02:00
Anne-Cath
172fda2157 N°7824 - Fix dictionnaries 2024-09-19 07:57:44 +02:00
Anne-Cath
5e7137e0c4 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-09-19 07:51:37 +02:00
Anne-Cath
4f0c45004a N°7824 - Fix dictionnaries 2024-09-19 07:43:04 +02:00
BenGrenoble
4cf5e47ec3 N°7796 - itop-ticket and itop-bridge-cmdb-ticket not coherents 2024-09-18 16:06:06 +02:00
Anne-Cath
143a59a19d Merge remote-tracking branch 'origin/support/3.2' into develop 2024-09-18 11:59:16 +02:00
Anne-Cath
60de41e6fd N°7824 - Update czech translations for iTop3.2 thanks to @Stetinac 2024-09-18 11:54:51 +02:00
Anne-Catherine
cbbb4cdb8c N°7639 - Switching tab starts reloading the data table (#651) 2024-09-18 10:56:58 +02:00
Anne-Catherine
8c5b919e72 N°7744 - Import CLI : add the possibility to use the field 'id' as reconciliationkey (#663) 2024-09-18 10:43:25 +02:00
Eric Espie
61b247b156 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-09-11 17:34:34 +02:00
Eric Espie
47c05b75d0 Merge commit '2147ae9105f3d3255e4b0dbfe0c33de505175041' into support/3.2 2024-09-11 17:33:38 +02:00
Eric Espie
2147ae9105 N°7802 - MTP from itophub/designer failing in itop 3.2.0 2024-09-11 17:24:08 +02:00
Molkobain
c1aa013053 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-09-06 11:59:46 +02:00
Eric Espie
4a99afae3b Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2024-08-26 13:55:55 +02:00
Molkobain
119dcf9c97 N°7730 - Rename constant as it is actually for ID and class selectors, not only IDs 2024-08-26 13:55:03 +02:00
Eric Espie
b7d14ca48e N°7730 - code hardening 2024-08-26 13:55:03 +02:00
Romain Quetiez
37cd12fb21 N°7662 Fix the handling of PHP native deprecations (e.g. call strftime on PHP 8.1) 2024-08-23 16:35:52 +02:00
Benjamin Dalsass
a3f9eed6e2 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-08-20 16:55:11 +02:00
Benjamin Dalsass
ffb61503dc N°7534 - Request to Improve the Display of an Inline Attachment
- reinforce algorithm
2024-08-20 16:52:15 +02:00
Molkobain
fbda0e38e1 💄 Update README.md 2024-08-20 16:34:32 +02:00
Molkobain
bfd8fb1c23 💄 Update README.md file with BEM methodology 2024-08-20 13:54:45 +02:00
Molkobain
c6760371ba 💄 Fix typo in $ibo-depression-100 CSS3 var. counterpart 2024-08-20 13:12:26 +02:00
Molkobain
8c704951e1 N°7730 - Rename constant as it is actually for ID and class selectors, not only IDs 2024-08-19 18:41:32 +02:00
Thomas Casteleyn
018f7808db N°5039 - DataSynchro: TEXT field too small for big linkset (#479) 2024-08-19 18:18:21 +02:00
Eric Espie
24c23628d6 N°7730 - code hardening 2024-08-19 15:15:22 +02:00
Benjamin Dalsass
1d3c71fd10 N°7534 - Request to Improve the Display of an Inline Attachment (#664) 2024-08-19 11:03:06 +02:00
Molkobain
e8d059fa77 N°7756 - Allow AttributeClass to declare "class_category" empty in XML 2024-08-17 23:29:56 +02:00
Molkobain
3b4a9dc368 📝 Update backoffice SCSS README.md 2024-08-13 15:05:32 +02:00
Anne-Cath
244ae33ad6 N°7745 - Portal: Fix js error in firefox when leaving object modal form with ckeditor form 2024-08-12 14:36:32 +02:00
jf-cbd
21c9332a7f Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-08-07 18:01:38 +02:00
jf-cbd
c1eb605195 Merge remote-tracking branch 'refs/remotes/origin/support/3.2.0' into support/3.2 2024-08-07 17:59:57 +02:00
jf-cbd
0ee1818f12 N°7732 - CSRF protection generating error when cancelling the creation of an object
N°7741 - PDF export on impact analysis not working
2024-08-07 17:56:34 +02:00
Benjamin Dalsass
ae6e0a08ea Merge remote-tracking branch 'origin/support/3.2' into develop 2024-08-07 08:33:27 +02:00
Benjamin Dalsass
31758cbe2b Merge remote-tracking branch 'origin/support/3.2.0' into support/3.2 2024-08-07 08:32:59 +02:00
Benjamin Dalsass
a4a1fa4ec9 N°7734 - Date time picker button show html icon 2024-08-07 08:32:06 +02:00
Benjamin Dalsass
d9d2e851f4 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-08-07 07:57:33 +02:00
Benjamin Dalsass
c32e186133 Merge remote-tracking branch 'origin/support/3.2.0' into support/3.2 2024-08-07 07:55:50 +02:00
Benjamin Dalsass
3d325caa06 N°7734 - Date time picker button show html icon 2024-08-07 07:54:40 +02:00
Molkobain
91c63cb12e Merge remote-tracking branch 'origin/support/3.2' into develop 2024-08-05 17:28:36 +02:00
Molkobain
22141aba7b Merge remote-tracking branch 'origin/support/3.2.0' into support/3.2 2024-08-05 16:10:49 +02:00
Molkobain
9b066b3b88 N°7410 - Add hyperlinks to news features 2024-08-05 09:17:51 +02:00
Romain Quetiez
7ea7f5a967 N°7728 Stop flooding error.log with low probability issues, and do not confuse the root cause and the fact that the API is deprecated. 2024-08-02 10:33:00 +02:00
Eric Espie
38041b25fb N°7722 - Fatal error if xml entry "redefine" doesn't exist 2024-07-31 14:42:08 +02:00
Molkobain
3adeea8172 N°7565 - Newsroom: Fix image being too big since latest CSS fixes 2024-07-31 10:38:16 +02:00
Molkobain
10c3641307 N°7720 - PHP 8.1: Fix usage of trim() with null parameter throwing deprecated notice during setup 2024-07-30 16:59:32 +02:00
Molkobain
af9b03cfa5 N°7565 - Fix typo in english entry 2024-07-30 08:59:35 +02:00
Molkobain
4ff354dd41 Update itop-version-history.md 2024-07-29 10:04:48 +02:00
Molkobain
bd8c325306 📝 Fix version history 2024-07-29 10:02:08 +02:00
Molkobain
9902cedc06 📝 Update version history 2024-07-29 09:56:52 +02:00
Molkobain
b64ee96636 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-29 09:56:15 +02:00
DudekArtur
806a2f0597 N°7693 - Update polish translations (#660)
Co-authored-by: Stephen Abello <stephen.abello@combodo.com>
2024-07-26 14:38:54 +02:00
vdumas
0cb9f7ab4f Fix dico typo on "decreasing" 2024-07-25 11:50:19 +02:00
Molkobain
8c1ff2dc1c Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-25 10:53:55 +02:00
Molkobain
dc6c894cb8 N°7697 - Remove unit test corrupting DB 2024-07-25 10:22:22 +02:00
Molkobain
3fa9b68882 N°7703 - Add reconciliation keys to newsroom action classes 2024-07-25 10:21:06 +02:00
Stephen Abello
0514f889d3 Update portal compiled stylesheet 2024-07-25 09:21:32 +02:00
Molkobain
f2ab409c9c Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-25 08:32:28 +02:00
vdumas
890841a818 N°7565 - Newsroom - dico tooltip 2024-07-24 17:55:09 +02:00
Anne-Catherine
aed71a6335 N°7146 - Fix style not applied in list in the end-users portal in iTop 3.0+ (#635)
* N°7146 - Fix style not applied in list in the end-users portal in iTop 3.0+ - Add Markup in search list in order to use style

* N°7146 - Fix style not applied in list in the end-users portal in iTop 3.0+

* WIP

* Fix link on external Key

* Add new markup

* Update datamodels/2.x/itop-portal-base/portal/src/Controller/ObjectController.php

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

---------

Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>
2024-07-24 17:20:13 +02:00
Benjamin Dalsass
c910d35e25 N°7646 - Check performances compared to beginning of 3.2 development
- use highlight js lib minified version
2024-07-24 15:15:23 +02:00
vdumas
0586a77043 Align untranslated dictionnary entries with english 2024-07-24 14:49:25 +02:00
Stephen Abello
b4b032693a Recompile setup css file 2024-07-24 14:24:37 +02:00
Stephen Abello
054fd8798b N°7552 - Move CKEditor from to npm dependencies 2024-07-24 14:20:18 +02:00
vdumas
35a7d907cd N°7685 - Align Calendar invitation presentation to Email and add tooltip 2024-07-24 13:09:58 +02:00
Molkobain
4c66cfe6e3 N°7697 - Fix unit test 2024-07-24 12:18:22 +02:00
Stephen Abello
4360ad6502 N°7698 - Fix plain text value in HTML field being seen empty 2024-07-24 12:01:19 +02:00
Lars Kaltefleiter
841a2474af N°7687 - Update German translations for iTop 3.2 (#656)
* German translations iTop 3.2

* restore deleted entries
2024-07-24 11:54:03 +02:00
DudekArtur
33b6f2cbc4 N°7693 - Update polish translations (#658) 2024-07-24 11:37:19 +02:00
Stephen Abello
4680b6a497 N°7655 - Handle highlightjs styles in fronts styles and force it to apply over inlined styles 2024-07-24 11:22:00 +02:00
Molkobain
db46763e13 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-24 11:02:36 +02:00
Molkobain
c5727d366f Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2024-07-24 11:01:30 +02:00
Molkobain
7ee1af3cc8 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-24 10:58:05 +02:00
Thomas Casteleyn
a749a4654e N°7697 - Add method to rename DB table during setup (#623) 2024-07-24 10:51:07 +02:00
Benjamin Dalsass
3c8a71f39f N°7646 - Check performances compared to beginning of 3.2 development (#659)
- Improve OQL calls in portal services list pages with a cached private key for encoding rules
2024-07-24 10:43:29 +02:00
Timothee
ed7411294e N°7647 exclude fatal error image from build 2024-07-24 10:05:31 +02:00
Molkobain
aaecce7574 N°7686 - Fix typo in dictionary entry 2024-07-24 10:04:20 +02:00
Molkobain
480e2a0fb5 N°7686 - Fix typo in dictionary entry 2024-07-24 09:57:15 +02:00
Thomas Casteleyn
a3a0f33ad4 N°7686 - Update Dutch translations (#657) 2024-07-24 09:01:12 +02:00
Molkobain
089da40941 N°7552 - CKEditor: Fix error message not disappearing when mandatory field is filled 2024-07-23 23:14:47 +02:00
Molkobain
ce0d17e042 N°7552 - CKEditor: Cleanup remains of previous mentions overloads 2024-07-23 22:06:21 +02:00
Molkobain
aba737ab48 N°7552 - CKEditor: Cleanup in portal.scss 2024-07-23 16:06:09 +02:00
Molkobain
a7c5d9e92a N°7552 - CKEditor: Fix rendering in the portal 2024-07-23 16:04:46 +02:00
Molkobain
b2a377d7f1 N°7611 - CKEditor: Harmonize table alignments 2024-07-23 13:56:17 +02:00
Molkobain
1d65234073 N°7552 - CKEditor: Fix unit tests for email notifications 2024-07-23 13:26:35 +02:00
Molkobain
38065791da N°7552 - CKEditor: Harmonize HTML content rendering in edit, read and emails 2024-07-23 11:53:22 +02:00
Molkobain
086d2f0ba4 N°7552 - CKEditor: Include CKE styles in the backoffice theme and emails style 2024-07-23 11:52:39 +02:00
Molkobain
377e06d99a N°7552 - CKEditor: Make style generate in a CSS file instead of an inline tag 2024-07-23 11:50:03 +02:00
vdumas
37cf1b2901 N°6992 - Fix "add lnk" popup title: use label instead of class name 2024-07-23 10:10:32 +02:00
Eric Espie
d9f280dc56 N°7679 - Debug output when licence read-only 2024-07-23 09:48:33 +02:00
vdumas
b207f9ea02 N°7131 - Allow Data Synchro to change the Org of a Person regardless of its Users 2024-07-22 15:18:42 +02:00
vdumas
2690f8171e N°7131 - Allow Data Synchro to change the Org of a Person regardless of its Users 2024-07-22 15:10:41 +02:00
Benjamin Dalsass
7136ac38d6 N°7678 - Bulk modify of objects with HTML field broken
- build ckeditor dist
2024-07-22 14:58:51 +02:00
Benjamin Dalsass
d4b5e98514 N°7678 - Bulk modify of objects with HTML field broken
- file have been restored from its previous version due to a wrong merge conflict resolution
2024-07-22 14:58:06 +02:00
Stephen Abello
0ede0719c5 N°7552 - Fix CKEditor sources not being scrollable in fullscreen 2024-07-22 11:00:54 +02:00
Benjamin Dalsass
06124a0d94 N°7552 - Polishing CKeditor
- remove list properties plugin
- remove unused plugins
2024-07-22 10:22:05 +02:00
Timothee
d19d88e5dc N°7647 Removing unused fonts 2024-07-19 16:39:05 +02:00
Timothee
b2d8b0bfe4 N°7647 Removing needless font format for font-awesome 2024-07-19 16:37:04 +02:00
Timothee
095ac3dfa5 N°7647 Refactoring to allow files blacklist 2024-07-19 15:20:40 +02:00
Stephen Abello
a86edc69ac N°7655 - Make code blocks edition closer to its readonly counterpart 2024-07-19 10:54:06 +02:00
Stephen Abello
21bc4cdc9b Remove unwanted default table configuration for CKEditor 2024-07-19 10:52:49 +02:00
Molkobain
b682ed7ad9 N°7410 - Change acknowledge button label 2024-07-19 10:14:06 +02:00
Molkobain
5e823d1f16 N°7565 - Add description to details and list zlists 2024-07-19 09:46:55 +02:00
Molkobain
d32949d1d3 N°7410 - Fix typo in template path 2024-07-18 16:06:12 +02:00
Molkobain
06c26c99bb N°7565 - Make visual adjustments on the newsroom
- New messages indicator next to the news providers removed as it was confusing and not bringing relevant information
- Add hand cursor on hover of the news
- News content now restricted to a certain height / width to avoid content overflowing everywhere
- Limit message height in the "view all" page
2024-07-18 15:54:32 +02:00
jf-cbd
22e599598a 💬 Fix translations 2024-07-18 10:49:26 +02:00
jf-cbd
d666050e0c 💬 Fix translations 2024-07-18 10:27:16 +02:00
jf-cbd
8018c586cd 💬 Improved translations 2024-07-18 09:48:52 +02:00
Molkobain
e73911c458 N°7611 - Remove table reset / unset CSS rules as overflow from HTML attribute has been fixed 2024-07-18 09:32:09 +02:00
Molkobain
7e79b27823 N°6543 - Fix AttributeHTML field not respecting column constraint due to typo in CSS selector 2024-07-17 21:44:51 +02:00
jf-cbd
5fa9ef05a6 💬 Improved translations 2024-07-17 17:36:20 +02:00
Stephen Abello
f239b658e6 Fix CKEditor change detector being misled by insert carriage plugin 2024-07-17 14:53:13 +02:00
jf-cbd
141c22ff67 Fix selectize bug when multiple selectize fields exist on the same page 2024-07-17 14:17:09 +02:00
Stephen Abello
23651ae510 N°2523 - Align CKEditor and HtmlDomSanitizer denied and allowed lists 2024-07-17 11:45:55 +02:00
Molkobain
45a1efe2ae N°7565 - Update TriggerOnReachingState action message 2024-07-17 11:12:45 +02:00
Molkobain
06c135aab1 N°7565 - Newsroom: Polishing last details
- Force subscription policy on existing mention triggers
- Add news action to all mention triggers
- Add action (not linked to any trigger) for TriggerOnPortalLogUpdate
- Add action (not linked to any trigger) for TriggerOnReachingState for assigned
2024-07-17 10:47:01 +02:00
Molkobain
2203694b3f N°7565 - Fix news icon/timestamp not aligned depending on message length 2024-07-17 10:40:41 +02:00
Molkobain
f7ab9646d0 N°7552 - Fix mentions during object creation 2024-07-17 10:30:28 +02:00
Molkobain
a4959cad15 N°7397 - Fix missing space in dictionaries 2024-07-17 09:52:19 +02:00
Timothee
1c0a7e9bd9 N°7589 pushing menu position down 2024-07-16 17:26:33 +02:00
jf-cbd
cc46761e34 ✏️ Fix typos + fix French translation 2024-07-16 15:39:32 +02:00
Romain Quetiez
e1c3e7a3a6 🦺 N°7629 Remove refactoring protection tests 2024-07-16 10:25:14 +02:00
Romain Quetiez
baf85e7a80 N°7629 Deprecate utils::GetClassesForInterface in favor of InterfaceDiscovery::FindItopClasses
Improve caching strategy and robustness
2024-07-16 10:13:13 +02:00
Timothee
32b371eac1 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-16 09:57:16 +02:00
Eric Espie
147aad9221 N°7631 - hardening code 2024-07-16 09:55:49 +02:00
Stephen Abello
41e0d39a5a Fix selectize element color when active 2024-07-16 09:29:12 +02:00
Timothee
91496b08bd N°7589 - Move oauth client menu under Integration and revert general configuration 2024-07-15 18:04:37 +02:00
Timothee
b3d20effd6 N°7589 - Move oauth client menu under Integration and revert general configuration 2024-07-15 17:51:25 +02:00
Molkobain
2e0b9bd6b4 Revert "N°7630 - Fix wrong enum/ext. key value selected when clicking on drop down list in modal"
No longer necessary as of 4b3e9432df

This reverts commit f0c37bd69e.
2024-07-15 17:01:39 +02:00
Molkobain
e497304b9d N°7630 N°7623 - Update SCSS to match new Selectize version 2024-07-15 16:32:44 +02:00
Molkobain
4b3e9432df N°7630 N°7623 - Fix visual glitches with selectize.js
Mind that as there is no released version of the necessary fixes yet, we upgraded the files based on e6ca6d3

More information on the issue resolving the matter: https://github.com/selectize/selectize.js/issues/1926
2024-07-15 16:24:45 +02:00
Molkobain
1d67d50a20 N°7397 - Update welcome popup content for iTop 3.2 (#648)
* N°7397 - Update welcome popup content for iTop 3.2

* N°7397 - Display illustrations as restylable SVG, add extension to twig to support absolute url in source function

* N°7397 - Update 3.2 welcome messages dictionaries, add French translations and set up other languages files

---------

Co-authored-by: Stephen Abello <stephen.abello@combodo.com>
2024-07-15 16:12:12 +02:00
Benjamin Dalsass
f65cadd24c Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-15 10:59:01 +02:00
Benjamin Dalsass
f0c37bd69e N°7630 - Fix wrong enum/ext. key value selected when clicking on drop down list in modal 2024-07-15 10:18:33 +02:00
Stephen Abello
21513f8df0 N°7552 - Fix CKEditors table, hyperlinks, and other options inputs not working in iTop portal 2024-07-12 10:49:20 +02:00
jf-cbd
40551b36e5 Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-07-12 10:40:09 +02:00
jf-cbd
eb73b6b2f5 N°7552 - Polishing CKeditor (Maximize/full screen mode) - built minified files 2024-07-11 17:17:01 +02:00
jf-cbd
504378261b N°7552 - Polishing CKeditor (Maximize/full screen mode) - built JS files 2024-07-11 17:11:05 +02:00
jf-cbd
a5ceddc40a N°7552 - Polishing CKeditor (Maximize/full screen mode) - remove maximize hybrid mode 2024-07-11 15:09:27 +02:00
jf-cbd
0fb23c4e5b N°7552 - Polishing CKeditor (Maximize/full screen mode) 2024-07-11 15:04:03 +02:00
DarkNight97boss
40c7559905 N°7652 - Update italian translations thanks to @DarkNight97boss (#653) 2024-07-11 13:17:44 +02:00
Benjamin Dalsass
edda622060 N°7443 - url in read-only attribute are displayed different between console and portal 2024-07-11 07:41:55 +02:00
Molkobain
c0056e75d0 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-10 11:39:14 +02:00
Molkobain
f1694707ee N°7644 - Update french dictionary 2024-07-10 11:36:33 +02:00
Eric Espie
97ec9666ed N°7620 - Changing height of multiple selection combo between 1 and 8 according to allowed values 2024-07-10 10:33:27 +02:00
Thomas Casteleyn
7b91e9e164 N°7644 - Add Brand logo and Model picture (#646)
* Add Brand logo and Model picture

* Add dictionary items for new attributes

* Add Model picture to summary card
2024-07-10 10:29:11 +02:00
Molkobain
ff0f1d8ce9 Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2024-07-10 10:09:43 +02:00
Thomas Casteleyn
1e3c425e81 N°7645 - PHP 8.1: Fix usage of strpos() & str_replace() with null value when compiling empty dictionary (#600)
* fix(compiler): provide empty string instead of null value

* Apply review suggestions
2024-07-10 10:07:12 +02:00
Molkobain
b2b5aeb08b Revert "N°7559 - Fix radio buttons in template fields"
This reverts commit dc8f676f75.
This reverts commit e9ef48b79c.
2024-07-09 11:30:12 +02:00
Stephen Abello
01ab3be8d3 N°7593 - Add robustness to scrollable tabs when the first tab is an Ajax tab 2024-07-09 10:36:39 +02:00
Molkobain
0d7ca88cc2 N°7606 - Improve log form entry robustness by ensuring CKEditor instance is loading before doing anything 2024-07-08 13:34:27 +02:00
Molkobain
4fee774fe5 N°7635 - Upgrade CKEditor dependency "ws" to v7.5.10 2024-07-08 10:40:49 +02:00
jf-cbd
e5825b5fcd Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-07-05 14:06:00 +02:00
jf-cbd
0d9f348e93 🎨 Reformatting log channels name 2024-07-05 12:05:51 +02:00
Molkobain
1ea0018a50 N°7565 - Fix newsroom element's date time alignment 2024-07-05 10:03:44 +02:00
Benjamin Dalsass
d3dc59c5da Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-05 09:25:53 +02:00
Benjamin Dalsass
3bec8fe7d2 Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2024-07-05 09:24:45 +02:00
Benjamin Dalsass
b2e3fb6354 N°7024 - Fix opening an object with abstract class indirect linked set in Portal 2024-07-05 09:10:19 +02:00
Molkobain
642a097b4b Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-05 00:19:17 +02:00
Molkobain
7cedc501f7 N°7565 - Add button in notifications center to access the "all news" page 2024-07-04 23:59:39 +02:00
Molkobain
296c5bd3d2 N°7565 - Refactor "other actions" tab to display action class name on the panel 2024-07-04 23:42:17 +02:00
Molkobain
a2bddea1e2 N°7565 - Forbid disabling internal newsroom provider 2024-07-04 23:33:38 +02:00
Molkobain
53cd9f8b5f N°7565 - Fix newsroom display limit not following user preferences 2024-07-04 23:23:40 +02:00
Molkobain
b344826c44 N°7565 - Increase default mention newsroom notification to priority "Important" instead of "Standard" 2024-07-04 23:07:41 +02:00
Molkobain
a0dc68ab9a N°7565 - Generate newsroom action for Person mention on installation/upgrade 2024-07-04 22:43:37 +02:00
Molkobain
22f2b5d0e5 N°7565 - Fix dictionaries counter-parts in other languages 2024-07-04 18:10:47 +02:00
Stephen Abello
c411900efd N°7552 - Fix CKEditor tables being impossible to resize 2024-07-04 16:17:59 +02:00
Molkobain
592aa93c40 N°7565 - Update newsroom dictionaries with tooltips 2024-07-04 15:59:26 +02:00
Stephen Abello
6c02dd75fa N°7552 - Correctly define custom z-index for CKEditor 2024-07-04 15:57:23 +02:00
Stephen Abello
39853d4949 N°7552 - Keep style from Word/Excel content 2024-07-04 15:11:11 +02:00
Stephen Abello
2404f5d00f N°7552 - Remove unused library masterminds/html5 2024-07-04 14:59:21 +02:00
jf-cbd
1c615c42b6 Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-07-04 13:56:53 +02:00
jf-cbd
f84746274f Merge remote-tracking branch 'refs/remotes/origin/support/3.1' into support/3.2 2024-07-04 13:55:59 +02:00
jf-cbd
45ce68e16e Merge remote-tracking branch 'refs/remotes/origin/support/3.0' into support/3.1 2024-07-04 13:55:11 +02:00
jf-cbd
1aef576403 N°7604 - Security hardening 2024-07-04 13:52:19 +02:00
Molkobain
c3582f0aff N°7552 - Fix mentions not taking triggers filter into account 2024-07-04 11:30:51 +02:00
Molkobain
8c4c0ff5e1 N°7552 - Adjust linkedset display as tag endpoint to return only necessary data 2024-07-04 11:30:51 +02:00
jf-cbd
b1f708dcad Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-07-04 11:01:53 +02:00
jf-cbd
7b6334d447 Merge remote-tracking branch 'refs/remotes/origin/support/3.1' into support/3.2 2024-07-04 11:01:16 +02:00
jf-cbd
bfd9be8280 Merge remote-tracking branch 'refs/remotes/origin/support/3.0' into support/3.1 2024-07-04 10:59:57 +02:00
jf-cbd
96e1388dde N°7603 - Security hardening + UI blocks examples updated 2024-07-04 10:56:08 +02:00
Timothee
fd64be1dcd N°7490 Upgrade tcpdf version 2024-07-04 10:41:18 +02:00
Stephen Abello
64974a2c54 N°7530 - Fix selectize results not stylized properly in other themes 2024-07-04 10:11:47 +02:00
Timothee
e363692822 Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2024-07-04 09:45:56 +02:00
Stephen Abello
d5d93ed147 N°7530 - Fix quickcreate autocomplete results not displaying properly 2024-07-04 09:43:55 +02:00
Romain Quetiez
c3547f29d0 Cosmetics on the test framework 2024-07-04 09:38:35 +02:00
Benjamin Dalsass
55f202b7cb Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-04 07:57:38 +02:00
Benjamin Dalsass
989ba7bbe7 N°7615 - Edition of a n-n link in edit mode not working
- Register input listeners on page ready
2024-07-04 07:55:30 +02:00
Eric Espie
ac90d8036f N°7516 - hardening code 2024-07-03 18:03:34 +02:00
Timothee
644da707d2 Fix merge conflit resolution d3b9965283 2024-07-03 17:13:47 +02:00
Timothee
86b48b8980 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-07-03 17:00:22 +02:00
Timothee
69c8791fc5 Fix merge conflit resolution d3b9965283 2024-07-03 16:48:08 +02:00
jf-cbd
247f1045fe Merge branch 'refs/heads/support/3.2' into develop 2024-07-03 15:52:49 +02:00
jf-cbd
8b35679fcf N°7124 - Security hardening 2024-07-03 15:51:43 +02:00
Stephen Abello
cb8af0a02b N°7602 - Fix issue when displaying CKEditor sources while CKEditor is maximized 2024-07-03 15:47:29 +02:00
Stephen Abello
0b442a2e1a N°7622 - Add carriage return after a block elements in ckeditor 5 2024-07-03 15:26:26 +02:00
Molkobain
f98e92b255 Restore documentation comment 2024-07-03 15:19:55 +02:00
Stephen Abello
eca1836bd8 N°7595 - Fix body text color variable being used before it's declared 2024-07-03 14:47:25 +02:00
Stephen Abello
17e8cad005 N°7595 - Fix CKEditor 5 font color in dark themes 2024-07-03 14:24:52 +02:00
Romain Quetiez
48e89dd27e N°7621 Fix extensibility point introduced in N°4498, JS code included via iBackofficeScriptExtension must be executed after the JS files have been loaded 2024-07-03 09:42:41 +02:00
Eric Espie
e9ef48b79c N°7559 - Fix radio buttons in template fields 2024-07-03 09:28:18 +02:00
jf-cbd
42e78ad3a3 Merge branch 'refs/heads/support/3.2' into develop 2024-07-02 17:21:43 +02:00
Romain Quetiez
ab9df22e76 📝 Reinforce the documentation of WebPage JS inclusion API 2024-07-02 17:18:32 +02:00
jf-cbd
84f2ecd80f Merge branch 'refs/heads/feature/N°7124-CSRF-in-several-iTop-pages' into support/3.2 2024-07-02 17:16:23 +02:00
jf-cbd
98f946c871 N°7124 - [SECU] Cross-Site Request Forgery (CSRF) in several iTop pages (finalize implementation) 2024-07-02 17:14:55 +02:00
odain
113826b2e7 Merge branch 'support/3.2' into develop 2024-07-02 17:13:30 +02:00
odain
452d97f2d0 N°7584 - fix log 2024-07-02 16:00:42 +02:00
odain
7fe8da8590 Merge branch 'support/3.2' into develop 2024-07-02 14:10:54 +02:00
odain
3f0e46230f N°7584 - authentication error log only when unescapable login_mode 2024-07-02 14:10:31 +02:00
Eric Espie
821251a53f Merge remote-tracking branch 'origin/support/3.2' into develop 2024-07-02 10:43:23 +02:00
Eric Espie
fad3259290 Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2024-07-02 10:42:28 +02:00
Eric Espie
5fd8678a3a N°7619 - Object deletion not cascaded to legacy extensions 2024-07-02 10:38:25 +02:00
Eric Espie
86df9ac035 N°7619 - Object deletion not cascaded to legacy extensions 2024-07-02 10:37:36 +02:00
Romain Quetiez
d4b342a35d Simplify for the sake of readability 2024-06-28 23:58:10 +02:00
Romain Quetiez
8c87f5a384 Merge branch 'refs/heads/feature/TestsGivenObjectInDB' into support/3.2 2024-06-28 17:04:35 +02:00
Eric Espie
984f676d6e GivenObjectInDB() and additional improvements in the tests suite to address false positive and slow tests
Rewrite tests on impact analysis to make them readable in the first place, then simplifiy the tests to focus on risk reduction, then complete with some edge case
Rewrite tests on impact graph build when the current user has restricted access to some parts of the graph
2024-06-28 15:24:13 +02:00
Molkobain
5334bbb303 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	.doc/itop-version-history.md
2024-06-28 15:08:31 +02:00
Molkobain
0f213b08a6 Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2024-06-28 15:02:46 +02:00
Molkobain
3f997b416f N°7313 - Revert "fix" as it breaks grouping on ext. keys, OQL functions and more
We keep the enhancements to the test framework though.
2024-06-28 14:54:51 +02:00
Molkobain
f88e231780 N°7610 - Fix inline images in log being displayed above the transition buttons 2024-06-28 12:20:46 +02:00
Molkobain
fff95fcebd N°7601 - Avoid image to float along side text when editing, which will render differently in read-only / notifications 2024-06-27 22:16:53 +02:00
Molkobain
de17664c49 N°7601 - Remove image alignment buttons from CKEditor toolbar 2024-06-27 22:07:39 +02:00
Molkobain
2b1826d2e6 N°7606 - Fix lost of submitted log entry when using the grouped stimuli buttons in creation/edit mode 2024-06-27 10:15:48 +02:00
Romain Quetiez
b8d8ec640d Cleanup of legacy tests (sending emails) 2024-06-25 15:41:50 +02:00
Romain Quetiez
e2c01e9e5d 🎉 Releasing 3.0.2-beta1 2024-06-25 15:34:30 +02:00
Molkobain
a55efdff18 N°7315 - Migrate usages of WebPage::add_linked_xxx() for LoginTwigRenderer class 2024-06-25 15:02:20 +02:00
Eric Espie
3c1af200f8 💄 better page consistency 2024-06-24 17:28:27 +02:00
Eric Espie
dc8f676f75 N°7559 - Fix radio buttons in template fields 2024-06-24 17:11:39 +02:00
Anne-Cath
a160f2e212 N°4342 - Improve generic bulk deletion function with memory limit handling - add tests 2024-06-24 14:55:36 +02:00
Eric Espie
012d5e7ae0 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-06-24 14:36:53 +02:00
Eric Espie
c1ce4f9a9f Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2024-06-24 14:30:28 +02:00
Eric Espie
cceb6809e7 Fix CI 2024-06-24 14:20:33 +02:00
Eric Espie
48b559472e Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php
2024-06-24 14:02:04 +02:00
Eric Espie
cddc452693 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-06-24 13:55:29 +02:00
Eric Espie
0904a21e3f Cleanup ItopTestCase 2024-06-24 11:50:37 +02:00
Eric Espie
e800946898 N°7454 - Add dedicated menu entry for config file edition 2024-06-21 14:13:46 +02:00
Eric Espie
e189034436 N°7454 - Add dedicated menu entry for config file edition 2024-06-21 14:05:05 +02:00
Timothee
4590013adc N°7581 Improve error message readability during object creation/modification in the portal (regression introduced with N°7545) 2024-06-21 12:48:43 +02:00
Timothee
66230199f8 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-06-21 12:41:17 +02:00
Timothee
1f1a2b660f N°7581 Improve error message readability during object creation/modification in the portal (regression introduced with N°7545) 2024-06-21 12:36:52 +02:00
Molkobain
b8b35194c9 Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2024-06-21 11:34:11 +02:00
Molkobain
4f36369ea1 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-06-21 11:32:21 +02:00
Molkobain
33a906f11a Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-06-21 11:29:18 +02:00
Molkobain
82d11eeb47 N°7127 - Upgrade handlebars.js to v4.7.8 2024-06-21 11:19:39 +02:00
Molkobain
fdef394070 N°7127 - Upgrade handlebars.js to v4.7.8 2024-06-21 11:14:21 +02:00
Timothee
a3a5630e88 N°7581 Improve error message readability during object creation/modification in the portal (regression introduced with N°7545) 2024-06-21 10:37:25 +02:00
Eric Espie
9f7fd6a3a1 N°7465 - Code hardening 2024-06-20 17:42:24 +02:00
Molkobain
112a6f9942 N°7588 - Fix .env.local not working for the portal since Symfony 5.4 migration 2024-06-20 17:09:50 +02:00
odain
60110a4302 N°7584 - Login mode with special characters are not supported 2024-06-20 16:41:50 +02:00
Eric Espie
4426e2adfe N°7516 - Code hardening 2024-06-20 16:28:38 +02:00
vdumas
0028b3cf17 N°7565 - Polishing Newsroom 2024-06-20 16:00:07 +02:00
Eric Espie
4867810ebe N°7516 - Code hardening 2024-06-20 15:36:19 +02:00
Eric Espie
03f4e9f621 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-06-20 11:08:59 +02:00
Eric Espie
6b2476d220 Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2024-06-20 11:08:23 +02:00
Eric Espie
b8bf66031b Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-06-20 11:07:57 +02:00
Eric Espie
2596a150bf Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-06-20 11:07:36 +02:00
Eric Espie
142d6c8993 N°7533 - Detect and warns on Galera clusters 2024-06-20 11:06:57 +02:00
Eric Espie
a3309365fc N°7533 - Prevent installation of iTop on Galera clusters 2024-06-20 10:50:50 +02:00
Eric Espie
9dcdb1bc8c N°7533 - Prevent installation of iTop on Galera clusters 2024-06-20 10:47:20 +02:00
Eric Espie
2275daf3b0 N°7533 - Prevent installation of iTop on Galera clusters 2024-06-20 09:49:41 +02:00
Molkobain
d145cb508a N°7552 - Fix regression from previous merge (f63abd9b)
Can't submit HTML field in the backoffice
2024-06-20 09:08:32 +02:00
Eric Espie
7028fd21bc N°7571 - fix warning due to the new version of Html2Text lib 2024-06-19 11:23:08 +02:00
Molkobain
51adfd0254 N°7565 - Refactor way of retrieving user's picture 2024-06-19 11:05:46 +02:00
Eric Espie
6e2ed188eb Merge branch 'support/3.2-beta' into support/3.2 2024-06-19 10:57:27 +02:00
Timothee
2a3516d593 Reapply "Merge remote-tracking branch 'origin/support/3.1' into support/3.2"
This reverts commit 3233b9776f.
2024-06-19 09:47:48 +02:00
Eric Espie
e5a60beba0 🚨 Fix autoload files 2024-06-18 17:54:12 +02:00
Eric Espie
2804076bf6 N°7514 - hardening code
(cherry picked from commit b8b9423aa85c65b25f7fb3d9c5ba0933317474d7)
2024-06-18 17:40:20 +02:00
Molkobain
f63abd9b6f Merge remote-tracking branch 'origin/feature/3.2-post-beta' into support/3.2
# Conflicts:
#	js/ckeditor/build/ckeditor.js
#	js/ckeditor/build/ckeditor.js.map
2024-06-18 17:33:13 +02:00
Eric Espie
4ebdc9d6ed fix unit tests 2024-06-18 16:15:35 +02:00
Eric Espie
6e364a0ab9 N°7514 - hardening code 2024-06-18 16:00:08 +02:00
Timothee
3233b9776f Revert "Merge remote-tracking branch 'origin/support/3.1' into support/3.2"
This reverts commit 905bbf9118, reversing
changes made to 1c00461f4d.
2024-06-17 18:03:12 +02:00
Timothee
905bbf9118 Merge remote-tracking branch 'origin/support/3.1' into support/3.2 2024-06-17 17:04:05 +02:00
Timothee
d70e5dff45 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-06-17 16:55:26 +02:00
Timothee
c4fc0ed982 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-06-17 16:51:30 +02:00
Timothee
320922a13d N°7545 Correctly display error message 2024-06-17 16:49:33 +02:00
odain
9514977ce2 ci bug investigation 2024-06-17 16:48:09 +02:00
Eric Espie
62d08dce56 ⬆️ fix composer filters 2024-06-17 15:06:06 +02:00
Eric Espie
71e36d264e fix unit tests 2024-06-17 14:52:55 +02:00
Romain Quetiez
055968bea9 Update README.md 2024-06-17 14:40:31 +02:00
Eric Espie
7dc1672b24 Add logs to unit tests 2024-06-17 11:55:36 +02:00
Romain Quetiez
d0457b73ec Merge branch 'refs/heads/support/3.2' into develop 2024-06-13 18:52:40 +02:00
Eric Espie
50e40879de N°7571 - HTML2Text lib 2024-06-13 18:16:26 +02:00
jf-cbd
7e8ff50886 Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop
# Conflicts:
#	js/ckeditor/src/plugins/maximize/maximize.plugin.ts
2024-06-13 17:53:06 +02:00
Eric Espie
6cc24e475a N°7571 - HTML2Text lib 2024-06-13 17:28:08 +02:00
Eric Espie
4232284dae N°7571 - HTML2Text lib 2024-06-13 15:33:54 +02:00
Benjamin Dalsass
6958fd0965 Merge remote-tracking branch 'origin/feature/3.2-post-beta' into feature/3.2-post-beta 2024-06-13 14:49:46 +02:00
Benjamin Dalsass
1f8726669b N°7567 - Error message on bulk modify on object with different values in a an HTML field 2024-06-13 14:48:06 +02:00
Eric Espie
47d65931e6 N°7571 - ⬆️ Bump HTML2Text library version 2024-06-13 14:45:06 +02:00
jf-cbd
bfbb046b10 N°7552 - Polishing CKeditor (maximized mode, focus impossible if editor in a modal)
Fullscreen mode for portal (native browser method)
Windowed mode for console (JS method)
portal.css not minified
2024-06-13 10:32:08 +02:00
Eric Espie
36a6ee4fc9 Merge remote-tracking branch 'origin/feature/3.2-post-beta' into develop 2024-06-12 16:50:02 +02:00
Eric Espie
8b256caa6c Merge remote-tracking branch 'origin/support/3.1' into feature/3.2-post-beta 2024-06-12 16:49:13 +02:00
Eric Espie
f96d28a0a8 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-06-12 16:48:39 +02:00
Eric Espie
d3b9965283 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	core/cmdbsource.class.inc.php
2024-06-12 16:48:06 +02:00
Eric Espie
f03d731b1d N°7533 - Prevent installation of iTop on Galera clusters 2024-06-12 16:14:23 +02:00
jf-cbd
291c513d3b Remove balloon remaining in full screen (on Firefox) 2024-06-11 09:36:21 +02:00
jf-cbd
8935de0809 Dev command to improve development 2024-06-11 09:36:21 +02:00
Benjamin Dalsass
db8b00e8df Merge remote-tracking branch 'origin/support/3.2' into develop 2024-06-10 16:15:20 +02:00
Benjamin Dalsass
5d31c372fa Merge remote-tracking branch 'origin/support/3.2' into develop 2024-06-10 10:08:11 +02:00
Benjamin Dalsass
0497122e25 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-06-07 08:55:30 +02:00
Benjamin Dalsass
645d68d5d0 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-06-07 08:20:40 +02:00
Romain Quetiez
f04b8368aa List explicitely the modules when finding inconsistencies in installation.xml 2024-06-04 21:58:18 +02:00
vdumas
f9fd4f1e2a N°6303 - Add search ticket bricks in portal 2024-06-04 21:58:18 +02:00
Benjamin Dalsass
55ed552ba7 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-06-04 10:59:39 +02:00
Benjamin Dalsass
7bfa23fab7 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-06-03 15:51:14 +02:00
Eric Espie
7bd427b4e7 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-30 12:08:40 +02:00
Benjamin Dalsass
a9a9fb1da2 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-30 09:21:58 +02:00
Eric Espie
6653b13144 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-29 18:21:06 +02:00
Benjamin Dalsass
8bea29f0e7 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-28 16:07:36 +02:00
Benjamin Dalsass
d556564ee1 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-28 08:31:18 +02:00
Benjamin Dalsass
2286a9e2a4 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-28 08:17:19 +02:00
Molkobain
30c10cb67f Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-24 23:58:32 +02:00
Benjamin Dalsass
74a42a4d4f Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-24 16:14:44 +02:00
Eric Espie
c31443cb79 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-24 10:18:48 +02:00
Eric Espie
a698aec7a5 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-21 15:08:32 +02:00
Eric Espie
04d2aaf05c Merge remote-tracking branch 'origin/support/3.2' into develop 2024-05-21 14:30:30 +02:00
jf-cbd
5103f898e1 Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-05-17 11:23:08 +02:00
Romain Quetiez
68ee3231f4 Merge branch 'support/3.2' into develop
# Conflicts:
#	tests/php-unit-tests/src/Hook/TestsRunStartHook.php
2024-05-16 20:24:38 +02:00
jf-cbd
62a7850a0b Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-04-30 15:12:21 +02:00
jf-cbd
20f1ec42b1 Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-04-30 10:58:35 +02:00
jf-cbd
e51e55b634 Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop
# Conflicts:
#	pages/ajax.render.php
2024-04-30 08:18:57 +02:00
Benjamin Dalsass
bc7db973ad Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-25 09:17:33 +02:00
jf-cbd
0489103e11 Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-04-24 17:13:36 +02:00
Eric Espie
b99249d2d2 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-24 16:11:58 +02:00
jf-cbd
23aed5415c Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop 2024-04-23 15:24:52 +02:00
jf-cbd
2adbcf5e6f Merge remote-tracking branch 'refs/remotes/origin/support/3.2' into develop
# Conflicts:
#	pages/ajax.render.php
2024-04-19 15:37:52 +02:00
jf-cbd
6a99e3c1c9 Revert "Merge branch 'refs/heads/support/3.2' into develop"
This reverts commit 8e1de5bf2d, reversing
changes made to f6808ee522.
2024-04-19 15:34:56 +02:00
jf-cbd
8e1de5bf2d Merge branch 'refs/heads/support/3.2' into develop 2024-04-19 11:28:55 +02:00
Molkobain
f6808ee522 Fix typo in README.md 2024-04-19 11:22:33 +02:00
Molkobain
4f650d3c5b Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-19 09:26:10 +02:00
Molkobain
ba641d96d5 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-18 18:51:05 +02:00
Molkobain
3dc514c67d Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-18 18:19:52 +02:00
Molkobain
5df7c58b1b Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-18 17:03:24 +02:00
Benjamin Dalsass
bb680ac514 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-17 16:05:56 +02:00
Stephen Abello
e18018cacd Merge branch 'support/3.2' into develop 2024-04-16 09:22:58 +02:00
odain
5d91c8832b Merge branch 'support/3.2' into develop 2024-04-15 10:25:01 +02:00
odain
c96c8ac30d Merge branch 'support/3.2' into develop 2024-04-12 17:19:45 +02:00
Molkobain
f930533d8b Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-12 11:08:59 +02:00
odain
29bfb8a8bf Merge branch 'support/3.2' into develop 2024-04-11 18:56:02 +02:00
Molkobain
f04359f398 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-11 17:04:13 +02:00
odain
c24052e976 Merge branch 'support/3.2' into develop 2024-04-10 15:35:17 +02:00
odain
5f8e43fc67 Merge branch 'support/3.2' into develop 2024-04-10 14:26:43 +02:00
jf-cbd
5427b146cf Merge branch 'refs/heads/support/3.2' into develop 2024-04-10 10:36:40 +02:00
odain
6f231ce800 Merge branch 'support/3.2' into develop 2024-04-09 21:37:20 +02:00
odain
d66d1c8739 Merge branch 'support/3.2' into develop 2024-04-09 11:07:21 +02:00
odain
50b4388b8d Merge branch 'support/3.2' into develop 2024-04-09 11:02:32 +02:00
odain
9cc8b75ffb Merge branch 'support/3.2' into develop
# Conflicts:
#	setup/applicationinstaller.class.inc.php
2024-04-09 10:13:45 +02:00
jf-cbd
e715342f7a Merge branch 'refs/heads/support/3.2' into develop 2024-04-08 17:26:40 +02:00
jf-cbd
c86ac4f9a7 Merge branch 'refs/heads/support/3.2' into develop 2024-04-08 17:13:34 +02:00
Molkobain
b79fe06be4 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-08 15:14:30 +02:00
jf-cbd
e5764ac495 Merge branch 'refs/heads/support/3.2' into develop 2024-04-08 14:14:31 +02:00
Molkobain
2ff7cb8956 Add Vincenzo Katriel Giuva (@DarkNight97boss) to the contributors list! 🙌 2024-04-05 16:18:52 +02:00
jf-cbd
3064ab4b25 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-05 15:01:41 +02:00
Molkobain
d2f8d2e903 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-04 15:22:25 +02:00
odain
036c7796e5 Merge branch 'support/3.2' into develop 2024-04-02 15:08:29 +02:00
Molkobain
356dd46537 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-02 10:40:57 +02:00
Molkobain
bb30e1abb8 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-04-02 10:29:30 +02:00
Stephen Abello
84d225e389 Merge branch 'support/3.2' into develop 2024-03-29 11:20:07 +01:00
Eric Espie
dcf2780cac Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-28 13:33:04 +01:00
Eric Espie
3e421c770b Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-27 11:30:02 +01:00
Anne-Cath
19cb0e873a Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-26 11:41:26 +01:00
Molkobain
ed81dbc4a2 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-21 17:43:41 +01:00
Eric Espie
a93d5b87e1 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-19 17:16:35 +01:00
Molkobain
d40a52e403 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-19 14:55:15 +01:00
Eric Espie
a4a3797cc1 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-19 14:07:55 +01:00
Pierre Goiffon
bd29e5f13a Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-18 18:20:19 +01:00
Pierre Goiffon
ce13ee8a55 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-18 18:17:20 +01:00
Pierre Goiffon
e5ed5fbc6a Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-14 17:28:11 +01:00
jf-cbd
ff7eca2f08 Merge branch 'support/3.2' into develop
# Conflicts:
#	lib/composer/installed.php
2024-03-14 17:17:55 +01:00
Pierre Goiffon
06be217030 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-13 10:08:40 +01:00
Pierre Goiffon
4880052559 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-13 09:28:07 +01:00
Pierre Goiffon
147043857b Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-12 18:39:44 +01:00
Pierre Goiffon
1bfa1a7746 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-12 17:44:49 +01:00
Benjamin Dalsass
3747a6a454 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-12 09:48:37 +01:00
Pierre Goiffon
f1e6246962 N°7299 Remove moment.js (moved to NPM) 2024-03-11 16:56:41 +01:00
Pierre Goiffon
d2c6190b59 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-11 16:40:49 +01:00
Pierre Goiffon
0f8247ce69 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-11 16:32:03 +01:00
Benjamin Dalsass
dae3be99ce Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-11 16:05:34 +01:00
Pierre Goiffon
6e625e0cc9 N°7329 remove js/jquery.autocomplete.js 2024-03-08 16:19:29 +01:00
Pierre Goiffon
a602101695 Merge remote-tracking branch 'origin/support/3.2' into develop
# Conflicts:
#	js/jquery-ui-1.11.4.custom.min.js
#	js/jquery-ui.custom.min.js
2024-03-08 16:16:18 +01:00
Pierre Goiffon
48d86fdbf5 N°4797 Remove js/jquery-ui-1.11.4.custom.min.js
File was deprecated in 3.0.0 with N°3184
Use instead JQuery lib in NPM (since 3.2.0 N°5621)
2024-03-08 12:47:14 +01:00
Pierre Goiffon
d6cfde0f94 N°7299 Remove jquery-migrate (moved to NPM) 2024-03-07 15:33:07 +01:00
Pierre Goiffon
1b9b4fdd39 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-07 15:28:27 +01:00
Benjamin Dalsass
8bb551eb10 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-07 14:34:20 +01:00
Molkobain
6177035f84 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-07 13:26:29 +01:00
Pierre Goiffon
e6d3ce5918 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-07 11:12:39 +01:00
Molkobain
9e08143981 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-06 14:47:29 +01:00
Pierre Goiffon
2014a48d22 N°7299 Remove CSS & JS files moved to NPM
Migration was made in 3.2 with N°5621
2024-03-04 11:19:59 +01:00
Pierre Goiffon
b7e1202cf1 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-04 10:43:16 +01:00
Pierre Goiffon
44b994414d Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-04 08:42:18 +01:00
Pierre Goiffon
a7265c2493 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-01 17:26:55 +01:00
jf-cbd
261ec270c5 ✏️ fix a typo 2024-03-01 17:24:39 +01:00
Eric Espie
356be511a8 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-03-01 09:44:24 +01:00
Anne-Cath
f896e72013 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-29 16:57:24 +01:00
Benjamin Dalsass
187a895265 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-28 17:16:55 +01:00
Pierre Goiffon
6224f8ca51 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-28 16:44:29 +01:00
Pierre Goiffon
3b27e6e466 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-28 15:13:06 +01:00
Anne-Cath
4e53deec9d Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-28 10:37:15 +01:00
Anne-Cath
2cafa78339 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-27 18:22:22 +01:00
Eric Espie
f3f86017b6 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-27 18:14:28 +01:00
Eric Espie
5479080eb2 Merge remote-tracking branch 'origin/support/3.1' into develop 2024-02-27 17:40:56 +01:00
Benjamin Dalsass
3cb3e8a7f9 N°6937 - Symfony 6.4 - Handle Symfony configuration files 2024-02-26 10:38:36 +01:00
Molkobain
6a907f1fed Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-22 16:08:09 +01:00
Benjamin Dalsass
241f845c08 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-21 10:50:44 +01:00
Benjamin Dalsass
623f63abb3 N°7279 - AttributeClass defined in XML datamodel compilation issue 2024-02-21 09:53:57 +01:00
Pierre Goiffon
293adc3a99 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-20 12:12:51 +01:00
Pierre Goiffon
dee9f90a0b Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-20 09:27:51 +01:00
Pierre Goiffon
a7348f0eb9 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-20 09:13:10 +01:00
Molkobain
7457735c04 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-19 20:24:26 +01:00
Molkobain
0f389b2a48 Merge remote-tracking branch 'origin/support/3.2' into develop 2024-02-19 10:00:21 +01:00
Stephen Abello
bfe01899a4 N°7243 - Add toast notifications to iTop (#614)
* N°7243 - Add toast notifications to iTop

* Apply suggestions from code review

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

* Apply suggestions from code review

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

* Apply suggestions from code review

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

* Apply suggestions from code review

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

* Apply suggestions from code review

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

* Apply suggestions from code review

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

* Update js/pages/backoffice/toolbox.js

* Update js/utils.js

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

---------

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

* Update setup/applicationinstaller.class.inc.php

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

---------

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

View File

@@ -79,6 +79,8 @@ Then, **for a method** of an eligible class:
## Installation
Note : PHP7 is required. Migrating to PHP8 requires some additional work which is questionable as an alternative way to generate a documentation is being considered.
```
cd .doc
composer require phpdocumentor/phpdocumentor:~2 --dev

View File

@@ -79,6 +79,26 @@ gitGraph
commit id: "2023-08-10" tag: "2.7.9"
checkout support/3.1
commit id: "2023-12-20" tag: "3.1.1"
checkout develop
commit id: "2024-01-15" tag: "Start 3.2" type: HIGHLIGHT
branch support/3.2 order: 830
checkout support/2.7
commit id: "2024-01-17a" tag: "2.7.10"
checkout support/3.0
commit id: "2024-01-17b" tag: "3.0.4"
checkout support/2.7
commit id: "2024-09-28" tag: "2.7.11"
checkout support/3.1
commit id: "2024-09-27" tag: "3.1.2"
checkout support/3.2
commit id: "2024-06-25" tag: "3.2.0-beta1" type: REVERSE
commit id: "2024-08-07" tag: "3.2.0"
checkout support/2.7
commit id: "2025-02-25" tag: "2.7.12"
checkout support/3.1
commit id: "2025-02-25 " tag: "3.1.3"
checkout support/3.2
commit id: "2025-02-25 " tag: "3.2.1"
```
To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start).

43
.github/workflows/action.yml vendored Normal file
View File

@@ -0,0 +1,43 @@
name: Add PRs to Combodo PRs Dashboard
on:
pull_request_target:
types:
- opened
jobs:
add-to-project:
name: Add PR to Combodo Project
runs-on: ubuntu-latest
steps:
- name: Check if author is a member of the organization
id: check-membership
run: |
ORG="Combodo"
AUTHOR=$(jq -r .pull_request.user.login "$GITHUB_EVENT_PATH")
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: token ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}" \
"https://api.github.com/orgs/$ORG/members/$AUTHOR")
if [ "$RESPONSE" == "404" ]; then
echo "project_url=https://github.com/orgs/Combodo/projects/5" >> $GITHUB_ENV
echo "is_member=false" >> $GITHUB_ENV
else
echo "project_url=https://github.com/orgs/Combodo/projects/4" >> $GITHUB_ENV
echo "is_member=true" >> $GITHUB_ENV
fi
- name: Add internal tag if member
if: env.is_member == 'true'
run: |
curl -X POST -H "Authorization: token ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/Combodo/iTop/issues/${{ github.event.pull_request.number }}/labels \
-d '{"labels":["internal"]}'
env:
is_member: ${{ env.is_member }}
- name: Add PR to the appropriate project
uses: actions/add-to-project@v1.0.2
with:
project-url: ${{ env.project_url }}
github-token: ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}

View File

@@ -53,12 +53,12 @@ switch ($sDependenciesHandlerCode) {
// Start handler
$oDependenciesHandler = new $sDependenciesHandlerFQCN();
$aDeniedButStillPresent = $oDependenciesHandler->ListDeniedButStillPresentFoldersAbsPaths();
$aDeniedButStillPresent = $oDependenciesHandler->ListDeniedButStillPresentFilesAbsPaths();
echo "\n";
foreach ($aDeniedButStillPresent as $sDir)
{
if (false === $oDependenciesHandler::IsQuestionnableFolder($sDir))
if (false === $oDependenciesHandler::IsQuestionnableFile($sDir))
{
echo "ERROR found INVALID denied test dir: '$sDir'\n";
throw new \RuntimeException("$sDir is in the denied list but doesn't comply with the rule (see IsQuestionnableFolder method)");
@@ -70,7 +70,12 @@ foreach ($aDeniedButStillPresent as $sDir)
}
try {
SetupUtils::rrmdir($sDir);
if(is_dir($sDir)){
SetupUtils::rrmdir($sDir);
}
else{
unlink($sDir);
}
echo "✔️ Remove denied test dir: '$sDir'\n";
}
catch (\Exception $e) {
@@ -80,10 +85,10 @@ foreach ($aDeniedButStillPresent as $sDir)
$aAllowedAndDeniedDirs = array_merge(
$oDependenciesHandler->ListAllowedFoldersAbsPaths(),
$oDependenciesHandler->ListDeniedFoldersAbsPaths()
$oDependenciesHandler->ListAllowedFilesAbsPaths(),
$oDependenciesHandler->ListDeniedFilesAbsPaths()
);
$aExistingDirs = $oDependenciesHandler->ListAllFoldersAbsPaths();
$aExistingDirs = $oDependenciesHandler->ListAllFilesAbsPaths();
$aMissing = array_diff($aExistingDirs, $aAllowedAndDeniedDirs);
if (false === empty($aMissing)) {
echo "Some new tests dirs exists !\n"

View File

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

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

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

View File

@@ -76,7 +76,9 @@ We would like to give a special thank you 🤗 to the people from the community
### Names
- Al Hallak, Amr (a.k.a [@v4yne1](https://github.com/v4yne1))
- Alves, David
- Audon, Florian
- Beck, Pedro
- Beer, Christian (a.k.a [@ChristianBeer](https://www.github.com/ChristianBeer))
- Bilger, Jean-François
@@ -88,33 +90,44 @@ We would like to give a special thank you 🤗 to the people from the community
- Colantoni, Maria Laura
- Couronné, Guy
- Dejin, Bie (a.k.a [@bdejin](https://github.com/bdejin))
- Delicado, Elodie
- Dvořák, Lukáš
- Goethals, Stefan
- Giuva, Vincenzo Katriel (a.k.a [@DarkNight97boss](https://github.com/DarkNight97boss))
- Gumble, David
- Heloir, Arthur
- Janssens, Jelle (a.k.a [@janssensjelle](https://github.com/janssensjelle))
- Ji, Leeb (冀利斌) (a.k.a [@chileeb](https://github.com/chileeb))
- Kaltefleiter, Lars (a.k.a [@larhip](https://www.github.com/larhip))
- Khamit, Shamil
- Kincel, Martin
- Konečný, Kamil
- Kunin, Vladimir
- Lassiter, Dennis
- Lassiter, Denis (a.k.a [@delassiter](https://github.com/delassiter))
- Lazcano, Federico
- Lucas, Jonathan
- Malik, Remie
- Mantel, Ina
- Martin, Pierre (a.k.a [@Worty](https://github.com/worty-syn))
- Melchiorre, Romain
- Mindêllo de Andrade, Lucas (a.k.a [@rokam](https://www.github.com/rokam))
- Mozart de Oliveira, Eduardo (a.k.a [@eduardomozart](https://github.com/eduardomozart))
- Raenker, Martin
- Roháč, Richard (a.k.a [@RohacRichard](https://github.com/RohacRichard))
- Rosenke, Stephan
- Rossi, Tommaso (a.k.a [@tomrss](https://www.github.com/tomrss))
- Rudner, Björn (a.k.a [@rudnerbjoern](https://github.com/rudnerbjoern))
- Šafránek, Jaroslav (a.k.a [jkcinik](https://sourceforge.net/u/jkcinik/profile/) on SourceForge)
- Seki, Shoji
- Shilov, Vladimir
- Stetina, Pavel (a.k.a [@Stetinac](https://github.com/Stetinac))
- Stukalov, Ilya (a.k.a [@ilya](https://www.github.com/ilya)-stukalov)
- Stukalov, Ilya (a.k.a [@ilya](https://www.github.com/ilya-stukalov))
- Tarjányi, Csaba (a.k.a [@tacsaby](https://github.com/tacsaby))
- Toraya, Chairat (a.k.a [@Kyokito1412](https://github.com/Kyokito1412))
- Tulio, Marco
- Turrubiates, Miguel
- Višnjić, Aldin (a.k.a[@viliald](https://github.com/viliald))
- Vlk, Karel (a.k.a [@vlk-charles](https://www.github.com/vlk-charles))
### Aliases
@@ -135,4 +148,6 @@ We would like to give a special thank you 🤗 to the people from the community
- [ITOMIG](https://www.itomig.de/)
- [Pimkie](https://www.pimkie.com/)
- [Super-Visions](https://www.super-visions.com/)
- [Defence Tech Cyber Security - Malware Lab](https://github.com/DefenceTechSecurity)
- Orange Cyberdefense
- MipihSIB

View File

@@ -825,49 +825,38 @@ class UserRightsProfile extends UserRightsAddOnAPI
{
// We are protected by GetSelectFilter: the object set contains objects allowed or shared for reading
// We have to answer NO for objects shared for reading purposes
if (self::HasSharing())
{
$aClassProps = SharedObject::GetSharedClassProperties($sClass);
if ($aClassProps)
{
// This class is shared, GetSelectFilter may allow some objects for read only
// But currently we are checking wether the objects might be written...
// Let's exclude the objects based on the relevant criteria
if (self::HasSharing() && SharedObject::GetSharedClassProperties($sClass)) {
// This class is shared, GetSelectFilter may allow some objects for read only
// But currently we are checking whether the objects might be written...
// Let's exclude the objects based on the relevant criteria
$sOrgAttCode = self::GetOwnerOrganizationAttCode($sClass);
if (!is_null($sOrgAttCode))
{
$aUserOrgs = $this->GetUserOrgs($oUser, $sClass);
if (!is_null($aUserOrgs) && count($aUserOrgs) > 0)
{
$iCountNO = 0;
$iCountYES = 0;
$oInstanceSet->Rewind();
while($oObject = $oInstanceSet->Fetch())
{
$iOrg = $oObject->Get($sOrgAttCode);
if (in_array($iOrg, $aUserOrgs))
{
$iCountYES++;
}
else
{
$iCountNO++;
}
}
if ($iCountNO == 0)
{
$iPermission = UR_ALLOWED_YES;
}
elseif ($iCountYES == 0)
{
$iPermission = UR_ALLOWED_NO;
}
else
{
$iPermission = UR_ALLOWED_DEPENDS;
// Use $oInstanceSet only if sClass is the main class
if (!is_a($oInstanceSet->GetClass(), $sClass, true)) {
/** @var \DBObjectSet $oInstanceSet */
throw new CoreException(__FUNCTION__.': Expecting object set to be of class '.$sClass.' but it is of class '.$oInstanceSet->GetClass(), ['OQL_Query' => $oInstanceSet->GetFilter()->ToOQL(), 'classes' => $oInstanceSet->GetSelectedClasses()]);
}
$sOrgAttCode = self::GetOwnerOrganizationAttCode($sClass);
if (!is_null($sOrgAttCode)) {
$aUserOrgs = $this->GetUserOrgs($oUser, $sClass);
if (!is_null($aUserOrgs) && count($aUserOrgs) > 0) {
$iCountNO = 0;
$iCountYES = 0;
$oInstanceSet->Rewind();
while ($oObject = $oInstanceSet->Fetch()) {
$iOrg = $oObject->Get($sOrgAttCode);
if (in_array($iOrg, $aUserOrgs)) {
$iCountYES++;
} else {
$iCountNO++;
}
}
if ($iCountNO == 0) {
$iPermission = UR_ALLOWED_YES;
} elseif ($iCountYES == 0) {
$iPermission = UR_ALLOWED_NO;
} else {
$iPermission = UR_ALLOWED_DEPENDS;
}
}
}
}
@@ -982,4 +971,3 @@ class UserRightsProfile extends UserRightsAddOnAPI
UserRights::SelectModule('UserRightsProfile');
?>

View File

@@ -399,25 +399,25 @@ class URP_ActionGrant extends UserRightsBaseClass
{
$aParams = array
(
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "profileid",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_urp_grant_actions",
"db_key_field" => "id",
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "profileid",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_urp_grant_actions",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
// Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked)
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name")));
MetaModel::Init_AddAttribute(new AttributeClass("class", array("class_category"=>"", "more_values"=>"", "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass" => "URP_Profiles", "jointype" => "", "allowed_values" => null, "sql" => "profileid", "is_null_allowed" => false, "on_target_delete" => DEL_SILENT, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("allowed_values" => null, "extkey_attcode" => 'profileid', "target_attcode" => "name")));
MetaModel::Init_AddAttribute(new AttributeClass("class", array("class_category" => "", "more_values" => "", "sql" => "class", "default_value" => null, "is_null_allowed" => false, "depends_on" => array(), "class_exclusion_list" => null)));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values" => new ValueSetEnum('yes,no'), "sql" => "permission", "default_value" => "yes", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("action", array("allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("action", array("allowed_values" => null, "sql" => "action", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('profileid', 'class', 'permission', 'action')); // Attributes to be displayed for the complete details
@@ -435,25 +435,25 @@ class URP_StimulusGrant extends UserRightsBaseClass
{
$aParams = array
(
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "profileid",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_urp_grant_stimulus",
"db_key_field" => "id",
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "profileid",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_urp_grant_stimulus",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
// Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked)
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name")));
MetaModel::Init_AddAttribute(new AttributeClass("class", array("class_category"=>"", "more_values"=>"", "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass" => "URP_Profiles", "jointype" => "", "allowed_values" => null, "sql" => "profileid", "is_null_allowed" => false, "on_target_delete" => DEL_SILENT, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("allowed_values" => null, "extkey_attcode" => 'profileid', "target_attcode" => "name")));
MetaModel::Init_AddAttribute(new AttributeClass("class", array("class_category" => "", "more_values" => "", "sql" => "class", "default_value" => null, "is_null_allowed" => false, "depends_on" => array(), "class_exclusion_list" => null)));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values" => new ValueSetEnum('yes,no'), "sql" => "permission", "default_value" => "yes", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("stimulus", array("allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("stimulus", array("allowed_values" => null, "sql" => "action", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('profileid', 'class', 'permission', 'stimulus')); // Attributes to be displayed for the complete details

View File

@@ -402,7 +402,7 @@ class URP_ClassProjection extends UserRightsBaseClass
MetaModel::Init_AddAttribute(new AttributeExternalKey("dimensionid", array("targetclass"=>"URP_Dimensions", "jointype"=> "", "allowed_values"=>null, "sql"=>"dimensionid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("dimension", array("allowed_values"=>null, "extkey_attcode"=> 'dimensionid', "target_attcode"=>"name")));
MetaModel::Init_AddAttribute(new AttributeClass("class", array("class_category"=>"", "more_values"=>"", "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeClass("class", array("class_category" => "", "more_values" => "", "sql" => "class", "default_value" => null, "is_null_allowed" => false, "depends_on" => array(), "class_exclusion_list" => null)));
MetaModel::Init_AddAttribute(new AttributeString("value", array("allowed_values"=>null, "sql"=>"value", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("attribute", array("allowed_values"=>null, "sql"=>"attribute", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
@@ -459,25 +459,25 @@ class URP_ActionGrant extends UserRightsBaseClass
{
$aParams = array
(
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "profileid",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_urp_grant_actions",
"db_key_field" => "id",
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "profileid",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_urp_grant_actions",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
// Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked)
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name")));
MetaModel::Init_AddAttribute(new AttributeClass("class", array("class_category"=>"", "more_values"=>"", "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass" => "URP_Profiles", "jointype" => "", "allowed_values" => null, "sql" => "profileid", "is_null_allowed" => false, "on_target_delete" => DEL_MANUAL, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("allowed_values" => null, "extkey_attcode" => 'profileid', "target_attcode" => "name")));
MetaModel::Init_AddAttribute(new AttributeClass("class", array("class_category" => "", "more_values" => "", "sql" => "class", "default_value" => null, "is_null_allowed" => false, "depends_on" => array(), "class_exclusion_list" => null)));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values" => new ValueSetEnum('yes,no'), "sql" => "permission", "default_value" => "yes", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("action", array("allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("action", array("allowed_values" => null, "sql" => "action", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('profileid', 'class', 'permission', 'action')); // Attributes to be displayed for the complete details
@@ -495,25 +495,25 @@ class URP_StimulusGrant extends UserRightsBaseClass
{
$aParams = array
(
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "profileid",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_urp_grant_stimulus",
"db_key_field" => "id",
"category" => "addon/userrights",
"key_type" => "autoincrement",
"name_attcode" => "profileid",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_urp_grant_stimulus",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
// Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked)
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name")));
MetaModel::Init_AddAttribute(new AttributeClass("class", array("class_category"=>"", "more_values"=>"", "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass" => "URP_Profiles", "jointype" => "", "allowed_values" => null, "sql" => "profileid", "is_null_allowed" => false, "on_target_delete" => DEL_MANUAL, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("allowed_values" => null, "extkey_attcode" => 'profileid', "target_attcode" => "name")));
MetaModel::Init_AddAttribute(new AttributeClass("class", array("class_category" => "", "more_values" => "", "sql" => "class", "default_value" => null, "is_null_allowed" => false, "depends_on" => array(), "class_exclusion_list" => null)));
MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("allowed_values" => new ValueSetEnum('yes,no'), "sql" => "permission", "default_value" => "yes", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("stimulus", array("allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("stimulus", array("allowed_values" => null, "sql" => "action", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('profileid', 'class', 'permission', 'stimulus')); // Attributes to be displayed for the complete details

View File

@@ -1,331 +0,0 @@
<?php
namespace Html2Text;
if (!function_exists('mb_split'))
{
function mb_split($pattern, $subject, $limit = -1)
{
return preg_split('/'.$pattern.'/', $subject, $limit);
}
}
/**
* Replace all occurrences of the search string with the replacement string.
*
* @author Sean Murphy <sean@iamseanmurphy.com>
* @copyright Copyright 2012 Sean Murphy. All rights reserved.
* @license http://creativecommons.org/publicdomain/zero/1.0/
* @link http://php.net/manual/function.str-replace.php
*
* @param mixed $search
* @param mixed $replace
* @param mixed $subject
* @param int $count
* @return mixed
*/
function mb_str_replace($search, $replace, $subject, &$count = 0) {
if (!is_array($subject)) {
// Normalize $search and $replace so they are both arrays of the same length
$searches = is_array($search) ? array_values($search) : array($search);
$replacements = is_array($replace) ? array_values($replace) : array($replace);
$replacements = array_pad($replacements, count($searches), '');
foreach ($searches as $key => $search) {
$parts = mb_split(preg_quote($search), $subject);
if (is_array($parts))
{
$count += count($parts) - 1;
$subject = implode($replacements[$key], $parts);
}
}
} else {
// Call mb_str_replace for each subject in array, recursively
foreach ($subject as $key => $value) {
$subject[$key] = mb_str_replace($search, $replace, $value, $count);
}
}
return $subject;
}
/******************************************************************************
* Copyright (c) 2010 Jevon Wright and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* or
*
* LGPL which is available at http://www.gnu.org/licenses/lgpl.html
*
*
* Contributors:
* Jevon Wright - initial API and implementation
* Denis Flaven - some fixes for properly handling UTF-8 characters
****************************************************************************/
class Html2Text {
/**
* Tries to convert the given HTML into a plain text format - best suited for
* e-mail display, etc.
*
* <p>In particular, it tries to maintain the following features:
* <ul>
* <li>Links are maintained, with the 'href' copied over
* <li>Information in the &lt;head&gt; is lost
* </ul>
*
* @param string html the input HTML
* @return string the HTML converted, as best as possible, to text
* @throws Html2TextException if the HTML could not be loaded as a {@link DOMDocument}
*/
static function convert($html) {
// replace &nbsp; with spaces
$html = str_replace("&nbsp;", " ", $html);
$html = mb_str_replace("\xc2\xa0", " ", $html); // DO NOT USE str_replace since it breaks the "à" character which is \xc3 \xa0 in UTF-8
$html = static::fixNewlines($html);
$doc = new \DOMDocument();
if (!@$doc->loadHTML('<?xml encoding="UTF-8">'.$html)) // Forces the UTF-8 character set for HTML fragments
{
throw new Html2TextException("Could not load HTML - badly formed?", $html);
}
$output = static::iterateOverNode($doc);
// remove leading and trailing spaces on each line
$output = preg_replace("/[ \t]*\n[ \t]*/im", "\n", $output);
$output = preg_replace("/ *\t */im", "\t", $output);
// remove unnecessary empty lines
$output = preg_replace("/\n\n\n*/im", "\n\n", $output);
// remove leading and trailing whitespace
$output = trim($output);
return $output;
}
/**
* Unify newlines; in particular, \r\n becomes \n, and
* then \r becomes \n. This means that all newlines (Unix, Windows, Mac)
* all become \ns.
*
* @param string text text with any number of \r, \r\n and \n combinations
* @return string the fixed text
*/
static function fixNewlines($text) {
// replace \r\n to \n
$text = str_replace("\r\n", "\n", $text);
// remove \rs
$text = str_replace("\r", "\n", $text);
return $text;
}
static function nextChildName($node) {
// get the next child
$nextNode = $node->nextSibling;
while ($nextNode != null) {
if ($nextNode instanceof \DOMElement) {
break;
}
$nextNode = $nextNode->nextSibling;
}
$nextName = null;
if ($nextNode instanceof \DOMElement && $nextNode != null) {
$nextName = strtolower($nextNode->nodeName);
}
return $nextName;
}
static function prevChildName($node) {
// get the previous child
$nextNode = $node->previousSibling;
while ($nextNode != null) {
if ($nextNode instanceof \DOMElement) {
break;
}
$nextNode = $nextNode->previousSibling;
}
$nextName = null;
if ($nextNode instanceof \DOMElement && $nextNode != null) {
$nextName = strtolower($nextNode->nodeName);
}
return $nextName;
}
static function iterateOverNode($node) {
if ($node instanceof \DOMText) {
// Replace whitespace characters with a space (equivilant to \s)
return preg_replace("/[\\t\\n\\f\\r ]+/im", " ", $node->wholeText);
}
if ($node instanceof \DOMDocumentType) {
// ignore
return "";
}
$nextName = static::nextChildName($node);
$prevName = static::prevChildName($node);
$name = strtolower($node->nodeName);
// start whitespace
switch ($name) {
case "hr":
return "---------------------------------------------------------------\n";
case "style":
case "head":
case "title":
case "meta":
case "script":
// ignore these tags
return "";
case "h1":
case "h2":
case "h3":
case "h4":
case "h5":
case "h6":
case "ol":
case "ul":
// add two newlines, second line is added below
$output = "\n";
break;
case "td":
case "th":
// add tab char to separate table fields
$output = "\t";
break;
case "tr":
case "p":
case "div":
// add one line
$output = "\n";
break;
case "li":
$output = "- ";
break;
default:
// print out contents of unknown tags
$output = "";
break;
}
// debug
//$output .= "[$name,$nextName]";
if (isset($node->childNodes)) {
for ($i = 0; $i < $node->childNodes->length; $i++) {
$n = $node->childNodes->item($i);
$text = static::iterateOverNode($n);
$output .= $text;
}
}
// end whitespace
switch ($name) {
case "h1":
case "h2":
case "h3":
case "h4":
case "h5":
case "h6":
$output .= "\n";
break;
case "p":
case "br":
// add one line
if ($nextName != "div")
$output .= "\n";
break;
case "div":
// add one line only if the next child isn't a div
if ($nextName != "div" && $nextName != null)
$output .= "\n";
break;
case "a":
// links are returned in [text](link) format
$href = $node->getAttribute("href");
$output = trim($output);
// remove double [[ ]] s from linking images
if (substr($output, 0, 1) == "[" && substr($output, -1) == "]") {
$output = substr($output, 1, strlen($output) - 2);
// for linking images, the title of the <a> overrides the title of the <img>
if ($node->getAttribute("title")) {
$output = $node->getAttribute("title");
}
}
// if there is no link text, but a title attr
if (!$output && $node->getAttribute("title")) {
$output = $node->getAttribute("title");
}
if ($href == null) {
// it doesn't link anywhere
if ($node->getAttribute("name") != null) {
$output = "[$output]";
}
} else {
if ($href == $output || $href == "mailto:$output" || $href == "http://$output" || $href == "https://$output") {
// link to the same address: just use link
$output;
} else {
// replace it
if ($output) {
$output = "[$output]($href)";
} else {
// empty string
$output = $href;
}
}
}
// does the next node require additional whitespace?
switch ($nextName) {
case "h1": case "h2": case "h3": case "h4": case "h5": case "h6":
$output .= "\n";
break;
}
break;
case "img":
if ($node->getAttribute("title")) {
$output = "[" . $node->getAttribute("title") . "]";
} elseif ($node->getAttribute("alt")) {
$output = "[" . $node->getAttribute("alt") . "]";
} else {
$output = "";
}
break;
case "li":
$output .= "\n";
break;
default:
// do nothing
}
return $output;
}
}

View File

@@ -1,28 +0,0 @@
<?php
/******************************************************************************
* Copyright (c) 2010 Jevon Wright and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* or
*
* LGPL which is available at http://www.gnu.org/licenses/lgpl.html
*
*
* Contributors:
* Jevon Wright - initial API and implementation
****************************************************************************/
namespace Html2Text;
class Html2TextException extends \Exception {
var $more_info;
public function __construct($message = "", $more_info = "") {
parent::__construct($message);
$this->more_info = $more_info;
}
}

View File

@@ -195,16 +195,31 @@ class ApplicationContext
/**
* Returns the context as string with the format name1=value1&name2=value2....
* @return string The context as a string to be appended to an href property
*
*/
public function GetForLink()
public function GetForLink(bool $bWithLeadingAmpersand = false)
{
// If there are no parameters, return an empty string
if(empty($this->aValues)){
return '';
}
// Build the query string with ampersand separated parameters
$aParams = array();
foreach($this->aValues as $sName => $sValue)
{
$aParams[] = "c[$sName]".'='.urlencode($sValue);
}
return implode("&", $aParams);
$sReturnValue = implode('&', $aParams);
// add the leading ampersand if requested
if($bWithLeadingAmpersand){
$sReturnValue = '&' . $sReturnValue;
}
return $sReturnValue;
}
/**
* @since 3.0.0 N°2534 - dashboard: bug with autorefresh that deactivates filtering on organisation
* Returns the params as c[menu]:..., c[org_id]:....
@@ -382,7 +397,7 @@ class ApplicationContext
$sUrl = call_user_func(array($sUrlMakerClass, 'MakeObjectUrl'), $sObjClass, $sObjKey);
if (utils::StrLen($sUrl) > 0) {
if ($bWithNavigationContext) {
return $sUrl."&".$oAppContext->GetForLink();
return $sUrl.$oAppContext->GetForLink(true);
} else {
return $sUrl;
}

View File

@@ -64,13 +64,13 @@ interface iLoginFSMExtension extends iLoginExtension
* If a page is displayed, the action must exit at this point
* if LoginWebPage::LOGIN_FSM_RETURN_ERROR is returned $iErrorCode must be set
* if LoginWebPage::LOGIN_FSM_RETURN_OK is returned then the login is OK and terminated
* if LoginWebPage::LOGIN_FSM_RETURN_IGNORE is returned then the FSM will proceed to next plugin or state
* if LoginWebPage::LOGIN_FSM_CONTINUE is returned then the FSM will proceed to next plugin or state
*
* @api
* @param string $sLoginState (see LoginWebPage::LOGIN_STATE_...)
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
*/
public function LoginAction($sLoginState, &$iErrorCode);
}
@@ -83,7 +83,7 @@ interface iLoginFSMExtension extends iLoginExtension
* * If a page is displayed, the action must exit at this point
* * if LoginWebPage::LOGIN_FSM_RETURN_ERROR is returned $iErrorCode must be set
* * if LoginWebPage::LOGIN_FSM_RETURN_OK is returned then the login is OK and terminated
* * if LoginWebPage::LOGIN_FSM_RETURN_IGNORE is returned then the FSM will proceed to next plugin or to next state
* * if LoginWebPage::LOGIN_FSM_CONTINUE is returned then the FSM will proceed to next plugin or to next state
*
* @api
* @package LoginExtensibilityAPI
@@ -136,7 +136,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
*/
protected function OnStart(&$iErrorCode)
{
@@ -150,7 +150,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
*/
protected function OnModeDetection(&$iErrorCode)
{
@@ -167,7 +167,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
*/
protected function OnReadCredentials(&$iErrorCode)
{
@@ -181,7 +181,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
*/
protected function OnCheckCredentials(&$iErrorCode)
{
@@ -192,7 +192,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
*/
protected function OnCredentialsOK(&$iErrorCode)
{
@@ -203,7 +203,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
*/
protected function OnUsersOK(&$iErrorCode)
{
@@ -214,7 +214,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
*/
protected function OnConnected(&$iErrorCode)
{
@@ -225,7 +225,7 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_CONTINUE
*/
protected function OnError(&$iErrorCode)
{
@@ -1711,6 +1711,16 @@ interface iRestServiceProvider
public function ExecOperation($sVersion, $sVerb, $aParams);
}
/**
* A REST service provider implementing this interface will have its input JSON data sanitized for logging purposes
* @since 2.7.13, 3.2.1-1
* @see \iRestServiceProvider
*/
interface iRestInputSanitizer
{
public function SanitizeJsonInput(string $sJsonInput): string;
}
/**
* Minimal REST response structure. Derive this structure to add response data and error codes.
*
@@ -1802,6 +1812,14 @@ class RestResult
* @api
*/
public $message;
/**
* Sanitize the content of this result to hide sensitive information
*/
public function SanitizeContent()
{
// The default implementation does nothing
}
}
/**

View File

@@ -311,7 +311,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
{
$sParams .= $sName.'='.urlencode($value).'&'; // Always add a trailing &
}
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/'.$oObj->GetUIPage().'?'.$sParams.'class='.get_class($oObj).'&id='.$oObj->getKey().'&'.$oAppContext->GetForLink().'&a=1';
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/'.$oObj->GetUIPage().'?'.$sParams.'class='.get_class($oObj).'&id='.$oObj->getKey().$oAppContext->GetForLink(true).'&a=1';
$oPage->add_early_script(<<<JS
if (!sessionStorage.getItem('$sSessionStorageKey'))
{
@@ -698,10 +698,10 @@ HTML
$sLinkedClass = $oAttDef->GetLinkedClass();
// Filter out links pointing to obsolete objects (if relevant)
$oOrmLinkSet = $this->Get($sAttCode);
$oLinkSet = $oOrmLinkSet->ToDBObjectSet(utils::ShowObsoleteData());
$oOrmLinkSet = $this->Get($sAttCode);
$oLinkSet = $oOrmLinkSet->ToDBObjectSet(utils::ShowObsoleteData());
$iCount = $oLinkSet->Count();
$iCount = $oLinkSet->Count();
if ($this->IsNew()) {
$iFlags = $this->GetInitialStateAttributeFlags($sAttCode);
} else {
@@ -766,9 +766,9 @@ HTML
$oPage->add($sHTMLValue);
} else {
if ($oAttDef->IsIndirect()) {
$oBlockLinkSetViewTable = new BlockIndirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef, $bReadOnly);
$oBlockLinkSetViewTable = new BlockIndirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef, $bReadOnly, $iCount);
} else {
$oBlockLinkSetViewTable = new BlockDirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef, $bReadOnly);
$oBlockLinkSetViewTable = new BlockDirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef, $bReadOnly, $iCount);
}
$oPage->AddUiBlock($oBlockLinkSetViewTable);
}
@@ -805,16 +805,16 @@ HTML
if (!$this->IsNew()) {
// Look for any trigger that considers this object as "In Scope"
// If any trigger has been found then display a tab with notifications
//
// If all triggers on an object have been deleted, we consider that we no longer need the event notification information
$aTriggers = $this->GetRelatedTriggersIDs();
if (count($aTriggers) > 0) {
$iId = $this->GetKey();
$aParams = array('triggers' => $aTriggers, 'id' => $iId);
$aParams = array('class' => get_class($this), 'id' => $iId);
$aNotifSearches = array();
$iNotifsCount = 0;
$aNotificationClasses = MetaModel::EnumChildClasses('EventNotification');
foreach ($aNotificationClasses as $sNotifClass) {
$aNotifSearches[$sNotifClass] = DBObjectSearch::FromOQL("SELECT $sNotifClass AS Ev JOIN Trigger AS T ON Ev.trigger_id = T.id WHERE T.id IN (:triggers) AND Ev.object_id = :id");
$aNotifSearches[$sNotifClass] = DBObjectSearch::FromOQL("SELECT $sNotifClass AS Ev WHERE Ev.object_id = :id AND Ev.object_class = :class");
$aNotifSearches[$sNotifClass]->SetInternalParams($aParams);
$oNotifSet = new DBObjectSet($aNotifSearches[$sNotifClass], array());
$iNotifsCount += $oNotifSet->Count();
@@ -1113,8 +1113,10 @@ HTML
}
// Note: DisplayBareHeader is called before adding $oObjectDetails to the page, so it can inject HTML before it through $oPage.
/** @var iTopWebPage $oPage */
/** @var \iTopWebPage $oPage */
$oKPI = new ExecutionKPI();
$aHeadersBlocks = $this->DisplayBareHeader($oPage, $bEditMode);
$oKPI->ComputeStatsForExtension($this, 'DisplayBareHeader');
if (false === empty($aHeadersBlocks['subtitle'])) {
$oObjectDetails->AddSubTitleBlocks($aHeadersBlocks['subtitle']);
}
@@ -1127,8 +1129,12 @@ HTML
$oPage->AddTabContainer(OBJECT_PROPERTIES_TAB, '', $oObjectDetails);
$oPage->SetCurrentTabContainer(OBJECT_PROPERTIES_TAB);
$oPage->SetCurrentTab('UI:PropertiesTab');
$oKPI = new ExecutionKPI();
$this->DisplayBareProperties($oPage, $bEditMode);
$oKPI->ComputeStatsForExtension($this, 'DisplayBareProperties');
$oKPI = new ExecutionKPI();
$this->DisplayBareRelations($oPage, $bEditMode);
$oKPI->ComputeStatsForExtension($this, 'DisplayBareRelations');
// Note: Adding the JS snippet which enables the image upload should have been done directly by the ActivityPanel which would have kept the independance principle
@@ -1331,7 +1337,7 @@ HTML
}
}
}
$aHeader['friendlyname'] = ['label' => MetaModel::GetName($sClassName)];
foreach ($aList[$sAlias] as $sAttCodeEx => $oAttDef) {
$sColLabel = $bLocalize ? MetaModel::GetLabel($sClassName, $sAttCodeEx) : $sAttCodeEx;
@@ -1352,6 +1358,7 @@ HTML
$aRow = [];
foreach ($aAuthorizedClasses as $sAlias => $sClassName) {
$oObj = $aObjects[$sAlias];
$aRow["friendlyname"] = $oObj->Get('friendlyname');
foreach ($aList[$sAlias] as $sAttCodeEx => $oAttDef) {
if (is_null($oObj)) {
$aRow[$oAttDef->GetCode()] = '';
@@ -3065,7 +3072,7 @@ JS
$oPage->add($oAppContext->GetForForm());
// Hook the cancel button via jQuery so that it can be unhooked easily as well if needed
$sDefaultUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_form&class='.$sClass.'&'.$oAppContext->GetForLink();
$sDefaultUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_form&class='.$sClass.$oAppContext->GetForLink(true);
$sCancelButtonOnClickScript = "let fOnClick{$this->m_iFormId}CancelButton = ";
if(isset($aExtraParams['js_handlers']['cancel_button_on_click'])){
@@ -3073,7 +3080,7 @@ JS
} else {
$sCancelButtonOnClickScript .= "function() { BackToDetails('$sClass', $iKey, '$sDefaultUrl', $sJSToken)};";
}
$sCancelButtonOnClickScript .= "$('#form_{$this->m_iFormId} button.cancel').on('click', fOnClick{$this->m_iFormId}CancelButton);";
$sCancelButtonOnClickScript .= "$('#form_{$this->m_iFormId} button.cancel').on('click.navigation.itop', fOnClick{$this->m_iFormId}CancelButton);";
$oPage->add_ready_script($sCancelButtonOnClickScript);
$iFieldsCount = count($aFieldsMap);
@@ -3439,8 +3446,18 @@ EOF
}
$sInputType = '';
$sInputId = 'att_'.$iFieldIndex;
$value = $this->Get($sAttCode);
$sDisplayValue = $this->GetEditValue($sAttCode);
if ($oAttDef instanceof AttributeDateTime && !$oAttDef->IsNullAllowed() && $value === $oAttDef->GetNullValue()) {
$value = $oAttDef->GetDefaultValue($this);
if ($value !== $oAttDef->GetNullValue()) {
// Set default date
$this->Set($sAttCode, $value);
$sDisplayValue = $this->GetEditValue($sAttCode);
}
}
$sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef,
$this->Get($sAttCode), $this->GetEditValue($sAttCode), $sInputId, '', $iExpectCode,
$value, $sDisplayValue, $sInputId, '', $iExpectCode,
$aArgs, true, $sInputType);
$aAttrib = array(
'label' => '<span>'.$oAttDef->GetLabel().'</span>',
@@ -4570,6 +4587,8 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBInsert()");
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBInsert($oNewObj, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
@@ -4651,7 +4670,22 @@ HTML;
return $oDeletionPlan;
}
protected function PostDeleteActions(): void
final protected function PreDeleteActions(): void
{
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBDelete()");
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete');
}
parent::PreDeleteActions();
}
final protected function PostDeleteActions(): void
{
parent::PostDeleteActions();
}
@@ -4665,6 +4699,8 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBDelete()");
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete');
@@ -4686,6 +4722,7 @@ HTML;
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified()");
$oKPI = new ExecutionKPI();
$bIsModified = $oExtensionInstance->OnIsModified($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnIsModified');
@@ -4745,6 +4782,8 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnCheckToWrite()");
$oKPI = new ExecutionKPI();
$aNewIssues = $oExtensionInstance->OnCheckToWrite($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToWrite');
@@ -4795,6 +4834,8 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnCheckToDelete()");
$oKPI = new ExecutionKPI();
$aNewIssues = $oExtensionInstance->OnCheckToDelete($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToDelete');
@@ -5166,7 +5207,7 @@ HTML
$aKeys = array_keys($aValues[$sAttCode]);
$currValue = $aKeys[0]; // The only value is the first key
if ($oAttDef->GetEditClass() == 'LinkedSet') {
$oOrmLinkSet = $oDummyObj->Get($sAttCode);
$oOrmLinkSet = $oDummyObj->Get($sAttCode);
LinkSetDataTransformer::StringToOrmLinkSet($aValues[$sAttCode][$currValue]['edit_value'], $oOrmLinkSet);
} else {
@@ -5220,7 +5261,7 @@ HTML
}
$oDummyObj->Set($sAttCode, $oTagSet);
} else if ($oAttDef->GetEditClass() == 'LinkedSet') {
$oOrmLinkSet = $oDummyObj->Get($sAttCode);
$oOrmLinkSet = $oDummyObj->Get($sAttCode);
foreach ($aMultiValues as $key => $sValue) {
LinkSetDataTransformer::StringToOrmLinkSet($sValue['edit_value'], $oOrmLinkSet);
}
@@ -5899,14 +5940,14 @@ JS
*
* @since 3.1.0
*/
final protected function FireEventCheckToWrite(): void
final protected function FireEventCheckToWrite(?string $sStimulusBeingApplied): void
{
$this->FireEvent(EVENT_DB_CHECK_TO_WRITE, ['is_new' => $this->IsNew()]);
$this->FireEvent(EVENT_DB_CHECK_TO_WRITE, ['is_new' => $this->IsNew(), 'stimulus_applied' => $sStimulusBeingApplied]);
}
final protected function FireEventBeforeWrite()
final protected function FireEventBeforeWrite(?string $sStimulusBeingApplied)
{
$this->FireEvent(EVENT_DB_BEFORE_WRITE, ['is_new' => $this->IsNew()]);
$this->FireEvent(EVENT_DB_BEFORE_WRITE, ['is_new' => $this->IsNew(), 'stimulus_applied' => $sStimulusBeingApplied]);
}
/**
@@ -5918,11 +5959,11 @@ JS
* @throws \CoreException
* @since 3.1.0
*/
final protected function FireEventAfterWrite(array $aChanges, bool $bIsNew): void
final protected function FireEventAfterWrite(array $aChanges, bool $bIsNew, ?string $sStimulusBeingApplied): void
{
$this->NotifyAttachedObjectsOnLinkClassModification();
$this->RemoveObjectAwaitingEventDbLinksChanged(get_class($this), $this->GetKey());
$this->FireEvent(EVENT_DB_AFTER_WRITE, ['is_new' => $bIsNew, 'changes' => $aChanges]);
$this->FireEvent(EVENT_DB_AFTER_WRITE, ['is_new' => $bIsNew, 'changes' => $aChanges, 'stimulus_applied' => $sStimulusBeingApplied]);
}
//////////////
@@ -6063,7 +6104,9 @@ JS
// We want to avoid launching the listener twice, first here, and secondly after saving the Ticket in the listener
// By disabling the event to be fired, we can remove the current object from the attribute !
$oObject = MetaModel::GetObject($sClass, $sId, false);
self::FireEventDbLinksChangedForObject($oObject);
if (!is_null($oObject)) {
self::FireEventDbLinksChangedForObject($oObject);
}
self::RemoveObjectAwaitingEventDbLinksChanged($sClass, $sId);
}
@@ -6071,13 +6114,11 @@ JS
{
self::SetEventDBLinksChangedBlocked(true);
// N°6408 The object can have been deleted
if (!is_null($oObject)) {
$oObject->FireEvent(EVENT_DB_LINKS_CHANGED);
$oObject->FireEvent(EVENT_DB_LINKS_CHANGED);
// Update the object if needed
if (count($oObject->ListChanges()) !== 0) {
$oObject->DBUpdate();
}
// Update the object if needed
if (count($oObject->ListChanges()) !== 0) {
$oObject->DBUpdate();
}
cmdbAbstractObject::SetEventDBLinksChangedBlocked(false);
}
@@ -6155,9 +6196,9 @@ JS
* @inheritDoc
* @throws \CoreException
*/
final protected function FireEventComputeValues(): void
final protected function FireEventComputeValues(?string $sStimulusBeingApplied): void
{
$this->FireEvent(EVENT_DB_COMPUTE_VALUES);
$this->FireEvent(EVENT_DB_COMPUTE_VALUES, ['is_new' => $this->IsNew(), 'stimulus_applied' => $sStimulusBeingApplied]);
}
/**

View File

@@ -296,21 +296,21 @@ abstract class Dashboard
public function FromParams($aParams)
{
$this->sLayoutClass = $aParams['layout_class'];
if (!is_subclass_of($this->sLayoutClass,DashboardLayout::class)) {
throw new InvalidParameterException('Invalid parameter layout_class "'.$aParams['layout_class'].'"');
}
$this->sTitle = $aParams['title'];
$this->bAutoReload = $aParams['auto_reload'] == 'true';
$this->iAutoReloadSec = max(MetaModel::GetConfig()->Get('min_reload_interval'), (int) $aParams['auto_reload_sec']);
foreach($aParams['cells'] as $aCell)
{
foreach($aParams['cells'] as $aCell) {
$aCellDashlets = array();
foreach($aCell as $aDashletParams)
{
foreach($aCell as $aDashletParams) {
$sDashletClass = $aDashletParams['dashlet_class'];
$sId = $aDashletParams['dashlet_id'];
/** @var \Dashlet $oNewDashlet */
$oNewDashlet = new $sDashletClass($this->oMetaModel, $sId);
if (isset($aDashletParams['dashlet_type']))
{
if (isset($aDashletParams['dashlet_type'])) {
$oNewDashlet->SetDashletType($aDashletParams['dashlet_type']);
}
$oForm = $oNewDashlet->GetForm();
@@ -524,9 +524,7 @@ EOF
*/
public function Render($oPage, $bEditMode = false, $aExtraParams = array(), $bCanEdit = true)
{
if (!array_key_exists('dashboard_div_id', $aExtraParams)) {
$aExtraParams['dashboard_div_id'] = utils::Sanitize($this->GetId(), '', 'element_identifier');
}
$aExtraParams['dashboard_div_id'] = utils::Sanitize($aExtraParams['dashboard_div_id'] ?? null, $this->GetId(), utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
/** @var \DashboardLayoutMultiCol $oLayout */
$oLayout = new $this->sLayoutClass();
@@ -1052,7 +1050,7 @@ EOF
$sSelectorHtml .= '</div>';
$sFile = addslashes($this->GetDefinitionFile());
$sReloadURL = $this->GetReloadURL();
$sReloadURL = json_encode($this->GetReloadURL());
$bFromDashboardPage = isset($aAjaxParams['from_dashboard_page']) ? isset($aAjaxParams['from_dashboard_page']) : false;
if ($bFromDashboardPage) {
@@ -1141,7 +1139,6 @@ JS
->AddCSSClass('ibo-action-button');
$oToolbar->AddSubBlock($oActionButton);
$aActions = array();
$sFile = addslashes(utils::LocalPath($this->sDefinitionFile));
$sJSExtraParams = json_encode($aExtraParams);
@@ -1166,7 +1163,7 @@ JS
$oToolbar->AddSubBlock($oActionButton)
->AddSubBlock($oActionsMenu);
$sReloadURL = $this->GetReloadURL();
$sReloadURL = json_encode($this->GetReloadURL());
$oPage->add_script(
<<<EOF
function EditDashboard(sId, sDashboardFile, aExtraParams)
@@ -1266,15 +1263,14 @@ EOF
$sOkButtonLabel = Dict::S('UI:Button:Save');
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$sId = utils::HtmlEntities($this->sId);
$sLayoutClass = utils::HtmlEntities($this->sLayoutClass);
$sId = json_encode($this->sId);
$sLayoutClass = json_encode($this->sLayoutClass);
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
$sAutoReloadSec = (string) $this->iAutoReloadSec;
$sTitle = utils::HtmlEntities($this->sTitle);
$sFile = utils::HtmlEntities($this->GetDefinitionFile());
$sFileForJS = json_encode($this->GetDefinitionFile());
$sTitle = json_encode($this->sTitle);
$sFile = json_encode($this->GetDefinitionFile());
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
$sReloadURL = $this->GetReloadURL();
$sReloadURL = json_encode($this->GetReloadURL());
$sExitConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
$sCancelConfirmationMessage = addslashes(Dict::S('UI:CancelConfirmationMessage'));
@@ -1328,15 +1324,15 @@ $('#dashboard_editor').dialog({
});
$('#dashboard_editor .ui-layout-center').runtimedashboard({
dashboard_id: '$sId',
layout_class: '$sLayoutClass',
title: '$sTitle',
dashboard_id: $sId,
layout_class: $sLayoutClass,
title: $sTitle,
auto_reload: $sAutoReload,
auto_reload_sec: $sAutoReloadSec,
submit_to: '$sUrl',
submit_parameters: {operation: 'save_dashboard', file: {$sFileForJS}, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
submit_parameters: {operation: 'save_dashboard', file: $sFile, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
render_to: '$sUrl',
render_parameters: {operation: 'render_dashboard', file: {$sFileForJS}, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
render_parameters: {operation: 'render_dashboard', file: $sFile, extra_params: $sJSExtraParams, reload_url: '$sReloadURL'},
new_dashlet_parameters: {operation: 'new_dashlet'}
});

View File

@@ -2138,7 +2138,7 @@ class DashletHeaderDynamic extends Dashlet
$oSet = new DBObjectSet($oFilter);
$iCount = $oSet->Count();
$oAppContext = new ApplicationContext();
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($oFilter->serialize());
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search'.$oAppContext->GetForLink(true).'&filter='.rawurlencode($oFilter->serialize());
$oSubTitle->AddHtml('<a class="summary" href="'.$sHyperlink.'">'.Dict::Format(str_replace('_', ':', $sSubtitle), $iCount).'</a>');
return $oPanel;

View File

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

View File

@@ -263,6 +263,8 @@ class DisplayBlock
/** param for export.php */
'refresh_action',
/**to add refresh button in datatable*/
'object_count',
/** int number of objects in list */
], DataTableUIBlockFactory::GetAllowedParams()),
static::ENUM_STYLE_LIST_SEARCH => array_merge([
'update_history',
@@ -706,7 +708,7 @@ class DisplayBlock
if ($bDoSearch)
{
// Keep the table_id identifying this table if we're performing a search
$sTableId = utils::ReadParam('_table_id_', null, false, 'raw_data');
$sTableId = utils::ReadParam('_table_id_', null, false, utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
if ($sTableId != null)
{
$aExtraParams['table_id'] = $sTableId;
@@ -1124,7 +1126,7 @@ JS
$oSingleGroupByValueFilter->SetShowObsoleteData($this->m_bShowObsoleteData);
}
$sHyperlink = utils::GetAbsoluteUrlAppRoot()
.'pages/UI.php?operation=search&'.$oAppContext->GetForLink()
.'pages/UI.php?operation=search'.$oAppContext->GetForLink(true)
.'&filter='.rawurlencode($oSingleGroupByValueFilter->serialize());
$aCounts[$sStateValue] = ['link' => $sHyperlink, 'label' => $aCounts[$sStateValue]];
}
@@ -1232,7 +1234,7 @@ JS
$iCount = $this->m_oSet->Count();
$sClassLabel = MetaModel::GetName($sClass);
$sClassIconUrl = MetaModel::GetClassIcon($sClass, false);
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($this->m_oFilter->serialize());
$sHyperlink = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search'.$oAppContext->GetForLink(true).'&filter='.rawurlencode($this->m_oFilter->serialize());
$aExtraParams['query_params'] = $this->m_oFilter->GetInternalParams();
$aRefreshParams = [
@@ -1241,7 +1243,7 @@ JS
];
if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY)) {
$sCreateActionUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=new&class='.$sClass.'&'.$oAppContext->GetForLink();
$sCreateActionUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=new&class='.$sClass.$oAppContext->GetForLink(true);
$sCreateActionLabel = Dict::Format('UI:Button:Create');
$oBlock = DashletFactory::MakeForDashletBadge($sClassIconUrl, $sHyperlink, $iCount, $sClassLabel, $sCreateActionUrl,
$sCreateActionLabel, $aRefreshParams);
@@ -1289,7 +1291,7 @@ JS
$aData = array();
$oAppContext = new ApplicationContext();
$sParams = $oAppContext->GetForLink();
$sParams = $oAppContext->GetForLink(true);
foreach ($aGroupBy as $iRow => $iCount) {
// Build the search for this subset
$oSubsetSearch = $this->m_oFilter->DeepClone();
@@ -1304,7 +1306,7 @@ JS
$aData[] = array(
'group' => $aLabels[$iRow],
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1&$sParams&filter=$sFilter\">$iCount</a>"
'value' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1$sParams&filter=$sFilter\">$iCount</a>"
); // TO DO: add the context information
}
$aAttribs = array(
@@ -1636,7 +1638,7 @@ JS
$sGroupByExpr = isset($aExtraParams['group_by_expr']) ? '&params[group_by_expr]='.$aExtraParams['group_by_expr'] : '';
$sFilter = $this->m_oFilter->serialize(false, $aQueryParams);
$oContext = new ApplicationContext();
$sContextParam = $oContext->GetForLink();
$sContextParam = $oContext->GetForLink(true);
$sAggregationFunction = isset($aExtraParams['aggregation_function']) ? $aExtraParams['aggregation_function'] : '';
$sAggregationAttr = isset($aExtraParams['aggregation_attribute']) ? $aExtraParams['aggregation_attribute'] : '';
$sLimit = isset($aExtraParams['limit']) ? $aExtraParams['limit'] : '';
@@ -1644,7 +1646,7 @@ JS
$sOrderDirection = isset($aExtraParams['order_direction']) ? $aExtraParams['order_direction'] : '';
if (isset($aExtraParams['group_by_label'])) {
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[group_by_label]={$aExtraParams['group_by_label']}&params[chart_type]=$sChartType&params[currentId]=$sChartId{$iChartCounter}&params[order_direction]=$sOrderDirection&params[order_by]=$sOrderBy&params[limit]=$sLimit&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sChartId{$iChartCounter}&filter=".rawurlencode($sFilter).'&'.$sContextParam;
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[group_by_label]={$aExtraParams['group_by_label']}&params[chart_type]=$sChartType&params[currentId]=$sChartId{$iChartCounter}&params[order_direction]=$sOrderDirection&params[order_by]=$sOrderBy&params[limit]=$sLimit&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sChartId{$iChartCounter}&filter=".rawurlencode($sFilter).$sContextParam;
} else {
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$sGroupBy{$sGroupByExpr}&params[chart_type]=$sChartType&params[currentId]=$sChartId{$iChartCounter}&params[order_direction]=$sOrderDirection&params[order_by]=$sOrderBy&params[limit]=$sLimit&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sChartId{$iChartCounter}&filter=".rawurlencode($sFilter).'&'.$sContextParam;
}
@@ -1681,24 +1683,24 @@ JS
$oBlock = null;
$sJSURLs = '';
$oContext = new ApplicationContext();
$sContextParam = $oContext->GetForLink(true);
if (isset($aExtraParams['group_by'])) {
$this->MakeGroupByQuery($aExtraParams, $oGroupByExp, $sGroupByLabel, $aGroupBy, $sAggregationFunction, $sFctVar, $sAggregationAttr, $sSql);
$aRes = CMDBSource::QueryToArray($sSql);
$oContext = new ApplicationContext();
$sContextParam = $oContext->GetForLink();
/** @var AttributeDefinition $oGroupByAttDef */
$oGroupByAttDef = $aGroupBy["grouped_by_1"]->GetAttDef();
$iTotalCount = 0;
$aURLs = array();
foreach ($aRes as $iRow => $aRow) {
$sValue = $aRow['grouped_by_1'];
$sPlainTextValue = $oGroupByAttDef->GetValueLabel($sValue);
$sHtmlValue = utils::EscapeHtml($sPlainTextValue);
$sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue);
$iTotalCount += $aRow['_itop_count_'];
$aValues[] = array(
'label' => $sPlainTextValue,
'label' => html_entity_decode(strip_tags($sHtmlValue), ENT_QUOTES, 'UTF-8'),
'label_html' => $sHtmlValue,
'value' => (float)$aRow[$sFctVar],
);
@@ -1708,14 +1710,14 @@ JS
$oSubsetSearch = $this->m_oFilter->DeepClone();
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($sValue));
$oSubsetSearch->AddConditionExpression($oCondition);
$aURLs[] = utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&format=html&filter=".rawurlencode($oSubsetSearch->serialize()).'&'.$sContextParam;
$aURLs[] = utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&format=html&filter=".rawurlencode($oSubsetSearch->serialize()).$sContextParam;
}
$sJSURLs = json_encode($aURLs);
}
if (isset($aExtraParams['group_by_label'])) {
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$aExtraParams[group_by]&params[group_by_label]={$aExtraParams['group_by_label']}&params[chart_type]=$sChartType&params[currentId]=$aExtraParams[currentId]&params[order_direction]=$aExtraParams[order_direction]&params[order_by]=$aExtraParams[order_by]&params[limit]=$aExtraParams[limit]&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sId&filter=".rawurlencode($this->m_oFilter->ToOQL()).'&'.$sContextParam;
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$aExtraParams[group_by]&params[group_by_label]={$aExtraParams['group_by_label']}&params[chart_type]=$sChartType&params[currentId]=$aExtraParams[currentId]&params[order_direction]=$aExtraParams[order_direction]&params[order_by]=$aExtraParams[order_by]&params[limit]=$aExtraParams[limit]&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sId&filter=".rawurlencode($this->m_oFilter->ToOQL()).$sContextParam;
} else {
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$aExtraParams[group_by]&params[chart_type]=$sChartType&params[currentId]=$aExtraParams[currentId]&params[order_direction]=$aExtraParams[order_direction]&params[order_by]=$aExtraParams[order_by]&params[limit]=$aExtraParams[limit]&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sId&filter=".rawurlencode($this->m_oFilter->ToOQL()).'&'.$sContextParam;
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/ajax.render.php?operation=chart&params[group_by]=$aExtraParams[group_by]&params[chart_type]=$sChartType&params[currentId]=$aExtraParams[currentId]&params[order_direction]=$aExtraParams[order_direction]&params[order_by]=$aExtraParams[order_by]&params[limit]=$aExtraParams[limit]&params[aggregation_function]=$sAggregationFunction&params[aggregation_attribute]=$sAggregationAttr&id=$sId&filter=".rawurlencode($this->m_oFilter->ToOQL()).$sContextParam;
}
switch ($sChartType) {
@@ -1788,7 +1790,7 @@ JS
$oBlock->sCsvFile = strtolower($this->m_oFilter->GetClass()).'.csv';
$oBlock->sDownloadLink = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?expression='.urlencode($this->m_oFilter->ToOQL(true)).'&format=csv&filename='.urlencode($oBlock->sCsvFile);
$oBlock->sLinkToToggle = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&'.$oAppContext->GetForLink().'&filter='.rawurlencode($this->m_oFilter->serialize()).'&format=csv';
$oBlock->sLinkToToggle = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search'.$oAppContext->GetForLink(true).'&filter='.rawurlencode($this->m_oFilter->serialize()).'&format=csv';
// Pass the parameters via POST, since expression may be very long
$aParamsToPost = array(
'expression' => $this->m_oFilter->ToOQL(true),
@@ -1863,7 +1865,11 @@ class MenuBlock extends DisplayBlock
$aSelectedClasses = $this->GetFilter()->GetSelectedClasses();
$bIsForLinkset = isset($aExtraParams['target_attr']);
$oSet = new CMDBObjectSet($this->GetFilter());
$iSetCount = $oSet->Count();
if(isset($aExtraParams['object_count'])){
$iSetCount = $aExtraParams['object_count'];
} else {
$iSetCount = $oSet->Count();
}
/** @var string $sRefreshAction JS snippet to run when clicking on the refresh button of the menu */
$sRefreshAction = $aExtraParams['refresh_action'] ?? '';
$bIsCreationInModal = isset($aExtraParams['creation_in_modal']) && $aExtraParams['creation_in_modal'] === true;
@@ -1888,10 +1894,7 @@ class MenuBlock extends DisplayBlock
&& (!isset($aExtraParams['menu']) || $aExtraParams['menu'] === "1" || $aExtraParams['menu'] === true)
) {
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
if (utils::IsNotNullOrEmptyString($sContext)) {
$sContext = '&'.$sContext;
}
$sContext = $oAppContext->GetForLink(true);
$sFilter = $this->GetFilter()->serialize();
@@ -2027,8 +2030,8 @@ class MenuBlock extends DisplayBlock
$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);
$bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sSelectedClass)) && UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_MODIFY) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
$bIsBulkDeleteAllowed = (bool) UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_DELETE);
// Refine filter on selected class so bullk actions occur on the right class
$oSelectedClassFilter = $this->GetFilter()->DeepClone();
@@ -2581,11 +2584,8 @@ class MenuBlock extends DisplayBlock
$sUrl = "{$sRootUrl}pages/{$sUIPage}?{$sUrlParams}";
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
if (utils::IsNotNullOrEmptyString($sContext)) {
$sUrl .= '&'.$sContext;
}
$sContext = $oAppContext->GetForLink(true);
return $sUrl;
return $sUrl . $sContext;
}
}

View File

@@ -303,7 +303,7 @@ class ExcelExporter
{
if ($this->sOutputFilePath == null)
{
return APPROOT.'data/bulk_export/'.$this->sToken.'.xlsx';
return utils::GetDataPath().'bulk_export/'.$this->sToken.'.xlsx';
}
else
{
@@ -313,14 +313,14 @@ class ExcelExporter
public static function GetExcelFileFromToken($sToken)
{
return @file_get_contents(APPROOT.'data/bulk_export/'.$sToken.'.xlsx');
return @file_get_contents(utils::GetDataPath().'bulk_export/'.$sToken.'.xlsx');
}
public static function CleanupFromToken($sToken)
{
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.status');
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.data');
@unlink(APPROOT.'data/bulk_export/'.$sToken.'.xlsx');
@unlink(utils::GetDataPath().'bulk_export/'.$sToken.'.status');
@unlink(utils::GetDataPath().'bulk_export/'.$sToken.'.data');
@unlink(utils::GetDataPath().'bulk_export/'.$sToken.'.xlsx');
}
public function Cleanup()
@@ -334,7 +334,7 @@ class ExcelExporter
*/
public static function CleanupOldFiles()
{
$aFiles = glob(APPROOT.'data/bulk_export/*.*');
$aFiles = glob(utils::GetDataPath().'bulk_export/*.*');
$iDelay = MetaModel::GetConfig()->Get('xlsx_exporter_cleanup_old_files_delay');
if($iDelay > 0)
@@ -416,14 +416,14 @@ class ExcelExporter
protected function CheckDataDir()
{
if(!is_dir(APPROOT."data/bulk_export"))
if(!is_dir(utils::GetDataPath()."bulk_export"))
{
@mkdir(APPROOT."data/bulk_export", 0777, true /* recursive */);
@mkdir(utils::GetDataPath()."bulk_export", 0777, true /* recursive */);
clearstatcache();
}
if (!is_writable(APPROOT."data/bulk_export"))
if (!is_writable(utils::GetDataPath()."bulk_export"))
{
throw new Exception('Data directory "'.APPROOT.'data/bulk_export" could not be written.');
throw new Exception('Data directory "'.utils::GetDataPath().'bulk_export" could not be written.');
}
}
@@ -433,12 +433,12 @@ class ExcelExporter
{
$sToken = $this->sToken;
}
return APPROOT."data/bulk_export/$sToken.status";
return utils::GetDataPath()."bulk_export/$sToken.status";
}
protected function GetDataFile()
{
return APPROOT.'data/bulk_export/'.$this->sToken.'.data';
return utils::GetDataPath().'bulk_export/'.$this->sToken.'.data';
}
protected function GetNewToken()

View File

@@ -60,6 +60,24 @@ class CoreCannotSaveObjectException extends CoreException
return $sContent;
}
public function getTextMessage()
{
$sTitle = Dict::S('UI:Error:SaveFailed');
$sContent = utils::HtmlEntities($sTitle);
if (count($this->aIssues) == 1) {
$sIssue = reset($this->aIssues);
$sContent .= utils::HtmlEntities($sIssue);
} else {
foreach ($this->aIssues as $sError) {
$sContent .= " ".utils::HtmlEntities($sError).", ";
}
}
return $sContent;
}
public function getIssues()
{
return $this->aIssues;

View File

@@ -67,7 +67,7 @@ class CoreException extends Exception
public function getHtmlDesc($sHighlightHtmlBegin = '<b>', $sHighlightHtmlEnd = '</b>')
{
return $this->getMessage();
return utils::EscapeHtml($this->getMessage());
}
/**

View File

@@ -209,7 +209,7 @@ class DesignerForm
public function RenderAsPropertySheet($oP, $bReturnHTML = false, $sNotifyParentSelector = null)
{
$sReturn = '';
$sActionUrl = addslashes($this->sSubmitTo);
$sActionUrl = addslashes($this->sSubmitTo ?? '');
$sJSSubmitParams = json_encode($this->aSubmitParams);
$sFormId = $this->GetFormId();
if ($this->oParentForm == null) {
@@ -1330,7 +1330,8 @@ class DesignerComboField extends DesignerFormField
{
if ($this->bMultipleSelection)
{
$sHtml = "<span><select $sCSSClasses multiple size=\"8\"id=\"$sId\" name=\"$sName\">";
$iSize = max(1, min(8, count($this->aAllowedValues)));
$sHtml = "<span><select $sCSSClasses multiple size=\"$iSize\" id=\"$sId\" name=\"$sName\">";
}
else
{

View File

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

View File

@@ -53,6 +53,12 @@ class LoginDefaultBefore extends AbstractLoginFSMExtension
{
// Force login mode
LoginWebPage::SetLoginModeAndReload($sProposedLoginMode);
} else {
$sRawLoginMode = utils::ReadParam('login_mode', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
if ($sProposedLoginMode !== $sRawLoginMode) {
IssueLog::Error("Authentication issue due to login_mode parameter sanitization. Please avoid special characters", null, ['sRawLoginMode' => $sRawLoginMode]);
//IssueLog::Error("Authentication issue due to login_mode parameter sanitization. Please avoid special characters", null, ['sRawLoginMode' => utils::HtmlEntities($sRawLoginMode)]);
}
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}

View File

@@ -52,12 +52,13 @@ class LoginTwigContext
/**
* Set the absolute path on disk of the folder containing the twig templates
*
* @param string $sPath absolute path of twig templates directory
* @api
*
*@param string $sAbsPath Absolute path of twig templates directory
*/
public function SetLoaderPath($sPath)
public function SetLoaderPath($sAbsPath)
{
$this->sTwigLoaderPath = $sPath;
$this->sTwigLoaderPath = $sAbsPath;
}
/**
@@ -84,24 +85,27 @@ class LoginTwigContext
}
/**
* Add the URL of a CSS file to link to the login screen
* Add the absolute URL of a CSS file to link to the login screen
*
* @param string $sFile URL of the CSS file to link
* @api
*
* @param string $sFileAbsURL Absolute URL of the CSS file to link
*/
public function AddCSSFile($sFile)
public function AddCSSFile($sFileAbsURL)
{
$this->aCSSFiles[] = $sFile;
$this->aCSSFiles[] = $sFileAbsURL;
}
/**
* Add the URL of a javascript file to link to the login screen
* @param string $sFile URL of the javascript file to link
* Add the absolute URL of a javascript file to link to the login screen
*
* @api
*
* @param string $sFileAbsURL Absolute URL of the javascript file to link
*/
public function AddJsFile($sFile)
public function AddJsFile($sFileAbsURL)
{
$this->aJsFiles[] = $sFile;
$this->aJsFiles[] = $sFileAbsURL;
}
/**
@@ -133,7 +137,7 @@ class LoginTwigContext
}
/**
* @return array
* @return array Absolute URLs of the CSS files
*/
public function GetCSSFiles()
{
@@ -141,7 +145,7 @@ class LoginTwigContext
}
/**
* @return array
* @return array Absolute URLs of the JS files
*/
public function GetJsFiles()
{
@@ -274,12 +278,12 @@ class LoginTwigRenderer
$aCSSFiles = $oFormData->GetCSSFiles();
foreach ($aCSSFiles as $sCSSFile)
{
$oPage->add_linked_stylesheet($sCSSFile);
$oPage->LinkStylesheetFromURI($sCSSFile);
}
$aJsFiles = $oFormData->GetJsFiles();
foreach ($aJsFiles as $sJsFile)
{
$oPage->add_linked_script($sJsFile);
$oPage->LinkScriptFromURI($sJsFile);
}
}

View File

@@ -1159,11 +1159,11 @@ class OQLMenuNode extends MenuNode
{
$sUsageId = utils::GetSafeId($sUsageId);
$oSearch = DBObjectSearch::FromOQL($sOql);
$sClass= $oSearch->GetClass();
$sClass= $oSearch->GetClass();
$sIcon = MetaModel::GetClassIcon($sClass, false);
if ($bSearchPane) {
$aParams = array_merge(['open' => $bSearchOpen, 'table_id' => $sUsageId, 'submit_on_load' => false], $aExtraParams);
$oBlock = new DisplayBlock($oSearch, 'search', false /* Asynchronous */, $aParams);
$oBlock = new DisplayBlock($oSearch, DisplayBlock::ENUM_STYLE_SEARCH, false /* Asynchronous */, $aParams);
$oBlock->Display($oPage, 0);
$oPage->add("<div class='sf_results_area ibo-add-margin-top-250' data-target='search_results'>");
}

View File

@@ -228,7 +228,7 @@ class privUITransactionFile
*/
public static function GetNewTransactionId()
{
if (!is_dir(APPROOT.'data/transactions'))
if (!is_dir(utils::GetDataPath().'transactions'))
{
if (!is_writable(APPROOT.'data'))
{
@@ -236,22 +236,22 @@ class privUITransactionFile
}
// condition avoids race condition N°2345
// See https://github.com/kalessil/phpinspectionsea/blob/master/docs/probable-bugs.md#mkdir-race-condition
if (!mkdir($concurrentDirectory = APPROOT.'data/transactions') && !is_dir($concurrentDirectory))
if (!mkdir($concurrentDirectory = utils::GetDataPath().'transactions') && !is_dir($concurrentDirectory))
{
throw new Exception('Failed to create the directory "'.APPROOT.'data/transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
throw new Exception('Failed to create the directory "'.utils::GetDataPath().'transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
}
}
if (!is_writable(APPROOT.'data/transactions'))
if (!is_writable(utils::GetDataPath().'transactions'))
{
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
throw new Exception('The directory "'.utils::GetDataPath().'transactions" must be writable to the application.');
}
$iCurrentUserId = static::GetCurrentUserId();
self::CleanupOldTransactions();
$sTransactionIdFullPath = tempnam(APPROOT.'data/transactions', static::GetUserPrefix());
$sTransactionIdFullPath = tempnam(utils::GetDataPath().'transactions', static::GetUserPrefix());
file_put_contents($sTransactionIdFullPath, $iCurrentUserId, LOCK_EX);
$sTransactionIdFileName = basename($sTransactionIdFullPath);
@@ -274,8 +274,8 @@ class privUITransactionFile
*/
public static function IsTransactionValid($id, $bRemoveTransaction = true)
{
// Constraint the transaction file within APPROOT.'data/transactions'
$sTransactionDir = realpath(APPROOT.'data/transactions');
// Constraint the transaction file within utils::GetDataPath().'transactions'
$sTransactionDir = realpath(utils::GetDataPath().'transactions');
$sFilepath = utils::RealPath($sTransactionDir.'/'.$id, $sTransactionDir);
if (($sFilepath === false) || (strlen($sTransactionDir) == strlen($sFilepath)))
{
@@ -348,7 +348,7 @@ class privUITransactionFile
clearstatcache();
$iLimit = time() - 24*3600;
$sPattern = $sTransactionDir ? "$sTransactionDir/*" : APPROOT.'data/transactions/*';
$sPattern = $sTransactionDir ? "$sTransactionDir/*" : utils::GetDataPath().'transactions/*';
$aTransactions = glob($sPattern);
foreach($aTransactions as $sFileName)
{
@@ -368,7 +368,7 @@ class privUITransactionFile
{
clearstatcache();
$aResult = array();
$aTransactions = glob(APPROOT.'data/transactions/'.self::GetUserPrefix().'*');
$aTransactions = glob(utils::GetDataPath().'transactions/'.self::GetUserPrefix().'*');
foreach($aTransactions as $sFileName)
{
$aResult[] = date('Y-m-d H:i:s', filemtime($sFileName)).' - '.basename($sFileName);

View File

@@ -250,7 +250,7 @@ class UIExtKeyWidget
foreach ($aAdditionalField as $sAdditionalField) {
array_push($aArguments, $oObj->Get($sAdditionalField));
}
$aOption['additional_field'] = utils::HtmlEntities(vsprintf($sFormatAdditionalField, $aArguments));
$aOption['additional_field'] = utils::HtmlEntities(utils::VSprintf($sFormatAdditionalField, $aArguments));
}
if (!empty($sObjectImageAttCode)) {
// Try to retrieve image for contact

View File

@@ -17,6 +17,7 @@ require_once(APPROOT.'application/displayblock.class.inc.php');
class UILinksWidget
{
protected $m_sClass;
protected $m_sClassLabel;
protected $m_sAttCode;
protected $m_sNameSuffix;
protected $m_sInputId;
@@ -24,6 +25,7 @@ class UILinksWidget
protected $m_sExtKeyToRemote;
protected $m_sExtKeyToMe;
protected $m_sLinkedClass;
protected $m_sLinkedClassLabel;
protected $m_sRemoteClass;
protected $m_bDuplicatesAllowed;
/** @var string[] list of editables attcodes */
@@ -46,6 +48,7 @@ class UILinksWidget
public function __construct($sClass, $sAttCode, $sInputId, $sNameSuffix = '', $bDuplicatesAllowed = false)
{
$this->m_sClass = $sClass;
$this->m_sClassLabel = MetaModel::GetName($this->m_sClass);
$this->m_sAttCode = $sAttCode;
$this->m_sInputId = $sInputId;
$this->m_sNameSuffix = $sNameSuffix;
@@ -56,6 +59,7 @@ class UILinksWidget
/** @var AttributeLinkedSetIndirect $oAttDef */
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode);
$this->m_sLinkedClass = $oAttDef->GetLinkedClass();
$this->m_sLinkedClassLabel = MetaModel::GetName($this->m_sLinkedClass);
$this->m_sExtKeyToRemote = $oAttDef->GetExtKeyToRemote();
$this->m_sExtKeyToMe = $oAttDef->GetExtKeyToMe();
@@ -365,11 +369,21 @@ class UILinksWidget
return $this->m_sClass;
}
public function GetClassLabel(): string
{
return $this->m_sClassLabel;
}
public function GetLinkedClass(): string
{
return $this->m_sLinkedClass;
}
public function GetLinkedClassLabel(): string
{
return $this->m_sLinkedClassLabel;
}
public function GetAttCode(): string
{
return $this->m_sAttCode;

View File

@@ -249,9 +249,9 @@ class appUserPreferences extends DBObject
(
"category" => "gui",
"key_type" => "autoincrement",
"name_attcode" => "userid",
"name_attcode" => "login",
"state_attcode" => "",
"reconc_keys" => array(),
"reconc_keys" => array("userid","login"),
"db_table" => "priv_app_preferences",
"db_key_field" => "id",
"db_finalclass_field" => "",
@@ -260,6 +260,10 @@ class appUserPreferences extends DBObject
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"User", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributePropertySet("preferences", array("allowed_values"=>null, "sql"=>"preferences", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("org_id", array("allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "org_id")));
MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "login")));
MetaModel::Init_SetZListItems('list', array('org_id','preferences'));
MetaModel::Init_SetZListItems('default_search', array('userid','login','org_id'));
}
/**

View File

@@ -22,10 +22,12 @@ use Combodo\iTop\Application\UI\Base\iUIBlock;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use Combodo\iTop\Application\UI\Hook\iKeyboardShortcut;
use Combodo\iTop\Application\WebPage\WebPage;
use Combodo\iTop\Service\InterfaceDiscovery\InterfaceDiscovery;
use Combodo\iTop\Service\Module\ModuleService;
use ScssPhp\ScssPhp\Compiler;
use ScssPhp\ScssPhp\OutputStyle;
use ScssPhp\ScssPhp\ValueConverter;
use Soundasleep\Html2Text;
/**
@@ -111,6 +113,11 @@ class utils
* @since 2.7.10 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER = 'element_identifier';
/**
* @var string For XML / HTML node id/class selector
* @since 3.1.2 3.2.1
*/
public const ENUM_SANITIZATION_FILTER_ELEMENT_SELECTOR = 'element_selector';
/**
* @var string For variables names
* @since 3.0.0
@@ -173,6 +180,7 @@ class utils
*/
private static $sAbsoluteUrlAppRootCache = null;
protected static function LoadParamFile($sParamFile)
{
if (!file_exists($sParamFile)) {
@@ -411,11 +419,26 @@ class utils
* @since 2.7.7, 3.0.2, 3.1.0 N°4899 - new 'url' filter
* @since 2.7.10 N°6606 use the utils::ENUM_SANITIZATION_* const
* @since 2.7.10 N°6606 new case for ENUM_SANITIZATION_FILTER_PHP_CLASS
* @since 3.2.1-1 N°8242 Allow value to be an array for every filter
*
* @link https://www.php.net/manual/en/filter.filters.sanitize.php PHP sanitization filters
*/
protected static function Sanitize_Internal($value, $sSanitizationFilter)
{
if (is_array($value))
{
$retValue = array();
foreach ($value as $key => $val)
{
$retValue[$key] = self::Sanitize_Internal($val, $sSanitizationFilter); // recursively check arrays
if ($retValue[$key] === false)
{
return false;
}
}
return $retValue;
}
switch ($sSanitizationFilter)
{
case static::ENUM_SANITIZATION_FILTER_INTEGER:
@@ -446,57 +469,50 @@ class utils
case static::ENUM_SANITIZATION_FILTER_PARAMETER:
case static::ENUM_SANITIZATION_FILTER_FIELD_NAME:
case static::ENUM_SANITIZATION_FILTER_TRANSACTION_ID:
if (is_array($value))
switch ($sSanitizationFilter)
{
$retValue = array();
foreach ($value as $key => $val)
{
$retValue[$key] = self::Sanitize_Internal($val, $sSanitizationFilter); // recursively check arrays
if ($retValue[$key] === false)
{
$retValue = false;
break;
}
}
}
else
{
switch ($sSanitizationFilter)
{
case static::ENUM_SANITIZATION_FILTER_TRANSACTION_ID:
// 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_TRANSACTION_ID:
// 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_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)
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)
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
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
break;
case static::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM:
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[ A-Za-z0-9_=%:+-]*$/')));
break;
case static::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM:
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[ A-Za-z0-9_=%:+-]*$/')));
break;
}
}
break;
// For XML / HTML node identifiers
case static::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER:
$retValue = preg_replace('/[^a-zA-Z0-9_-]/', '', $value);
$retValue = filter_var($retValue, FILTER_VALIDATE_REGEXP,
['options' => ['regexp' => '/^[A-Za-z0-9][A-Za-z0-9_-]*$/']]);
break;
// For XML / HTML node id selector
case static::ENUM_SANITIZATION_FILTER_ELEMENT_SELECTOR:
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
['options' => ['regexp' => '/^[#\.][A-Za-z0-9][A-Za-z0-9_-]*$/']]);
break;
case static::ENUM_SANITIZATION_FILTER_VARIABLE_NAME:
@@ -505,8 +521,8 @@ class utils
// For URL
case static::ENUM_SANITIZATION_FILTER_URL:
// N°6350 - returns only valid URLs
$retValue = filter_var($value, FILTER_VALIDATE_URL);
$retValue = filter_var($value, FILTER_SANITIZE_URL);
$retValue = filter_var($retValue, FILTER_VALIDATE_URL);
break;
default:
@@ -1435,9 +1451,12 @@ class utils
* @return string A path to a folder into which any module can store cache data
* The corresponding folder is created or cleaned upon code compilation
*/
public static function GetCachePath()
public static function GetCachePath(string $sEnvironment = null): string
{
return static::GetDataPath().'cache-'.MetaModel::GetEnvironment().'/';
if (is_null($sEnvironment)) {
$sEnvironment = MetaModel::GetEnvironment();
}
return static::GetDataPath()."cache-$sEnvironment/";
}
/**
@@ -1497,12 +1516,12 @@ class utils
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
/** @var \DBObjectSet $param */
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$sContext = $oAppContext->GetForLink(true);
$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}";
$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');
@@ -1536,6 +1555,11 @@ class utils
}
$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')");
if (ApplicationMenu::IsMenuIdEnabled('RunQueriesMenu')) {
$oMenuItemPlay = new JSPopupMenuItem('UI:Menu:OpenOQL', Dict::S('UI:Edit:TestQuery'), "OpenOql('$sOQL')");
$oMenuItemPlay->SetIconClass('fas fa-play');
$aResult[] = $oMenuItemPlay;
}
break;
@@ -1672,8 +1696,8 @@ class utils
$oAppContext = new ApplicationContext();
$sUrl = $sAppRootUrl
.'pages/UI.php?operation=search&'
.$oAppContext->GetForLink()
.'pages/UI.php?operation=search'
.$oAppContext->GetForLink(true)
.'&filter='.rawurlencode($oDataTableSearchFilter->serialize());
$sUrl .= '&aParams='.rawurlencode($sParams); // Not working... yet, cause not handled by UI.php
@@ -2056,6 +2080,127 @@ SQL;
);
}
/**
* Format a string using vsprintf with safety checks to avoid ValueError
*
* This method fills missing arguments with their original format specifiers,
* then calls vsprintf with the complete array.
*
* @param string $sFormat The format string
* @param array $aArgs The arguments to format
* @param bool $bLogErrors Whether to log errors (defaults to true)
*
* @return string The formatted string
* @since 3.2.2
*/
public static function VSprintf(string $sFormat, array $aArgs, bool $bLogErrors = true): string
{
// Extract all format specifiers
$sPattern = '/%(?:(?:[1-9][0-9]*)\$)?[-+\'0# ]*(?:[0-9]*|\*)?(?:\.(?:[0-9]*|\*))?(?:[hlL])?[diouxXeEfFgGcrs%]/';
preg_match_all($sPattern, $sFormat, $aMatches, PREG_OFFSET_CAPTURE);
// Process matches, keeping track of their positions and excluding escaped percent signs (%%)
$aSpecifierMatches = [];
foreach ($aMatches[0] as $sMatch) {
if ($sMatch[0] !== '%%') {
$aSpecifierMatches[] = $sMatch;
}
}
// Check for positional specifiers and build position map
$bHasPositional = false;
$iMaxPosition = 0;
$aPositions = [];
$aUniquePositions = [];
foreach ($aSpecifierMatches as $index => $match) {
$sSpec = $match[0];
if (preg_match('/^%([1-9][0-9]*)\$/', $sSpec, $posMatch)) {
$bHasPositional = true;
$iPosition = (int)$posMatch[1] - 1; // Convert to 0-based
$aPositions[$index] = $iPosition;
$aUniquePositions[$iPosition] = true;
$iMaxPosition = max($iMaxPosition, $iPosition + 1);
} else {
$aPositions[$index] = $index;
$aUniquePositions[$index] = true;
$iMaxPosition = max($iMaxPosition, $index + 1);
}
}
// Count unique positions, this tells us how many arguments we actually need
$iExpectedCount = count($aUniquePositions);
$iActualCount = count($aArgs);
// If we have enough arguments, just use vsprintf
if ($iActualCount >= $iExpectedCount) {
return vsprintf($sFormat, $aArgs);
}
// else log the error if needed
if ($bLogErrors) {
IssueLog::Warning("Format string requires $iExpectedCount arguments, but only $iActualCount provided. Format: '$sFormat'" );
}
// Create a replacement map
if ($bHasPositional) {
// For positional, we need to handle the exact positions
$aReplacements = array_fill(0, $iMaxPosition, null);
// Fill in the real arguments first
foreach ($aArgs as $index => $sValue) {
if ($index < $iMaxPosition) {
$aReplacements[$index] = $sValue;
}
}
// For null values in the replacement map, use the original specifier
foreach ($aSpecifierMatches as $index => $sMatch) {
$iPosition = $aPositions[$index];
if ($aReplacements[$iPosition] === null) {
// Use the original format specifier when we don't have an argument
$aReplacements[$iPosition] = $sMatch[0];
}
}
// Remove any remaining nulls (for positions that weren't referenced)
$aReplacements = array_filter($aReplacements, static function($val) { return $val !== null; });
} else {
// For non-positional, we need to map each position
$aReplacements = [];
$iUsed = 0;
// Create a map of what values to use for each position
$aPositionValues = [];
for ($i = 0; $i < $iMaxPosition; $i++) {
if (isset($aUniquePositions[$i])) {
if ($iUsed < $iActualCount) {
// We have an actual argument for this position
$aPositionValues[$i] = $aArgs[$iUsed++];
} else {
// Mark this position to use the original specifier
$aPositionValues[$i] = null;
}
}
}
// Build the replacements array preserving the original order
foreach ($aSpecifierMatches as $index => $sMatch) {
$iPosition = $aPositions[$index];
if (isset($aPositionValues[$iPosition])) {
$aReplacements[] = $aPositionValues[$iPosition];
} else {
// Use the original format specifier when we don't have an argument
$aReplacements[] = $sMatch[0];
// Mark this position as used, so if it appears again, it gets the same replacement
$aPositionValues[$iPosition] = $sMatch[0];
}
}
}
// Process the format string with our filled-in arguments
return vsprintf($sFormat, $aReplacements);
}
/**
* Convert a string containing some (valid) HTML markup to plain text
*
@@ -2067,7 +2212,7 @@ SQL;
{
try {
//return '<?xml encoding="UTF-8">'.$sHtml;
return \Html2Text\Html2Text::convert('<?xml encoding="UTF-8">'.$sHtml);
return Html2Text::convert('<?xml encoding="UTF-8">'.$sHtml, ['ignore_errors' => true]);
}
catch (Exception $e) {
return $e->getMessage();
@@ -2374,53 +2519,75 @@ SQL;
return $bRet;
}
/**
* @param $sPath
*
* @return false|\ormDocument
* @throws \Exception
*
* @deprecated 3.2.1 use utils::GetDocumentFromSelfURL instead
*/
public static function IsSelfURL($sPath)
{
return self::GetDocumentFromSelfURL($sPath);
}
/**
* Check if the given URL is a link to download a document/image on the CURRENT iTop
* In such a case we can read the content of the file directly in the database (if the users rights allow) and return the ormDocument
*
* @Since 3.2.1 a local URL is transformed into a local file to read
*
* @param string $sPath
* @return false|ormDocument
* @throws Exception
*/
public static function IsSelfURL($sPath)
public static function GetDocumentFromSelfURL(string $sPath)
{
$result = false;
$sPageUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.document.php';
if (substr($sPath, 0, strlen($sPageUrl)) == $sPageUrl)
{
if (utils::StartsWith($sPath, $sPageUrl)) {
// If the URL is an URL pointing to this instance of iTop, then
// extract the "query" part of the URL and analyze it
$sQuery = parse_url($sPath, PHP_URL_QUERY);
if ($sQuery !== null)
{
if ($sQuery !== null) {
$aParams = array();
foreach(explode('&', $sQuery) as $sChunk)
{
foreach (explode('&', $sQuery) as $sChunk) {
$aParts = explode('=', $sChunk ?? '');
if (count($aParts) != 2) continue;
if (count($aParts) != 2) {
continue;
}
$aParams[$aParts[0]] = urldecode($aParts[1]);
}
$result = array_key_exists('operation', $aParams) && array_key_exists('class', $aParams) && array_key_exists('id', $aParams) && array_key_exists('field', $aParams) && ($aParams['operation'] == 'download_document');
if ($result)
{
if ($result) {
// This is a 'download_document' operation, let's retrieve the document directly from the database
$sClass = $aParams['class'];
$iKey = $aParams['id'];
$sAttCode = $aParams['field'];
$oObj = MetaModel::GetObject($sClass, $iKey, false /* must exist */); // Users rights apply here !!
if ($oObj)
{
if ($oObj) {
/**
* @var ormDocument $result
*/
$result = clone $oObj->Get($sAttCode);
return $result;
return clone $oObj->Get($sAttCode);
}
}
}
throw new Exception('Invalid URL. This iTop URL is not pointing to a valid Document/Image.');
}
return $result;
if (utils::StartsWith($sPath, utils::GetAbsoluteUrlAppRoot())) {
$sFilePath = utils::LocalPath(APPROOT.substr($sPath, strlen(utils::GetAbsoluteUrlAppRoot())));
if (false === $sFilePath) {
return false;
}
$sFilePath = APPROOT.$sFilePath;
return ormDocument::FromFile($sFilePath);
}
return false;
}
/**
@@ -2428,115 +2595,70 @@ SQL;
* - an URL pointing to a blob (image/document) on the current iTop server
* - an http(s) URL
* - the local file system (but only if you are an administrator)
* @param string $sPath
*
* @param string|null $sPath
* @return ormDocument|null
* @throws Exception
*/
public static function FileGetContentsAndMIMEType($sPath)
{
$oUploadedDoc = null;
$aKnownExtensions = array(
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'png' => 'image/png',
'pdf' => 'application/pdf',
'doc' => 'application/msword',
'dot' => 'application/msword',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'vsd' => 'application/x-visio',
'vdx' => 'application/visio.drawing',
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'odp' => 'application/vnd.oasis.opendocument.presentation',
'zip' => 'application/zip',
'txt' => 'text/plain',
'htm' => 'text/html',
'html' => 'text/html',
'exe' => 'application/octet-stream',
);
$sData = null;
$sMimeType = 'text/plain'; // Default MIME Type: treat the file as a bunch a characters...
$sFileName = 'uploaded-file'; // Default name for downloaded-files
$sExtension = '.txt'; // Default file extension in case we don't know the MIME Type
if(empty($sPath))
{
if (utils::IsNullOrEmptyString($sPath)) {
// Empty path (NULL or '') means that there is no input, making an empty document.
$oUploadedDoc = new ormDocument('', '', '');
return new ormDocument('', '', '');
}
elseif (static::IsURL($sPath))
{
if ($oUploadedDoc = static::IsSelfURL($sPath))
{
// Nothing more to do, we've got it !!
}
else
{
// Remote file, let's use the HTTP headers to find the MIME Type
$sData = @file_get_contents($sPath);
if ($sData === false)
{
throw new Exception("Failed to load the file from the URL '$sPath'.");
}
else
{
if (isset($http_response_header))
{
$aHeaders = static::ParseHeaders($http_response_header);
$sMimeType = array_key_exists('Content-Type', $aHeaders) ? strtolower($aHeaders['Content-Type']) : 'application/x-octet-stream';
// Compute the file extension from the MIME Type
foreach ($aKnownExtensions as $sExtValue => $sMime) {
if ($sMime === $sMimeType) {
$sExtension = '.'.$sExtValue;
break;
}
}
}
$sPathName = pathinfo($sPath, PATHINFO_FILENAME);
if (utils::IsNotNullOrEmptyString($sPathName)) {
$sFileName = $sPathName;
}
$sFileName .= $sExtension;
}
$oUploadedDoc = new ormDocument($sData, $sMimeType, $sFileName);
}
}
else if (UserRights::IsAdministrator())
{
// Only administrators are allowed to read local files
$sData = @file_get_contents($sPath);
if ($sData === false)
{
throw new Exception("Failed to load the file '$sPath'. The file does not exist or the current process is not allowed to access it.");
}
$sExtension = strtolower(pathinfo($sPath, PATHINFO_EXTENSION));
$sFileName = basename($sPath);
if (array_key_exists($sExtension, $aKnownExtensions))
{
$sMimeType = $aKnownExtensions[$sExtension];
if (static::IsURL($sPath)) {
$oUploadedDoc = static::GetDocumentFromSelfURL($sPath);
if ($oUploadedDoc) {
return $oUploadedDoc;
}
else if (extension_loaded('fileinfo'))
{
$finfo = new finfo(FILEINFO_MIME);
$sMimeType = $finfo->file($sPath);
// Remote file, let's use the HTTP headers to find the MIME Type
$sData = @file_get_contents($sPath);
if ($sData === false) {
IssueLog::Error(<<<TXT
Failed to load the file from URL. This can happen for multiple reasons:
- Invalid URL
- URL using HTTPS with an untrusted certificate on the remote server
- ...
TXT
, LogChannels::CORE, [
'URL' => $sPath,
]);
throw new Exception("Failed to load the file from the URL '$sPath'.");
}
$oUploadedDoc = new ormDocument($sData, $sMimeType, $sFileName);
$sMimeType = 'text/plain'; // Default MIME Type: treat the file as a bunch a characters...
$sFileName = 'uploaded-file'; // Default name for downloaded-files
$sExtension = '.txt'; // Default file extension in case we don't know the MIME Type
if (isset($http_response_header)) {
$aHeaders = static::ParseHeaders($http_response_header);
$sMimeType = array_key_exists('Content-Type', $aHeaders) ? strtolower($aHeaders['Content-Type']) : 'application/x-octet-stream';
// Compute the file extension from the MIME Type
foreach (ormDocument::GetKnownExtensions() as $sExtValue => $sMime) {
if ($sMime === $sMimeType) {
$sExtension = '.'.$sExtValue;
break;
}
}
}
$sPathName = pathinfo($sPath, PATHINFO_FILENAME);
if (utils::IsNotNullOrEmptyString($sPathName)) {
$sFileName = $sPathName;
}
$sFileName .= $sExtension;
return new ormDocument($sData, $sMimeType, $sFileName);
}
return $oUploadedDoc;
// Local file
if (UserRights::IsAdministrator()) {
// Only administrators are allowed to read local files
return ormDocument::FromFile($sPath);
}
return null;
}
protected static function ParseHeaders($aHeaders)
@@ -2782,95 +2904,12 @@ SQL;
*
* @return array classes are returned in the same order as the module dependency tree, so core classes on top
* @since 3.0.0
* @deprecated 3.2.0 Use {@see InterfaceDiscovery::FindItopClasses()} instead
*/
public static function GetClassesForInterface(string $sInterface, string $sClassNameFilter = '', $aExcludedPath = []): array
{
$aMatchingClasses = [];
if (!utils::IsDevelopmentEnvironment()) {
// Try to read from cache
$aFilePath = explode("\\", $sInterface);
$sInterfaceName = end($aFilePath);
$sCacheFileName = utils::GetCachePath()."ImplementingInterfaces/$sInterfaceName.php";
if (is_file($sCacheFileName)) {
$aMatchingClasses = include $sCacheFileName;
}
}
if (empty($aMatchingClasses)) {
$aAutoloadClassMaps = [APPROOT.'lib/composer/autoload_classmap.php'];
// guess all the autoload class maps from the extensions
$aAutoloadClassMaps = array_merge($aAutoloadClassMaps, glob(APPROOT.'env-'.utils::GetCurrentEnvironment().'/*/vendor/composer/autoload_classmap.php'));
$aClassMap = [];
$aAutoloaderErrors = [];
foreach ($aAutoloadClassMaps as $sAutoloadFile) {
if (false === static::RealPath($sAutoloadFile, APPROOT)) {
// can happen when we still have the autoloader symlink in env-*, but it points to a file that no longer exists
$aAutoloaderErrors[] = $sAutoloadFile;
continue;
}
$aTmpClassMap = include $sAutoloadFile;
/** @noinspection SlowArrayOperationsInLoopInspection we are getting an associative array so the documented workarounds cannot be used */
$aClassMap = array_merge($aClassMap, $aTmpClassMap);
}
if (count($aAutoloaderErrors) > 0) {
IssueLog::Debug(
"\utils::GetClassesForInterface cannot load some of the autoloader files",
LogChannels::CORE,
['autoloader_errors' => $aAutoloaderErrors]
);
}
// Add already loaded classes
$aCurrentClasses = array_fill_keys(get_declared_classes(), '');
$aClassMap = array_merge($aCurrentClasses, $aClassMap);
foreach ($aClassMap as $sPHPClass => $sPHPFile) {
$bSkipped = false;
// Check if our class matches name filter, or is in an excluded path
if ($sClassNameFilter !== '' && strpos($sPHPClass, $sClassNameFilter) === false) {
$bSkipped = true;
}
// For some PHP classes we don't have their file path as they are already in memory, so we never filter on their paths
elseif (utils::IsNotNullOrEmptyString($sPHPFile)) {
$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
}
}
if(!$bSkipped){
try {
$oRefClass = new ReflectionClass($sPHPClass);
if ($oRefClass->implementsInterface($sInterface) &&
!$oRefClass->isInterface() && !$oRefClass->isAbstract() && !$oRefClass->isTrait()) {
$aMatchingClasses[] = $sPHPClass;
}
} catch (Exception $e) {
}
}
}
if (!utils::IsDevelopmentEnvironment()) {
// Save to cache
$sCacheContent = "<?php\n\nreturn ".var_export($aMatchingClasses, true).";";
SetupUtils::builddir(dirname($sCacheFileName));
file_put_contents($sCacheFileName, $sCacheContent);
}
}
return $aMatchingClasses;
$oInterfaceDiscoveryService = InterfaceDiscovery::GetInstance();
return $oInterfaceDiscoveryService->FindItopClasses($sInterface);
}
/**
@@ -2884,9 +2923,9 @@ SQL;
{
$aResultPref = [];
$aShortcutPrefs = appUserPreferences::GetPref('keyboard_shortcuts', []);
// Note: Mind the 4 blackslashes, see utils::GetClassesForInterface()
$aShortcutClasses = utils::GetClassesForInterface(iKeyboardShortcut::class, '', array('[\\\\/]lib[\\\\/]', '[\\\\/]node_modules[\\\\/]', '[\\\\/]test[\\\\/]', '[\\\\/]tests[\\\\/]'));
/** @var iKeyboardShortcut[] $aShortcutClasses */
$aShortcutClasses = InterfaceDiscovery::GetInstance()->FindItopClasses(iKeyboardShortcut::class);
foreach ($aShortcutClasses as $cShortcutPlugin) {
$sTriggeredElement = $cShortcutPlugin::GetShortcutTriggeredElementSelector();
foreach ($cShortcutPlugin::GetShortcutKeys() as $aShortcutKey) {
@@ -3190,30 +3229,13 @@ SQL;
* @throws \Exception
* @since 3.0.0
*/
public static function GetMentionedObjectsFromText(string $sText, string $sFormat = self::ENUM_TEXT_FORMAT_HTML): array
public static function GetMentionedObjectsFromText(string $sText): array
{
// First transform text so it can be parsed
switch ($sFormat) {
case static::ENUM_TEXT_FORMAT_HTML:
$sText = static::HtmlToText($sText);
break;
default:
// Don't transform it
break;
}
// Then parse text to find objects
$aMentionedObjects = array();
$aMentionMatches = array();
// Note: As the sanitizer (or CKEditor autocomplete plugin? 🤔) removes data-* attributes from the hyperlink,
// - we can't use the following (simpler) regexp that only checks data attributes on hyperlinks, which would have worked for hyperlinks pointing to any GUIs: '/<a\s*([^>]*)data-object-class="([^"]*)"\s*data-object-id="([^"]*)">/i'
// - instead we use a regexp to match the following pattern '[Some object label](<APP_ROOT_URL>...&class=<OBJECT_CLASS>&id=<OBJECT_ID>...)' which only works for the backoffice
// If we change the sanitizer, we might want to switch to the other regexp as it's universal and easier to read
$sAppRootUrlForRegExp = addcslashes(utils::GetAbsoluteUrlAppRoot(), '/&');
preg_match_all("/\[([^\]]*)\]\({$sAppRootUrlForRegExp}[^\)]*\&class=([^\)\&]*)\&id=([\d]*)[^\)]*\)/i", $sText, $aMentionMatches);
$aMentionedObjects = [];
$aMentionMatches = [];
$sText = html_entity_decode($sText);
preg_match_all('/<a\s*([^>]*)data-object-class="([^"]*)"\s.*data-object-key="([^"]*)"/Ui', $sText, $aMentionMatches);
foreach ($aMentionMatches[0] as $iMatchIdx => $sCompleteMatch) {
$sMatchedClass = $aMentionMatches[2][$iMatchIdx];
$sMatchedId = $aMentionMatches[3][$iMatchIdx];

View File

@@ -11,7 +11,7 @@ define('APPCONF', APPROOT.'conf/');
*
* @see ITOP_CORE_VERSION to get full iTop core version
*/
define('ITOP_DESIGN_LATEST_VERSION', '3.2');
define('ITOP_DESIGN_LATEST_VERSION', '3.3');
/**
* Constant containing the iTop core version, whatever application was built
@@ -23,7 +23,7 @@ define('ITOP_DESIGN_LATEST_VERSION', '3.2');
* @used-by utils::GetItopVersionWikiSyntax()
* @used-by iTopModulesPhpVersionIntegrationTest
*/
define('ITOP_CORE_VERSION', '3.2.0');
define('ITOP_CORE_VERSION', '3.3.0');
/**
* @var string

View File

@@ -34,7 +34,7 @@
"symfony/yaml": "~6.4.0",
"tecnickcom/tcpdf": "^6.6.0",
"thenetworg/oauth2-azure": "^2.0",
"masterminds/html5": "^2.8.0"
"soundasleep/html2text": "~2.1"
},
"require-dev": {
"symfony/debug-bundle": "~6.4.0",

240
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "c8ab1de6659dd4fa2ded6043615b86e6",
"content-hash": "18d0c769ec5c249b96e3be8f168bbb11",
"packages": [
{
"name": "apereo/phpcas",
@@ -1018,73 +1018,6 @@
},
"time": "2023-03-17T15:20:52+00:00"
},
{
"name": "masterminds/html5",
"version": "2.9.0",
"source": {
"type": "git",
"url": "https://github.com/Masterminds/html5-php.git",
"reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
"reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
"shasum": ""
},
"require": {
"ext-dom": "*",
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.7-dev"
}
},
"autoload": {
"psr-4": {
"Masterminds\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Matt Butcher",
"email": "technosophos@gmail.com"
},
{
"name": "Matt Farina",
"email": "matt@mattfarina.com"
},
{
"name": "Asmir Mustafic",
"email": "goetas@gmail.com"
}
],
"description": "An HTML5 parser and serializer.",
"homepage": "http://masterminds.github.io/html5-php",
"keywords": [
"HTML5",
"dom",
"html",
"parser",
"querypath",
"serializer",
"xml"
],
"support": {
"issues": "https://github.com/Masterminds/html5-php/issues",
"source": "https://github.com/Masterminds/html5-php/tree/2.9.0"
},
"time": "2024-03-31T07:05:07+00:00"
},
{
"name": "nikic/php-parser",
"version": "v4.18.0",
@@ -2041,6 +1974,61 @@
},
"time": "2024-01-13T12:36:40+00:00"
},
{
"name": "soundasleep/html2text",
"version": "2.1.0",
"source": {
"type": "git",
"url": "https://github.com/soundasleep/html2text.git",
"reference": "83502b6f8f1aaef8e2e238897199d64f284b4af3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/soundasleep/html2text/zipball/83502b6f8f1aaef8e2e238897199d64f284b4af3",
"reference": "83502b6f8f1aaef8e2e238897199d64f284b4af3",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"php": "^7.3|^8.0"
},
"require-dev": {
"phpstan/phpstan": "^1.9",
"phpunit/phpunit": "^7.0|^8.0|^9.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Soundasleep\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jevon Wright",
"homepage": "https://jevon.org",
"role": "Developer"
}
],
"description": "A PHP script to convert HTML into a plain text format",
"homepage": "https://github.com/soundasleep/html2text",
"keywords": [
"email",
"html",
"php",
"text"
],
"support": {
"email": "support@jevon.org",
"issues": "https://github.com/soundasleep/html2text/issues",
"source": "https://github.com/soundasleep/html2text/tree/2.1.0"
},
"time": "2023-01-06T09:28:15+00:00"
},
{
"name": "symfony/cache",
"version": "v6.4.2",
@@ -3941,6 +3929,82 @@
],
"time": "2023-01-26T09:26:14+00:00"
},
{
"name": "symfony/polyfill-php81",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php81.git",
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"type": "library",
"extra": {
"thanks": {
"url": "https://github.com/symfony/polyfill",
"name": "symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php81\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-php83",
"version": "v1.28.0",
@@ -4856,20 +4920,20 @@
},
{
"name": "tecnickcom/tcpdf",
"version": "6.6.5",
"version": "6.7.5",
"source": {
"type": "git",
"url": "https://github.com/tecnickcom/TCPDF.git",
"reference": "5fce932fcee4371865314ab7f6c0d85423c5c7ce"
"reference": "951eabf0338ec2522bd0d5d9c79b08a3a3d36b36"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/5fce932fcee4371865314ab7f6c0d85423c5c7ce",
"reference": "5fce932fcee4371865314ab7f6c0d85423c5c7ce",
"url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/951eabf0338ec2522bd0d5d9c79b08a3a3d36b36",
"reference": "951eabf0338ec2522bd0d5d9c79b08a3a3d36b36",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
"php": ">=5.5.0"
},
"type": "library",
"autoload": {
@@ -4916,7 +4980,7 @@
],
"support": {
"issues": "https://github.com/tecnickcom/TCPDF/issues",
"source": "https://github.com/tecnickcom/TCPDF/tree/6.6.5"
"source": "https://github.com/tecnickcom/TCPDF/tree/6.7.5"
},
"funding": [
{
@@ -4924,7 +4988,7 @@
"type": "custom"
}
],
"time": "2023-09-06T15:09:26+00:00"
"time": "2024-04-20T17:25:10+00:00"
},
{
"name": "thenetworg/oauth2-azure",
@@ -4988,30 +5052,38 @@
},
{
"name": "twig/twig",
"version": "v3.8.0",
"version": "v3.16.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d"
"reference": "475ad2dc97d65d8631393e721e7e44fb544f0561"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/475ad2dc97d65d8631393e721e7e44fb544f0561",
"reference": "475ad2dc97d65d8631393e721e7e44fb544f0561",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"php": ">=8.0.2",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php80": "^1.22"
"symfony/polyfill-php81": "^1.29"
},
"require-dev": {
"phpstan/phpstan": "^2.0",
"psr/container": "^1.0|^2.0",
"symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0"
"symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0"
},
"type": "library",
"autoload": {
"files": [
"src/Resources/core.php",
"src/Resources/debug.php",
"src/Resources/escaper.php",
"src/Resources/string_loader.php"
],
"psr-4": {
"Twig\\": "src/"
}
@@ -5044,7 +5116,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.8.0"
"source": "https://github.com/twigphp/Twig/tree/v3.16.0"
},
"funding": [
{
@@ -5056,7 +5128,7 @@
"type": "tidelift"
}
],
"time": "2023-11-21T18:54:41+00:00"
"time": "2024-11-29T08:27:05+00:00"
},
{
"name": "webmozart/assert",

View File

@@ -181,7 +181,7 @@ abstract class Action extends cmdbAbstractObject
{
parent::DisplayBareRelations($oPage, false);
if ($oPage instanceof iTopWebPage) {
if ($oPage instanceof iTopWebPage && !$this->IsNew()) {
$this->GenerateLastExecutionsTab($oPage, $bEditMode);
}
}
@@ -566,6 +566,7 @@ class ActionEmail extends ActionNotification
$oLog->Set('trigger_id', $oTrigger->GetKey());
$oLog->Set('action_id', $this->GetKey());
$oLog->Set('object_id', $aContextArgs['this->object()']->GetKey());
$oLog->Set('object_class', get_class($aContextArgs['this->object()']));
// Must be inserted now so that it gets a valid id that will make the link
// between an eventual asynchronous task (queued) and the log
$oLog->DBInsertNoReload();
@@ -628,7 +629,7 @@ class ActionEmail extends ActionNotification
*/
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
{
$sStyles = file_get_contents(APPROOT.'css/email.css');
$sStyles = file_get_contents(APPROOT . utils::GetCSSFromSASS("css/email.scss"));
$sStyles .= MetaModel::GetConfig()->Get('email_css');
$oEmail = new EMail();
@@ -867,7 +868,13 @@ class ActionEmail extends ActionNotification
*/
protected function BuildMessageBody(bool $bHighlightPlaceholders = false): string
{
$sBody = $this->Get('body');
// Wrap content with a specific class in order to apply styles of HTML fields through the emogrifier (see `css/email.scss`)
$sBody = <<<HTML
<div class="email-is-html-content">
{$this->Get('body')}
</div>
HTML;
/** @var ormDocument $oHtmlTemplate */
$oHtmlTemplate = $this->Get('html_template');
if ($oHtmlTemplate && !$oHtmlTemplate->IsEmpty()) {

View File

@@ -89,7 +89,7 @@ abstract class AsyncTask extends DBObject
// The value is set from null to planned in the setup program
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('planned,running,idle,error'), "sql"=>"status", "default_value"=>"planned", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("started", array("allowed_values"=>null, "sql"=>"started", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("planned", array("allowed_values"=>null, "sql"=>"planned", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("event_id", array("targetclass"=>"Event", "jointype"=> "", "allowed_values"=>null, "sql"=>"event_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
@@ -489,7 +489,7 @@ class AsyncSendNewsroom extends AsyncTask {
MetaModel::Init_AddAttribute(new AttributeInteger("object_id", array("allowed_values"=>null, "sql"=>"object_id", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("object_class", array("allowed_values"=>null, "sql"=>"object_class", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeText("url", array("allowed_values"=>null, "sql"=>"url", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>'NOW()', "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
}

View File

@@ -142,7 +142,7 @@ abstract class AttributeDefinition
protected $aCSSClasses;
public function GetType()
public function GetType()
{
return Dict::S('Core:'.get_class($this));
}
@@ -1715,8 +1715,8 @@ class AttributeLinkedSet extends AttributeDefinition
public function GetEditMode()
{
return $this->GetOptional('edit_mode', LINKSET_EDITMODE_ACTIONS);
}
}
/**
* @return int see LINKSET_EDITWHEN_* constants
* @since 3.1.1 3.2.0 N°6385
@@ -1758,7 +1758,7 @@ class AttributeLinkedSet extends AttributeDefinition
{
return $this->GetOptional('with_php_computation', false);
}
public function GetLinkedClass()
{
return $this->Get('linked_class');
@@ -2069,7 +2069,7 @@ class AttributeLinkedSet extends AttributeDefinition
public function GetImportColumns()
{
$aColumns = array();
$aColumns[$this->GetCode()] = 'TEXT'.CMDBSource::GetSqlStringColumnDefinition();
$aColumns[$this->GetCode()] = 'MEDIUMTEXT'.CMDBSource::GetSqlStringColumnDefinition();
return $aColumns;
}
@@ -3793,7 +3793,7 @@ class AttributeClass extends AttributeString
public static function ListExpectedParams()
{
return array_merge(parent::ListExpectedParams(), array("class_category", "more_values"));
return array_merge(parent::ListExpectedParams(), array('class_category', 'more_values'));
}
public function __construct($sCode, $aParams)
@@ -3819,10 +3819,35 @@ class AttributeClass extends AttributeString
return $sDefault;
}
/**
* @param array $aArgs
* @param string $sContains
*
* @return array|null
* @throws \CoreException
*/
public function GetAllowedValues($aArgs = array(), $sContains = '')
{
$oValSetDef = $this->GetValuesDef();
if (!$oValSetDef) {
return null;
}
$aListClass = $oValSetDef->GetValues($aArgs, $sContains);
/* @since 3.3.0 remove elements in class_exclusion_list*/
$sClassExclusionList = $this->GetOptional('class_exclusion_list',null);
if (!empty($sClassExclusionList)) {
foreach (explode(',', $sClassExclusionList) as $sClassName) {
unset($aListClass[trim($sClassName)]);
}
}
return $aListClass;
}
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
if (empty($sValue))
{
if (empty($sValue)) {
return '';
}
@@ -4193,7 +4218,7 @@ class AttributeFinalClass extends AttributeString
*/
class AttributePassword extends AttributeString implements iAttributeNoGroupBy
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
@@ -4270,7 +4295,7 @@ class AttributePassword extends AttributeString implements iAttributeNoGroupBy
*/
class AttributeEncryptedString extends AttributeString implements iAttributeNoGroupBy
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
protected function GetSQLCol($bFullSpec = false)
{
@@ -4995,7 +5020,7 @@ class AttributeCaseLog extends AttributeLongText
}
else
{
if (strlen($proposedValue) > 0)
if (utils::StrLen($proposedValue) > 0)
{
//N°5135 - add impersonation information in caselog
if (UserRights::IsImpersonated()){
@@ -6346,10 +6371,15 @@ class AttributeDateTime extends AttributeDBField
$oFormField = parent::MakeFormField($oObject, $oFormField);
// After call to the parent as it sets the current value
$oFormField->SetCurrentValue($this->GetFormat()->Format($oObject->Get($this->GetCode())));
// After call to the parent as it sets the current value
$oValue = $oObject->Get($this->GetCode());
if ($oValue === $this->GetNullValue()) {
$oValue = $this->GetDefaultValue($oObject);
}
$oFormField->SetCurrentValue($this->GetFormat()->Format($oValue));
return $oFormField;
return $oFormField;
}
/**
@@ -6433,8 +6463,26 @@ class AttributeDateTime extends AttributeDBField
public function GetDefaultValue(DBObject $oHostObject = null)
{
if (!$this->IsNullAllowed()) {
return date($this->GetInternalFormat());
$sDefaultValue = $this->Get('default_value');
if (utils::IsNotNullOrEmptyString($sDefaultValue)) {
try {
$sDefaultDate = Expression::FromOQL($sDefaultValue)->Evaluate([]);
} catch (Exception $e) {
try {
$sDefaultDate = Expression::FromOQL('"'.$sDefaultValue.'"')->Evaluate([]);
} catch (Exception $e) {
IssueLog::Error("Invalid default value '$sDefaultValue' for field '{$this->GetCode()}' on class '{$this->GetHostClass()}', defaulting to null");
return $this->GetNullValue();
}
}
try {
$oDate = new DateTimeImmutable($sDefaultDate);
} catch (Exception $e) {
IssueLog::Error("Invalid default value '$sDefaultValue' for field '{$this->GetCode()}' on class '{$this->GetHostClass()}', defaulting to null");
return $this->GetNullValue();
}
return $oDate->format($this->GetInternalFormat());
}
return $this->GetNullValue();
}
@@ -9433,8 +9481,13 @@ class AttributeStopWatch extends AttributeDefinition
case 'deadline':
if ($value)
{
$sDate = date(AttributeDateTime::GetInternalFormat(), $value);
$sRet = AttributeDeadline::FormatDeadline($sDate);
if (is_int($value))
{
$sDate = date(AttributeDateTime::GetInternalFormat(), $value);
$sRet = AttributeDeadline::FormatDeadline($sDate);
} else {
$sRet = $value;
}
}
else
{
@@ -9984,7 +10037,7 @@ class AttributeSubItem extends AttributeDefinition
*/
class AttributeOneWayPassword extends AttributeDefinition implements iAttributeNoGroupBy
{
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_RAW;
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
@@ -10926,12 +10979,12 @@ abstract class AttributeSet extends AttributeDBFieldVoid
$sDescription = utils::EscapeHtml($this->GetValueDescription($sValue));
$oFilter = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sValue'");
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$sContext = $oAppContext->GetForLink(true);
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($oFilter->GetClass());
$sFilter = rawurlencode($oFilter->serialize());
$sLink = '';
if ($bWithLink && $this->bDisplayLink) {
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter.$sContext;
$sLink = ' href="'.$sUrl.'"';
}
@@ -12248,13 +12301,13 @@ class AttributeTagSet extends AttributeSet
$sTagDescription = $oTag->Get('description');
$oFilter = DBSearch::FromOQL("SELECT $sClass WHERE $sAttCode MATCHES '$sTagCode'");
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$sContext = $oAppContext->GetForLink(true);
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($oFilter->GetClass());
$sFilter = rawurlencode($oFilter->serialize());
$sLink = '';
if ($bWithLink && $this->bDisplayLink) {
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter."&{$sContext}";
$sUrl = utils::GetAbsoluteUrlAppRoot()."pages/$sUIPage?operation=search&filter=".$sFilter.$sContext;
$sLink = ' href="'.$sUrl.'"';
}

View File

@@ -201,6 +201,7 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
static::MODULE_SETTING_TIME,
static::GetDefaultModuleSettingTime()
);
$sProcessTime = trim($sProcessTime);
if (!preg_match('/[0-2]\d:[0-5]\d/', $sProcessTime))
{
throw new ProcessInvalidConfigException($this->GetModuleName().": wrong format for setting '".static::MODULE_SETTING_TIME."' (found '$sProcessTime')");
@@ -230,7 +231,7 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
$iFirstDayOfWeek = $aDays[0];
$iDayMove = $oNow->format('N') - $iFirstDayOfWeek;
$oRet = clone $oNow;
$oRet->modify('-'.$iDayMove.' days');
$oRet->modify(-$iDayMove.' days');
$oRet->modify('+1 weeks');
}
else
@@ -238,7 +239,7 @@ abstract class AbstractWeeklyScheduledProcess implements iScheduledProcess
$iNextDayOfWeek = $aDays[$iNextPos];
$iMove = $iNextDayOfWeek - $oNow->format('N');
$oRet = clone $oNow;
$oRet->modify('+'.$iMove.' days');
$oRet->modify($iMove.' days');
}
list($sHours, $sMinutes) = explode(':', $sProcessTime);
$oRet->setTime((int)$sHours, (int)$sMinutes);

View File

@@ -472,8 +472,10 @@ class BulkChange
protected $m_bLocalizedValues;
/** @var array Cache for resolving external keys based on the given search criterias */
protected $m_aExtKeysMappingCache;
/** @var int number of columns */
protected $m_iNbCol;
public function __construct($sClass, $aData, $aAttList, $aExtKeys, $aReconcilKeys, $sSynchroScope = null, $aOnDisappear = null, $sDateFormat = null, $bLocalize = false)
public function __construct($sClass, $aData, $aAttList, $aExtKeys, $aReconcilKeys, $sSynchroScope = null, $aOnDisappear = null, $sDateFormat = null, $bLocalize = false, $iNbCol = 0)
{
$this->m_sClass = $sClass;
$this->m_aData = $aData;
@@ -485,6 +487,7 @@ class BulkChange
$this->m_sDateFormat = $sDateFormat;
$this->m_bLocalizedValues = $bLocalize;
$this->m_aExtKeysMappingCache = array();
$this->m_iNbCol =$iNbCol;
}
protected function ResolveExternalKey($aRowData, $sAttCode, &$aResults)
@@ -519,7 +522,7 @@ class BulkChange
foreach ($this->m_aExtKeys[$sAttCode] as $sForeignAttCode => $iCol)
{
// The foreign attribute is one of our reconciliation key
if (strlen($aRowData[$iCol]) > 0)
if (isset($aRowData[$iCol]) && strlen($aRowData[$iCol]) > 0)
{
return false;
}
@@ -1181,6 +1184,9 @@ class BulkChange
foreach($this->m_aData as $iRow => $aRowData)
{
$sFormat = $sDateTimeFormat;
if(!isset($this->m_aData[$iRow][$iCol])){
continue;
}
$sValue = $this->m_aData[$iRow][$iCol];
if (!empty($sValue))
{
@@ -1238,6 +1244,12 @@ class BulkChange
try {
foreach ($this->m_aData as $iRow => $aRowData) {
set_time_limit(intval($iLoopTimeLimit));
// Stop if not the good number of cols in $aRowData
if($this->m_iNbCol>0 && count($aRowData) != $this->m_iNbCol){
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::Format('UI:CSVReport-Row-Issue-NbField',count($aRowData),$this->m_iNbCol) );
continue;
}
if (isset($aResult[$iRow]["__STATUS__"])) {
// An issue at the earlier steps - skip the rest
continue;
@@ -1348,7 +1360,11 @@ class BulkChange
{
if (!array_key_exists($iCol, $aResult[$iRow]))
{
$aResult[$iRow][$iCol] = new CellStatus_Void($aRowData[$iCol]);
if(isset($aRowData[$iCol])) {
$aResult[$iRow][$iCol] = new CellStatus_Void($aRowData[$iCol]);
} else {
$aResult[$iRow][$iCol] = new CellStatus_Issue('', null, Dict::S('UI:CSVReport-Value-Issue-NoValue'));
}
}
}
foreach($this->m_aExtKeys as $sAttCode => $aForeignAtts)
@@ -1362,7 +1378,11 @@ class BulkChange
if (!array_key_exists($iCol, $aResult[$iRow]))
{
// The foreign attribute is one of our reconciliation key
$aResult[$iRow][$iCol] = new CellStatus_Void($aRowData[$iCol]);
if(isset($aRowData[$iCol])) {
$aResult[$iRow][$iCol] = new CellStatus_Void($aRowData[$iCol]);
} else {
$aResult[$iRow][$iCol] = new CellStatus_Issue('', null, 'UI:CSVReport-Value-Issue-NoValue');
}
}
}
}
@@ -1406,7 +1426,7 @@ class BulkChange
$aDetails = array();
while ($oChange = $oBulkChanges->Fetch())
{
$sDate = '<a href="csvimport.php?step=10&changeid='.$oChange->GetKey().'&'.$oAppContext->GetForLink().'">'.$oChange->Get('date').'</a>';
$sDate = '<a href="csvimport.php?step=10&changeid='.$oChange->GetKey().$oAppContext->GetForLink(true).'">'.$oChange->Get('date').'</a>';
$sUser = $oChange->GetUserName();
if (preg_match('/^(.*)\\(CSV\\)$/i', $oChange->Get('userinfo'), $aMatches))
{
@@ -1488,7 +1508,7 @@ EOF
function OnTruncatedHistoryToggle(bShowAll)
{
$('#csv_history_reload').html('<img src="' + GetAbsoluteUrlAppRoot() + 'images/indicator.gif"/>');
$.get(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?{$sAppContext}', {operation: 'displayCSVHistory', showall: bShowAll}, function(data)
$.get(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?$sAppContext', {operation: 'displayCSVHistory', showall: bShowAll}, function(data)
{
$('#$sAjaxDivId').html(data);
}

View File

@@ -25,7 +25,7 @@ define('EXPORTER_DEFAULT_CHUNK_SIZE', 1000);
class BulkExportException extends Exception
{
protected $sLocalizedMessage;
public function __construct($message, $sLocalizedMessage, $code = null, $previous = null)
public function __construct($message, $sLocalizedMessage, $code = 0, $previous = null)
{
parent::__construct($message, $code, $previous);
$this->sLocalizedMessage = $sLocalizedMessage;
@@ -70,7 +70,7 @@ class BulkExportResult extends DBObject
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("created", array("allowed_values"=>null, "sql"=>"created", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("user_id", array("allowed_values"=>null, "sql"=>"user_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("chunk_size", array("allowed_values"=>null, "sql"=>"chunk_size", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("format", array("allowed_values"=>null, "sql"=>"format", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
@@ -474,14 +474,14 @@ abstract class BulkExport
*/
protected function MakeTmpFile($sExtension)
{
if(!is_dir(APPROOT."data/bulk_export"))
if(!is_dir(utils::GetDataPath()."bulk_export"))
{
@mkdir(APPROOT."data/bulk_export", 0777, true /* recursive */);
@mkdir(utils::GetDataPath()."bulk_export", 0777, true /* recursive */);
clearstatcache();
}
if (!is_writable(APPROOT."data/bulk_export"))
if (!is_writable(utils::GetDataPath()."bulk_export"))
{
throw new Exception('Data directory "'.APPROOT.'data/bulk_export" could not be written.');
throw new Exception('Data directory "'.utils::GetDataPath().'bulk_export" could not be written.');
}
$iNum = rand();
@@ -489,7 +489,7 @@ abstract class BulkExport
{
$iNum++;
$sToken = sprintf("%08x", $iNum);
$sFileName = APPROOT."data/bulk_export/$sToken.".$sExtension;
$sFileName = utils::GetDataPath()."bulk_export/$sToken.".$sExtension;
$hFile = @fopen($sFileName, 'x');
}
while($hFile === false);

View File

@@ -33,7 +33,7 @@ class CMDBChange extends DBObject
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("allowed_values"=>null, "sql"=>"user_id", "targetclass"=>"User", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("origin", array("allowed_values"=>new ValueSetEnum(implode(',', [CMDBChangeOrigin::INTERACTIVE, CMDBChangeOrigin::CSV_INTERACTIVE, CMDBChangeOrigin::CSV_IMPORT, CMDBChangeOrigin::WEBSERVICE_SOAP, CMDBChangeOrigin::WEBSERVICE_REST, CMDBChangeOrigin::SYNCHRO_DATA_SOURCE, CMDBChangeOrigin::EMAIL_PROCESSING, CMDBChangeOrigin::CUSTOM_EXTENSION])), "sql"=>"origin", "default_value"=>CMDBChangeOrigin::INTERACTIVE, "is_null_allowed"=>true, "depends_on"=>array())));

View File

@@ -1169,8 +1169,8 @@ class CMDBSource
*/
public static function IsSameFieldTypes($sItopGeneratedFieldType, $sDbFieldType)
{
list($sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
list($sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sDbFieldType);
[$sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
[$sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sDbFieldType);
if (strcasecmp($sItopFieldDataType, $sDbFieldDataType) !== 0)
{
@@ -1603,7 +1603,19 @@ class CMDBSource
return false;
}
/**
public static function GetClusterNb()
{
$result = 0;
$sSql = "SHOW STATUS LIKE 'wsrep_cluster_size';";
$aRows = self::QueryToArray($sSql);
if (count($aRows) > 0)
{
$result = $aRows[0]['Value'];
}
return intval($result);
}
/**
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-database.html
* @return string query to upgrade database charset and collation if needed, null if not
* @throws \MySQLException

View File

@@ -108,7 +108,7 @@ class DefaultWorkingTimeComputer implements iWorkingTimeComputer
// Default implementation: 24x7, no holidays: to compute the deadline, just add
// the specified duration to the given date/time
$oResult = clone $oStartDate;
$oResult->modify('+'.$iDuration.' seconds');
$oResult->modify($iDuration.' seconds');
if (class_exists('WorkingTimeRecorder'))
{
WorkingTimeRecorder::SetValues($oStartDate->format('U'), $oResult->format('U'), $iDuration, WorkingTimeRecorder::COMPUTED_END);

View File

@@ -29,7 +29,7 @@ define('ITOP_APPLICATION_SHORT', 'iTop');
*
* @see ITOP_CORE_VERSION to get iTop core version
*/
define('ITOP_VERSION', '3.2.0-dev');
define('ITOP_VERSION', '3.3.0-dev');
define('ITOP_VERSION_NAME', 'Fullmoon');
define('ITOP_REVISION', 'svn');
@@ -71,7 +71,7 @@ define('DEFAULT_MAX_DISPLAY_LIMIT', 30);
define('DEFAULT_STANDARD_RELOAD_INTERVAL', 5 * 60);
define('DEFAULT_FAST_RELOAD_INTERVAL', 1 * 60);
define('DEFAULT_SECURE_CONNECTION_REQUIRED', false);
define('DEFAULT_ALLOWED_LOGIN_TYPES', 'form|external|basic');
define('DEFAULT_ALLOWED_LOGIN_TYPES', 'form|external|basic|token');
define('DEFAULT_EXT_AUTH_VARIABLE', '$_SERVER[\'REMOTE_USER\']');
define('DEFAULT_ENCRYPTION_KEY', '@iT0pEncr1pti0n!'); // We'll use a random generated key later (if possible)
define('DEFAULT_ENCRYPTION_LIB', 'Mcrypt'); // We'll define the best encryption available later
@@ -1233,6 +1233,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'sessions_tracking.session_handler_extension' => [
'type' => 'string',
'description' => 'to store more data in itop session files, set your own iSessionHandlerExtension implementation class in this variable',
'default' => '',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'sessions_tracking.gc_threshold' => [
'type' => 'integer',
'description' => 'fallback in case cron is not active: probability in percent that session files are cleanup during any itop request (100 means always)',
@@ -1436,6 +1444,14 @@ class Config
'quick_create.max_history_results' => [
'type' => 'integer',
'description' => 'Max. number of elements in the history',
'default' => 5,
'value' => 5,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'quick_create.max_popular_results' => [
'type' => 'integer',
'description' => 'Max. number of elements in the popular classes section',
'default' => 10,
'value' => 10,
'source_of_value' => '',
@@ -1747,6 +1763,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'developer_mode.interface_cache.enabled' => [
'type' => 'bool',
'description' => 'If true then InterfaceDiscovery uses dynamic cache (in developer_mode)',
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'theme.enable_precompilation' => [
'type' => 'bool',
'description' => 'If false, theme compilation will not use any precompiled file setup optimization.)',
@@ -1787,6 +1811,13 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'http.request.user_agent' => [
'type' => 'string',
'description' => 'HTTP request user agent, use this to set a custom agent on external requests.',
'default' => ITOP_APPLICATION.'/'.ITOP_VERSION,
'source_of_value' => '',
'show_in_conf_sample' => false,
]
];
public function IsProperty($sPropCode)

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.2">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.3">
<classes>
<class id="lnkActionNotificationToContact" _delta="define">
<parent>cmdbAbstractObject</parent>
@@ -16,6 +16,13 @@
<attribute id="contact_id"/>
</attributes>
</naming>
<reconciliation>
<attributes>
<attribute id="action_id"/>
<attribute id="contact_id"/>
<attribute id="trigger_id"/>
</attributes>
</reconciliation>
<uniqueness_rules>
<rule>
<attributes>
@@ -92,7 +99,7 @@
</presentation>
<methods/>
</class>
<class id="ActionNewsroom" _delta="define">
<class id="ActionNewsroom" _delta="define">
<php_parent>
<name>ActionNotification</name>
</php_parent>
@@ -116,6 +123,11 @@
<style>
<icon>../../images/icons/icons8-notification.svg</icon>
</style>
<reconciliation>
<attributes>
<attribute id="name"/>
</attributes>
</reconciliation>
</properties>
<fields>
<field id="title" xsi:type="AttributeString">
@@ -136,7 +148,7 @@
<display_max_height>96</display_max_height>
<storage_max_width>256</storage_max_width>
<storage_max_height>256</storage_max_height>
<default_image />
<default_image/>
</field>
<field id="priority" xsi:type="AttributeEnum">
<sql>priority</sql>
@@ -171,7 +183,7 @@
</field>
<field id="url" xsi:type="AttributeString">
<sql>url</sql>
<default_value>$this->url()$</default_value>
<default_value>$this-&gt;url()$</default_value>
<is_null_allowed>false</is_null_allowed>
</field>
</fields>
@@ -185,18 +197,21 @@
<item id="name">
<rank>10</rank>
</item>
<item id="status">
<item id="description">
<rank>20</rank>
</item>
<item id="language">
<item id="status">
<rank>30</rank>
</item>
<item id="title">
<item id="language">
<rank>40</rank>
</item>
<item id="message">
<item id="title">
<rank>50</rank>
</item>
<item id="message">
<rank>60</rank>
</item>
</items>
</item>
<item id="fieldset:ActionNewsroom:trigger">
@@ -241,12 +256,15 @@
<item id="title">
<rank>10</rank>
</item>
<item id="status">
<item id="description">
<rank>20</rank>
</item>
<item id="language">
<item id="status">
<rank>30</rank>
</item>
<item id="language">
<rank>40</rank>
</item>
</items>
</list>
</presentation>
@@ -337,21 +355,37 @@
}
}
if ($bIsAsync === true) {
AsyncSendNewsroom::AddToQueue($this->GetKey(), $oTrigger->GetKey(), $aRecipientsIds, $sMessage, $sTitle, $sUrl, $iObjectId, $sObjectClass);
} else {
foreach ($aRecipientsIds as $iRecipientId) {
$oEvent = Combodo\iTop\Service\Notification\Event\EventNotificationNewsroomService::MakeEventFromAction($this, $iRecipientId, $oTrigger->GetKey(), $sMessage, $sTitle, $sUrl, $iObjectId, $sObjectClass);
$oEvent->DBInsertNoReload();
}
}
try {
if ($bIsAsync === true) {
AsyncSendNewsroom::AddToQueue($this->GetKey(), $oTrigger->GetKey(), $aRecipientsIds, $sMessage, $sTitle, $sUrl, $iObjectId, $sObjectClass);
} else {
foreach ($aRecipientsIds as $iRecipientId) {
$oEvent = Combodo\iTop\Service\Notification\Event\EventNotificationNewsroomService::MakeEventFromAction($this, $iRecipientId, $oTrigger->GetKey(), $sMessage, $sTitle, $sUrl, $iObjectId, $sObjectClass);
$oEvent->DBInsertNoReload();
}
}
} catch (CoreCannotSaveObjectException $e) {
ExceptionLog::LogException($e);
foreach($aRecipientsIds as $iRecipientId) {
$oEvent = Combodo\iTop\Service\Notification\Event\EventNotificationNewsroomService::MakeEventFromAction($this,
$iRecipientId,
$oTrigger->GetKey(),
Dict::S('Core:EventNotificationNewsroom:ErrorOnDBInsert'),
Dict::S('Core:EventNotificationNewsroom:ErrorNotificationNotSent'),
$sUrl,
$iObjectId,
$sObjectClass
);
$oEvent->DBInsertNoReload();
}
}
$this->SetNotificationLanguage($sPreviousLanguage, $aPreviousPluginProperties['language_code'] ?? null);
}
]]></code>
</method>
<method id="GetAsynchronousGlobalSetting">
<comment></comment>
<comment/>
<static>true</static>
<access>public</access>
<code><![CDATA[
@@ -363,7 +397,7 @@
</method>
</methods>
</class>
<class id="EventNotificationNewsroom" _delta="define">
<class id="EventNotificationNewsroom" _delta="define">
<php_parent>
<name>EventNotification</name>
</php_parent>
@@ -380,9 +414,14 @@
<attribute id="title"/>
</attributes>
</naming>
<reconciliation>
<attributes>
<attribute id="title"/>
</attributes>
</reconciliation>
<order>
<columns>
<column id="date" ascending="false" />
<column id="date" ascending="false"/>
</columns>
</order>
</properties>
@@ -396,7 +435,7 @@
<sql>icon</sql>
<default_value/>
<is_null_allowed>true</is_null_allowed>
<default_image />
<default_image/>
</field>
<field id="priority" xsi:type="AttributeEnum">
<sql>priority</sql>
@@ -478,14 +517,14 @@
</items>
</details>
<summary>
<items>
<item id="date">
<rank>10</rank>
</item>
<item id="message">
<rank>20</rank>
</item>
</items>
<items>
<item id="date">
<rank>10</rank>
</item>
<item id="message">
<rank>20</rank>
</item>
</items>
</summary>
<list>
<items>
@@ -827,6 +866,18 @@
<field id="language" xsi:type="AttributeApplicationLanguage"/>
</fields>
</class>
<class id="Event" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>DBObject</parent>
<properties>
<category>core/cmdb,view_in_gui</category>
</properties>
<fields>
<field id="message" xsi:type="AttributeText"/>
<field id="date" xsi:type="AttributeDateTime"/>
<field id="userinfo" xsi:type="AttributeString"/>
</fields>
</class>
<class id="EventNotification" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>Event</parent>
@@ -973,6 +1024,12 @@
<type>string</type>
<default/>
</property>
<property id="class_exclusion_list">
<php_param>class_exclusion_list</php_param>
<mandatory>false</mandatory>
<type>string</type>
<default/>
</property>
<property id="min_up">
<php_param>min_up</php_param>
<mandatory>true</mandatory>

View File

@@ -212,6 +212,8 @@ abstract class DBObject implements iDisplay
private $aEventListeners = [];
private array $aAllowedTransitions = [];
private ?string $sStimulusBeingApplied = null;
/**
* DBObject constructor.
*
@@ -768,6 +770,42 @@ abstract class DBObject implements iDisplay
$this->Set($sAttCode, $sValue);
}
/**
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
* @throws \OQLException
* @throws \ReflectionException
*/
protected function PreDeleteActions(): void
{
$this->SetReadOnly('No modification allowed before delete');
$this->FireEventAboutToDelete();
$oKPI = new ExecutionKPI();
$this->OnDelete();
$oKPI->ComputeStatsForExtension($this, 'OnDelete');
// Activate any existing trigger
$sClass = get_class($this);
$aParams = array('class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
$oSet = new DBObjectSet(DBObjectSearch::FromOQL('SELECT TriggerOnObjectDelete AS t WHERE t.target_class IN (:class_list)'), array(),
$aParams);
while ($oTrigger = $oSet->Fetch()) {
/** @var \TriggerOnObjectDelete $oTrigger */
try {
$oKPI = new ExecutionKPI();
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch (Exception $e) {
$oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e);
}
finally {
$oKPI->ComputeStatsForExtension($this, 'TriggerOnObjectDelete');
}
}
}
/**
* @return void
* @throws \ReflectionException
@@ -1170,7 +1208,7 @@ abstract class DBObject implements iDisplay
if ($aCallInfo["function"] != "ComputeValues") continue;
return; //skip!
}
$this->FireEventComputeValues();
$this->FireEventComputeValues($this->sStimulusBeingApplied);
$oKPI = new ExecutionKPI();
$this->ComputeValues();
$oKPI->ComputeStatsForExtension($this, 'ComputeValues');
@@ -2094,7 +2132,7 @@ abstract class DBObject implements iDisplay
return "Bad type";
}
elseif (($oAtt instanceof AttributeClassAttCodeSet) || ($oAtt instanceof AttributeEnumSet))
elseif ($oAtt instanceof AttributeSet)
{
if (is_string($toCheck))
{
@@ -2633,7 +2671,7 @@ abstract class DBObject implements iDisplay
// Ultimate check - ensure DB integrity
$this->SetReadOnly('No modification allowed during CheckToCreate');
$this->FireEventCheckToWrite();
$this->FireEventCheckToWrite($this->sStimulusBeingApplied);
$this->SetReadWrite();
$oKPI = new ExecutionKPI();
@@ -2829,6 +2867,14 @@ abstract class DBObject implements iDisplay
protected function ListChangedValues(array $aProposal)
{
$aDelta = array();
$sClass = get_class($this);
if (MetaModel::HasLifecycle($sClass) && utils::IsNotNullOrEmptyString($this->sStimulusBeingApplied)) {
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
if (!in_array($sStateAttCode, $aProposal)) {
// Same state but the transition was asked, act as if the state was changed
$aDelta[$sStateAttCode] = $this->m_aCurrValues[$sStateAttCode];
}
}
foreach ($aProposal as $sAtt => $proposedValue)
{
if (!array_key_exists($sAtt, $this->m_aOrigValues))
@@ -3362,7 +3408,7 @@ abstract class DBObject implements iDisplay
$this->OnInsert();
$oKPI->ComputeStatsForExtension($this, 'OnInsert');
$this->FireEventBeforeWrite();
$this->FireEventBeforeWrite(null);
// If not automatically computed, then check that the key is given by the caller
if (!MetaModel::IsAutoIncrementKey($sRootClass)) {
@@ -3458,6 +3504,7 @@ abstract class DBObject implements iDisplay
$this->m_bIsInDB = true;
$this->m_bDirty = false;
$this->m_bFullyLoaded = true;
foreach ($this->m_aCurrValues as $sAttCode => $value) {
if (is_object($value)) {
$value = clone $value;
@@ -3496,7 +3543,7 @@ abstract class DBObject implements iDisplay
*/
protected function PostInsertActions(): void
{
$this->FireEventAfterWrite([], true);
$this->FireEventAfterWrite([], true, null);
$oKPI = new ExecutionKPI();
$this->AfterInsert();
$oKPI->ComputeStatsForExtension($this, 'AfterInsert');
@@ -3577,13 +3624,13 @@ abstract class DBObject implements iDisplay
*/
public function DBUpdate()
{
$this->LogCRUDEnter(__METHOD__);
if (!MetaModel::StartReentranceProtection($this)) {
$this->LogCRUDExit(__METHOD__, 'Rejected (reentrance)');
return false;
}
$this->LogCRUDEnter(__METHOD__);
if (!$this->m_bIsInDB)
{
throw new CoreException("DBUpdate: could not update a newly created object, please call DBInsert instead");
@@ -3604,7 +3651,7 @@ abstract class DBObject implements iDisplay
$this->OnUpdate();
$oKPI->ComputeStatsForExtension($this, 'OnUpdate');
$this->FireEventBeforeWrite();
$this->FireEventBeforeWrite($this->sStimulusBeingApplied);
// Freeze the changes at this point
$this->InitPreviousValuesForUpdatedAttributes();
@@ -3772,7 +3819,7 @@ abstract class DBObject implements iDisplay
}
try {
$this->PostUpdateActions($aChanges, $sClass);
$this->PostUpdateActions($this->m_aPreviousValuesForUpdatedAttributes, $sClass);
}
catch (Exception $e) {
$this->LogCRUDExit(__METHOD__, 'Error: '.$e->getMessage());
@@ -3815,7 +3862,9 @@ abstract class DBObject implements iDisplay
*/
protected function PostUpdateActions(array $aChanges): void
{
$this->FireEventAfterWrite($aChanges, false);
$sStimulusBeingApplied = $this->sStimulusBeingApplied;
$this->sStimulusBeingApplied = null;
$this->FireEventAfterWrite($aChanges, false, $sStimulusBeingApplied);
$oKPI = new ExecutionKPI();
$this->AfterUpdate();
$oKPI->ComputeStatsForExtension($this, 'AfterUpdate');
@@ -3827,39 +3876,37 @@ abstract class DBObject implements iDisplay
$this->ActivateOnObjectUpdateTriggersForTargetObjects();
$sClass = get_class($this);
if (MetaModel::HasLifecycle($sClass))
if (utils::IsNotNullOrEmptyString($sStimulusBeingApplied))
{
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
if (isset($this->m_aPreviousValuesForUpdatedAttributes[$sStateAttCode])) {
$sPreviousState = $this->m_aPreviousValuesForUpdatedAttributes[$sStateAttCode];
// Change state triggers...
$aParams = array(
'class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL),
'previous_state' => $sPreviousState,
'new_state' => $this->Get($sStateAttCode),
);
$oSet = new DBObjectSet(DBObjectSearch::FromOQL('SELECT TriggerOnStateLeave AS t WHERE t.target_class IN (:class_list) AND t.state=:previous_state'), array(), $aParams);
while ($oTrigger = $oSet->Fetch()) {
/** @var \TriggerOnStateLeave $oTrigger */
try {
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch (Exception $e) {
$oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e);
}
$sPreviousState = $this->m_aPreviousValuesForUpdatedAttributes[$sStateAttCode];
// Change state triggers...
$aParams = array(
'class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL),
'previous_state' => $sPreviousState,
'new_state' => $this->Get($sStateAttCode),
);
$oSet = new DBObjectSet(DBObjectSearch::FromOQL('SELECT TriggerOnStateLeave AS t WHERE t.target_class IN (:class_list) AND t.state=:previous_state'), array(), $aParams);
while ($oTrigger = $oSet->Fetch()) {
/** @var \TriggerOnStateLeave $oTrigger */
try {
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch (Exception $e) {
$oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e);
}
}
$oSet = new DBObjectSet(DBObjectSearch::FromOQL('SELECT TriggerOnStateEnter AS t WHERE t.target_class IN (:class_list) AND t.state=:new_state'), array(), $aParams);
while ($oTrigger = $oSet->Fetch()) {
/** @var \TriggerOnStateEnter $oTrigger */
try {
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch (Exception $e) {
$oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e);
}
$oSet = new DBObjectSet(DBObjectSearch::FromOQL('SELECT TriggerOnStateEnter AS t WHERE t.target_class IN (:class_list) AND t.state=:new_state'), array(), $aParams);
while ($oTrigger = $oSet->Fetch()) {
/** @var \TriggerOnStateEnter $oTrigger */
try {
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch (Exception $e) {
$oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e);
}
}
}
@@ -3987,7 +4034,7 @@ abstract class DBObject implements iDisplay
foreach ($aUpdatedLogAttCodes as $sAttCode) {
/** @var \ormCaseLog $oUpdatedCaseLog */
$oUpdatedCaseLog = $this->Get($sAttCode);
$aMentionedObjects = array_merge_recursive($aMentionedObjects, utils::GetMentionedObjectsFromText($oUpdatedCaseLog->GetModifiedEntry()));
$aMentionedObjects = array_merge_recursive($aMentionedObjects, utils::GetMentionedObjectsFromText($oUpdatedCaseLog->GetModifiedEntry(ormCaseLog::ENUM_FORMAT_HTML)));
}
// 3 - Trigger for those objects
@@ -4092,16 +4139,17 @@ abstract class DBObject implements iDisplay
CMDBSource::DeleteFrom($sDeleteSQL);
}
/**
* @internal
*
* @throws ArchivedObjectException
* @throws CoreException
* @throws CoreUnexpectedValue
* @throws MySQLException
* @throws MySQLHasGoneAwayException
* @throws OQLException
*/
/**
* @internal
*
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @throws \Random\RandomException
* @throws \ReflectionException
*/
protected function DBDeleteSingleObject()
{
$this->LogCRUDEnter(__METHOD__);
@@ -4112,29 +4160,7 @@ abstract class DBObject implements iDisplay
return;
}
$this->SetReadOnly("No modification allowed before delete");
$this->FireEventAboutToDelete();
$oKPI = new ExecutionKPI();
$this->OnDelete();
$oKPI->ComputeStatsForExtension($this, 'OnDelete');
// Activate any existing trigger
$sClass = get_class($this);
$aParams = array('class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectDelete AS t WHERE t.target_class IN (:class_list)"), array(),
$aParams);
while ($oTrigger = $oSet->Fetch())
{
/** @var \TriggerOnObjectDelete $oTrigger */
try
{
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch(Exception $e) {
$oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e);
}
}
$this->PreDeleteActions();
$this->RecordObjDeletion($this->m_iKey); // May cause a reload for storing history information
@@ -4485,6 +4511,8 @@ abstract class DBObject implements iDisplay
*/
public function ApplyStimulus($sStimulusCode, $bDoNotWrite = false)
{
$this->LogCRUDEnter(__METHOD__, "Code: $sStimulusCode");
$sClass = get_class($this);
if (!MetaModel::HasLifecycle($sClass))
{
@@ -4511,6 +4539,8 @@ abstract class DBObject implements iDisplay
} else {
$aBackupValues[$sAttCode] = $value;
}
} else {
$aBackupValues[$sAttCode] = $oAttDef->GetNullValue();
}
}
@@ -4518,7 +4548,6 @@ abstract class DBObject implements iDisplay
// Change the state before proceeding to the actions, this is necessary because an action might
// trigger another stimuli (alternative: push the stimuli into a queue)
$sPreviousState = $this->Get($sStateAttCode);
$sNewState = $aTransitionDef['target_state'];
$this->Set($sStateAttCode, $sNewState);
@@ -4526,67 +4555,71 @@ abstract class DBObject implements iDisplay
// array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD
$bSuccess = true;
$sActionDesc = '';
foreach ($aTransitionDef['actions'] as $actionHandler)
{
if (is_string($actionHandler))
{
// Old (pre-2.1.0 modules) action definition without any parameter
$aActionCallSpec = array($this, $actionHandler);
$sActionDesc = $sClass.'::'.$actionHandler;
// Prevent current object from being updated by the actions
$this->AddCurrentObjectInCrudStack('APPLY_STIMULUS');
$bIsNewlyProtected = MetaModel::StartReentranceProtection($this);
try {
foreach ($aTransitionDef['actions'] as $actionHandler) {
if (is_string($actionHandler)) {
// Old (pre-2.1.0 modules) action definition without any parameter
$aActionCallSpec = array($this, $actionHandler);
$sActionDesc = $sClass.'::'.$actionHandler;
if (!is_callable($aActionCallSpec))
{
throw new CoreException("Unable to call action: $sClass::$actionHandler");
}
$bRet = call_user_func($aActionCallSpec, $sStimulusCode);
}
else // if (is_array($actionHandler))
{
// New syntax: 'verb' and typed parameters
$sAction = $actionHandler['verb'];
$sActionDesc = "$sClass::$sAction";
$aParams = array();
foreach($actionHandler['params'] as $aDefinition)
{
$sParamType = array_key_exists('type', $aDefinition) ? $aDefinition['type'] : 'string';
switch($sParamType)
{
case 'int':
$value = (int)$aDefinition['value'];
break;
case 'float':
$value = (float)$aDefinition['value'];
break;
case 'bool':
$value = (bool)$aDefinition['value'];
break;
case 'reference':
$value = ${$aDefinition['value']};
break;
case 'string':
default:
$value = (string)$aDefinition['value'];
if (!is_callable($aActionCallSpec)) {
throw new CoreException("Unable to call action: $sClass::$actionHandler");
}
$aParams[] = $value;
$bRet = call_user_func($aActionCallSpec, $sStimulusCode);
} else // if (is_array($actionHandler))
{
// New syntax: 'verb' and typed parameters
$sAction = $actionHandler['verb'];
$sActionDesc = "$sClass::$sAction";
$aParams = array();
foreach ($actionHandler['params'] as $aDefinition) {
$sParamType = array_key_exists('type', $aDefinition) ? $aDefinition['type'] : 'string';
switch ($sParamType) {
case 'int':
$value = (int)$aDefinition['value'];
break;
case 'float':
$value = (float)$aDefinition['value'];
break;
case 'bool':
$value = (bool)$aDefinition['value'];
break;
case 'reference':
$value = ${$aDefinition['value']};
break;
case 'string':
default:
$value = (string)$aDefinition['value'];
}
$aParams[] = $value;
}
$aCallSpec = array($this, $sAction);
$bRet = call_user_func_array($aCallSpec, $aParams);
}
// if one call fails, the whole is considered as failed
// (in case there is no returned value, null is obtained and means "ok")
if ($bRet === false) {
IssueLog::Info("Lifecycle action $sActionDesc returned false on object #$sClass:".$this->GetKey());
$bSuccess = false;
}
$aCallSpec = array($this, $sAction);
$bRet = call_user_func_array($aCallSpec, $aParams);
}
// if one call fails, the whole is considered as failed
// (in case there is no returned value, null is obtained and means "ok")
if ($bRet === false)
{
IssueLog::Info("Lifecycle action $sActionDesc returned false on object #$sClass:".$this->GetKey());
$bSuccess = false;
} finally {
if ($bIsNewlyProtected) {
// Stops protection only if the object was not already protected
MetaModel::StopReentranceProtection($this);
}
$this->RemoveCurrentObjectInCrudStack();
}
if ($bSuccess)
{
$this->sStimulusBeingApplied = $sStimulusCode;
// Stop watches
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
@@ -4615,6 +4648,7 @@ abstract class DBObject implements iDisplay
$this->m_aCurrValues[$sAttCode] = $aBackupValues[$sAttCode];
}
}
$this->LogCRUDExit(__METHOD__, 'Current State: '.$this->Get($sStateAttCode));
return $bSuccess;
}
@@ -6601,7 +6635,7 @@ abstract class DBObject implements iDisplay
* @return void
* @since 3.1.0
*/
protected function FireEventCheckToWrite(): void
protected function FireEventCheckToWrite(?string $sStimulusBeingApplied): void
{
}
@@ -6609,7 +6643,7 @@ abstract class DBObject implements iDisplay
* @return void
* @since 3.1.0
*/
protected function FireEventBeforeWrite()
protected function FireEventBeforeWrite(?string $sStimulusBeingApplied)
{
}
@@ -6619,7 +6653,7 @@ abstract class DBObject implements iDisplay
* @return void
* @since 3.1.0
*/
protected function FireEventAfterWrite(array $aChanges, bool $bIsNew): void
protected function FireEventAfterWrite(array $aChanges, bool $bIsNew, ?string $sStimulusBeingApplied): void
{
}
@@ -6657,7 +6691,7 @@ abstract class DBObject implements iDisplay
* @return void
* @since 3.1.0
*/
protected function FireEventComputeValues(): void
protected function FireEventComputeValues(?string $sStimulusBeingApplied): void
{
}
@@ -6699,12 +6733,13 @@ abstract class DBObject implements iDisplay
$oRootClass = MetaModel::GetRootClass($sClass);
foreach (self::$m_aCrudStack as $aCrudStackEntry) {
if (($oRootClass === $aCrudStackEntry['class'])
&& ($sConvertedId === $aCrudStackEntry['id'])) {
if (($oRootClass === $aCrudStackEntry['class']) && ($sConvertedId === $aCrudStackEntry['id'])) {
IssueLog::Trace('CRUD '.__METHOD__." $sClass:$sId IS in CRUD Stack", LogChannels::DM_CRUD);
return true;
}
}
IssueLog::Trace('CRUD '.__METHOD__." $sClass:$sId NOT in CRUD Stack", LogChannels::DM_CRUD);
return false;
}
@@ -6722,10 +6757,12 @@ abstract class DBObject implements iDisplay
$sRootClass = MetaModel::GetRootClass($sClass);
foreach (self::$m_aCrudStack as $aCrudStackEntry) {
if ($sRootClass === $aCrudStackEntry['class']) {
IssueLog::Trace("CRUD ".__METHOD__." $sClass IS in CRUD Stack", LogChannels::DM_CRUD);
return true;
}
}
IssueLog::Trace('CRUD '.__METHOD__." $sClass NOT in CRUD Stack", LogChannels::DM_CRUD);
return false;
}
@@ -6735,17 +6772,20 @@ abstract class DBObject implements iDisplay
* @param string $sCrudType
*
* @return void
* @throws \CoreException
* @since 3.1.0 N°5609
*/
private function AddCurrentObjectInCrudStack(string $sCrudType): void
{
$this->LogCRUDDebug(__METHOD__);
$sRootClass = MetaModel::GetRootClass(get_class($this));
$sKey = (string)$this->GetKey();
self::$m_aCrudStack[] = [
'type' => $sCrudType,
'class' => $sRootClass,
'id' => (string)$this->GetKey(), // GetKey() doesn't have type hinting, so forcing type to avoid getting an int
'id' => $sKey, // GetKey() doesn't have type hinting, so forcing type to avoid getting an int
];
$iCount = count(self::$m_aCrudStack);
$this->LogCRUDDebug(__METHOD__, "$sCrudType $sRootClass:$sKey count $iCount");
}
/**
@@ -6757,10 +6797,15 @@ abstract class DBObject implements iDisplay
*/
private function UpdateCurrentObjectInCrudStack(): void
{
$this->LogCRUDDebug(__METHOD__);
$aCurrentCrudStack = array_pop(self::$m_aCrudStack);
$aCurrentCrudStack['id'] = (string)$this->GetKey();
$sOldId = $aCurrentCrudStack['id'];
$sNewId = (string)$this->GetKey();
$aCurrentCrudStack['id'] = $sNewId;
self::$m_aCrudStack[] = $aCurrentCrudStack;
$sClass = $aCurrentCrudStack['class'];
$sType = $aCurrentCrudStack['type'];
$iCount = count(self::$m_aCrudStack);
$this->LogCRUDDebug(__METHOD__, "$sType $sClass:$sOldId => $sClass:$sNewId count $iCount");
}
/**
@@ -6772,7 +6817,11 @@ abstract class DBObject implements iDisplay
private function RemoveCurrentObjectInCrudStack(): void
{
$aRemoved = array_pop(self::$m_aCrudStack);
$this->LogCRUDDebug(__METHOD__, $aRemoved['class'].':'.$aRemoved['id']);
$sType = $aRemoved['type'];
$sClass = $aRemoved['class'];
$sId = $aRemoved['id'];
$iCount = count(self::$m_aCrudStack);
$this->LogCRUDDebug(__METHOD__, "$sType $sClass:$sId count $iCount");
}
/**
@@ -6789,37 +6838,53 @@ abstract class DBObject implements iDisplay
protected function LogCRUDEnter($sFunction, $sComment = '')
{
$sClass = get_class($this);
if (utils::StartsWith($sClass, 'CMDBChange')) {
return;
}
$sKey = $this->GetKey();
$sUUID = $this->m_sObjectUniqId;
$sPadding = str_pad('', count(self::$m_aCrudStack), '-');
IssueLog::Debug("CRUD +$sPadding> $sFunction $sClass:$sKey $sComment", LogChannels::DM_CRUD);
IssueLog::Debug("CRUD +$sPadding> $sFunction $sClass:$sKey ($sUUID) $sComment", LogChannels::DM_CRUD);
}
protected function LogCRUDExit($sFunction, $sComment = '')
{
$sClass = get_class($this);
if (utils::StartsWith($sClass, 'CMDBChange')) {
return;
}
$sKey = $this->GetKey();
$sUUID = $this->m_sObjectUniqId;
$sPadding = str_pad('', count(self::$m_aCrudStack), '-');
if (strlen($sComment) === 0) {
IssueLog::Trace("CRUD <$sPadding+ $sFunction $sClass:$sKey", LogChannels::DM_CRUD);
} else {
IssueLog::Debug("CRUD <$sPadding+ $sFunction $sClass:$sKey $sComment", LogChannels::DM_CRUD);
IssueLog::Debug("CRUD <$sPadding+ $sFunction $sClass:$sKey ($sUUID) $sComment", LogChannels::DM_CRUD);
}
}
protected function LogCRUDDebug($sFunction, $sComment = '')
{
$sClass = get_class($this);
if (utils::StartsWith($sClass, 'CMDBChange')) {
return;
}
$sKey = $this->GetKey();
$sUUID = $this->m_sObjectUniqId;
$sPadding = str_pad('', count(self::$m_aCrudStack), '-');
IssueLog::Debug("CRUD --$sPadding $sFunction $sClass:$sKey $sComment", LogChannels::DM_CRUD);
IssueLog::Debug("CRUD --$sPadding $sFunction $sClass:$sKey ($sUUID) $sComment", LogChannels::DM_CRUD);
}
protected function LogCRUDError($sFunction, $sComment = '')
{
$sClass = get_class($this);
if (utils::StartsWith($sClass, 'CMDBChange')) {
return;
}
$sKey = $this->GetKey();
$sUUID = $this->m_sObjectUniqId;
$sPadding = str_pad('', count(self::$m_aCrudStack), '!');
IssueLog::Error("CRUD !!$sPadding Error $sFunction $sClass:$sKey $sComment", LogChannels::DM_CRUD);
IssueLog::Error("CRUD !!$sPadding Error $sFunction $sClass:$sKey ($sUUID) $sComment", LogChannels::DM_CRUD);
}
/**

View File

@@ -51,7 +51,7 @@ class DBProperty extends DBObject
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 AttributeString("value", array("allowed_values"=>null, "sql"=>"value", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("change_date", array("allowed_values"=>null, "sql"=>"change_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("change_date", array("allowed_values"=>null, "sql"=>"change_date", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("change_comment", array("allowed_values"=>null, "sql"=>"change_comment", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
}

View File

@@ -1552,13 +1552,13 @@ abstract class DBSearch
}
$sLogFile = 'queries.latest';
file_put_contents(APPROOT.'data/'.$sLogFile.'.html', $sHtml);
file_put_contents(utils::GetDataPath().$sLogFile.'.html', $sHtml);
$sLog = "<?php\n\$aQueriesLog = ".var_export(self::$m_aQueriesLog, true).";";
file_put_contents(APPROOT.'data/'.$sLogFile.'.log', $sLog);
file_put_contents(utils::GetDataPath().$sLogFile.'.log', $sLog);
// Cumulate the queries
$sAllQueries = APPROOT.'data/queries.log';
$sAllQueries = utils::GetDataPath().'queries.log';
if (file_exists($sAllQueries))
{
// Merge the new queries into the existing log

View File

@@ -501,7 +501,8 @@ class DesignElement extends \DOMElement
{
$sSearchId = $oRefNode->getAttribute('id');
}
$sXPath = './'.$oRefNode->tagName."[@id='$sSearchId']";
$sQuotedId = DesignDocument::XPathQuote($sSearchId);
$sXPath = './'.$oRefNode->tagName."[@id=$sQuotedId]";
$oRes = $oXPath->query($sXPath, $oRoot);
}

View File

@@ -70,7 +70,7 @@ class Dict
public static function GetUserLanguage()
{
if (self::$m_sCurrentLanguage == null) // May happen when no user is logged in (i.e login screen, non authentifed page)
if (self::$m_sCurrentLanguage == null) // May happen when no user is logged in (i.e. login screen, non-authenticated page)
{
// In which case let's use the default language
return self::$m_sDefaultLanguage;
@@ -122,7 +122,7 @@ class Dict
}
/**
* Returns a localised string from the dictonary with its associated lang code
* Returns a localised string from the dictionary with its associated lang code
*
* @param string $sStringCode The code identifying the dictionary entry
* @param string $sDefault Default value if there is no match in the dictionary
@@ -201,12 +201,12 @@ class Dict
if ($sLocalizedFormat == $sFormatCode)
{
// Make sure the information will be displayed (ex: an error occuring before the dictionary gets loaded)
// Make sure the information will be displayed (ex: an error occurring before the dictionary gets loaded)
return $sFormatCode.' - '.implode(', ', $aArguments);
}
try{
return vsprintf($sLocalizedFormat, $aArguments);
return utils::VSprintf($sLocalizedFormat, $aArguments);
} catch(\Throwable $e){
\IssueLog::Error("Cannot format dict key", null, ["sFormatCode" => $sFormatCode, "sLangCode" => $sLangCode, 'exception_msg' => $e->getMessage() ]);
return $sFormatCode.' - '.implode(', ', $aArguments);
@@ -214,9 +214,9 @@ class Dict
}
/**
* Initialize a the entries for a given language (replaces the former Add() method)
* Initialize the entries for a given language (replaces the former Add() method)
* @param string $sLanguageCode Code identifying the language i.e. 'FR-FR', 'EN-US'
* @param array $aEntries Hash array of dictionnary entries
* @param array $aEntries Hash array of dictionary entries
*/
public static function SetEntries($sLanguageCode, $aEntries)
{
@@ -271,9 +271,9 @@ class Dict
if (self::$m_aData[$sLangCode] === false) {
unset(self::$m_aData[$sLangCode]);
} else if (! is_array(self::$m_aData[$sLangCode])) {
// N°4125: we dont fix dictionnary corrupted cache (on iTop side).
// N°4125: we don't fix dictionary corrupted cache (on iTop side).
// but we log an error in a dedicated channel to let itop administrator be aware of a potential APCu issue to fix.
IssueLog::Error("APCu corrupted data (with $sLangCode dictionnary). APCu configuration and running version should be troubleshooted...", LogChannels::APC);
IssueLog::Error("APCu corrupted data (with $sLangCode dictionary). APCu configuration and running version should be troubleshooted...", LogChannels::APC);
$bResult = true;
} else {
$bResult = true;
@@ -296,7 +296,7 @@ class Dict
/**
* Enable caching (cached using APC)
* @param string $sApplicationPrefix The prefix for uniquely identiying this iTop instance
* @param string $sApplicationPrefix The prefix for uniquely identifying this iTop instance
*/
public static function EnableCache($sApplicationPrefix)
{
@@ -305,7 +305,7 @@ class Dict
/**
* Reset the cached entries (cached using APC)
* @param string $sApplicationPrefix The prefix for uniquely identiying this iTop instance
* @param string $sApplicationPrefix The prefix for uniquely identifying this iTop instance
*/
public static function ResetCache($sApplicationPrefix)
{
@@ -385,7 +385,7 @@ class Dict
// sLanguageCode: Code identifying the language i.e. FR-FR
// sEnglishLanguageDesc: Description of the language code, in English. i.e. French (France)
// sLocalizedLanguageDesc: Description of the language code, in its own language. i.e. Français (France)
// aEntries: Hash array of dictionnary entries
// aEntries: Hash array of dictionary entries
// ~~ or ~* can be used to indicate entries still to be translated.
public static function Add($sLanguageCode, $sEnglishLanguageDesc, $sLocalizedLanguageDesc, $aEntries)
{

View File

@@ -453,6 +453,11 @@ class DisplayableNode extends GraphNode
}
}
public static function GetTooltipAttributes($sClass)
{
return MetaModel::GetZListItems($sClass, 'list');
}
public function GetTooltip($aContextDefs)
{
$sHtml = '';
@@ -474,7 +479,7 @@ class DisplayableNode extends GraphNode
$sHtml .= '<hr/>';
}
$sHtml .= '<table><tbody>';
foreach(MetaModel::GetZListItems($sSubClass, 'list') as $sAttCode)
foreach(static::GetTooltipAttributes($sSubClass) as $sAttCode)
{
$oAttDef = MetaModel::GetAttributeDef($sSubClass, $sAttCode);
$sHtml .= '<tr><td>'.$oAttDef->GetLabel().':&nbsp;</td><td>'.$oCurrObj->GetAsHtml($sAttCode).'</td></tr>';
@@ -864,6 +869,7 @@ class DisplayableGraph extends SimpleGraph
*/
public static function FromRelationGraph(RelationGraph $oGraph, $iGroupingThreshold = 20, $bDirectionDown = true, $bForPdf = false)
{
$oKPI = new ExecutionKPI();
$oNewGraph = new DisplayableGraph();
$oNewGraph->bDirectionDown = $bDirectionDown;
$iPreviousTimeLimit = ini_get('max_execution_time');
@@ -1017,6 +1023,7 @@ class DisplayableGraph extends SimpleGraph
}
set_time_limit(intval($iPreviousTimeLimit));
$oKPI->ComputeStats('FromRelationGraph', '');
return $oNewGraph;
}
@@ -1027,6 +1034,7 @@ class DisplayableGraph extends SimpleGraph
*/
public function InitFromGraphviz()
{
$oKPI = new ExecutionKPI();
$sDot = $this->DumpAsXDot();
if (strpos($sDot, 'digraph') === false)
{
@@ -1054,6 +1062,7 @@ class DisplayableGraph extends SimpleGraph
}
}
}
$oKPI->ComputeStats('InitFromGraphviz', '');
}
public function GetBoundingBox()
@@ -1470,8 +1479,8 @@ class DisplayableGraph extends SimpleGraph
try {
$this->InitFromGraphviz();
$sExportAsPdfURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_pdf&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
$sContext = $oAppContext->GetForLink();
$sDrillDownURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class=%1$s&id=%2$s&'.$sContext;
$sContext = $oAppContext->GetForLink(true);
$sDrillDownURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class=%1$s&id=%2$s'.$sContext;
$sExportAsDocumentURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_attachment&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
$sLoadFromURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_json&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
$sAttachmentExportTitle = '';
@@ -1496,6 +1505,7 @@ class DisplayableGraph extends SimpleGraph
'excluded' => $aExcludedByClass,
'grouping_threshold' => $iGroupingThreshold,
'export_as_pdf' => array('url' => $sExportAsPdfURL, 'label' => Dict::S('UI:Relation:ExportAsPDF')),
'transaction_id' => utils::GetNewTransactionId(),
'export_as_attachment' => array('url' => $sExportAsDocumentURL, 'label' => Dict::S('UI:Relation:ExportAsAttachment'), 'obj_class' => $sObjClass, 'obj_key' => $iObjKey),
'drill_down' => array('url' => $sDrillDownURL, 'label' => Dict::S('UI:Relation:DrillDown')),
'labels' => array(

View File

@@ -154,7 +154,7 @@ class EMail implements iEMail
*/
public function SetInReplyTo(string $sMessageId)
{
$this->AddToHeader('In-Reply-To', $sMessageId);
$this->oMailer->SetInReplyTo($sMessageId);
}
public function SetBody($sBody, $sMimeType = 'text/html', $sCustomStyles = null)

View File

@@ -39,7 +39,7 @@ class Event extends DBObject implements iDisplay
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeText("message", array("allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("date", array("allowed_values"=>null, "sql"=>"date", "default_value"=>"NOW()", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
@@ -131,7 +131,7 @@ class EventNotification extends Event
"db_finalclass_field" => "",
"order_by_default" => array('date' => false),
'indexes' => array(
array('object_id'),
array( 'object_class', 'object_id'),
)
);
MetaModel::Init_Params($aParams);
@@ -139,9 +139,11 @@ class EventNotification extends Event
MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", array("targetclass"=>"Trigger", "jointype"=> "", "allowed_values"=>null, "sql"=>"trigger_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass" => "Action", "jointype" => "", "allowed_values" => null, "sql" => "action_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeInteger("object_id", array("allowed_values" => null, "sql" => "object_id", "default_value" => 0, "is_null_allowed" => false, "depends_on" => array())));
//@since 3.2.0
MetaModel::Init_AddAttribute(new AttributeClass("object_class", array("class_category"=>"", "more_values"=>"", "sql"=>"object_class", "default_value"=>null, "is_null_allowed"=>true /*to avoid setting AbstractResource as default in database*/, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'message', 'userinfo', 'trigger_id', 'action_id', 'object_id')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('date', 'message', 'userinfo', 'trigger_id', 'action_id', 'object_class', 'object_id')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'message')); // Attributes to be displayed for a list
// Search criteria
// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form
@@ -176,7 +178,7 @@ class EventNotificationEmail extends EventNotification
MetaModel::Init_AddAttribute(new AttributeTable("attachments", array("allowed_values"=>null, "sql"=>"attachments", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'message', 'trigger_id', 'action_id', 'object_id', 'to', 'cc', 'bcc', 'from', 'subject', 'body', 'attachments')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'message', 'trigger_id', 'action_id', 'object_class', 'object_id', 'to', 'cc', 'bcc', 'from', 'subject', 'body', 'attachments')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('date', 'message', 'to', 'subject', 'attachments')); // Attributes to be displayed for a list
// Search criteria
@@ -231,7 +233,7 @@ class EventIssue extends Event
if (array_key_exists('_GET', $GLOBALS) && is_array($GLOBALS['_GET']))
{
$this->Set('arguments_get', $GLOBALS['_GET']);
$this->Set('arguments_get', $this->SanitizeRequestParams($GLOBALS['_GET']));
}
else
{
@@ -240,22 +242,7 @@ class EventIssue extends Event
if (array_key_exists('_POST', $GLOBALS) && is_array($GLOBALS['_POST']))
{
$aPost = array();
foreach($GLOBALS['_POST'] as $sKey => $sValue)
{
if (is_string($sValue))
{
if (mb_strlen($sValue) < 256) {
$aPost[$sKey] = $sValue;
} else {
$aPost[$sKey] = "!long string: ".mb_strlen($sValue)." chars";
}
} else {
// Not a string (avoid warnings in case the value cannot be easily casted into a string)
$aPost[$sKey] = @(string)$sValue;
}
}
$this->Set('arguments_post', $aPost);
$this->Set('arguments_post', $this->SanitizeRequestParams($GLOBALS['_POST']));
} else {
$this->Set('arguments_post', array());
}
@@ -274,6 +261,29 @@ class EventIssue extends Event
$this->Set('page', mb_substr($this->Get('page'), 0, 210)." -truncated ($sLength chars)");
}
}
protected function SanitizeRequestParams(array $aParams): array
{
$aSanitizedParams = [];
foreach ($aParams as $sKey => $sValue) {
if (is_string($sValue)) {
if (stristr($sKey, 'pwd') !== false || stristr($sKey, 'passwd') !== false || stristr($sKey, 'password') !== false) {
$aSanitizedParams[$sKey] = '****';
} elseif (mb_strlen($sValue) < 256) {
$aSanitizedParams[$sKey] = $sValue;
} else {
$aSanitizedParams[$sKey] = '!long string: '.mb_strlen($sValue).' chars';
}
} else {
// Not a string (avoid warnings in case the value cannot be easily cast into a string)
$aSanitizedParams[$sKey] = @(string)$sValue;
}
}
return $aSanitizedParams;
}
}

View File

@@ -278,7 +278,7 @@ class HTMLDOMSanitizer extends DOMSanitizer
protected static $aTagsWhiteList = array(
'html' => array(),
'body' => array(),
'a' => array('href', 'name', 'style', 'class', 'target', 'title', 'data-role', 'data-object-class', 'data-object-id'),
'a' => array('href', 'name', 'style', 'class', 'target', 'title', 'data-role', 'data-object-class', 'data-object-id', 'data-object-key'),
'p' => array('style', 'class'),
'blockquote' => array('style', 'class'),
'br' => array(),
@@ -301,6 +301,8 @@ class HTMLDOMSanitizer extends DOMSanitizer
'section' => array('style', 'class'),
'code' => array('style', 'class'),
'table' => array('style', 'class', 'width', 'summary', 'align', 'border', 'cellpadding', 'cellspacing'),
'colgroup' => array(),
'col' => array('style'),
'thead' => array('style', 'class'),
'tbody' => array('style', 'class'),
'tr' => array('style', 'class', 'colspan', 'rowspan'),
@@ -352,6 +354,8 @@ class HTMLDOMSanitizer extends DOMSanitizer
'font-style',
'height',
'margin',
'margin-left',
'margin-right',
'padding',
'text-align',
'vertical-align',

View File

@@ -54,7 +54,7 @@ class InlineImage extends DBObject
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeDateTime("expire", array("allowed_values"=>null, "sql"=>'expire', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array(), "always_load_in_tables"=>false)));
MetaModel::Init_AddAttribute(new AttributeDateTime("expire", array("allowed_values" => null, "sql" => 'expire', "default_value" => 'DATE_ADD(NOW(), INTERVAL 1 DAY)', "is_null_allowed" => false, "depends_on" => array(), "always_load_in_tables" => false)));
MetaModel::Init_AddAttribute(new AttributeString("temp_id", array("allowed_values"=>null, "sql"=>'temp_id', "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array(), "always_load_in_tables"=>false)));
MetaModel::Init_AddAttribute(new AttributeString("item_class", array("allowed_values"=>null, "sql"=>'item_class', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array(), "always_load_in_tables"=>false)));
MetaModel::Init_AddAttribute(new AttributeObjectKey("item_id", array("class_attcode"=>'item_class', "allowed_values"=>null, "sql"=>'item_id', "is_null_allowed"=>true, "depends_on"=>array(), "always_load_in_tables"=>false)));
@@ -541,80 +541,6 @@ JS
CombodoCKEditorHandler.EnableImageUpload('#' + $(this).attr('id'), '$sAbsoluteUrlAppRoot'+'pages/ajax.render.php?operation=cke_img_upload&temp_id=$sTempId&obj_class=$sObjClass&obj_key=$iObjKey');
});
JS;
return
<<<JS
// Hook the file upload of all CKEditor instances
$('.htmlEditor').each(function() {
var oEditor = $(this).ckeditorGet();
oEditor.config.filebrowserBrowseUrl = '$sAbsoluteUrlAppRoot'+'pages/ajax.render.php?operation=cke_browse&temp_id=$sTempId&obj_class=$sObjClass&obj_key=$iObjKey';
oEditor.on( 'fileUploadResponse', function( evt ) {
var fileLoader = evt.data.fileLoader;
var xhr = fileLoader.xhr;
var data = evt.data;
try {
var response = JSON.parse( xhr.responseText );
// Error message does not need to mean that upload finished unsuccessfully.
// It could mean that ex. file name was changes during upload due to naming collision.
if ( response.error && response.error.message ) {
data.message = response.error.message;
}
// But !uploaded means error.
if ( !response.uploaded ) {
evt.cancel();
} else {
data.fileName = response.fileName;
data.url = response.url;
// Do not call the default listener.
evt.stop();
}
} catch ( err ) {
// Response parsing error.
data.message = fileLoader.lang.filetools.responseError;
window.console && window.console.log( xhr.responseText );
evt.cancel();
}
} );
oEditor.on( 'fileUploadRequest', function( evt ) {
evt.data.fileLoader.uploadUrl += '?operation=cke_img_upload&temp_id=$sTempId&obj_class=$sObjClass';
}, null, null, 4 ); // Listener with priority 4 will be executed before priority 5.
oEditor.on( 'instanceReady', function() {
if(!CKEDITOR.env.iOS && $('#'+oEditor.id+'_toolbox .ibo-vendors-ckeditor--toolbar-fullscreen-button').length == 0)
{
$('#'+oEditor.id+'_toolbox').append('<span class="ibo-vendors-ckeditor--toolbar-fullscreen-button editor-fullscreen-button" data-role="ibo-vendors-ckeditor--toolbar-fullscreen-button" title="$sToggleFullScreen">&nbsp;</span>');
$('#'+oEditor.id+'_toolbox .ibo-vendors-ckeditor--toolbar-fullscreen-button').on('click', function() {
oEditor.execCommand('maximize');
if ($(this).closest('.cke_maximized').length != 0)
{
$('#'+oEditor.id+'_toolbar_collapser').trigger('click');
}
});
}
if (oEditor.widgets.registered.uploadimage)
{
oEditor.widgets.registered.uploadimage.onUploaded = function( upload ) {
var oData = JSON.parse(upload.xhr.responseText);
this.replaceWith( '<img src="' + upload.url + '" ' +
'width="' + oData.width + '" ' +
'height="' + oData.height + '">' );
}
}
});
});
JS
;
}
public static function EnableCKEditor5ImageUpload(DBObject $oObject, $sTempId){
return <<<JS
// Hook the file upload of all CKEditor instances
JS;
}

View File

@@ -469,7 +469,8 @@ class ExecutionKPI
// Invoke extensions to log the KPI operation
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
//$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
$sExtension = '';
$oKPILogData = new KpiLogData(
KpiLogData::TYPE_STATS,
$sOperation,

View File

@@ -542,32 +542,32 @@ class FileLog
*/
class LogChannels
{
public const APC = 'apc';
public const APC = 'Apc';
/**
* @var string Everything related to the backup / restore
* @since 3.1.0
*/
public const BACKUP = 'backup';
public const BACKUP = 'Backup';
/**
* @since 3.0.0
*/
public const CLI = 'CLI';
public const CLI = 'Cli';
/**
* @var string
* @since 2.7.7 N°4558 use this new channel when logging DB transactions
* @since 3.0.0 logs info in CMDBSource (see commit a117906f)
*/
public const CMDB_SOURCE = 'cmdbsource';
public const CMDB_SOURCE = 'CmdbSource';
/**
* @since 3.0.0
*/
public const CONSOLE = 'console';
public const CONSOLE = 'Console';
public const CORE = 'core';
public const CORE = 'Core';
/**
* @var string Everything related to the datatable component
@@ -592,10 +592,10 @@ class LogChannels
* @var string Everything related to the datamodel CRUD
* @since 3.1.0
*/
public const DM_CRUD = 'DMCRUD';
public const DM_CRUD = 'DMCrud';
/**
* @var string Everything related to the datamodel CRUD
* @var string Everything related to webrequests
* @since 3.1.0
*/
public const WEB_REQUEST = 'WebRequest';
@@ -610,7 +610,7 @@ class LogChannels
* @var string
* @since 2.7.9 3.0.3 3.1.0 N°5588
*/
public const EXPORT = 'export';
public const EXPORT = 'Export';
public const INLINE_IMAGE = 'InlineImage';
@@ -619,9 +619,9 @@ class LogChannels
* @since 3.0.1 N°4849
* @since 2.7.7 N°4635
*/
public const NOTIFICATIONS = 'notifications';
public const NOTIFICATIONS = 'Notifications';
public const PORTAL = 'portal';
public const PORTAL = 'Portal';
public const TEMPORARY_OBJECTS = 'TemporaryObjects';
@@ -629,7 +629,13 @@ class LogChannels
* @var string
* @since 3.1.0
*/
public const ROUTER = 'router';
public const ROUTER = 'Router';
/**
* @var string
* @since 3.2.0
*/
public const SECURITY = 'Security';
}
@@ -1167,7 +1173,7 @@ class DeprecatedCallsLog extends LogAPI
/**
* This will catch a message for all E_DEPRECATED and E_USER_DEPRECATED errors.
* This handler is set in DeprecatedCallsLog::Enable
* This handler is set in {@see DeprecatedCallsLog::Enable}
*
* @param int $errno
* @param string $errstr
@@ -1187,52 +1193,22 @@ class DeprecatedCallsLog extends LogAPI
return false;
}
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 4);
if (isset($aStack[2]['function']) && ($aStack[2]['function'] == 'ForwardToTriggerError')) {
// Let the notice bubble up
return false;
}
if (false === static::IsLogLevelEnabledSafe(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD)) {
// returns true so that nothing is throwned !
// returns true so that nothing is thrown!
return true;
}
$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 (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')) {
// We are generating a trigger_error ourselves, we don't want to trace them !
return false;
}
$sCallerFile = $aStack[$iStackDeprecatedMethodLevel]['file'];
$sCallerLine = $aStack[$iStackDeprecatedMethodLevel]['line'];
$sMessage = "Call to {$sDeprecatedObject}::{$sDeprecatedMethod} in {$sCallerFile}#L{$sCallerLine}";
$iStackCallerMethodLevel = $iStackDeprecatedMethodLevel + 1; // level 3 = caller of the deprecated method
if (array_key_exists($iStackCallerMethodLevel, $aStack)) {
$sCallerObject = $aStack[$iStackCallerMethodLevel]['class'] ?? null;
$sCallerMethod = $aStack[$iStackCallerMethodLevel]['function'] ?? null;
$sMessage .= ' (';
if (!is_null($sCallerObject)) {
$sMessage .= "{$sCallerObject}::{$sCallerMethod}";
} else {
$sCallerMethodFile = $aStack[$iStackCallerMethodLevel]['file'];
$sCallerMethodLine = $aStack[$iStackCallerMethodLevel]['line'];
if (!is_null($sCallerMethod)) {
$sMessage .= "call to {$sCallerMethod}() in {$sCallerMethodFile}#L{$sCallerMethodLine}";
} else {
$sMessage .= "{$sCallerMethodFile}#L{$sCallerMethodLine}";
}
}
$sMessage .= ')';
}
if (!empty($errstr)) {
$sMessage .= ' : '.$errstr;
}
$aStack = static::StripCallStack($aStack);
$sMessage = "$errstr, called from ".static::SummarizeCallStack($aStack);
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_LIBMETHOD);
static::ForwardToTriggerError($sMessage);
return true;
}
@@ -1258,6 +1234,8 @@ class DeprecatedCallsLog extends LogAPI
}
/**
* Call this helper at the beginning of a deprecated file (in its global scope)
*
* @since 3.0.1 3.1.0 N°4725 silently handles ConfigException
* @since 3.0.4 3.1.0 N°4725 remove forgotten throw PHPDoc annotation
*
@@ -1292,9 +1270,12 @@ class DeprecatedCallsLog extends LogAPI
}
static::Warning($sMessage, static::ENUM_CHANNEL_FILE);
static::ForwardToTriggerError($sMessage);
}
/**
* Call this helper when calling a deprecated extension method
*
* @param string $sImplementationClass Class implementing the deprecated API
* @param string $sDeprecatedApi Class name of the deprecated API
* @param string $sDeprecatedMethod Method name of the deprecated API
@@ -1321,9 +1302,12 @@ class DeprecatedCallsLog extends LogAPI
}
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_API);
static::ForwardToTriggerError($sMessage);
}
/**
* Call this helper within deprecated methods
*
* @param string|null $sAdditionalMessage
*
* @link https://www.php.net/debug_backtrace
@@ -1341,52 +1325,24 @@ class DeprecatedCallsLog extends LogAPI
}
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
$sMessage = self::GetMessageFromStack($aStack);
if (!is_null($sAdditionalMessage)) {
$sMessage .= ' : '.$sAdditionalMessage;
if (isset($aStack[1]['class'])) {
$sFunctionDesc = $aStack[1]['class'].$aStack[1]['type'].$aStack[1]['function'];
}
else {
$sFunctionDesc = $aStack[1]['function'];
}
$sMessage = "Function $sFunctionDesc() is deprecated";
if (!is_null($sAdditionalMessage)) {
$sMessage .= ': '.$sAdditionalMessage;
}
$sMessage .= '. Caller: '.self::SummarizeCallStack(array_slice($aStack, 1));
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_METHOD);
}
/**
* @param array $aDebugBacktrace data from {@see debug_backtrace()}
*
* @return string message to print to the log
*/
private static function GetMessageFromStack(array $aDebugBacktrace): string
{
// level 0 = current method
// level 1 = deprecated method, containing the `NotifyDeprecatedPhpMethod` call
$sMessage = 'Call'.self::GetMessageForCurrentStackLevel($aDebugBacktrace[1], " to ");
// level 2 = caller of the deprecated method
if (array_key_exists(2, $aDebugBacktrace)) {
$sMessage .= ' (from ';
$sMessage .= self::GetMessageForCurrentStackLevel($aDebugBacktrace[2]);
$sMessage .= ')';
}
return $sMessage;
}
private static function GetMessageForCurrentStackLevel(array $aCurrentLevelDebugTrace, ?string $sPrefix = ""): string
{
$sMessage = "";
if (array_key_exists('class', $aCurrentLevelDebugTrace)) {
$sDeprecatedObject = $aCurrentLevelDebugTrace['class'];
$sDeprecatedMethod = $aCurrentLevelDebugTrace['function'] ?? "";
$sMessage = "{$sPrefix}{$sDeprecatedObject}::{$sDeprecatedMethod} in ";
}
if (array_key_exists('file', $aCurrentLevelDebugTrace)) {
$sCallerFile = $aCurrentLevelDebugTrace['file'];
$sCallerLine = $aCurrentLevelDebugTrace['line'] ?? "";
$sMessage .= "{$sCallerFile}#L{$sCallerLine}";
}
return $sMessage;
static::ForwardToTriggerError($sMessage);
}
/**
@@ -1416,14 +1372,11 @@ class DeprecatedCallsLog extends LogAPI
}
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_ENDPOINT);
static::ForwardToTriggerError($sMessage);
}
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array()): void
{
if (true === utils::IsDevelopmentEnvironment()) {
trigger_error($sMessage, E_USER_DEPRECATED);
}
try {
parent::Log($sLevel, $sMessage, $sChannel, $aContext);
}
@@ -1431,6 +1384,61 @@ class DeprecatedCallsLog extends LogAPI
// nothing much we can do... and we don't want to crash the caller !
}
}
/**
* Strips some elements from the top of the call stack to skip calls that are not relevant to report the deprecated call
* @param array $aCallStack Call stack as returned by {@see debug_backtrace()}
*/
protected static function StripCallStack($aCallStack): array
{
if (!isset($aCallStack[0]['line'])) {
$aCallStack = array_slice($aCallStack, 1);
}
if (isset($aCallStack[1]['function']) && $aCallStack[1]['function'] === 'trigger_deprecation') {
$aCallStack = array_slice($aCallStack, 1);
}
return $aCallStack;
}
protected static function SummarizeCallStack($aCallStack, $bRecurse = true)
{
if (count($aCallStack) == 0) {
return null;
}
$sFileLine = $aCallStack[0]['file'].'#'.$aCallStack[0]['line'];
$sSummary = $sFileLine;
// If possible and meaningful, add the class and method
if (isset($aCallStack[1]['class'])) {
$sSummary = $aCallStack[1]['class'].$aCallStack[1]['type'].$aCallStack[1]['function']." ($sFileLine)";
}
elseif (isset($aCallStack[1]['function'])) {
if (in_array($aCallStack[1]['function'], ['include', 'require', 'include_once', 'require_once'])) {
// No need to show the generic mechanism of inclusion
$bRecurse = false;
}
else {
$sSummary = $aCallStack[1]['function']." ($sFileLine)";
}
}
if ($bRecurse) {
$sUpperSummary = static::SummarizeCallStack(array_slice($aCallStack, 1), false);
if (!is_null($sUpperSummary)) {
$sSummary .= ', itself called from '.$sUpperSummary;
}
}
return $sSummary;
}
private static function ForwardToTriggerError(string $sMessage): void
{
if (true === utils::IsDevelopmentEnvironment()) {
trigger_error($sMessage, E_USER_DEPRECATED);
}
}
}

View File

@@ -6873,6 +6873,9 @@ abstract class MetaModel
/**
* Instantiate an object already persisted to the Database.
*
* Note that LinkedSet attributes are not loaded.
* DBObject::Reload() will be called when getting a LinkedSet attribute
*
* @api
* @see MetaModel::GetObjectWithArchive to get object even if it's archived
* @see utils::PushArchiveMode() to enable search on archived objects
@@ -7109,7 +7112,7 @@ abstract class MetaModel
if ($iKey < 0) {
return "$sTargetClass: $iKey (invalid value)";
}
$oObj = self::GetObject($sTargetClass, $iKey, false);
$oObj = self::GetObject($sTargetClass, $iKey, false);
if (is_null($oObj)) {
// Whatever we are looking for, the root class is the key to search for
$sRootClass = self::GetRootClass($sTargetClass);
@@ -7474,6 +7477,8 @@ abstract class MetaModel
* @param string|null $sFilterInstanceOf [optional] if given, only instance of this string will be returned
*
* @return array classes=>instance implementing the given interface
*
* @see \Combodo\iTop\Service\InterfaceDiscovery\InterfaceDiscovery::FindItopClasses() to add extensibility to modules
*/
public static function EnumPlugins($sInterface, $sFilterInstanceOf = null)
{
@@ -7527,8 +7532,41 @@ abstract class MetaModel
return $aEntries;
}
public static function ResetAllCaches($sEnvironment = null)
{
if (is_null($sEnvironment)) {
$sEnvironment = MetaModel::GetEnvironment();
}
$sEnvironmentId = md5(APPROOT).'-'.$sEnvironment;
$sAppIdentity = 'itop-'.$sEnvironmentId;
require_once(APPROOT.'/core/dict.class.inc.php');
Dict::ResetCache($sAppIdentity);
if (function_exists('apc_delete')) {
foreach (self::GetCacheEntries($sEnvironmentId) as $sKey => $aAPCInfo) {
$sAPCKey = $aAPCInfo['info'];
apc_delete($sAPCKey);
}
}
require_once(APPROOT.'core/userrights.class.inc.php');
UserRights::FlushPrivileges();
// Reset the opcache since otherwise the PHP "model" files may still be cached !!
if (function_exists('opcache_reset')) {
// Zend opcode cache
opcache_reset();
}
require_once(APPROOT.'setup/setuputils.class.inc.php');
SetupUtils::rrmdir(utils::GetCachePath($sEnvironment));
}
/**
* @internal
* @param string $sEnvironmentId
* @deprecated 3.2.1
*/
public static function ResetCache($sEnvironmentId = null)
{
@@ -7552,6 +7590,13 @@ abstract class MetaModel
require_once(APPROOT.'core/userrights.class.inc.php');
UserRights::FlushPrivileges();
// Reset the opcache since otherwise the PHP "model" files may still be cached !!
if (function_exists('opcache_reset'))
{
// Zend opcode cache
opcache_reset();
}
}
/**

View File

@@ -77,7 +77,7 @@ abstract class ModelReflection
return $sFormatCode.' - '.implode(', ', $aArguments);
}
return vsprintf($sLocalizedFormat, $aArguments);
return utils::VSprintf($sLocalizedFormat, $aArguments);
}
/**

View File

@@ -33,17 +33,19 @@ class ParseyyToken implements ArrayAccess
return $this->string;
}
function offsetExists($offset)
function offsetExists($offset): bool
{
return isset($this->metadata[$offset]);
}
function offsetGet($offset)
// Return type mixed is not supported by PHP 7.4, we can remove the following PHP attribute and add the return type once iTop min PHP version is PHP 8.0+
#[\ReturnTypeWillChange]
function offsetGet($offset)
{
return $this->metadata[$offset];
}
function offsetSet($offset, $value)
function offsetSet($offset, $value): void
{
if ($offset === null) {
if (isset($value[0])) {
@@ -66,7 +68,7 @@ class ParseyyToken implements ArrayAccess
}
}
function offsetUnset($offset)
function offsetUnset($offset): void
{
unset($this->metadata[$offset]);
}

View File

@@ -182,14 +182,14 @@ class PHP_LexerGenerator_Lexer
$this->token = self::COMMENTEND;
return true;
}
if (preg_match('/\G%([a-z]+)/', $this->data, $token, null, $this->N)) {
if (preg_match('/\G%([a-z]+)/', $this->data, $token, 0, $this->N)) {
$this->value = $token[1];
$this->N += strlen($token[1]) + 1;
$this->state = 'DeclarePI';
$this->token = self::PI;
return true;
}
if (preg_match('/\G[a-zA-Z_][a-zA-Z0-9_]*/', $this->data, $token, null, $this->N)) {
if (preg_match('/\G[a-zA-Z_][a-zA-Z0-9_]*/', $this->data, $token, 0, $this->N)) {
$this->value = $token[0];
$this->token = self::PATTERN;
$this->N += strlen($token[0]);
@@ -216,7 +216,7 @@ class PHP_LexerGenerator_Lexer
if ($this->data[$this->N] == '{') {
return $this->lexCode();
}
if (!preg_match("/\G[^\n]+/", $this->data, $token, null, $this->N)) {
if (!preg_match("/\G[^\n]+/", $this->data, $token, 0, $this->N)) {
$this->error('Unexpected end of file');
return false;
}
@@ -242,7 +242,7 @@ class PHP_LexerGenerator_Lexer
if ($this->data[$this->N] == '{') {
return $this->lexCode();
}
if (!preg_match("/\G[^\n]+/", $this->data, $token, null, $this->N)) {
if (!preg_match("/\G[^\n]+/", $this->data, $token, 0, $this->N)) {
$this->error('Unexpected end of file');
return false;
}
@@ -406,7 +406,7 @@ class PHP_LexerGenerator_Lexer
if ($this->data[$this->N] == '\'') {
return $this->lexQuote('\'');
}
if (preg_match('/\G%([a-zA-Z_]+)/', $this->data, $token, null, $this->N)) {
if (preg_match('/\G%([a-zA-Z_]+)/', $this->data, $token, 0, $this->N)) {
$this->value = $token[1];
$this->N += strlen($token[1]) + 1;
$this->state = 'DeclarePIRule';
@@ -419,7 +419,7 @@ class PHP_LexerGenerator_Lexer
if ($this->data[$this->N] == '"') {
return $this->lexQuote();
}
if (preg_match('/\G[a-zA-Z_][a-zA-Z0-9_]*/', $this->data, $token, null, $this->N)) {
if (preg_match('/\G[a-zA-Z_][a-zA-Z0-9_]*/', $this->data, $token, 0, $this->N)) {
$this->value = $token[0];
$this->N += strlen($token[0]);
$this->token = self::SUBPATTERN;

View File

@@ -33,17 +33,19 @@ class PHP_LexerGenerator_ParseryyToken implements ArrayAccess
return $this->_string;
}
function offsetExists($offset)
function offsetExists($offset): bool
{
return isset($this->metadata[$offset]);
}
// Return type mixed is not supported by PHP 7.4, we can remove the following PHP attribute and add the return type once iTop min PHP version is PHP 8.0+
#[\ReturnTypeWillChange]
function offsetGet($offset)
{
return $this->metadata[$offset];
}
function offsetSet($offset, $value)
function offsetSet($offset, $value): void
{
if ($offset === null) {
if (isset($value[0])) {
@@ -66,7 +68,7 @@ class PHP_LexerGenerator_ParseryyToken implements ArrayAccess
}
}
function offsetUnset($offset)
function offsetUnset($offset): void
{
unset($this->metadata[$offset]);
}
@@ -278,7 +280,7 @@ class PHP_LexerGenerator_Parser#line 171 "Parser.php"
$match = false;
foreach ($yy_yymore_patterns[' . $this->token . '] as $index => $rule) {
if (preg_match(\'/\' . $rule . \'/' . $this->patternFlags . '\',
' . $this->input . ', $yymatches, null, ' . $this->counter . ')) {
' . $this->input . ', $yymatches, 0, ' . $this->counter . ')) {
$yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
if ($match) {
if (strlen($yymatches[0]) > strlen($match[0][0])) {
@@ -350,7 +352,7 @@ class PHP_LexerGenerator_Parser#line 171 "Parser.php"
$pattern . '\';' . "\n");
fwrite($this->out, '
do {
if (preg_match($yy_global_pattern,' . $this->input . ', $yymatches, null, ' .
if (preg_match($yy_global_pattern,' . $this->input . ', $yymatches, 0, ' .
$this->counter .
')) {
$yysubmatches = $yymatches;
@@ -408,7 +410,7 @@ class PHP_LexerGenerator_Parser#line 171 "Parser.php"
}
$yysubmatches = array();
if (preg_match(\'/\' . $yy_yymore_patterns[' . $this->token . '][1] . \'/' . $this->patternFlags . '\',
' . $this->input . ', $yymatches, null, ' . $this->counter .')) {
' . $this->input . ', $yymatches, 0, ' . $this->counter .')) {
$yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
next($yymatches); // skip global match

View File

@@ -187,7 +187,7 @@ require_once 'PHP/LexerGenerator/Exception.php';
$match = false;
foreach ($yy_yymore_patterns[' . $this->token . '] as $index => $rule) {
if (preg_match(\'/\' . $rule . \'/' . $this->patternFlags . '\',
' . $this->input . ', $yymatches, null, ' . $this->counter . ')) {
' . $this->input . ', $yymatches, 0, ' . $this->counter . ')) {
$yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
if ($match) {
if (strlen($yymatches[0]) > strlen($match[0][0])) {
@@ -259,7 +259,7 @@ require_once 'PHP/LexerGenerator/Exception.php';
$pattern . '\';' . "\n");
fwrite($this->out, '
do {
if (preg_match($yy_global_pattern,' . $this->input . ', $yymatches, null, ' .
if (preg_match($yy_global_pattern,' . $this->input . ', $yymatches, 0, ' .
$this->counter .
')) {
$yysubmatches = $yymatches;
@@ -317,7 +317,7 @@ require_once 'PHP/LexerGenerator/Exception.php';
}
$yysubmatches = array();
if (preg_match(\'/\' . $yy_yymore_patterns[' . $this->token . '][1] . \'/' . $this->patternFlags . '\',
' . $this->input . ', $yymatches, null, ' . $this->counter .')) {
' . $this->input . ', $yymatches, 0, ' . $this->counter .')) {
$yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
next($yymatches); // skip global match

View File

@@ -110,7 +110,7 @@ class PHP_LexerGenerator_Regex_Lexer
$yy_global_pattern = '/\G(\\\\\\\\)|\G([^[\\\\^$.|()?*+{}]+)|\G(\\\\[][{}*.^$|?()+])|\G(\\[)|\G(\\|)|\G(\\\\[frnt]|\\\\x[0-9a-fA-F][0-9a-fA-F]?|\\\\[0-7][0-7][0-7]|\\\\x\\{[0-9a-fA-F]+\\})|\G(\\\\[0-9][0-9])|\G(\\\\[abBGcedDsSwW0C]|\\\\c\\\\)|\G(\\^)|\G(\\\\A)|\G(\\))|\G(\\$)|\G(\\*\\?|\\+\\?|[*?+]|\\{[0-9]+\\}|\\{[0-9]+,\\}|\\{[0-9]+,[0-9]+\\})|\G(\\\\[zZ])|\G(\\(\\?)|\G(\\()|\G(\\.)|\G(\\\\[1-9])|\G(\\\\p\\{\\^?..?\\}|\\\\P\\{..?\\}|\\\\X)|\G(\\\\p\\{C[cfnos]?|L[lmotu]?|M[cen]?|N[dlo]?|P[cdefios]?|S[ckmo]?|Z[lps]?\\})|\G(\\\\p\\{\\^C[cfnos]?|L[lmotu]?|M[cen]?|N[dlo]?|P[cdefios]?|S[ckmo]?|Z[lps]?\\})|\G(\\\\p[CLMNPSZ])|\G(\\\\)/';
do {
if (preg_match($yy_global_pattern,$this->input, $yymatches, null, $this->N)) {
if (preg_match($yy_global_pattern,$this->input, $yymatches, 0, $this->N)) {
$yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
if (!count($yymatches)) {
@@ -180,7 +180,7 @@ class PHP_LexerGenerator_Regex_Lexer
}
$yysubmatches = array();
if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/',
$this->input, $yymatches, null, $this->N)) {
$this->input, $yymatches, 0, $this->N)) {
$yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
next($yymatches); // skip global match
@@ -360,7 +360,7 @@ class PHP_LexerGenerator_Regex_Lexer
$yy_global_pattern = '/\G(\\^)|\G(\\])|\G(.)/';
do {
if (preg_match($yy_global_pattern,$this->input, $yymatches, null, $this->N)) {
if (preg_match($yy_global_pattern,$this->input, $yymatches, 0, $this->N)) {
$yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
if (!count($yymatches)) {
@@ -410,7 +410,7 @@ class PHP_LexerGenerator_Regex_Lexer
}
$yysubmatches = array();
if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/',
$this->input, $yymatches, null, $this->N)) {
$this->input, $yymatches, 0, $this->N)) {
$yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
next($yymatches); // skip global match
@@ -497,7 +497,7 @@ class PHP_LexerGenerator_Regex_Lexer
$yy_global_pattern = '/\G(\\\\\\\\)|\G(\\])|\G(\\\\[frnt]|\\\\x[0-9a-fA-F][0-9a-fA-F]?|\\\\[0-7][0-7][0-7]|\\\\x\\{[0-9a-fA-F]+\\})|\G(\\\\[bacedDsSwW0C]|\\\\c\\\\|\\\\x\\{[0-9a-fA-F]+\\}|\\\\[0-7][0-7][0-7]|\\\\x[0-9a-fA-F][0-9a-fA-F]?)|\G(\\\\[0-9][0-9])|\G(\\\\[1-9])|\G(\\\\[]\.\-\^])|\G(-(?!]))|\G([^\-\\\\])|\G(\\\\)|\G(.)/';
do {
if (preg_match($yy_global_pattern,$this->input, $yymatches, null, $this->N)) {
if (preg_match($yy_global_pattern,$this->input, $yymatches, 0, $this->N)) {
$yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
if (!count($yymatches)) {
@@ -555,7 +555,7 @@ class PHP_LexerGenerator_Regex_Lexer
}
$yysubmatches = array();
if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/',
$this->input, $yymatches, null, $this->N)) {
$this->input, $yymatches, 0, $this->N)) {
$yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
next($yymatches); // skip global match
@@ -678,7 +678,7 @@ class PHP_LexerGenerator_Regex_Lexer
$yy_global_pattern = '/\G(\\\\\\\\)|\G(\\\\\\])|\G(\\\\[bacedDsSwW0C]|\\\\c\\\\|\\\\x\\{[0-9a-fA-F]+\\}|\\\\[0-7][0-7][0-7]|\\\\x[0-9a-fA-F][0-9a-fA-F]?)|\G(\\\\[0-9][0-9])|\G(\\\\[1-9])|\G([^\-\\\\])|\G(\\\\)/';
do {
if (preg_match($yy_global_pattern,$this->input, $yymatches, null, $this->N)) {
if (preg_match($yy_global_pattern,$this->input, $yymatches, 0, $this->N)) {
$yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
if (!count($yymatches)) {
@@ -732,7 +732,7 @@ class PHP_LexerGenerator_Regex_Lexer
}
$yysubmatches = array();
if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/',
$this->input, $yymatches, null, $this->N)) {
$this->input, $yymatches, 0, $this->N)) {
$yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
next($yymatches); // skip global match
@@ -842,7 +842,7 @@ class PHP_LexerGenerator_Regex_Lexer
$yy_global_pattern = '/\G([imsxUX]+-[imsxUX]+|[imsxUX]+|-[imsxUX]+)|\G(:)|\G(\\))|\G(P<[^>]+>)|\G(<=)|\G(<!)|\G(=)|\G(!)|\G(>)|\G(\\(\\?)|\G(#[^)]+)|\G(R)|\G(.)/';
do {
if (preg_match($yy_global_pattern,$this->input, $yymatches, null, $this->N)) {
if (preg_match($yy_global_pattern,$this->input, $yymatches, 0, $this->N)) {
$yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
if (!count($yymatches)) {
@@ -902,7 +902,7 @@ class PHP_LexerGenerator_Regex_Lexer
}
$yysubmatches = array();
if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/',
$this->input, $yymatches, null, $this->N)) {
$this->input, $yymatches, 0, $this->N)) {
$yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
next($yymatches); // skip global match

View File

@@ -33,17 +33,19 @@ class PHP_LexerGenerator_Regex_yyToken implements ArrayAccess
return $this->_string;
}
function offsetExists($offset)
function offsetExists($offset): bool
{
return isset($this->metadata[$offset]);
}
// Return type mixed is not supported by PHP 7.4, we can remove the following PHP attribute and add the return type once iTop min PHP version is PHP 8.0+
#[\ReturnTypeWillChange]
function offsetGet($offset)
{
return $this->metadata[$offset];
}
function offsetSet($offset, $value)
function offsetSet($offset, $value): void
{
if ($offset === null) {
if (isset($value[0])) {
@@ -66,7 +68,7 @@ class PHP_LexerGenerator_Regex_yyToken implements ArrayAccess
}
}
function offsetUnset($offset)
function offsetUnset($offset): void
{
unset($this->metadata[$offset]);
}

View File

@@ -2,6 +2,7 @@
#
# Rebuild the iTop Lexer / Parser
# PEAR is required to build (really?)
# PHP 8.0+ is not supported by the parser generator
# Launch this batch from the core/oql/build directory
# with ./build.bash
#

View File

@@ -1,5 +1,6 @@
rem must be run with current directory = the directory of the batch
rem PEAR is required to build
rem PHP 8.0+ is not supported by the parser generator
php -d include_path=".;C:\iTop\PHP\PEAR" ".\PHP\LexerGenerator\cli.php" ..\oql-lexer.plex
php ".\PHP\ParserGenerator\cli.php" ..\oql-parser.y
php -r "echo date('Y-m-d');" > ..\version.txt

View File

@@ -575,6 +575,15 @@ class BinaryExpression extends Expression
case 'LIKE':
$sType = 'like';
break;
case 'NOT LIKE':
$sType = 'notlike';
break;
case 'IN':
$sType = 'in';
break;
case 'NOT IN':
$sType = 'notin';
break;
default:
throw new Exception("Operator '$sOperator' not yet supported");
}
@@ -639,7 +648,26 @@ class BinaryExpression extends Expression
case 'like':
$sEscaped = preg_quote($mRight, '/');
$sEscaped = str_replace(array('%', '_', '\\\\.*', '\\\\.'), array('.*', '.', '%', '_'), $sEscaped);
$result = (int) preg_match("/$sEscaped/i", $mLeft);
$pregRes = preg_match("/$sEscaped/i", $mLeft);
if ($pregRes === false) {
throw new Exception("Error in regular expression '$sEscaped'");
}
$result = ($pregRes === 1);
break;
case 'notlike':
$sEscaped = preg_quote($mRight, '/');
$sEscaped = str_replace(array('%', '_', '\\\\.*', '\\\\.'), array('.*', '.', '%', '_'), $sEscaped);
$pregRes = preg_match("/$sEscaped/i", $mLeft);
if ($pregRes === false) {
throw new Exception("Error in regular expression '$sEscaped'");
}
$result = ($pregRes !== 1);
break;
case 'in':
$result = in_array($mLeft, $mRight);
break;
case 'notin':
$result = !in_array($mLeft, $mRight);
break;
}
return $result;
@@ -2250,7 +2278,12 @@ class ListExpression extends Expression
*/
public function Evaluate(array $aArgs)
{
throw new Exception('list expression not yet supported');
//throw new Exception('list expression not yet supported');
$aResult = [];
foreach ($this->m_aExpressions as $oExpressions) {
$aResult[] = $oExpressions->Evaluate($aArgs);
}
return $aResult;
}
/**

View File

@@ -235,7 +235,7 @@ class OQLLexerRaw
$match = false;
foreach ($yy_yymore_patterns[$this->token] as $index => $rule) {
if (preg_match('/' . $rule . '/',
$this->data, $yymatches, null, $this->count)) {
$this->data, $yymatches, 0, $this->count)) {
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
if ($match) {
if (strlen($yymatches[0]) > strlen($match[0][0])) {

File diff suppressed because it is too large Load Diff

View File

@@ -71,28 +71,24 @@ class_list(A) ::= class_list(L) COMA class_name(X). {
where_statement(A) ::= WHERE condition(C). { A = C;}
where_statement(A) ::= . { A = null;}
join_statement(A) ::= join_item(J) join_statement(S). {
join_statement(A) ::= join_statement(S) JOIN join_item(J). {
// insert the join statement on top of the existing list
array_unshift(S, J);
array_push(S, J);
// and return the updated array
A = S;
}
join_statement(A) ::= join_item(J). {
A = Array(J);
}
join_statement(A) ::= . { A = null;}
join_statement(A) ::= . { A = [];}
join_item(A) ::= JOIN class_name(X) AS_ALIAS class_name(Y) ON join_condition(C).
join_item(A) ::= class_name(X) AS_ALIAS class_name(Y) ON join_condition(C).
{
// create an array with one single item
A = new OqlJoinSpec(X, Y, C);
}
join_item(A) ::= JOIN class_name(X) ON join_condition(C).
join_item(A) ::= class_name(X) ON join_condition(C).
{
// create an array with one single item
A = new OqlJoinSpec(X, X, C);
}
join_condition(A) ::= field_id(X) EQ field_id(Y). { A = new BinaryOqlExpression(X, '=', Y); }
join_condition(A) ::= field_id(X) BELOW field_id(Y). { A = new BinaryOqlExpression(X, 'BELOW', Y); }
join_condition(A) ::= field_id(X) BELOW_STRICT field_id(Y). { A = new BinaryOqlExpression(X, 'BELOW_STRICT', Y); }

View File

@@ -36,7 +36,13 @@ class UnknownClassOqlException extends OqlNormalizeException
{
public function __construct($sInput, OqlName $oName, $aExpecting = null)
{
parent::__construct('Unknown class', $sInput, $oName, $aExpecting);
$aAllowedClasses = [];
foreach ($aExpecting as $sClass) {
if (UserRights::IsActionAllowed($sClass, UR_ACTION_READ)) {
$aAllowedClasses[] = $sClass;
}
}
parent::__construct('Unknown class', $sInput, $oName, $aAllowedClasses);
}
public function GetUserFriendlyDescription()

View File

@@ -48,6 +48,42 @@ class ormDocument
* @since 3.1.0
*/
public const DEFAULT_DOWNLOADS_COUNT = 0;
private static $aKnownExtensions = [
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'png' => 'image/png',
'pdf' => 'application/pdf',
'doc' => 'application/msword',
'dot' => 'application/msword',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'vsd' => 'application/x-visio',
'vdx' => 'application/visio.drawing',
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'odp' => 'application/vnd.oasis.opendocument.presentation',
'zip' => 'application/zip',
'txt' => 'text/plain',
'htm' => 'text/html',
'html' => 'text/html',
'exe' => 'application/octet-stream',
];
public static function GetKnownExtensions(): array
{
return self::$aKnownExtensions;
}
protected $m_data;
protected $m_sMimeType;
@@ -76,6 +112,36 @@ class ormDocument
$this->m_iDownloadsCount = $iDownloadsCount;
}
/**
* @param string $sPath Absolute path of the document to read
*
* @return \ormDocument
* @throws \Exception
*/
public static function FromFile(string $sPath): ormDocument
{
$sPath = utils::RealPath($sPath, APPROOT);
if (false === $sPath) {
throw new Exception("Failed to load the file '$sPath'. The file does not exist or the current process is not allowed to access it.");
}
$sData = @file_get_contents($sPath);
if (false === $sData) {
throw new Exception("Failed to load the file '$sPath'. The file does not exist or the current process is not allowed to access it.");
}
$sExtension = strtolower(pathinfo($sPath, PATHINFO_EXTENSION));
$sFileName = basename($sPath);
$sMimeType = 'text/plain';
if (array_key_exists($sExtension, ormDocument::$aKnownExtensions)) {
$sMimeType = ormDocument::$aKnownExtensions[$sExtension];
} else if (extension_loaded('fileinfo')) {
$fInfo = new finfo(FILEINFO_MIME);
$sMimeType = $fInfo->file($sPath);
}
return new ormDocument($sData, $sMimeType, $sFileName);
}
public function __toString()
{
if($this->IsEmpty()) return '';
@@ -314,7 +380,7 @@ class ormDocument
'document' => $oDocument,
'content_disposition' => $sContentDisposition,
);
EventService::FireEvent(new EventData(EVENT_DOWNLOAD_DOCUMENT, $sClass, $aEventData));
EventService::FireEvent(new EventData(\EVENT_DOWNLOAD_DOCUMENT, $sClass, $aEventData));
$oPage->TrashUnexpectedOutput();
$oPage->SetContentType($oDocument->GetMimeType());
$oPage->SetContentDisposition($sContentDisposition,$oDocument->GetFileName());

View File

@@ -42,8 +42,8 @@ class iTopOwnershipToken extends DBObject
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeDateTime("acquired", array("allowed_values"=>null, "sql"=>'acquired', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("last_seen", array("allowed_values"=>null, "sql"=>'last_seen', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("acquired", array("allowed_values"=>null, "sql"=>'acquired', "default_value"=>'NOW()', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("last_seen", array("allowed_values"=>null, "sql"=>'last_seen', "default_value"=>'NOW()', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("obj_class", array("allowed_values"=>null, "sql"=>'obj_class', "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("obj_key", array("allowed_values"=>null, "sql"=>'obj_key', "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("token", array("allowed_values"=>null, "sql"=>'token', "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));

View File

@@ -203,6 +203,7 @@ class RelationEdge extends GraphEdge
*/
class RelationGraph extends SimpleGraph
{
private static bool $bOptimizationEnabled = true;
protected $aSourceNodes; // Index of source nodes (for a quicker access)
protected $aSinkNodes; // Index of sink nodes (for a quicker access)
protected $aRedundancySettings; // Cache of user settings
@@ -217,6 +218,16 @@ class RelationGraph extends SimpleGraph
$this->aContextSearches = array();
}
static public function GetOptimizationEnabled(): bool
{
return static::$bOptimizationEnabled;
}
static public function SetOptimizationEnabled(bool $bEnabled)
{
static::$bOptimizationEnabled = $bEnabled;
}
/**
* Add an object that will be the starting point for building the relations downstream
*
@@ -261,16 +272,23 @@ class RelationGraph extends SimpleGraph
IssueLog::Error("Invalid context query '$sOQL'. A context query must contain at least two columns.");
throw new Exception("Invalid context query '$sOQL'. A context query must contain at least two columns. Columns: ".implode(', ', $aAliases).'. ');
}
$aAliasNames = array_keys($aAliases);
$oCondition = new BinaryExpression(new FieldExpression('id', $aAliasNames[0]), '=', new VariableExpression('id'));
$oSearch->AddConditionExpression($oCondition);
$sClass = $oSearch->GetClass();
if (!array_key_exists($sClass, $this->aContextSearches))
{
$this->aContextSearches[$sClass] = array();
if (static::GetOptimizationEnabled()) {
$sClass = $oSearch->GetClass();
$this->aContextSearches[$sClass][] = array('key' => $key, 'search' => $oSearch);
}
else {
$aAliasNames = array_keys($aAliases);
$oCondition = new BinaryExpression(new FieldExpression('id', $aAliasNames[0]), '=', new VariableExpression('id'));
$oSearch->AddConditionExpression($oCondition);
$sClass = $oSearch->GetClass();
if (!array_key_exists($sClass, $this->aContextSearches))
{
$this->aContextSearches[$sClass] = array();
}
$this->aContextSearches[$sClass][] = array('key' => $key, 'search' => $oSearch);
}
$this->aContextSearches[$sClass][] = array('key' => $key, 'search' => $oSearch);
}
/**
@@ -283,6 +301,8 @@ class RelationGraph extends SimpleGraph
*/
public function IsPartOfContext(DBObject $oObj, &$aRootCauses)
{
$oKPI = new ExecutionKPI();
$bRet = false;
$sFinalClass = get_class($oObj);
$aParentClasses = MetaModel::EnumParentClasses($sFinalClass, ENUM_PARENT_CLASSES_ALL);
@@ -313,6 +333,7 @@ class RelationGraph extends SimpleGraph
}
}
}
$oKPI->ComputeStats(__METHOD__, get_class($oObj));
return $bRet;
}
@@ -329,6 +350,7 @@ class RelationGraph extends SimpleGraph
*/
public function ComputeRelatedObjectsDown($sRelCode, $iMaxDepth, $bEnableRedundancy, $aUnreachableObjects = array())
{
$oKPI = new ExecutionKPI();
//echo "<h5>Sources only...</h5>\n".$this->DumpAsHtmlImage()."<br/>\n";
// Build the graph out of the sources
foreach ($this->aSourceNodes as $oSourceNode)
@@ -336,8 +358,10 @@ class RelationGraph extends SimpleGraph
$this->AddRelatedObjects($sRelCode, true, $oSourceNode, $iMaxDepth, $bEnableRedundancy);
//echo "<h5>After processing of {$oSourceNode->GetId()}</h5>\n".$this->DumpAsHtmlImage()."<br/>\n";
}
$oKPI->ComputeAndReport(__FUNCTION__.' - AddRelatedObjects');
// Mark the unreachable nodes
$oKPI = new ExecutionKPI();
foreach ($aUnreachableObjects as $oObj)
{
$sNodeId = RelationObjectNode::MakeId($oObj);
@@ -347,29 +371,26 @@ class RelationGraph extends SimpleGraph
$oNode->SetProperty('is_reached_allowed', false);
}
}
$oKPI->ComputeAndReport(__FUNCTION__.' - Mark unreachable nodes');
// Determine the reached nodes
$oKPI = new ExecutionKPI();
foreach ($this->aSourceNodes as $oSourceNode)
{
$oSourceNode->ReachDown('is_reached', true);
//echo "<h5>After reaching from {$oSourceNode->GetId()}</h5>\n".$this->DumpAsHtmlImage()."<br/>\n";
}
$oKPI->ComputeAndReport(__FUNCTION__.' - Determine reached nodes');
// Mark also the "context" nodes as reached and record the "root causes" for each node
$oIterator = new RelationTypeIterator($this, 'Node');
foreach($oIterator as $oNode)
{
$oObj = $oNode->GetProperty('object');
$aRootCauses = array();
if (!is_null($oObj) && $this->IsPartOfContext($oObj, $aRootCauses))
{
$oNode->SetProperty('context_root_causes', $aRootCauses);
$oNode->ReachDown('is_reached', true);
}
}
$oKPI = new ExecutionKPI();
$this->MarkContextNodesAsReached();
$oKPI->ComputeAndReport(__FUNCTION__.' - Mark context nodes as reached');
$oKPI = new ExecutionKPI();
if ( MetaModel::GetConfig()->Get('relations.complete_analysis')) {
$this->ApplyUserRightsOnGraph();
}
$oKPI->ComputeAndReport(__FUNCTION__.' - Apply user rights on graph');
}
/**
@@ -384,6 +405,7 @@ class RelationGraph extends SimpleGraph
*/
public function ComputeRelatedObjectsUp($sRelCode, $iMaxDepth, $bEnableRedundancy)
{
$oKPI = new ExecutionKPI();
//echo "<h5>Sinks only...</h5>\n".$this->DumpAsHtmlImage()."<br/>\n";
// Build the graph out of the sinks
foreach ($this->aSinkNodes as $oSinkNode)
@@ -407,6 +429,7 @@ class RelationGraph extends SimpleGraph
if ( MetaModel::GetConfig()->Get('relations.complete_analysis')) {
$this->ApplyUserRightsOnGraph();
}
$oKPI->ComputeStats('GetRelatedObjects-Up', '');
}
@@ -423,6 +446,11 @@ class RelationGraph extends SimpleGraph
*/
protected function AddRelatedObjects($sRelCode, $bDown, $oObjectNode, $iMaxDepth, $bEnableRedundancy)
{
if (static::GetOptimizationEnabled()) {
$this->AddRelatedObjects_Optimized();
return;
}
if ($iMaxDepth > 0)
{
if ($oObjectNode instanceof RelationRedundancyNode)
@@ -513,6 +541,64 @@ class RelationGraph extends SimpleGraph
}
}
public function AddRelatedObjects_Optimized()
{
$sRelCode = 'impact';
// TODO : dehardcode against the source nodes
$sSQLQuery = file_get_contents(APPROOT.'/impact_server1.sql');
$aRes = CMDBSource::QueryToArray($sSQLQuery, MYSQLI_ASSOC);
$oKPI = new ExecutionKPI();
$aClassToId = [];
foreach ($aRes as $aRow) {
if (is_null($aRow['parent_class']) || is_null($aRow['parent_id'])) {
// No parent => source node already present in the graph
continue;
}
$aClassToId[$aRow['class']][$aRow['id']] = $aRow['name'];
}
$oKPI->ComputeAndReport('AddRelatedObjectsDown - Create aClassToId');
$oKPI = new ExecutionKPI();
// One query per class => not too bad
// Note: it is not possible to use the root class as we need to load the tooltip attributes
foreach ($aClassToId as $sClass => $aIds) {
$oSearch = new DBObjectSearch($sClass);
$oSearch->AllowAllData();
$oSearch->AddCondition('id', array_keys($aIds), 'IN');
$oSet = new DBObjectSet($oSearch);
$oSet->OptimizeColumnLoad([$oSearch->GetClassAlias() => DisplayableNode::GetTooltipAttributes($sClass)]);
while ($oObj = $oSet->Fetch()) {
$sNodeId = RelationObjectNode::MakeId($oObj);
if (!isset($this->aNodes[$sNodeId])) {
$this->aNodes[$sNodeId] = new RelationObjectNode($this, $oObj);
}
}
}
$oKPI->ComputeAndReport('AddRelatedObjectsDown - Query each class and create nodes: '.count($aClassToId));
$oKPI = new ExecutionKPI();
foreach ($aRes as $aRow) {
if (is_null($aRow['parent_class']) || is_null($aRow['parent_id'])) {
// No parent, so no edge
continue;
}
if ($aRow['backtracking'] == 0) {
$oSourceNode = $this->GetNode("{$aRow['parent_class']}::{$aRow['parent_id']}");
$oSinkNode = $this->GetNode("{$aRow['class']}::{$aRow['id']}");
}
else {
$oSourceNode = $this->GetNode("{$aRow['class']}::{$aRow['id']}");
$oSinkNode = $this->GetNode("{$aRow['parent_class']}::{$aRow['parent_id']}");
}
new RelationEdge($this, $oSourceNode, $oSinkNode);
}
$oKPI->ComputeAndReport('AddRelatedObjectsDown - Create edges : '.count($aRes));
}
/**
* Determine if there is a redundancy (or use the existing one) and add the corresponding nodes/edges
*
@@ -747,4 +833,71 @@ class RelationGraph extends SimpleGraph
}
}
/**
* Mark "context" nodes as reached and record the "root causes" for each node
*/
private function MarkContextNodesAsReached()
{
if (static::GetOptimizationEnabled()) {
$this->MarkContextNodesAsReached_Optimized();
return;
}
$oIterator = new RelationTypeIterator($this, 'Node');
foreach($oIterator as $oNode)
{
$oObj = $oNode->GetProperty('object');
$aRootCauses = array();
if (!is_null($oObj) && $this->IsPartOfContext($oObj, $aRootCauses))
{
$oNode->SetProperty('context_root_causes', $aRootCauses);
$oNode->ReachDown('is_reached', true);
}
}
}
private function MarkContextNodesAsReached_Optimized()
{
$oIterator = new RelationTypeIterator($this, 'Node');
// 1. Group all nodes by class
$aClassToId = [];
foreach ($oIterator as $oNode) {
$oObj = $oNode->GetProperty('object');
if ($oObj) {
$aClassToId[get_class($oObj)][$oObj->GetKey()] = $oNode;
}
}
// 2. For each class, perform a search to find the context objects
$aContextNodes = [];
foreach ($aClassToId as $sClass => $aIdToNodes) {
foreach (MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL) as $sParentClass)
{
if (!array_key_exists($sParentClass, $this->aContextSearches)) {
continue; // No context search for this class
}
foreach ($this->aContextSearches[$sParentClass] as $aContextQuery)
{
$oKPI = new ExecutionKPI();
/** @var \DBSearch $oSearch */
$oSearch = $aContextQuery['search']->DeepClone();
$oSearch->AddCondition('id', array_keys($aIdToNodes), 'IN');
$aAliasNames = array_keys($oSearch->GetSelectedClasses());
$sObjectAlias = $aAliasNames[0];
$sRootCauseAlias = $aAliasNames[1];
$oSet = new DBObjectSet($oSearch);
$oSet->OptimizeColumnLoad([$sObjectAlias => [], $sRootCauseAlias => []]);
while($aRow = $oSet->FetchAssoc()) {
$oRootCauseObject = $aRow[$sRootCauseAlias];
if (is_null($oRootCauseObject)) continue;
$oNode = $aIdToNodes[$aRow[$sObjectAlias]->GetKey()];
$aRootCauses = $oNode->GetProperty('context_root_causes', []);
$aRootCauses[$aContextQuery['key']][$oRootCauseObject->GetKey()] = $oRootCauseObject;
$oNode->SetProperty('context_root_causes', $aRootCauses);
}
$oKPI->ComputeStats('Query to find context objects', $sClass.' - '.$aContextQuery['key']);
}
}
}
}
}

View File

@@ -44,6 +44,8 @@ class ObjectResult
* @var string
* @api
*/
use SanitizeTrait;
public $message;
/**
* @var mixed|null
@@ -74,6 +76,52 @@ class ObjectResult
$this->fields = array();
}
/**
* Creates an ObjectResult from a DBObject.
*
* @param DBObject $oObj The object.
* @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 ?
* @param integer $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
*
* @return ObjectResult
*/
public static function FromDBObject(DBObject $oObj, ?array $aFieldSpec = null, $bExtendedOutput = false, $iCode = 0, $sMessage = '') : ObjectResult {
$oObjRes = new ObjectResult($oObj::class, $oObj->GetKey());
$oObjRes->code = $iCode;
$oObjRes->message = $sMessage;
$aFields = null;
if (!is_null($aFieldSpec))
{
// Enum all classes in the hierarchy, starting with the current one
foreach (MetaModel::EnumParentClasses($oObj::class, ENUM_PARENT_CLASSES_ALL, false) as $sRefClass)
{
if (array_key_exists($sRefClass, $aFieldSpec))
{
$aFields = $aFieldSpec[$sRefClass];
break;
}
}
}
if (is_null($aFields))
{
// No fieldspec given, or not found...
$aFields = array('id', 'friendlyname');
}
foreach ($aFields as $sAttCode)
{
$oObjRes->AddField($oObj, $sAttCode, $bExtendedOutput);
}
return $oObjRes;
}
/**
* Helper to make an output value for a given attribute
*
@@ -156,6 +204,18 @@ class ObjectResult
{
$this->fields[$sAttCode] = $this->MakeResultValue($oObject, $sAttCode, $bExtendedOutput);
}
public function SanitizeContent()
{
foreach($this->fields as $sFieldAttCode => $fieldValue) {
try {
$oAttDef = MetaModel::GetAttributeDef($this->class, $sFieldAttCode);
} catch (Exception $e) { // for special cases like ID
continue;
}
$this->SanitizeFieldIfSensitive($this->fields, $sFieldAttCode, $fieldValue, $oAttDef);
}
}
}
@@ -189,38 +249,21 @@ class RestResultWithObjects extends RestResult
*/
public function AddObject($iCode, $sMessage, $oObject, $aFieldSpec = null, $bExtendedOutput = false)
{
$sClass = get_class($oObject);
$oObjRes = new ObjectResult($sClass, $oObject->GetKey());
$oObjRes->code = $iCode;
$oObjRes->message = $sMessage;
$aFields = null;
if (!is_null($aFieldSpec))
{
// Enum all classes in the hierarchy, starting with the current one
foreach (MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL, false) as $sRefClass)
{
if (array_key_exists($sRefClass, $aFieldSpec))
{
$aFields = $aFieldSpec[$sRefClass];
break;
}
}
}
if (is_null($aFields))
{
// No fieldspec given, or not found...
$aFields = array('id', 'friendlyname');
}
foreach ($aFields as $sAttCode)
{
$oObjRes->AddField($oObject, $sAttCode, $bExtendedOutput);
}
$oObjRes = ObjectResult::FromDBObject($oObject, $aFieldSpec, $bExtendedOutput, $iCode, $sMessage);
$sObjKey = get_class($oObject).'::'.$oObject->GetKey();
$this->objects[$sObjKey] = $oObjRes;
}
public function SanitizeContent()
{
parent::SanitizeContent();
foreach($this->objects as $sObjKey => $oObjRes)
{
$oObjRes->SanitizeContent();
}
}
}
/**
@@ -308,9 +351,10 @@ class RestDelete
*
* @package Core
*/
class CoreServices implements iRestServiceProvider
class CoreServices implements iRestServiceProvider, iRestInputSanitizer
{
/**
use SanitizeTrait;
/**
* Enumerate services delivered by this class
*
* @param string $sVersion The version (e.g. 1.0) supported by the services
@@ -528,18 +572,18 @@ class CoreServices implements iRestServiceProvider
}
else
{
if (!$bExtendedOutput && RestUtils::GetOptionalParam($aParams, 'output_fields', '*') != '*')
if (!$bExtendedOutput && RestUtils::GetOptionalParam($aParams, 'output_fields', '*') != '*')
{
$aFields = $aShowFields[$sClass];
//Id is not a valid attribute to optimize
if (in_array('id', $aFields))
if (in_array('id', $aFields))
{
unset($aFields[array_search('id', $aFields)]);
}
$aAttToLoad = array($oObjectSet->GetClassAlias() => $aFields);
$oObjectSet->OptimizeColumnLoad($aAttToLoad);
}
while ($oObject = $oObjectSet->Fetch())
{
$oResult->AddObject(0, '', $oObject, $aShowFields, $bExtendedOutput);
@@ -737,6 +781,33 @@ class CoreServices implements iRestServiceProvider
return $oResult;
}
public function SanitizeJsonInput(string $sJsonInput): string
{
$sSanitizedJsonInput = $sJsonInput;
$aJsonData = json_decode($sSanitizedJsonInput, true);
$sOperation = $aJsonData['operation'];
switch ($sOperation) {
case 'core/check_credentials':
if (isset($aJsonData['password'])) {
$aJsonData['password'] = '*****';
}
break;
case 'core/update':
case 'core/create':
default :
$sClass = $aJsonData['class'];
if (isset($aJsonData['fields'])) {
foreach ($aJsonData['fields'] as $sFieldAttCode => $fieldValue) {
$oAttDef = MetaModel::GetAttributeDef($sClass, $sFieldAttCode);
$this->SanitizeFieldIfSensitive($aJsonData['fields'], $sFieldAttCode, $fieldValue, $oAttDef);
}
}
break;
}
return json_encode($aJsonData, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
}
/**
* Helper for object deletion
*/
@@ -875,3 +946,53 @@ class CoreServices implements iRestServiceProvider
return $iLimit * max(0, $iPage - 1);
}
}
/**
* Sanitizes sensitive fields on a "json ready" representation of a DBObject
* Useful for logging purposes
*/
trait SanitizeTrait
{
/**
* Sanitize a field if it is sensitive.
*
* @param array $fields The fields array
* @param string $sFieldAttCode The attribute code
* @param mixed $oAttDef The attribute definition
* @throws Exception
*/
private function SanitizeFieldIfSensitive(array &$fields, string $sFieldAttCode, $fieldValue, $oAttDef): void
{
// for simple attribute
if ($oAttDef instanceof iAttributeNoGroupBy) { // iAttributeNoGroupBy is equivalent to sensitive attribute
$fields[$sFieldAttCode] = '*****';
return;
}
// for 1-n / n-n relation
if ($oAttDef instanceof AttributeLinkedSet) {
foreach ($fieldValue as $i => $aLnkValues) {
foreach ($aLnkValues as $sLnkAttCode => $sLnkValue) {
$oLnkAttDef = MetaModel::GetAttributeDef($oAttDef->GetLinkedClass(), $sLnkAttCode);
if ($oLnkAttDef instanceof iAttributeNoGroupBy) { // 1-n relation
$fields[$sFieldAttCode][$i][$sLnkAttCode] = '*****';
}
elseif ($oAttDef instanceof AttributeLinkedSetIndirect && $oLnkAttDef instanceof AttributeExternalField) { // for n-n relation
$oExtKeyAttDef = MetaModel::GetAttributeDef($oLnkAttDef->GetTargetClass(), $oLnkAttDef->GetExtAttCode());
if ($oExtKeyAttDef instanceof iAttributeNoGroupBy) {
$fields[$sFieldAttCode][$i][$sLnkAttCode] = '*****';
}
}
}
}
return;
}
// for external attribute
if ($oAttDef instanceof AttributeExternalField) {
$oExtKeyAttDef = MetaModel::GetAttributeDef($oAttDef->GetTargetClass(), $oAttDef->GetExtAttCode());
if ($oExtKeyAttDef instanceof iAttributeNoGroupBy) {
$fields[$sFieldAttCode] = '*****';
}
}
}
}

View File

@@ -500,25 +500,27 @@ EOF
if (file_exists($sDotExecutable))
{
// create the file with Graphviz
if (!is_dir(APPROOT."data"))
if (!is_dir(utils::GetDataPath()))
{
@mkdir(APPROOT."data");
@mkdir(utils::GetDataPath());
}
if (!is_dir(APPROOT."data/tmp"))
if (!is_dir(utils::GetDataPath()."tmp"))
{
@mkdir(APPROOT."data/tmp");
@mkdir(utils::GetDataPath()."tmp");
}
$sImageFilePath = tempnam(APPROOT."data/tmp", 'png-');
$sImageFilePath = tempnam(utils::GetDataPath()."tmp", 'png-');
$sDotDescription = $this->GetDotDescription();
$sDotFilePath = tempnam(APPROOT."data/tmp", 'dot-');
$sDotFilePath = tempnam(utils::GetDataPath()."tmp", 'dot-');
$rFile = @fopen($sDotFilePath, "w");
@fwrite($rFile, $sDotDescription);
@fclose($rFile);
$aOutput = array();
$CommandLine = "\"$sDotExecutable\" -v -Tpng < \"$sDotFilePath\" -o\"$sImageFilePath\" 2>&1";
$oKPI = new ExecutionKPI();
exec($CommandLine, $aOutput, $iRetCode);
$oKPI->ComputeStats('Graphviz execution png', 'png');
if ($iRetCode != 0)
{
$sHtml = '';
@@ -556,25 +558,27 @@ EOF
if (file_exists($sDotExecutable))
{
// create the file with Graphviz
if (!is_dir(APPROOT."data"))
if (!is_dir(utils::GetDataPath()))
{
@mkdir(APPROOT."data");
@mkdir(utils::GetDataPath());
}
if (!is_dir(APPROOT."data/tmp"))
if (!is_dir(utils::GetDataPath()."tmp"))
{
@mkdir(APPROOT."data/tmp");
@mkdir(utils::GetDataPath()."tmp");
}
$sXdotFilePath = tempnam(APPROOT."data/tmp", 'xdot-');
$sXdotFilePath = tempnam(utils::GetDataPath()."tmp", 'xdot-');
$sDotDescription = $this->GetDotDescription(true); // true => don't put (localized) labels in the file, since it makes it harder to parse
$sDotFilePath = tempnam(APPROOT."data/tmp", 'dot-');
$sDotFilePath = tempnam(utils::GetDataPath()."tmp", 'dot-');
$rFile = @fopen($sDotFilePath, "w");
@fwrite($rFile, $sDotDescription);
@fclose($rFile);
$aOutput = array();
$CommandLine = "\"$sDotExecutable\" -v -Tdot < \"$sDotFilePath\" -o\"$sXdotFilePath\" 2>&1";
$oKPI = new ExecutionKPI();
exec($CommandLine, $aOutput, $iRetCode);
$oKPI->ComputeStats('Graphviz execution dot', 'dot');
if ($iRetCode != 0)
{
$sHtml = '';

View File

@@ -17,6 +17,8 @@
* You should have received a copy of the GNU Affero General Public License
*/
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
/**
* A user defined trigger, to customize the application
* A trigger will activate an action
@@ -170,19 +172,20 @@ abstract class TriggerOnObject extends Trigger
{
$aParams = array
(
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "description",
"category" => "grant_by_profile,core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "description",
"complementary_name_attcode" => ['finalclass', 'complement'],
"state_attcode" => "",
"state_attcode" => "",
"reconc_keys" => ['description'],
"db_table" => "priv_trigger_onobject",
"db_key_field" => "id",
"db_table" => "priv_trigger_onobject",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeClass("target_class", array("class_category" => "bizmodel", "more_values" => "User,UserExternal,UserInternal,UserLDAP,UserLocal", "sql" => "target_class", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeClass("target_class",
array("class_category" => "bizmodel", "more_values" => "User,UserExternal,UserInternal,UserLDAP,UserLocal", "sql" => "target_class", "default_value" => null, "is_null_allowed" => false, "depends_on" => array(), "class_exclusion_list" => "Attachment")));
MetaModel::Init_AddAttribute(new AttributeOQL("filter", array("allowed_values" => null, "sql" => "filter", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
@@ -200,7 +203,7 @@ abstract class TriggerOnObject extends Trigger
{
parent::DoCheckToWrite();
$sFilter = trim($this->Get('filter'));
$sFilter = trim($this->Get('filter') ?? '');
if (strlen($sFilter) > 0)
{
try
@@ -270,6 +273,36 @@ abstract class TriggerOnObject extends Trigger
}
}
/**
* if the target class is Attachment, then the trigger is read-only
* @param $sAttCode
* @param $aReasons
* @param $sTargetState
* @return int
* @throws ArchivedObjectException
* @throws CoreException
*/
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState='')
{
// Force the computed field to be read-only, preventing it to be written
if ($this->Get('target_class') == 'Attachment' ) {
return OPT_ATT_READONLY;
}
return parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
}
public function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
{
$aHeaderBlocks = parent::DisplayBareHeader($oPage, $bEditMode);
if ($this->Get('target_class') == 'Attachment' ) {
$oPage->AddUiBlock(AlertUIBlockFactory::MakeForWarning('', Dict::S('Class:TriggerOnObject:TriggerClassAttachment/ReadOnlyMessage')));
$oPage->add_ready_script("$('#UIMenuModify').hide();");
}
return $aHeaderBlocks;
}
/**
* Activate trigger based on attribute list given instead of changed attributes
*
@@ -529,6 +562,7 @@ class TriggerOnObjectCreate extends TriggerOnObject
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
}
/**

View File

@@ -1264,19 +1264,13 @@ class UserRights
{
$sUserPicturesFolder = 'images/user-pictures/';
$sUserPicturePlaceholderPrefKey = 'user_picture_placeholder';
$sPictureUrl = null;
// First, check cache
if (array_key_exists($sLogin, static::$m_aCacheContactPictureAbsUrl)) {
return static::$m_aCacheContactPictureAbsUrl[$sLogin];
}
// Then, the default picture
if ($bAllowDefaultPicture === true) {
$sPictureUrl = utils::GetAbsoluteUrlAppRoot().$sUserPicturesFolder.'user-profile-default-256px.png';
} else {
$sPictureUrl = null;
}
// Then check if the user has a contact attached and if it has an picture defined
$sContactId = UserRights::GetContactId($sLogin);
if (!empty($sContactId)) {
@@ -1323,6 +1317,11 @@ class UserRights
}
// Else, no contact and no login, then it's for an unknown origin (system, extension, ...)
// Then, the default picture
if (utils::IsNullOrEmptyString($sPictureUrl) && $bAllowDefaultPicture === true) {
$sPictureUrl = utils::GetAbsoluteUrlAppRoot().$sUserPicturesFolder.'user-profile-default-256px.png';
}
// Update cache
static::$m_aCacheContactPictureAbsUrl[$sLogin] = $sPictureUrl;
@@ -1922,50 +1921,45 @@ class UserRights
*/
protected static function FindUser($sLogin, $sAuthentication = 'any', $bAllowDisabledUsers = false)
{
if ($sAuthentication == 'any')
{
$oUser = self::FindUser($sLogin, 'internal');
if ($oUser == null)
{
$oUser = self::FindUser($sLogin, 'external');
if ($sAuthentication === 'any') {
$oUser = self::FindUser($sLogin, 'internal', $bAllowDisabledUsers);
if ($oUser !== null) {
return $oUser;
}
return self::FindUser($sLogin, 'external', $bAllowDisabledUsers);
}
else
{
if (!isset(self::$m_aCacheUsers))
{
self::$m_aCacheUsers = array('internal' => array(), 'external' => array());
}
if (!isset(self::$m_aCacheUsers[$sAuthentication][$sLogin]))
{
switch($sAuthentication)
{
case 'external':
$sBaseClass = 'UserExternal';
break;
case 'internal':
$sBaseClass = 'UserInternal';
break;
default:
echo "<p>sAuthentication = $sAuthentication</p>\n";
assert(false); // should never happen
}
$oSearch = DBObjectSearch::FromOQL("SELECT $sBaseClass WHERE login = :login");
$oSearch->AllowAllData();
if (!$bAllowDisabledUsers)
{
$oSearch->AddCondition('status', 'enabled');
}
$oSet = new DBObjectSet($oSearch, array(), array('login' => $sLogin));
$oUser = $oSet->fetch();
self::$m_aCacheUsers[$sAuthentication][$sLogin] = $oUser;
}
$oUser = self::$m_aCacheUsers[$sAuthentication][$sLogin];
if (!isset(self::$m_aCacheUsers)) {
self::$m_aCacheUsers = [ 'internal' => [], 'external' => [] ];
}
return $oUser;
if (! isset(self::$m_aCacheUsers[$sAuthentication]) || ! array_key_exists($sLogin, self::$m_aCacheUsers[$sAuthentication])) {
switch($sAuthentication) {
case 'external':
$sBaseClass = 'UserExternal';
break;
case 'internal':
$sBaseClass = 'UserInternal';
break;
default:
echo "<p>sAuthentication = $sAuthentication</p>\n";
assert(false); // should never happen
}
$oSearch = DBObjectSearch::FromOQL("SELECT $sBaseClass WHERE login = :login");
$oSearch->AllowAllData();
if (!$bAllowDisabledUsers) {
$oSearch->AddCondition('status', 'enabled');
}
$oSet = new DBObjectSet($oSearch, array(), array('login' => $sLogin));
$oUser = $oSet->fetch();
self::$m_aCacheUsers[$sAuthentication][$sLogin] = $oUser;
}
return self::$m_aCacheUsers[$sAuthentication][$sLogin];
}
/**
@@ -2126,6 +2120,8 @@ class StimulusChecker extends ActionChecker
{
var $sState = null;
public mixed $iState = null;
public function __construct(DBSearch $oFilter, $sState, $iStimulusCode)
{
parent::__construct($oFilter, $iStimulusCode);

View File

@@ -435,7 +435,7 @@ class ValueSetObjects extends ValueSetDefinition
foreach ($aAdditionalField as $sAdditionalField) {
array_push($aArguments, $oObject->Get($sAdditionalField));
}
$aData['additional_field'] = vsprintf($sFormatAdditionalField, $aArguments);
$aData['additional_field'] = utils::VSprintf($sFormatAdditionalField, $aArguments);
} else {
$aData['additional_field'] = '';
}

View File

@@ -1,75 +1,108 @@
## Description
This is a brief description of the SASS 7-1 system and how to use it.
- [File structure](#file-structure)
- [Usage](#usage)
# Description
This is a brief description of the how the backoffice theme is structured using both BEM and SASS 7-1 systems and how to use them.
* [7-1 pattern](#7-1-pattern)
* [File structure](#file-structure)
* [Usage](#usage)
* [BEM methodology](#bem-methodology)
* [Principles](#principles)
* [Examples](#examples)
# 7-1 pattern
## File structure
SCSS files are structured following the [7-1 pattern](https://sass-guidelin.es/#the-7-1-pattern). \
@rveitch made a great summary with the following, which can also be found [here](https://gist.github.com/rveitch/84cea9650092119527bc).
_Note: Folders with an * are customizations we made to the original 7-1 pattern to best fit our needs_
_Note: Folders with an * are customizations we made to the original 7-1 pattern to best fit our needs_
```
css/backoffice/
|
| utils/
| | _variables.scss # Sass Variables
| | _functions.scss # Sass Functions
| | _mixins.scss # Sass Mixins
| | _helpers.scss # Class & placeholders helpers
| | variables/ # Sass Variables used in Functions, Mixins, Helpers, ...
| | |- colors/
| | | |- _base.scss
| | | |- _base-palette.scss # Base colors used everywhere
| | | |- _lifecycle-palette.scss # Colors used for lifecycle of an object (e.g. representing states such as new, frozen, done, ...), based on the base colors
| | | |- _semantic-palette.scss # Colors used for semantic meaning (e.g. red for errors, green for success, ...), based on the base colors
| | | ...
| | |
| | |- _depression.scss
| | |- _elevation.scss
| | |- _size.scss # Base sizes used everywhere (spacings, ...)
| | |- _spacing.scss
| | |- _typography.scss # Typography sizes, weights, families, ...
| | ...
| |
| | functions/ # Sass Functions
| | |- _color.scss # Color manipulation functions
| |
| | mixins/ # Sass Mixins
| | helpers/ # Class & placeholders helpers
|
| vendors/
| | _bootstrap.scss # Bootstrap
| | _jquery-ui.scss # jQuery UI
| ... # Etc…
| vendors/ # Third-party libs, should be either:
| # - Overload of the lib SCSS variables (BEST way, but possible only if the lib exposes them. e.g. Bulma)
| # - Overload of the lib necessary CSS classes only (not great as it duplicates some rules in the browser, which add weight and computation. e.g. dataTables)
| # - Duplicate the lib CSS completly to insert SCSS variables (not great as it will be outdated when updating the lib itself. e.g. jQuery UI)
| | _bulma-variables-overload.scss # Bulma CSS framework
| | _jquery-ui.scss # jQuery UI
| ... # Etc…
|
| base/
| | _reset.scss # Reset/normalize
| | _typography.scss # Typography rules
| ... # Etc…
| | _reset.scss # Reset/normalize
| | _typography.scss # Typography fonts imports
| ... # Etc…
|
| components/
| | _buttons.scss # Buttons
| | _carousel.scss # Carousel
| | _cover.scss # Cover
| | _dropdown.scss # Dropdown
| ... # Etc…
| components/ # Components of the UI, each corresponding to a UI block and being usable as a standalone
| | _button.scss
| | _button-group.scss
| | _global-search.scss
| | _quick-create.scss
| ...
|
| layout/
| | _navigation.scss # Navigation
| | _grid.scss # Grid system
| | _header.scss # Header
| | _footer.scss # Footer
| | _sidebar.scss # Sidebar
| | _forms.scss # Forms
| ... # Etc…
| layout/ # Elements of the UI made of several components, making the layout of the app
| | activity-panel/
| | dashboard/
| | object/ # DM object display (details, summary card, ...)
| | tab-container/
| ...
|
|- *application/ # Elements that are not usable as a standalone (like componants and layouts are) and very application (the backoffice) specific
|- *application/ # Elements that are not usable as a standalone (like componants and layouts are) and very application (the backoffice) specific
| |- display-block
| |- tabular-fields
| ...
|
|- *datamodel/ # SCSS / CSS3 variables and CSS classes for PHP classes of the DM that are part of the core (not in a module) and cannot be styled otherwise
|- *datamodel/ # SCSS / CSS3 variables and CSS classes for *PHP* classes of the DM that are part of the core (not in a module) and cannot be styled otherwise
| |- _action.scss
| |- _user.scss
| ...
|
| pages/
| | _home.scss # Home specific styles
| | _contact.scss # Contact specific styles
| ... # Etc…
| pages/ # SCSS / CSS3 variables and CSS classes for HTML elements specific to backoffice pages
| | _base.scss # Base for all backoffice pages
| | _audit.scss # Audit page
| | _csv-import.scss # CSV Import page
| ... # Etc…
|
|- *blocks-integrations # Specific rules for the integration of a block with another one, those kind of rules should never be in the block partial directly
| |- _panel-with-datatable.scss # Changes the negative margins of the datatable so it overlaps the panel's original padding
|- *blocks-integrations # Specific rules for the integration of a UI block with another one, those kind of rules should NEVER be in the block partial directly
| |- alert/
| | |- _alert-with-blocks.scss # How an alert should be displayed when after another block
| |- button/
| | |- _button-with-button.scss # How a button should be displayed when after another button
| | |- _button-with-button-group.scss # How a button should be displayed when before/after a button group
| |- panel/
| | |- _panel-with-blocks.scss # How a panel should be displayed when after another block
| | |- _panel-within-main-content.scss # How a panel becomes sticky when in the main content
| | |- _panel-within-modal.scss # How a panel becomes sticky when in a modal
| |- _tab-container-within-panel.scss # Changes the negative margins of the datatable so it overlaps the panel's original padding
| ...
|
| themes/
| | _theme.scss # Default theme
| | _admin.scss # Admin theme
| ... # Etc…
| | _page-banner.scss # ???
| ... # Etc…
|
|
`- _shame.scss # Shame file, should contain all the ugly hacks (https://sass-guidelin.es/#shame-file)
` main.scss # Main Sass file
|- _fallback.scss # Fallback file, should only contain rules that make standard HTML tags fallback to the style of a custom CSS class
|- _shame.scss # Shame file, should contain all the ugly hacks (https://sass-guidelin.es/#shame-file)
` main.scss # Main Sass file
```
## Usage
@@ -84,7 +117,58 @@ To avoid common errors, files should be imported in the final file in the follow
- Components
- Layout
- \*Application
- \*Datamodel
- Pages
- \*Block integrations
- Themes
- Shame file
- Shame file
# BEM methodology
## Principles
[BEM is a methodology](https://getbem.com/) that helps you to create reusable components and code sharing in frontend development. \
The main idea is to use discriminant classes instead of nested basic selectors for 2 main reasons:
* It's easier to understand the purpose of a specific class when seeing it in the HTML markup of the SCSS file
* It's easier to override a specific class when needed as you don't need to use a selector at least as precise/complex as the one you want to override
In our implementation, we start with the code of the UI block, followed by the sub-element, then the property or modifier. Separation is made of `--` instead of `__`.
## Examples
### Classes and CSS properties example
```scss
// SCSS variables:
// - For CSS properties: CSS class, followed by CSS property
$ibo-button--padding-y: 6px !default;
$ibo-button--padding-x: 9px !default;
$ibo-button--border: 0 !default;
$ibo-button--border-radius: $ibo-border-radius-400 !default;
$ibo-button--box-shadow-bottom: 0px 2px 0px !default;
$ibo-button--box-shadow-top: inset 0px 2px 0px !default;
$ibo-button--label--margin-left: $ibo-spacing-200 !default;
// CSS classes:
.ibo-button {
padding: $ibo-button--padding-y $ibo-button--padding-x;
border: $ibo-button--border;
border-radius: $ibo-button--border-radius;
}
.ibo-button--label {
margin-left: $ibo-button--label--margin-left;
}
```
### States example
```scss
// SCSS variables:
// Same rule as before, but with a `--is-` or `--on--` suffix
$ibo-quick-create--input--padding: $ibo-spacing-0 default;
$ibo-quick-create--input--padding-x--is-opened: $ibo-spacing-300 !default;
$ibo-quick-create--input--padding-y--is-opened: $ibo-spacing-300 !default;
$ibo-quick-create--input--width: $ibo-size-0 !default;
$ibo-quick-create--input--width--is-opened: 245px !default;
$ibo-quick-create--input--background-color: $ibo-color-white-100 !default;
$ibo-quick-create--input--background-color--on-hover: $ibo-color-grey-200 !default;
```

View File

@@ -1,4 +1,4 @@
/*!
/*
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -131,7 +131,12 @@ $ibo-welcome-popup--message-title--margin-bottom: $ibo-spacing-700 !default;
}
.ibo-welcome-popup--message-illustration {
display: flex;
min-width: $ibo-welcome-popup--message-illustration--min-width;
aspect-ratio: 1;
background-size: $ibo-welcome-popup--message-illustration--background-size;
/* Raw svg needs their height overridden in case its defined in one of their attributes */
> svg {
height: auto;
}
}

View File

@@ -3,18 +3,18 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
$ibo-scrollbar--scrollbar-width: 8px !default;
$ibo-scrollbar--scrollbar-height: $ibo-scrollbar--scrollbar-width !default; /* For horizontal scrollbars */
$ibo-scrollbar--scrollbar-track-background-color: $ibo-color-transparent !default;
$ibo-scrollbar--scrollbar-track-border-radius: $ibo-border-radius-500 !default;
$ibo-scrollbar--scrollbar-thumb-background-color: $ibo-color-grey-300 !default;
$ibo-scrollbar--scrollbar-width: $common-scrollbar--scrollbar-width !default;
$ibo-scrollbar--scrollbar-height: $common-scrollbar--scrollbar-height !default; /* For horizontal scrollbars */
$ibo-scrollbar--scrollbar-track-background-color: $common-scrollbar--scrollbar-track-background-color !default;
$ibo-scrollbar--scrollbar-track-border-radius: $common-scrollbar--scrollbar-track-border-radius !default;
$ibo-scrollbar--scrollbar-thumb-background-color: $common-scrollbar--scrollbar-thumb-background-color !default;
$ibo-scrollbar--scrollbar-thumb-border: none !default;
$ibo-scrollbar--scrollbar-thumb-border-radius: $ibo-border-radius-500 !default;
$ibo-scrollbar--scrollbar-thumb-border-radius: $common-scrollbar--scrollbar-thumb-border-radius !default;
$ibo-svg-illustration--fill: $ibo-color-primary-500 !default;
$ibo-svg-illustration--fill: $common-svg-illustration--fill !default;
$ibo-content-block--background-color: $ibo-color-white-100 !default;
$ibo-content-block--border: 1px solid $ibo-color-grey-400 !default;
$ibo-content-block--background-color: $common-content-block--background-color !default;
$ibo-content-block--border: $common-content-block--border !default;
/* CSS variables */
:root{

View File

@@ -4,147 +4,3 @@
*/
/* This is an overload of the default lib. stylesheet to use local fonts instead of the CDN */
@font-face {
font-family: Raleway;
font-weight: 100;
font-style: normal;
font-display: swap;
src: local('Raleway Thin'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-100-normal.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 100;
font-style: italic;
font-display: swap;
src: local('Raleway Thin'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-100-italic.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 200;
font-style: normal;
font-display: swap;
src: local('Raleway ExtraLight'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-200-normal.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 200;
font-style: italic;
font-display: swap;
src: local('Raleway ExtraLight'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-200-italic.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 300;
font-style: normal;
font-display: swap;
src: local('Raleway Light'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-300-normal.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 300;
font-style: italic;
font-display: swap;
src: local('Raleway Light'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-300-italic.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 400;
font-style: normal;
font-display: swap;
src: local('Raleway'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-400-normal.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 400;
font-style: italic;
font-display: swap;
src: local('Raleway'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-400-italic.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 500;
font-style: normal;
font-display: swap;
src: local('Raleway Medium'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-500-normal.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 500;
font-style: italic;
font-display: swap;
src: local('Raleway Medium'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-500-italic.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 600;
font-style: normal;
font-display: swap;
src: local('Raleway SemiBold'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-600-normal.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 600;
font-style: italic;
font-display: swap;
src: local('Raleway SemiBold'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-600-italic.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 700;
font-style: normal;
font-display: swap;
src: local('Raleway'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-700-normal.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 700;
font-style: italic;
font-display: swap;
src: local('Raleway'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-700-italic.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 800;
font-style: normal;
font-display: swap;
src: local('Raleway ExtraBold'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-800-normal.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 800;
font-style: italic;
font-display: swap;
src: local('Raleway ExtraBold'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-800-italic.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 900;
font-style: normal;
font-display: swap;
src: local('Raleway Black'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-900-normal.woff') format('woff'),
}
@font-face {
font-family: Raleway;
font-weight: 900;
font-style: italic;
font-display: swap;
src: local('Raleway Black'),
url($approot-relative + 'node_modules/@fontsource/raleway/files/raleway-all-900-italic.woff') format('woff'),
}

View File

@@ -81,8 +81,8 @@ $ibo-field--enable-bulk--checkbox--margin-left: $ibo-spacing-300 !default;
}
}
/*N°6543 - We need the rule to keep text inside the column when width is defined*/
&[data-attribute-type="AttributeHtml"],
/*N°6543 - We need the rule to keep text inside the column when width is defined */
&[data-attribute-type="AttributeHTML"],
&[data-attribute-type="AttributeText"] {
&[data-attribute-flag-read-only="true"] {
display: grid;
@@ -195,8 +195,6 @@ $ibo-field--enable-bulk--checkbox--margin-left: $ibo-spacing-300 !default;
/* Hack to force a table to fit its container without overflow (see N°2127) */
/* Note that along with with, we now display a "expand" icon on large fields so we can have a better view of its content */
.HTML {
@extend .ibo-vendors-ckeditor--display-content;
table {
table-layout: fixed;
width: 100%;

View File

@@ -4,7 +4,8 @@
*/
/* SCSS variables */
$ibo-navigation-menu--notifications-menu--min-width: 250px;
$ibo-navigation-menu--notifications-menu--min-width: 550px;
$ibo-navigation-menu--notifications-menu--max-width: 90vw;
$ibo-navigation-menu--notifications--item--image--margin-x: 6px !default;
$ibo-navigation-menu--notifications--item--image--margin-y: $ibo-spacing-0 !default;
@@ -13,9 +14,11 @@ $ibo-navigation-menu--notifications--item--image--max-height: 20px !default;
$ibo-navigation-menu--notifications--item--image--border-radius: $ibo-border-radius-full !default;
$ibo-navigation-menu--notifications--item--bottom-text--margin-left: auto !default;
$ibo-navigation-menu--notifications--item--bottom-text--margin-right: auto !default;
$ibo-navigation-menu--notifications--item--content--padding-y: $ibo-spacing-0 !default;
$ibo-navigation-menu--notifications--item--content--padding-x: 14px !default;
$ibo-navigation-menu--notifications--item--content--max-height: 128px !default;
$ibo-navigation-menu--notifications--item--content--img--max-height: 100px !default;
$ibo-navigation-menu--notifications--item--content--img--padding: 5px !default;
@@ -39,13 +42,15 @@ $ibo-navigation-menu--notifications-show-all-multiple--ibo-popover-menu--indicat
$ibo-navigation-menu--notifications-dismiss-all--icon--margin: $ibo-spacing-0 10px $ibo-spacing-0 $ibo-spacing-0 !default;
$ibo-popover-menu--item--no-message--image--svg--width : 100% !default;
$ibo-popover-menu--item--no-message--image--svg--width : 220px !default;
$ibo-popover-menu--item--no-message--image--svg--height : inherit !default;
$ibo-popover-menu--item--no-message--image--svg--padding : 15px !default;
#ibo-navigation-menu--notifications-menu {
flex-flow: column;
min-width: $ibo-navigation-menu--notifications-menu--min-width;
min-width: min(#{$ibo-navigation-menu--notifications-menu--min-width}, 90vw);
max-width: $ibo-navigation-menu--notifications-menu--max-width;
.ibo-navigation-menu--notifications--messages-section {
overflow: auto;
}
@@ -76,9 +81,14 @@ $ibo-popover-menu--item--no-message--image--svg--padding : 15px !default;
float: right;
align-self: center;
margin-left: $ibo-navigation-menu--notifications--item--bottom-text--margin-left;
margin-right: $ibo-navigation-menu--notifications--item--bottom-text--margin-right;
}
.ibo-navigation-menu--notifications--item--content {
flex-grow: 1;
padding: $ibo-navigation-menu--notifications--item--content--padding-y $ibo-navigation-menu--notifications--item--content--padding-x;
max-height: $ibo-navigation-menu--notifications--item--content--max-height;
overflow-y: auto;
white-space: normal; /* Overload rule from popupover menu item so that content wraps instead of being on a gigantic line */
img {
max-height: $ibo-navigation-menu--notifications--item--content--img--max-height;
@@ -88,6 +98,7 @@ $ibo-popover-menu--item--no-message--image--svg--padding : 15px !default;
.ibo-navigation-menu--notifications-item {
display: flex;
flex-direction: row;
cursor: pointer;
.ibo-navigation-menu--notifications--item--content a {
@extend %ibo-hyperlink-forced-colors;
@@ -100,6 +111,8 @@ $ibo-popover-menu--item--no-message--image--svg--padding : 15px !default;
border: $ibo-navigation-menu--notifications--item--new-message-indicator--border;
border-radius: $ibo-navigation-menu--notifications--item--new-message-indicator--border-radius;
margin-top: $ibo-navigation-menu--notifications--item--new-message-indicator--margin-top;
flex-shrink: 0; /* Avoid indicator to be shrinked into a non round shape */
&.ibo-is-priority-1{
background-color: $ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--background-color;
border: $ibo-navigation-menu--notifications--item--new-message-indicator--is-priority-1--border;
@@ -134,8 +147,7 @@ $ibo-popover-menu--item--no-message--image--svg--padding : 15px !default;
text-align: center;
}
.ibo-popover-menu--item--no-message--image > svg {
display: flex;
width: $ibo-popover-menu--item--no-message--image--svg--width;
max-width: $ibo-popover-menu--item--no-message--image--svg--width;
height: $ibo-popover-menu--item--no-message--image--svg--height;
padding: $ibo-popover-menu--item--no-message--image--svg--padding;
}

View File

@@ -39,6 +39,8 @@ $ibo-quick-create--compartment-title--line-spacing: $ibo-spacing-300 !default;
$ibo-quick-create--compartment-content--text-color: $ibo-color-grey-900 !default;
$ibo-quick-create--compartment-results--element--max-height: unset !default;
$ibo-quick-create--compartment-element--padding-x: $ibo-spacing-300 !default;
$ibo-quick-create--compartment-element--padding-y: $ibo-spacing-200 !default;
$ibo-quick-create--compartment-element--margin-x: -1 * $ibo-quick-create--compartment-element--padding-x !default;
@@ -49,6 +51,7 @@ $ibo-quick-create--compartment-element-image--margin-right: $ibo-spacing-300 !de
$ibo-quick-create--compartment-element-image--width: 20px !default;
$ibo-quick-create--compartment-results--container--width: 100% !important !default;
$ibo-quick-create--compartment-results--container--background-color: transparent !default;
$ibo-quick-create--compartment--placeholder-image--margin-top: $ibo-spacing-600 !default;
$ibo-quick-create--compartment--placeholder-image--margin-bottom: $ibo-spacing-500 !default;
@@ -219,28 +222,38 @@ $ibo-quick-create--compartment--placeholder-hint--text-color: $ibo-color-grey-70
}
.ibo-quick-create--compartment-results--container{
position: static;
width: $ibo-quick-create--compartment-results--container--width;
background: $ibo-quick-create--compartment-results--container--background-color;
border: none;
box-shadow: none;
}
.ibo-quick-create--compartment-results--element > .option{
padding: $ibo-quick-create--compartment-element--padding-y $ibo-quick-create--compartment-element--padding-x;
margin-left: $ibo-quick-create--compartment-element--margin-x;
margin-right: $ibo-quick-create--compartment-element--margin-x;
color: inherit;
.ibo-quick-create--compartment-results--element {
overflow: unset;
max-height: $ibo-quick-create--compartment-results--element--max-height;
&> .option {
padding: $ibo-quick-create--compartment-element--padding-y $ibo-quick-create--compartment-element--padding-x;
margin-left: $ibo-quick-create--compartment-element--margin-x;
margin-right: $ibo-quick-create--compartment-element--margin-x;
color: inherit;
@extend %ibo-text-truncated-with-ellipsis;
@extend %ibo-text-truncated-with-ellipsis;
&.active{
background-color: $ibo-quick-create--compartment-element--background-color--is-active;
border-radius: $ibo-quick-create--compartment-element--border-radius--is-active;
}
&:hover{
cursor: pointer;
@extend a;
}
&.active {
background-color: $ibo-quick-create--compartment-element--background-color--is-active;
border-radius: $ibo-quick-create--compartment-element--border-radius--is-active;
}
.highlight{
font-weight: bold;
}
&:hover {
cursor: pointer;
@extend a;
}
.highlight {
font-weight: bold;
}
}
}
.ibo-quick-create--compartment--placeholder{

View File

@@ -124,7 +124,7 @@ $ibo-fieldsorter--selected--background-color: $ibo-color-blue-200 !default;
justify-content: end;
}
/* N°6543 - We need the rule to keep text inside the column when width is defined */
> [data-attribute-type="AttributeHtml"],
> [data-attribute-type="AttributeHTML"],
> [data-attribute-type="AttributeText"] {
max-width: 100%;
overflow: auto;

View File

@@ -41,8 +41,8 @@ $ibo-input-select--select-wrapper--action-button--margin-right: 20px !default;
$ibo-input-select--action-button--padding-left: 6px !default;
$ibo-input-select--action-button--padding-right: $ibo-spacing-100 !default;
$ibo-input-select--autocomplete-item-image--size: 25px !default;
$ibo-input-select--autocomplete-item-image--margin-right: 0.5rem !default;
$ibo-input-select--autocomplete-item-image--size: 30px !default;
$ibo-input-select--autocomplete-item-image--margin-right: 0.7rem !default;
$ibo-input-select--autocomplete-item-image--background-color: $ibo-color-blue-100 !default;
$ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-600 !default;
@@ -233,6 +233,7 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60
}
.ibo-input-select--autocomplete-item-txt {
@extend %ibo-font-ral-nor-100;
@extend %ibo-text-truncated-with-ellipsis;
}

View File

@@ -14,8 +14,8 @@ $ibo-dm-class--Action--complementary-color: $ibo-color-white-100 !default;
.ibo-dm-class--Action {
--ibo-main-color: #{$ibo-dm-class--Action--main-color};
--ibo-main-color--100: #{ibo-adjust-alpha(ibo-adjust-lightness($ibo-dm-class--Action--main-color, $ibo-color-base-lightness-100), $ibo-color-base-opacity-for-lightness-100)};
--ibo-main-color--900: #{ibo-adjust-alpha(ibo-adjust-lightness($ibo-dm-class--Action--main-color, $ibo-color-base-lightness-900), $ibo-color-base-opacity-for-lightness-900)};
--ibo-main-color--100: #{common-adjust-alpha(common-adjust-lightness($ibo-dm-class--Action--main-color, $ibo-color-base-lightness-100), $ibo-color-base-opacity-for-lightness-100)};
--ibo-main-color--900: #{common-adjust-alpha(common-adjust-lightness($ibo-dm-class--Action--main-color, $ibo-color-base-lightness-900), $ibo-color-base-opacity-for-lightness-900)};
--ibo-complementary-color: #{$ibo-dm-class--Action--complementary-color};
}
@@ -35,8 +35,8 @@ $ibo-dm-enum--Action-status-enabled--complementary-color: $ibo-lifecycle-active-
.ibo-dm-enum--Action-status-enabled {
--ibo-main-color: #{$ibo-dm-enum--Action-status-enabled--main-color};
--ibo-main-color--100: #{ibo-adjust-alpha(ibo-adjust-lightness($ibo-dm-enum--Action-status-enabled--main-color, $ibo-color-base-lightness-100), $ibo-color-base-opacity-for-lightness-100)};
--ibo-main-color--900: #{ibo-adjust-alpha(ibo-adjust-lightness($ibo-dm-enum--Action-status-enabled--main-color, $ibo-color-base-lightness-900), $ibo-color-base-opacity-for-lightness-900)};
--ibo-main-color--100: #{common-adjust-alpha(common-adjust-lightness($ibo-dm-enum--Action-status-enabled--main-color, $ibo-color-base-lightness-100), $ibo-color-base-opacity-for-lightness-100)};
--ibo-main-color--900: #{common-adjust-alpha(common-adjust-lightness($ibo-dm-enum--Action-status-enabled--main-color, $ibo-color-base-lightness-900), $ibo-color-base-opacity-for-lightness-900)};
--ibo-complementary-color: #{$ibo-dm-enum--Action-status-enabled--complementary-color};
}
@@ -56,8 +56,8 @@ $ibo-dm-enum--Action-status-disabled--complementary-color: $ibo-lifecycle-frozen
.ibo-dm-enum--Action-status-disabled {
--ibo-main-color: #{$ibo-dm-enum--Action-status-disabled--main-color};
--ibo-main-color--100: #{ibo-adjust-alpha(ibo-adjust-lightness($ibo-dm-enum--Action-status-disabled--main-color, $ibo-color-base-lightness-100), $ibo-color-base-opacity-for-lightness-100)};
--ibo-main-color--900: #{ibo-adjust-alpha(ibo-adjust-lightness($ibo-dm-enum--Action-status-disabled--main-color, $ibo-color-base-lightness-900), $ibo-color-base-opacity-for-lightness-900)};
--ibo-main-color--100: #{common-adjust-alpha(common-adjust-lightness($ibo-dm-enum--Action-status-disabled--main-color, $ibo-color-base-lightness-100), $ibo-color-base-opacity-for-lightness-100)};
--ibo-main-color--900: #{common-adjust-alpha(common-adjust-lightness($ibo-dm-enum--Action-status-disabled--main-color, $ibo-color-base-lightness-900), $ibo-color-base-opacity-for-lightness-900)};
--ibo-complementary-color: #{$ibo-dm-enum--Action-status-disabled--complementary-color};
}
@@ -78,8 +78,8 @@ $ibo-dm-enum--Action-status-test--complementary-color: $ibo-lifecycle-inactive-s
.ibo-dm-enum--Action-status-test {
--ibo-main-color: #{$ibo-dm-enum--Action-status-test--main-color};
--ibo-main-color--100: #{ibo-adjust-alpha(ibo-adjust-lightness($ibo-dm-enum--Action-status-test--main-color, $ibo-color-base-lightness-100), $ibo-color-base-opacity-for-lightness-100)};
--ibo-main-color--900: #{ibo-adjust-alpha(ibo-adjust-lightness($ibo-dm-enum--Action-status-test--main-color, $ibo-color-base-lightness-900), $ibo-color-base-opacity-for-lightness-900)};
--ibo-main-color--100: #{common-adjust-alpha(common-adjust-lightness($ibo-dm-enum--Action-status-test--main-color, $ibo-color-base-lightness-100), $ibo-color-base-opacity-for-lightness-100)};
--ibo-main-color--900: #{common-adjust-alpha(common-adjust-lightness($ibo-dm-enum--Action-status-test--main-color, $ibo-color-base-lightness-900), $ibo-color-base-opacity-for-lightness-900)};
--ibo-complementary-color: #{$ibo-dm-enum--Action-status-test--complementary-color};
}

View File

@@ -14,8 +14,8 @@ $ibo-dm-class--User--complementary-color: $ibo-color-white-100 !default;
.ibo-dm-class--User {
--ibo-main-color: #{$ibo-dm-class--User--main-color};
--ibo-main-color--100: #{ibo-adjust-alpha(ibo-adjust-lightness($ibo-dm-class--User--main-color, $ibo-color-base-lightness-100), $ibo-color-base-opacity-for-lightness-100)};
--ibo-main-color--900: #{ibo-adjust-alpha(ibo-adjust-lightness($ibo-dm-class--User--main-color, $ibo-color-base-lightness-900), $ibo-color-base-opacity-for-lightness-900)};
--ibo-main-color--100: #{common-adjust-alpha(common-adjust-lightness($ibo-dm-class--User--main-color, $ibo-color-base-lightness-100), $ibo-color-base-opacity-for-lightness-100)};
--ibo-main-color--900: #{common-adjust-alpha(common-adjust-lightness($ibo-dm-class--User--main-color, $ibo-color-base-lightness-900), $ibo-color-base-opacity-for-lightness-900)};
--ibo-complementary-color: #{$ibo-dm-class--User--complementary-color};
}
.ibo-dm-class-alt--User {
@@ -34,8 +34,8 @@ $ibo-dm-enum--User-status-enabled--complementary-color: $ibo-lifecycle-active-st
.ibo-dm-enum--User-status-enabled {
--ibo-main-color: #{$ibo-dm-enum--User-status-enabled--main-color};
--ibo-main-color--100: #{ibo-adjust-alpha(ibo-adjust-lightness($ibo-dm-enum--User-status-enabled--main-color, $ibo-color-base-lightness-100), $ibo-color-base-opacity-for-lightness-100)};
--ibo-main-color--900: #{ibo-adjust-alpha(ibo-adjust-lightness($ibo-dm-enum--User-status-enabled--main-color, $ibo-color-base-lightness-900), $ibo-color-base-opacity-for-lightness-900)};
--ibo-main-color--100: #{common-adjust-alpha(common-adjust-lightness($ibo-dm-enum--User-status-enabled--main-color, $ibo-color-base-lightness-100), $ibo-color-base-opacity-for-lightness-100)};
--ibo-main-color--900: #{common-adjust-alpha(common-adjust-lightness($ibo-dm-enum--User-status-enabled--main-color, $ibo-color-base-lightness-900), $ibo-color-base-opacity-for-lightness-900)};
--ibo-complementary-color: #{$ibo-dm-enum--User-status-enabled--complementary-color};
}
.ibo-dm-enum-alt--User-status-enabled {
@@ -54,8 +54,8 @@ $ibo-dm-enum--User-status-disabled--complementary-color: $ibo-lifecycle-inactive
.ibo-dm-enum--User-status-disabled {
--ibo-main-color: #{$ibo-dm-enum--User-status-disabled--main-color};
--ibo-main-color--100: #{ibo-adjust-alpha(ibo-adjust-lightness($ibo-dm-enum--User-status-disabled--main-color, $ibo-color-base-lightness-100), $ibo-color-base-opacity-for-lightness-100)};
--ibo-main-color--900: #{ibo-adjust-alpha(ibo-adjust-lightness($ibo-dm-enum--User-status-disabled--main-color, $ibo-color-base-lightness-900), $ibo-color-base-opacity-for-lightness-900)};
--ibo-main-color--100: #{common-adjust-alpha(common-adjust-lightness($ibo-dm-enum--User-status-disabled--main-color, $ibo-color-base-lightness-100), $ibo-color-base-opacity-for-lightness-100)};
--ibo-main-color--900: #{common-adjust-alpha(common-adjust-lightness($ibo-dm-enum--User-status-disabled--main-color, $ibo-color-base-lightness-900), $ibo-color-base-opacity-for-lightness-900)};
--ibo-complementary-color: #{$ibo-dm-enum--User-status-disabled--complementary-color};
}
.ibo-dm-enum-alt--User-status-disabled {

View File

@@ -18,7 +18,6 @@ $ibo-caselog-entry--main-information--decoration--width: 3px !default;
display: none;
}
.ibo-activity-entry--main-information-content{
@extend .ibo-vendors-ckeditor--display-content;
}
/* Highlight color */

View File

@@ -2,7 +2,7 @@
* @copyright Copyright (C) 2010-2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
@import "../common/main";
@import "utils/all";
@import "vendors/all";
@import "base/all";

View File

@@ -4,7 +4,7 @@
*/
$ibo-notifications--view-all--container--grid-gap: $ibo-spacing-600 !default;
$ibo-notifications--view-all--container--object-summary--panel--body--max-height: unset !default;
$ibo-notifications--view-all--container--object-summary--panel--body--max-height: 400px !default;
$ibo-notifications--view-all--item--unread--highlight--background-color: $ibo-color-red-600 !default;
$ibo-notifications--view-all--item--read--highlight--background-color: $ibo-color-grey-200 !default;

View File

@@ -7,28 +7,20 @@
* Adjust the lightness of $sColor to the absolute $fTargetLightness value.
* It is different than lighten() / darken() that shift the current lightness by X%
*
* @deprecated Use common-adjust-lightness() instead
* @return Modified color value in HSLA format
*/
@function ibo-adjust-lightness($sColor, $fTargetLightness) {
$iHue: hue($sColor);
$fSaturation: saturation($sColor);
$fLightness: lightness($sColor);
$fAlpha: alpha($sColor);
@return hsla($iHue, $fSaturation, $fTargetLightness, $fAlpha);
@return common-adjust-lightness($sColor, $fTargetLightness);
}
/**
* Adjust the alpha chanel (opacity) of $sColor to the absolute $fTargetAlpha value.
* It is different than opacify() / transparentize() that shift the current alpha value by X%
*
* @deprecated Use common-adjust-alpha() instead
* @return Modified color value in HSLA format
*/
@function ibo-adjust-alpha($sColor, $fTargetAlpha) {
$iHue: hue($sColor);
$fSaturation: saturation($sColor);
$fLightness: lightness($sColor);
$fAlpha: alpha($sColor);
@return hsla($iHue, $fSaturation, $fLightness, $fTargetAlpha);
@return common-adjust-alpha($sColor, $fTargetAlpha);
}

View File

@@ -3,9 +3,9 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
$ibo-class-icon--small--size: 32px !default;
$ibo-class-icon--medium--size: 48px !default;
$ibo-class-icon--large--size: 64px !default;
$ibo-class-icon--small--size: $common-class-icon--small--size !default;
$ibo-class-icon--medium--size: $common-class-icon--medium--size !default;
$ibo-class-icon--large--size: $common-class-icon--large--size !default;
.ibo-class-icon{
&.ibo-is-small{

View File

@@ -3,24 +3,7 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
$ibo-text-colors: (
'primary': $ibo-color-primary-800,
'secondary': $ibo-color-secondary-800,
'neutral': $ibo-color-secondary-800,
'information': $ibo-color-information-800,
'success': $ibo-color-success-800,
'failure': $ibo-color-danger-800,
'warning': $ibo-color-warning-800,
'danger': $ibo-color-danger-800,
'grey' : $ibo-color-grey-800,
'blue-grey': $ibo-color-blue-grey-800,
'blue': $ibo-color-blue-800,
'cyan': $ibo-color-cyan-800,
'green': $ibo-color-green-800,
'orange': $ibo-color-orange-800,
'red': $ibo-color-red-800,
'pink': $ibo-color-pink-800,
) !default;
$ibo-text-colors: $common-text-colors !default;
@each $sColor, $sColorValue in $ibo-text-colors {
.ibo-text.ibo-is-#{$sColor} {

View File

@@ -8,36 +8,3 @@
/* To use it, simply "@extend %fa-regular-base" in a rule and put the desired icon "content: '\f054'" */
/******************************************************************************************************************************/
%fa-regular-base{
font-family: "Font Awesome 5 Free";
font-weight: 400;
-webkit-font-smoothing: antialiased;
display: inline-block;
font-style: normal;
font-variant: normal;
text-rendering: auto;
line-height: 1;
}
%fa-solid-base{
font-family: "Font Awesome 5 Free";
font-weight: 900;
-webkit-font-smoothing: antialiased;
display: inline-block;
font-style: normal;
font-variant: normal;
text-rendering: auto;
line-height: 1;
}
%fc-regular-base{
display: inline-block;
font-family: CombodoRegular;
font-style: normal;
font-variant: normal;
font-weight: normal;
text-rendering: auto;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

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

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