Compare commits

...

192 Commits

Author SHA1 Message Date
Anne-Cath
891a7bcbdd N°7326 - JS error in editing object when a tab with list is deleted 2025-03-17 11:17:25 +01:00
Anne-Cath
60e54e6160 N°7326 - JS error in editing object when a tab with list is deleted 2025-03-17 11:17:20 +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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
f6298370a7 Adding new modules 2025-02-04 11:56:32 +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
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
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
Eric Espie
a3c911e93e N°8139 - Avoid double writing in lifecycle action 2025-01-29 09:32:21 +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
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
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
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
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
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
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
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
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
95dbe4c859 N°7873 - ci fix remove useless require_once 2025-01-14 07:52:07 +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
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
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
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
11fc958a7b 🐛 N°6284 - Add data-object-key 2024-12-31 15:01:36 +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
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
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
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
f0685e33e1 N°8031 - Add robustness and fix SF parameter binding being ambiguous in extensions bricks 2024-12-19 15:14:46 +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
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
jf-cbd
2dffab9ca0 🌐 Update ZH CN dictionaries 2024-12-18 10:39:25 +01:00
jf-cbd
1cbfa4f1b1 🌐 Update ZH CN dictionaries 2024-12-18 10:11:52 +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
v-dumas
56d3ac668c N°8047 - SuperUser profile moved under iTop Community 3.2.1 2024-12-17 16:31:59 +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
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
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
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
odain
ce187550f6 N°7750 - Fix display regression 2024-12-12 12:21:57 +01:00
Eric Espie
5d15a08824 🥅 Fast setup error display 2024-12-12 09:55:16 +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
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
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
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
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
jf-cbd
a797878b17 Security hardening 2024-12-03 09:54:31 +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
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
odain
d7a8d335d5 N°7446 - Fix in CI environment when class already loaded 2024-11-29 12:22:38 +01:00
odain
bd1d447677 N°7446 - Fix in CI environment 2024-11-29 12:08:16 +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
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
Lars Kaltefleiter
596e26a96f 🌐 N°7932 Translations - British English (#677)
* Translation into EN GB
2024-11-18 14:38:53 +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
e9f16935b6 N°6613 - Fix user picture pushing invalid data 2024-11-13 13:20:27 +01:00
1369 changed files with 136199 additions and 86731 deletions

View File

@@ -93,6 +93,12 @@ gitGraph
checkout support/3.2 checkout support/3.2
commit id: "2024-06-25" tag: "3.2.0-beta1" type: REVERSE commit id: "2024-06-25" tag: "3.2.0-beta1" type: REVERSE
commit id: "2024-08-07" tag: "3.2.0" 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). To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start).

View File

@@ -106,6 +106,7 @@ We would like to give a special thank you 🤗 to the people from the community
- Raenker, Martin - Raenker, Martin
- Roháč, Richard (a.k.a [@RohacRichard](https://github.com/RohacRichard)) - Roháč, Richard (a.k.a [@RohacRichard](https://github.com/RohacRichard))
- Rosenke, Stephan - Rosenke, Stephan
- Rossi, Tommaso (a.k.a [@tomrss](https://www.github.com/tomrss))
- Rudner, Björn (a.k.a [@rudnerbjoern](https://github.com/rudnerbjoern)) - 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) - Šafránek, Jaroslav (a.k.a [jkcinik](https://sourceforge.net/u/jkcinik/profile/) on SourceForge)
- Seki, Shoji - Seki, Shoji
@@ -115,6 +116,7 @@ We would like to give a special thank you 🤗 to the people from the community
- Tarjányi, Csaba (a.k.a [@tacsaby](https://github.com/tacsaby)) - Tarjányi, Csaba (a.k.a [@tacsaby](https://github.com/tacsaby))
- Tulio, Marco - Tulio, Marco
- Turrubiates, Miguel - Turrubiates, Miguel
- Vlk, Karel (a.k.a [@vlk-charles](https://www.github.com/vlk-charles))
### Aliases ### Aliases

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 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 // We have to answer NO for objects shared for reading purposes
if (self::HasSharing()) if (self::HasSharing() && SharedObject::GetSharedClassProperties($sClass)) {
{ // This class is shared, GetSelectFilter may allow some objects for read only
$aClassProps = SharedObject::GetSharedClassProperties($sClass); // But currently we are checking whether the objects might be written...
if ($aClassProps) // Let's exclude the objects based on the relevant criteria
{
// 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
$sOrgAttCode = self::GetOwnerOrganizationAttCode($sClass); // Use $oInstanceSet only if sClass is the main class
if (!is_null($sOrgAttCode)) if (!is_a($oInstanceSet->GetClass(), $sClass, true)) {
{ /** @var \DBObjectSet $oInstanceSet */
$aUserOrgs = $this->GetUserOrgs($oUser, $sClass); 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()]);
if (!is_null($aUserOrgs) && count($aUserOrgs) > 0) }
{ $sOrgAttCode = self::GetOwnerOrganizationAttCode($sClass);
$iCountNO = 0; if (!is_null($sOrgAttCode)) {
$iCountYES = 0; $aUserOrgs = $this->GetUserOrgs($oUser, $sClass);
$oInstanceSet->Rewind(); if (!is_null($aUserOrgs) && count($aUserOrgs) > 0) {
while($oObject = $oInstanceSet->Fetch()) $iCountNO = 0;
{ $iCountYES = 0;
$iOrg = $oObject->Get($sOrgAttCode); $oInstanceSet->Rewind();
if (in_array($iOrg, $aUserOrgs)) while ($oObject = $oInstanceSet->Fetch()) {
{ $iOrg = $oObject->Get($sOrgAttCode);
$iCountYES++; if (in_array($iOrg, $aUserOrgs)) {
} $iCountYES++;
else } else {
{ $iCountNO++;
$iCountNO++;
}
}
if ($iCountNO == 0)
{
$iPermission = UR_ALLOWED_YES;
}
elseif ($iCountYES == 0)
{
$iPermission = UR_ALLOWED_NO;
}
else
{
$iPermission = UR_ALLOWED_DEPENDS;
} }
} }
if ($iCountNO == 0) {
$iPermission = UR_ALLOWED_YES;
} elseif ($iCountYES == 0) {
$iPermission = UR_ALLOWED_NO;
} else {
$iPermission = UR_ALLOWED_DEPENDS;
}
} }
} }
} }
@@ -982,4 +971,3 @@ class UserRightsProfile extends UserRightsAddOnAPI
UserRights::SelectModule('UserRightsProfile'); UserRights::SelectModule('UserRightsProfile');
?>

View File

@@ -1711,6 +1711,11 @@ interface iRestServiceProvider
public function ExecOperation($sVersion, $sVerb, $aParams); public function ExecOperation($sVersion, $sVerb, $aParams);
} }
interface iRestInputSanitizer
{
public function SanitizeJsonInput(string $sJsonInput): string;
}
/** /**
* Minimal REST response structure. Derive this structure to add response data and error codes. * Minimal REST response structure. Derive this structure to add response data and error codes.
* *
@@ -1802,6 +1807,14 @@ class RestResult
* @api * @api
*/ */
public $message; public $message;
/**
* Sanitize the content of this result to hide sensitive information
*/
public function SanitizeContent()
{
// The default implementation does nothing
}
} }
/** /**

File diff suppressed because it is too large Load Diff

View File

@@ -1266,13 +1266,12 @@ EOF
$sOkButtonLabel = Dict::S('UI:Button:Save'); $sOkButtonLabel = Dict::S('UI:Button:Save');
$sCancelButtonLabel = Dict::S('UI:Button:Cancel'); $sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$sId = utils::HtmlEntities($this->sId); $sId = json_encode($this->sId);
$sLayoutClass = utils::HtmlEntities($this->sLayoutClass); $sLayoutClass = json_encode($this->sLayoutClass);
$sAutoReload = $this->bAutoReload ? 'true' : 'false'; $sAutoReload = $this->bAutoReload ? 'true' : 'false';
$sAutoReloadSec = (string) $this->iAutoReloadSec; $sAutoReloadSec = (string) $this->iAutoReloadSec;
$sTitle = utils::HtmlEntities($this->sTitle); $sTitle = json_encode($this->sTitle);
$sFile = utils::HtmlEntities($this->GetDefinitionFile()); $sFile = json_encode($this->GetDefinitionFile());
$sFileForJS = json_encode($this->GetDefinitionFile());
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php'; $sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
$sReloadURL = $this->GetReloadURL(); $sReloadURL = $this->GetReloadURL();
@@ -1328,15 +1327,15 @@ $('#dashboard_editor').dialog({
}); });
$('#dashboard_editor .ui-layout-center').runtimedashboard({ $('#dashboard_editor .ui-layout-center').runtimedashboard({
dashboard_id: '$sId', dashboard_id: $sId,
layout_class: '$sLayoutClass', layout_class: $sLayoutClass,
title: '$sTitle', title: $sTitle,
auto_reload: $sAutoReload, auto_reload: $sAutoReload,
auto_reload_sec: $sAutoReloadSec, auto_reload_sec: $sAutoReloadSec,
submit_to: '$sUrl', 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_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'} new_dashlet_parameters: {operation: 'new_dashlet'}
}); });

View File

@@ -238,6 +238,10 @@ The object can be modified.]]></description>
<description>Creation flag</description> <description>Creation flag</description>
<type>boolean</type> <type>boolean</type>
</event_datum> </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"> <event_datum id="debug_info">
<description>Debug string</description> <description>Debug string</description>
<type>string</type> <type>string</type>
@@ -263,6 +267,10 @@ Call $this->AddCheckWarning($sWarningMessage) to display a warning.
<description>Creation flag</description> <description>Creation flag</description>
<type>boolean</type> <type>boolean</type>
</event_datum> </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"> <event_datum id="debug_info">
<description>Debug string</description> <description>Debug string</description>
<type>string</type> <type>string</type>
@@ -290,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> <description><![CDATA[For updates, the list of changes done during this operation]]></description>
<type>array</type> <type>array</type>
</event_datum> </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"> <event_datum id="debug_info">
<description>Debug string</description> <description>Debug string</description>
<type>string</type> <type>string</type>
@@ -420,6 +432,14 @@ The only action allowed is to deny transitions with $this->DenyTransition($sTran
<description>The object inserted</description> <description>The object inserted</description>
<type>DBObject</type> <type>DBObject</type>
</event_datum> </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"> <event_datum id="debug_info">
<description>Debug string</description> <description>Debug string</description>
<type>string</type> <type>string</type>

View File

@@ -2024,8 +2024,8 @@ class MenuBlock extends DisplayBlock
$sSelectedClassName = MetaModel::GetName($sSelectedClass); $sSelectedClassName = MetaModel::GetName($sSelectedClass);
// Check rights on class // Check rights on class
$bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sSelectedClass)) && UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_MODIFY, $oSet) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject')); $bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sSelectedClass)) && UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_MODIFY) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject'));
$bIsBulkDeleteAllowed = (bool) UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_DELETE, $sSelectedClass); $bIsBulkDeleteAllowed = (bool) UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_DELETE);
// Refine filter on selected class so bullk actions occur on the right class // Refine filter on selected class so bullk actions occur on the right class
$oSelectedClassFilter = $this->GetFilter()->DeepClone(); $oSelectedClassFilter = $this->GetFilter()->DeepClone();

View File

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

View File

@@ -90,6 +90,27 @@ class UILinksWidgetDirect
return ConsoleBlockRenderer::RenderBlockTemplateInPage($oPage, $oBlock); return ConsoleBlockRenderer::RenderBlockTemplateInPage($oPage, $oBlock);
} }
/**
* @param WebPage $oPage
* @param $oValue
* @param $aArgs
* @param $sFormPrefix
* @param $oCurrentObj
* @return BlockIndirectLinkSetEditTable
* @throws ArchivedObjectException
* @throws ConfigException
* @throws CoreException
* @throws CoreUnexpectedValue
* @since 3.2
*/
public function GetBlock(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj)
{
$oBlock = new BlockDirectLinkSetEditTable($this, $this->sInputid);
$oBlock->InitTable($oPage, $oValue, $sFormPrefix, $oCurrentObj);
return $oBlock;
}
/** /**
* @param WebPage $oPage * @param WebPage $oPage
* @param string $sProposedRealClass * @param string $sProposedRealClass

View File

@@ -148,6 +148,27 @@ class UILinksWidget
return ConsoleBlockRenderer::RenderBlockTemplateInPage($oPage, $oBlock); return ConsoleBlockRenderer::RenderBlockTemplateInPage($oPage, $oBlock);
} }
/**
* @param WebPage $oPage
* @param $oValue
* @param $aArgs
* @param $sFormPrefix
* @param $oCurrentObj
* @return BlockIndirectLinkSetEditTable
* @throws ArchivedObjectException
* @throws ConfigException
* @throws CoreException
* @throws CoreUnexpectedValue
* @since 3.2
*/
public function GetBlock(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj): BlockIndirectLinkSetEditTable
{
$oBlock = new BlockIndirectLinkSetEditTable($this);
$oBlock->InitTable($oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $this->m_aTableConfig);
return $oBlock;
}
/** /**
* @param WebPage $oPage * @param WebPage $oPage
* @param DBObject $oCurrentObj * @param DBObject $oCurrentObj

View File

@@ -249,9 +249,9 @@ class appUserPreferences extends DBObject
( (
"category" => "gui", "category" => "gui",
"key_type" => "autoincrement", "key_type" => "autoincrement",
"name_attcode" => "userid", "name_attcode" => "login",
"state_attcode" => "", "state_attcode" => "",
"reconc_keys" => array("userid"), "reconc_keys" => array("userid","login"),
"db_table" => "priv_app_preferences", "db_table" => "priv_app_preferences",
"db_key_field" => "id", "db_key_field" => "id",
"db_finalclass_field" => "", "db_finalclass_field" => "",
@@ -260,8 +260,10 @@ class appUserPreferences extends DBObject
MetaModel::Init_Params($aParams); 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 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 AttributePropertySet("preferences", array("allowed_values"=>null, "sql"=>"preferences", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_SetZListItems('list', array('preferences',)); MetaModel::Init_AddAttribute(new AttributeExternalField("org_id", array("allowed_values" => null, "extkey_attcode" => 'userid', "target_attcode" => "org_id")));
MetaModel::Init_SetZListItems('default_search', array('userid')); 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

@@ -180,6 +180,7 @@ class utils
*/ */
private static $sAbsoluteUrlAppRootCache = null; private static $sAbsoluteUrlAppRootCache = null;
protected static function LoadParamFile($sParamFile) protected static function LoadParamFile($sParamFile)
{ {
if (!file_exists($sParamFile)) { if (!file_exists($sParamFile)) {
@@ -418,11 +419,26 @@ class utils
* @since 2.7.7, 3.0.2, 3.1.0 N°4899 - new 'url' filter * @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 use the utils::ENUM_SANITIZATION_* const
* @since 2.7.10 N°6606 new case for ENUM_SANITIZATION_FILTER_PHP_CLASS * @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 * @link https://www.php.net/manual/en/filter.filters.sanitize.php PHP sanitization filters
*/ */
protected static function Sanitize_Internal($value, $sSanitizationFilter) 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) switch ($sSanitizationFilter)
{ {
case static::ENUM_SANITIZATION_FILTER_INTEGER: case static::ENUM_SANITIZATION_FILTER_INTEGER:
@@ -453,52 +469,36 @@ class utils
case static::ENUM_SANITIZATION_FILTER_PARAMETER: case static::ENUM_SANITIZATION_FILTER_PARAMETER:
case static::ENUM_SANITIZATION_FILTER_FIELD_NAME: case static::ENUM_SANITIZATION_FILTER_FIELD_NAME:
case static::ENUM_SANITIZATION_FILTER_TRANSACTION_ID: case static::ENUM_SANITIZATION_FILTER_TRANSACTION_ID:
if (is_array($value)) switch ($sSanitizationFilter)
{ {
$retValue = array(); case static::ENUM_SANITIZATION_FILTER_TRANSACTION_ID:
foreach ($value as $key => $val) // 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
$retValue[$key] = self::Sanitize_Internal($val, $sSanitizationFilter); // recursively check arrays // - See N°1835
if ($retValue[$key] === false) // - 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_=-]*$/')));
$retValue = false; break;
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_ROUTE: case static::ENUM_SANITIZATION_FILTER_ROUTE:
case static::ENUM_SANITIZATION_FILTER_OPERATION: case static::ENUM_SANITIZATION_FILTER_OPERATION:
// - Routes should be of the "controller_namespace_code.controller_method_name" form // - 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 // - 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_-]*$/'))); $retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[\.A-Za-z0-9_-]*$/')));
break; break;
case static::ENUM_SANITIZATION_FILTER_PARAMETER: 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' $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) // Characters are used in serialized filters (starting 2.5, only the url encoded versions are presents, but the "=" is kept for BC)
break; break;
case static::ENUM_SANITIZATION_FILTER_FIELD_NAME: case static::ENUM_SANITIZATION_FILTER_FIELD_NAME:
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[A-Za-z0-9_]+(->[A-Za-z0-9_]+)*$/'))); // att_code or att_code->name or AttCode->Name or AttCode->Key2->Name $retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[A-Za-z0-9_]+(->[A-Za-z0-9_]+)*$/'))); // att_code or att_code->name or AttCode->Name or AttCode->Key2->Name
break; break;
case static::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM: case static::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM:
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[ A-Za-z0-9_=%:+-]*$/'))); $retValue = filter_var($value, FILTER_VALIDATE_REGEXP, array("options" => array("regexp" => '/^[ A-Za-z0-9_=%:+-]*$/')));
break; break;
}
} }
break; break;
@@ -1451,9 +1451,12 @@ class utils
* @return string A path to a folder into which any module can store cache data * @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 * 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/";
} }
/** /**
@@ -2390,53 +2393,75 @@ SQL;
return $bRet; 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 * 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 * 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 * @param string $sPath
* @return false|ormDocument * @return false|ormDocument
* @throws Exception * @throws Exception
*/ */
public static function IsSelfURL($sPath) public static function GetDocumentFromSelfURL(string $sPath)
{ {
$result = false;
$sPageUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.document.php'; $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 // If the URL is an URL pointing to this instance of iTop, then
// extract the "query" part of the URL and analyze it // extract the "query" part of the URL and analyze it
$sQuery = parse_url($sPath, PHP_URL_QUERY); $sQuery = parse_url($sPath, PHP_URL_QUERY);
if ($sQuery !== null) if ($sQuery !== null) {
{
$aParams = array(); $aParams = array();
foreach(explode('&', $sQuery) as $sChunk) foreach (explode('&', $sQuery) as $sChunk) {
{
$aParts = explode('=', $sChunk ?? ''); $aParts = explode('=', $sChunk ?? '');
if (count($aParts) != 2) continue; if (count($aParts) != 2) {
continue;
}
$aParams[$aParts[0]] = urldecode($aParts[1]); $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'); $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 // This is a 'download_document' operation, let's retrieve the document directly from the database
$sClass = $aParams['class']; $sClass = $aParams['class'];
$iKey = $aParams['id']; $iKey = $aParams['id'];
$sAttCode = $aParams['field']; $sAttCode = $aParams['field'];
$oObj = MetaModel::GetObject($sClass, $iKey, false /* must exist */); // Users rights apply here !! $oObj = MetaModel::GetObject($sClass, $iKey, false /* must exist */); // Users rights apply here !!
if ($oObj) if ($oObj) {
{
/** /**
* @var ormDocument $result * @var ormDocument $result
*/ */
$result = clone $oObj->Get($sAttCode); return clone $oObj->Get($sAttCode);
return $result;
} }
} }
} }
throw new Exception('Invalid URL. This iTop URL is not pointing to a valid Document/Image.'); 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;
} }
/** /**
@@ -2444,68 +2469,28 @@ SQL;
* - an URL pointing to a blob (image/document) on the current iTop server * - an URL pointing to a blob (image/document) on the current iTop server
* - an http(s) URL * - an http(s) URL
* - the local file system (but only if you are an administrator) * - the local file system (but only if you are an administrator)
* @param string $sPath *
* @param string|null $sPath
* @return ormDocument|null * @return ormDocument|null
* @throws Exception * @throws Exception
*/ */
public static function FileGetContentsAndMIMEType($sPath) public static function FileGetContentsAndMIMEType($sPath)
{ {
$oUploadedDoc = null; if (utils::IsNullOrEmptyString($sPath)) {
$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))
{
// Empty path (NULL or '') means that there is no input, making an empty document. // 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 (static::IsURL($sPath)) {
if ($oUploadedDoc = static::IsSelfURL($sPath)) $oUploadedDoc = static::GetDocumentFromSelfURL($sPath);
{ if ($oUploadedDoc) {
// Nothing more to do, we've got it !! return $oUploadedDoc;
} }
else
{ // Remote file, let's use the HTTP headers to find the MIME Type
// Remote file, let's use the HTTP headers to find the MIME Type $sData = @file_get_contents($sPath);
$sData = @file_get_contents($sPath); if ($sData === false) {
if ($sData === false) IssueLog::Error(<<<TXT
{
IssueLog::Error(<<<TXT
Failed to load the file from URL. This can happen for multiple reasons: Failed to load the file from URL. This can happen for multiple reasons:
- Invalid URL - Invalid URL
- URL using HTTPS with an untrusted certificate on the remote server - URL using HTTPS with an untrusted certificate on the remote server
@@ -2514,54 +2499,40 @@ TXT
, LogChannels::CORE, [ , LogChannels::CORE, [
'URL' => $sPath, 'URL' => $sPath,
]); ]);
throw new Exception("Failed to load the file from the URL '$sPath'."); 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 = 'text/plain'; // Default MIME Type: treat the file as a bunch a characters...
{ $sFileName = 'uploaded-file'; // Default name for downloaded-files
$sMimeType = $aKnownExtensions[$sExtension]; $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;
}
}
} }
else if (extension_loaded('fileinfo')) $sPathName = pathinfo($sPath, PATHINFO_FILENAME);
{ if (utils::IsNotNullOrEmptyString($sPathName)) {
$finfo = new finfo(FILEINFO_MIME); $sFileName = $sPathName;
$sMimeType = $finfo->file($sPath);
} }
$oUploadedDoc = new ormDocument($sData, $sMimeType, $sFileName); $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) protected static function ParseHeaders($aHeaders)
@@ -3132,30 +3103,13 @@ TXT
* @throws \Exception * @throws \Exception
* @since 3.0.0 * @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 $aMentionedObjects = [];
switch ($sFormat) { $aMentionMatches = [];
case static::ENUM_TEXT_FORMAT_HTML: $sText = html_entity_decode($sText);
$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);
preg_match_all('/<a\s*([^>]*)data-object-class="([^"]*)"\s.*data-object-key="([^"]*)"/Ui', $sText, $aMentionMatches);
foreach ($aMentionMatches[0] as $iMatchIdx => $sCompleteMatch) { foreach ($aMentionMatches[0] as $iMatchIdx => $sCompleteMatch) {
$sMatchedClass = $aMentionMatches[2][$iMatchIdx]; $sMatchedClass = $aMentionMatches[2][$iMatchIdx];
$sMatchedId = $aMentionMatches[3][$iMatchIdx]; $sMatchedId = $aMentionMatches[3][$iMatchIdx];

View File

@@ -23,7 +23,7 @@ define('ITOP_DESIGN_LATEST_VERSION', '3.2');
* @used-by utils::GetItopVersionWikiSyntax() * @used-by utils::GetItopVersionWikiSyntax()
* @used-by iTopModulesPhpVersionIntegrationTest * @used-by iTopModulesPhpVersionIntegrationTest
*/ */
define('ITOP_CORE_VERSION', '3.2.0'); define('ITOP_CORE_VERSION', '3.2.1');
/** /**
* @var string * @var string

102
composer.lock generated
View File

@@ -3929,6 +3929,82 @@
], ],
"time": "2023-01-26T09:26:14+00:00" "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", "name": "symfony/polyfill-php83",
"version": "v1.28.0", "version": "v1.28.0",
@@ -4976,30 +5052,38 @@
}, },
{ {
"name": "twig/twig", "name": "twig/twig",
"version": "v3.8.0", "version": "v3.16.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/twigphp/Twig.git", "url": "https://github.com/twigphp/Twig.git",
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" "reference": "475ad2dc97d65d8631393e721e7e44fb544f0561"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", "url": "https://api.github.com/repos/twigphp/Twig/zipball/475ad2dc97d65d8631393e721e7e44fb544f0561",
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", "reference": "475ad2dc97d65d8631393e721e7e44fb544f0561",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=7.2.5", "php": ">=8.0.2",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "^1.8", "symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3", "symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php80": "^1.22" "symfony/polyfill-php81": "^1.29"
}, },
"require-dev": { "require-dev": {
"phpstan/phpstan": "^2.0",
"psr/container": "^1.0|^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", "type": "library",
"autoload": { "autoload": {
"files": [
"src/Resources/core.php",
"src/Resources/debug.php",
"src/Resources/escaper.php",
"src/Resources/string_loader.php"
],
"psr-4": { "psr-4": {
"Twig\\": "src/" "Twig\\": "src/"
} }
@@ -5032,7 +5116,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/twigphp/Twig/issues", "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": [ "funding": [
{ {
@@ -5044,7 +5128,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2023-11-21T18:54:41+00:00" "time": "2024-11-29T08:27:05+00:00"
}, },
{ {
"name": "webmozart/assert", "name": "webmozart/assert",

View File

@@ -181,7 +181,7 @@ abstract class Action extends cmdbAbstractObject
{ {
parent::DisplayBareRelations($oPage, false); parent::DisplayBareRelations($oPage, false);
if ($oPage instanceof iTopWebPage) { if ($oPage instanceof iTopWebPage && !$this->IsNew()) {
$this->GenerateLastExecutionsTab($oPage, $bEditMode); $this->GenerateLastExecutionsTab($oPage, $bEditMode);
} }
} }

View File

@@ -89,7 +89,7 @@ abstract class AsyncTask extends DBObject
// The value is set from null to planned in the setup program // 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 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("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 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()))); 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 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 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 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; protected $aCSSClasses;
public function GetType() public function GetType()
{ {
return Dict::S('Core:'.get_class($this)); return Dict::S('Core:'.get_class($this));
} }
@@ -4193,7 +4193,7 @@ class AttributeFinalClass extends AttributeString
*/ */
class AttributePassword extends AttributeString implements iAttributeNoGroupBy 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) * Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
@@ -4270,7 +4270,7 @@ class AttributePassword extends AttributeString implements iAttributeNoGroupBy
*/ */
class AttributeEncryptedString 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) protected function GetSQLCol($bFullSpec = false)
{ {
@@ -4440,7 +4440,7 @@ class AttributeText extends AttributeString
{ {
// Is there a way to know the current limitation for mysql? // Is there a way to know the current limitation for mysql?
// See mysql_field_len() // See mysql_field_len()
return 65535; return 16383; // number of characters (that can be 1-4 bytes long), not of bytes
} }
public static function RenderWikiHtml($sText, $bWikiOnly = false) public static function RenderWikiHtml($sText, $bWikiOnly = false)
@@ -6346,9 +6346,6 @@ class AttributeDateTime extends AttributeDBField
$oFormField = parent::MakeFormField($oObject, $oFormField); $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())));
return $oFormField; return $oFormField;
} }
@@ -6433,8 +6430,26 @@ class AttributeDateTime extends AttributeDBField
public function GetDefaultValue(DBObject $oHostObject = null) public function GetDefaultValue(DBObject $oHostObject = null)
{ {
if (!$this->IsNullAllowed()) { $sDefaultValue = $this->Get('default_value');
return date($this->GetInternalFormat()); 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(); return $this->GetNullValue();
} }
@@ -9433,8 +9448,13 @@ class AttributeStopWatch extends AttributeDefinition
case 'deadline': case 'deadline':
if ($value) if ($value)
{ {
$sDate = date(AttributeDateTime::GetInternalFormat(), $value); if (is_int($value))
$sRet = AttributeDeadline::FormatDeadline($sDate); {
$sDate = date(AttributeDateTime::GetInternalFormat(), $value);
$sRet = AttributeDeadline::FormatDeadline($sDate);
} else {
$sRet = $value;
}
} }
else else
{ {
@@ -9984,7 +10004,7 @@ class AttributeSubItem extends AttributeDefinition
*/ */
class AttributeOneWayPassword extends AttributeDefinition implements iAttributeNoGroupBy 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) * Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)

View File

@@ -70,7 +70,7 @@ class BulkExportResult extends DBObject
); );
MetaModel::Init_Params($aParams); 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("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 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()))); MetaModel::Init_AddAttribute(new AttributeString("format", array("allowed_values"=>null, "sql"=>"format", "default_value"=>'', "is_null_allowed"=>false, "depends_on"=>array())));

View File

@@ -33,7 +33,7 @@ class CMDBChange extends DBObject
); );
MetaModel::Init_Params($aParams); MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes(); //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 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 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()))); 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

@@ -850,6 +850,18 @@
<field id="language" xsi:type="AttributeApplicationLanguage"/> <field id="language" xsi:type="AttributeApplicationLanguage"/>
</fields> </fields>
</class> </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"> <class id="EventNotification" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php --> <!-- Generated by toolkit/export-class-to-meta.php -->
<parent>Event</parent> <parent>Event</parent>

View File

@@ -212,6 +212,8 @@ abstract class DBObject implements iDisplay
private $aEventListeners = []; private $aEventListeners = [];
private array $aAllowedTransitions = []; private array $aAllowedTransitions = [];
private ?string $sStimulusBeingApplied = null;
/** /**
* DBObject constructor. * DBObject constructor.
* *
@@ -758,10 +760,10 @@ abstract class DBObject implements iDisplay
*/ */
public function SetTrim($sAttCode, $sValue) public function SetTrim($sAttCode, $sValue)
{ {
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode); if (!$this->StringFitsInField($sAttCode, $sValue)) {
$iMaxSize = $oAttDef->GetMaxSize(); $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
$sLength = mb_strlen($sValue); $iMaxSize = $oAttDef->GetMaxSize();
if ($iMaxSize && ($sLength > $iMaxSize)) { $sLength = mb_strlen($sValue);
$sMessage = " -truncated ($sLength chars)"; $sMessage = " -truncated ($sLength chars)";
$sValue = mb_substr($sValue, 0, $iMaxSize - mb_strlen($sMessage)).$sMessage; $sValue = mb_substr($sValue, 0, $iMaxSize - mb_strlen($sMessage)).$sMessage;
} }
@@ -816,6 +818,24 @@ abstract class DBObject implements iDisplay
$oKPI->ComputeStatsForExtension($this, 'AfterDelete'); $oKPI->ComputeStatsForExtension($this, 'AfterDelete');
} }
/**
* @param string $sAttCode
* @param string $sValue
*
* @return bool
* @throws \Exception
*
* @Since 3.2.2
*/
public function StringFitsInField(string $sAttCode, string $sValue): bool
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
$iMaxSize = $oAttDef->GetMaxSize();
$sLength = mb_strlen($sValue);
return !($iMaxSize && ($sLength > $iMaxSize));
}
/** /**
* Compute (and optionally start) the StopWatches deadlines * Compute (and optionally start) the StopWatches deadlines
* *
@@ -1206,7 +1226,7 @@ abstract class DBObject implements iDisplay
if ($aCallInfo["function"] != "ComputeValues") continue; if ($aCallInfo["function"] != "ComputeValues") continue;
return; //skip! return; //skip!
} }
$this->FireEventComputeValues(); $this->FireEventComputeValues($this->sStimulusBeingApplied);
$oKPI = new ExecutionKPI(); $oKPI = new ExecutionKPI();
$this->ComputeValues(); $this->ComputeValues();
$oKPI->ComputeStatsForExtension($this, 'ComputeValues'); $oKPI->ComputeStatsForExtension($this, 'ComputeValues');
@@ -2130,7 +2150,7 @@ abstract class DBObject implements iDisplay
return "Bad type"; return "Bad type";
} }
elseif (($oAtt instanceof AttributeClassAttCodeSet) || ($oAtt instanceof AttributeEnumSet)) elseif ($oAtt instanceof AttributeSet)
{ {
if (is_string($toCheck)) if (is_string($toCheck))
{ {
@@ -2669,7 +2689,7 @@ abstract class DBObject implements iDisplay
// Ultimate check - ensure DB integrity // Ultimate check - ensure DB integrity
$this->SetReadOnly('No modification allowed during CheckToCreate'); $this->SetReadOnly('No modification allowed during CheckToCreate');
$this->FireEventCheckToWrite(); $this->FireEventCheckToWrite($this->sStimulusBeingApplied);
$this->SetReadWrite(); $this->SetReadWrite();
$oKPI = new ExecutionKPI(); $oKPI = new ExecutionKPI();
@@ -2865,6 +2885,14 @@ abstract class DBObject implements iDisplay
protected function ListChangedValues(array $aProposal) protected function ListChangedValues(array $aProposal)
{ {
$aDelta = array(); $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) foreach ($aProposal as $sAtt => $proposedValue)
{ {
if (!array_key_exists($sAtt, $this->m_aOrigValues)) if (!array_key_exists($sAtt, $this->m_aOrigValues))
@@ -3398,7 +3426,7 @@ abstract class DBObject implements iDisplay
$this->OnInsert(); $this->OnInsert();
$oKPI->ComputeStatsForExtension($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 not automatically computed, then check that the key is given by the caller
if (!MetaModel::IsAutoIncrementKey($sRootClass)) { if (!MetaModel::IsAutoIncrementKey($sRootClass)) {
@@ -3533,7 +3561,7 @@ abstract class DBObject implements iDisplay
*/ */
protected function PostInsertActions(): void protected function PostInsertActions(): void
{ {
$this->FireEventAfterWrite([], true); $this->FireEventAfterWrite([], true, null);
$oKPI = new ExecutionKPI(); $oKPI = new ExecutionKPI();
$this->AfterInsert(); $this->AfterInsert();
$oKPI->ComputeStatsForExtension($this, 'AfterInsert'); $oKPI->ComputeStatsForExtension($this, 'AfterInsert');
@@ -3614,13 +3642,13 @@ abstract class DBObject implements iDisplay
*/ */
public function DBUpdate() public function DBUpdate()
{ {
$this->LogCRUDEnter(__METHOD__);
if (!MetaModel::StartReentranceProtection($this)) { if (!MetaModel::StartReentranceProtection($this)) {
$this->LogCRUDExit(__METHOD__, 'Rejected (reentrance)'); $this->LogCRUDExit(__METHOD__, 'Rejected (reentrance)');
return false; return false;
} }
$this->LogCRUDEnter(__METHOD__);
if (!$this->m_bIsInDB) if (!$this->m_bIsInDB)
{ {
throw new CoreException("DBUpdate: could not update a newly created object, please call DBInsert instead"); throw new CoreException("DBUpdate: could not update a newly created object, please call DBInsert instead");
@@ -3641,7 +3669,7 @@ abstract class DBObject implements iDisplay
$this->OnUpdate(); $this->OnUpdate();
$oKPI->ComputeStatsForExtension($this, 'OnUpdate'); $oKPI->ComputeStatsForExtension($this, 'OnUpdate');
$this->FireEventBeforeWrite(); $this->FireEventBeforeWrite($this->sStimulusBeingApplied);
// Freeze the changes at this point // Freeze the changes at this point
$this->InitPreviousValuesForUpdatedAttributes(); $this->InitPreviousValuesForUpdatedAttributes();
@@ -3809,7 +3837,7 @@ abstract class DBObject implements iDisplay
} }
try { try {
$this->PostUpdateActions($aChanges, $sClass); $this->PostUpdateActions($this->m_aPreviousValuesForUpdatedAttributes, $sClass);
} }
catch (Exception $e) { catch (Exception $e) {
$this->LogCRUDExit(__METHOD__, 'Error: '.$e->getMessage()); $this->LogCRUDExit(__METHOD__, 'Error: '.$e->getMessage());
@@ -3852,7 +3880,9 @@ abstract class DBObject implements iDisplay
*/ */
protected function PostUpdateActions(array $aChanges): void protected function PostUpdateActions(array $aChanges): void
{ {
$this->FireEventAfterWrite($aChanges, false); $sStimulusBeingApplied = $this->sStimulusBeingApplied;
$this->sStimulusBeingApplied = null;
$this->FireEventAfterWrite($aChanges, false, $sStimulusBeingApplied);
$oKPI = new ExecutionKPI(); $oKPI = new ExecutionKPI();
$this->AfterUpdate(); $this->AfterUpdate();
$oKPI->ComputeStatsForExtension($this, 'AfterUpdate'); $oKPI->ComputeStatsForExtension($this, 'AfterUpdate');
@@ -3864,39 +3894,37 @@ abstract class DBObject implements iDisplay
$this->ActivateOnObjectUpdateTriggersForTargetObjects(); $this->ActivateOnObjectUpdateTriggersForTargetObjects();
$sClass = get_class($this); $sClass = get_class($this);
if (MetaModel::HasLifecycle($sClass)) if (utils::IsNotNullOrEmptyString($sStimulusBeingApplied))
{ {
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass); $sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
if (isset($this->m_aPreviousValuesForUpdatedAttributes[$sStateAttCode])) { $sPreviousState = $this->m_aPreviousValuesForUpdatedAttributes[$sStateAttCode];
$sPreviousState = $this->m_aPreviousValuesForUpdatedAttributes[$sStateAttCode]; // Change state triggers...
// Change state triggers... $aParams = array(
$aParams = array( 'class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL),
'class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL), 'previous_state' => $sPreviousState,
'previous_state' => $sPreviousState, 'new_state' => $this->Get($sStateAttCode),
'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);
$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()) {
while ($oTrigger = $oSet->Fetch()) { /** @var \TriggerOnStateLeave $oTrigger */
/** @var \TriggerOnStateLeave $oTrigger */ try {
try { $oTrigger->DoActivate($this->ToArgs('this'));
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch (Exception $e) {
$oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e);
}
} }
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); $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()) { while ($oTrigger = $oSet->Fetch()) {
/** @var \TriggerOnStateEnter $oTrigger */ /** @var \TriggerOnStateEnter $oTrigger */
try { try {
$oTrigger->DoActivate($this->ToArgs('this')); $oTrigger->DoActivate($this->ToArgs('this'));
} }
catch (Exception $e) { catch (Exception $e) {
$oTrigger->LogException($e, $this); $oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e); utils::EnrichRaisedException($oTrigger, $e);
}
} }
} }
} }
@@ -4024,7 +4052,7 @@ abstract class DBObject implements iDisplay
foreach ($aUpdatedLogAttCodes as $sAttCode) { foreach ($aUpdatedLogAttCodes as $sAttCode) {
/** @var \ormCaseLog $oUpdatedCaseLog */ /** @var \ormCaseLog $oUpdatedCaseLog */
$oUpdatedCaseLog = $this->Get($sAttCode); $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 // 3 - Trigger for those objects
@@ -4501,6 +4529,8 @@ abstract class DBObject implements iDisplay
*/ */
public function ApplyStimulus($sStimulusCode, $bDoNotWrite = false) public function ApplyStimulus($sStimulusCode, $bDoNotWrite = false)
{ {
$this->LogCRUDEnter(__METHOD__, "Code: $sStimulusCode");
$sClass = get_class($this); $sClass = get_class($this);
if (!MetaModel::HasLifecycle($sClass)) if (!MetaModel::HasLifecycle($sClass))
{ {
@@ -4527,6 +4557,8 @@ abstract class DBObject implements iDisplay
} else { } else {
$aBackupValues[$sAttCode] = $value; $aBackupValues[$sAttCode] = $value;
} }
} else {
$aBackupValues[$sAttCode] = $oAttDef->GetNullValue();
} }
} }
@@ -4534,7 +4566,6 @@ abstract class DBObject implements iDisplay
// Change the state before proceeding to the actions, this is necessary because an action might // 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) // trigger another stimuli (alternative: push the stimuli into a queue)
$sPreviousState = $this->Get($sStateAttCode);
$sNewState = $aTransitionDef['target_state']; $sNewState = $aTransitionDef['target_state'];
$this->Set($sStateAttCode, $sNewState); $this->Set($sStateAttCode, $sNewState);
@@ -4542,67 +4573,71 @@ abstract class DBObject implements iDisplay
// array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD // array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD
$bSuccess = true; $bSuccess = true;
$sActionDesc = ''; // Prevent current object from being updated by the actions
foreach ($aTransitionDef['actions'] as $actionHandler) $this->AddCurrentObjectInCrudStack('APPLY_STIMULUS');
{ $bIsNewlyProtected = MetaModel::StartReentranceProtection($this);
if (is_string($actionHandler)) try {
{ foreach ($aTransitionDef['actions'] as $actionHandler) {
// Old (pre-2.1.0 modules) action definition without any parameter if (is_string($actionHandler)) {
$aActionCallSpec = array($this, $actionHandler); // Old (pre-2.1.0 modules) action definition without any parameter
$sActionDesc = $sClass.'::'.$actionHandler; $aActionCallSpec = array($this, $actionHandler);
$sActionDesc = $sClass.'::'.$actionHandler;
if (!is_callable($aActionCallSpec)) if (!is_callable($aActionCallSpec)) {
{ throw new CoreException("Unable to call action: $sClass::$actionHandler");
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'];
} }
$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 } finally {
// (in case there is no returned value, null is obtained and means "ok") if ($bIsNewlyProtected) {
if ($bRet === false) // Stops protection only if the object was not already protected
{ MetaModel::StopReentranceProtection($this);
IssueLog::Info("Lifecycle action $sActionDesc returned false on object #$sClass:".$this->GetKey());
$bSuccess = false;
} }
$this->RemoveCurrentObjectInCrudStack();
} }
if ($bSuccess) if ($bSuccess)
{ {
$this->sStimulusBeingApplied = $sStimulusCode;
// Stop watches // Stop watches
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{ {
@@ -4631,6 +4666,7 @@ abstract class DBObject implements iDisplay
$this->m_aCurrValues[$sAttCode] = $aBackupValues[$sAttCode]; $this->m_aCurrValues[$sAttCode] = $aBackupValues[$sAttCode];
} }
} }
$this->LogCRUDExit(__METHOD__, 'Current State: '.$this->Get($sStateAttCode));
return $bSuccess; return $bSuccess;
} }
@@ -6617,7 +6653,7 @@ abstract class DBObject implements iDisplay
* @return void * @return void
* @since 3.1.0 * @since 3.1.0
*/ */
protected function FireEventCheckToWrite(): void protected function FireEventCheckToWrite(?string $sStimulusBeingApplied): void
{ {
} }
@@ -6625,7 +6661,7 @@ abstract class DBObject implements iDisplay
* @return void * @return void
* @since 3.1.0 * @since 3.1.0
*/ */
protected function FireEventBeforeWrite() protected function FireEventBeforeWrite(?string $sStimulusBeingApplied)
{ {
} }
@@ -6635,7 +6671,7 @@ abstract class DBObject implements iDisplay
* @return void * @return void
* @since 3.1.0 * @since 3.1.0
*/ */
protected function FireEventAfterWrite(array $aChanges, bool $bIsNew): void protected function FireEventAfterWrite(array $aChanges, bool $bIsNew, ?string $sStimulusBeingApplied): void
{ {
} }
@@ -6673,7 +6709,7 @@ abstract class DBObject implements iDisplay
* @return void * @return void
* @since 3.1.0 * @since 3.1.0
*/ */
protected function FireEventComputeValues(): void protected function FireEventComputeValues(?string $sStimulusBeingApplied): void
{ {
} }
@@ -6715,12 +6751,13 @@ abstract class DBObject implements iDisplay
$oRootClass = MetaModel::GetRootClass($sClass); $oRootClass = MetaModel::GetRootClass($sClass);
foreach (self::$m_aCrudStack as $aCrudStackEntry) { foreach (self::$m_aCrudStack as $aCrudStackEntry) {
if (($oRootClass === $aCrudStackEntry['class']) if (($oRootClass === $aCrudStackEntry['class']) && ($sConvertedId === $aCrudStackEntry['id'])) {
&& ($sConvertedId === $aCrudStackEntry['id'])) { IssueLog::Trace('CRUD '.__METHOD__." $sClass:$sId IS in CRUD Stack", LogChannels::DM_CRUD);
return true; return true;
} }
} }
IssueLog::Trace('CRUD '.__METHOD__." $sClass:$sId NOT in CRUD Stack", LogChannels::DM_CRUD);
return false; return false;
} }
@@ -6738,10 +6775,12 @@ abstract class DBObject implements iDisplay
$sRootClass = MetaModel::GetRootClass($sClass); $sRootClass = MetaModel::GetRootClass($sClass);
foreach (self::$m_aCrudStack as $aCrudStackEntry) { foreach (self::$m_aCrudStack as $aCrudStackEntry) {
if ($sRootClass === $aCrudStackEntry['class']) { if ($sRootClass === $aCrudStackEntry['class']) {
IssueLog::Trace("CRUD ".__METHOD__." $sClass IS in CRUD Stack", LogChannels::DM_CRUD);
return true; return true;
} }
} }
IssueLog::Trace('CRUD '.__METHOD__." $sClass NOT in CRUD Stack", LogChannels::DM_CRUD);
return false; return false;
} }
@@ -6751,17 +6790,20 @@ abstract class DBObject implements iDisplay
* @param string $sCrudType * @param string $sCrudType
* *
* @return void * @return void
* @throws \CoreException
* @since 3.1.0 N°5609 * @since 3.1.0 N°5609
*/ */
private function AddCurrentObjectInCrudStack(string $sCrudType): void private function AddCurrentObjectInCrudStack(string $sCrudType): void
{ {
$this->LogCRUDDebug(__METHOD__);
$sRootClass = MetaModel::GetRootClass(get_class($this)); $sRootClass = MetaModel::GetRootClass(get_class($this));
$sKey = (string)$this->GetKey();
self::$m_aCrudStack[] = [ self::$m_aCrudStack[] = [
'type' => $sCrudType, 'type' => $sCrudType,
'class' => $sRootClass, '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");
} }
/** /**
@@ -6773,10 +6815,15 @@ abstract class DBObject implements iDisplay
*/ */
private function UpdateCurrentObjectInCrudStack(): void private function UpdateCurrentObjectInCrudStack(): void
{ {
$this->LogCRUDDebug(__METHOD__);
$aCurrentCrudStack = array_pop(self::$m_aCrudStack); $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; 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");
} }
/** /**
@@ -6788,7 +6835,11 @@ abstract class DBObject implements iDisplay
private function RemoveCurrentObjectInCrudStack(): void private function RemoveCurrentObjectInCrudStack(): void
{ {
$aRemoved = array_pop(self::$m_aCrudStack); $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");
} }
/** /**
@@ -6805,37 +6856,53 @@ abstract class DBObject implements iDisplay
protected function LogCRUDEnter($sFunction, $sComment = '') protected function LogCRUDEnter($sFunction, $sComment = '')
{ {
$sClass = get_class($this); $sClass = get_class($this);
if (utils::StartsWith($sClass, 'CMDBChange')) {
return;
}
$sKey = $this->GetKey(); $sKey = $this->GetKey();
$sUUID = $this->m_sObjectUniqId;
$sPadding = str_pad('', count(self::$m_aCrudStack), '-'); $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 = '') protected function LogCRUDExit($sFunction, $sComment = '')
{ {
$sClass = get_class($this); $sClass = get_class($this);
if (utils::StartsWith($sClass, 'CMDBChange')) {
return;
}
$sKey = $this->GetKey(); $sKey = $this->GetKey();
$sUUID = $this->m_sObjectUniqId;
$sPadding = str_pad('', count(self::$m_aCrudStack), '-'); $sPadding = str_pad('', count(self::$m_aCrudStack), '-');
if (strlen($sComment) === 0) { if (strlen($sComment) === 0) {
IssueLog::Trace("CRUD <$sPadding+ $sFunction $sClass:$sKey", LogChannels::DM_CRUD); IssueLog::Trace("CRUD <$sPadding+ $sFunction $sClass:$sKey", LogChannels::DM_CRUD);
} else { } 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 = '') protected function LogCRUDDebug($sFunction, $sComment = '')
{ {
$sClass = get_class($this); $sClass = get_class($this);
if (utils::StartsWith($sClass, 'CMDBChange')) {
return;
}
$sKey = $this->GetKey(); $sKey = $this->GetKey();
$sUUID = $this->m_sObjectUniqId;
$sPadding = str_pad('', count(self::$m_aCrudStack), '-'); $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 = '') protected function LogCRUDError($sFunction, $sComment = '')
{ {
$sClass = get_class($this); $sClass = get_class($this);
if (utils::StartsWith($sClass, 'CMDBChange')) {
return;
}
$sKey = $this->GetKey(); $sKey = $this->GetKey();
$sUUID = $this->m_sObjectUniqId;
$sPadding = str_pad('', count(self::$m_aCrudStack), '!'); $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("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 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()))); 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

@@ -39,7 +39,7 @@ class Event extends DBObject implements iDisplay
MetaModel::Init_Params($aParams); MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes(); //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 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())));
// 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())));

View File

@@ -278,7 +278,7 @@ class HTMLDOMSanitizer extends DOMSanitizer
protected static $aTagsWhiteList = array( protected static $aTagsWhiteList = array(
'html' => array(), 'html' => array(),
'body' => 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'), 'p' => array('style', 'class'),
'blockquote' => array('style', 'class'), 'blockquote' => array('style', 'class'),
'br' => array(), 'br' => array(),
@@ -354,6 +354,8 @@ class HTMLDOMSanitizer extends DOMSanitizer
'font-style', 'font-style',
'height', 'height',
'margin', 'margin',
'margin-left',
'margin-right',
'padding', 'padding',
'text-align', 'text-align',
'vertical-align', 'vertical-align',

View File

@@ -54,7 +54,7 @@ class InlineImage extends DBObject
); );
MetaModel::Init_Params($aParams); MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes(); 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("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 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))); 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)));

View File

@@ -7532,8 +7532,41 @@ abstract class MetaModel
return $aEntries; 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 * @param string $sEnvironmentId
* @deprecated 3.2.1
*/ */
public static function ResetCache($sEnvironmentId = null) public static function ResetCache($sEnvironmentId = null)
{ {
@@ -7557,6 +7590,13 @@ abstract class MetaModel
require_once(APPROOT.'core/userrights.class.inc.php'); require_once(APPROOT.'core/userrights.class.inc.php');
UserRights::FlushPrivileges(); 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

@@ -182,14 +182,14 @@ class PHP_LexerGenerator_Lexer
$this->token = self::COMMENTEND; $this->token = self::COMMENTEND;
return true; 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->value = $token[1];
$this->N += strlen($token[1]) + 1; $this->N += strlen($token[1]) + 1;
$this->state = 'DeclarePI'; $this->state = 'DeclarePI';
$this->token = self::PI; $this->token = self::PI;
return true; 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->value = $token[0];
$this->token = self::PATTERN; $this->token = self::PATTERN;
$this->N += strlen($token[0]); $this->N += strlen($token[0]);
@@ -216,7 +216,7 @@ class PHP_LexerGenerator_Lexer
if ($this->data[$this->N] == '{') { if ($this->data[$this->N] == '{') {
return $this->lexCode(); 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'); $this->error('Unexpected end of file');
return false; return false;
} }
@@ -242,7 +242,7 @@ class PHP_LexerGenerator_Lexer
if ($this->data[$this->N] == '{') { if ($this->data[$this->N] == '{') {
return $this->lexCode(); 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'); $this->error('Unexpected end of file');
return false; return false;
} }
@@ -406,7 +406,7 @@ class PHP_LexerGenerator_Lexer
if ($this->data[$this->N] == '\'') { if ($this->data[$this->N] == '\'') {
return $this->lexQuote('\''); 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->value = $token[1];
$this->N += strlen($token[1]) + 1; $this->N += strlen($token[1]) + 1;
$this->state = 'DeclarePIRule'; $this->state = 'DeclarePIRule';
@@ -419,7 +419,7 @@ class PHP_LexerGenerator_Lexer
if ($this->data[$this->N] == '"') { if ($this->data[$this->N] == '"') {
return $this->lexQuote(); 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->value = $token[0];
$this->N += strlen($token[0]); $this->N += strlen($token[0]);
$this->token = self::SUBPATTERN; $this->token = self::SUBPATTERN;

View File

@@ -33,17 +33,19 @@ class PHP_LexerGenerator_ParseryyToken implements ArrayAccess
return $this->_string; return $this->_string;
} }
function offsetExists($offset) function offsetExists($offset): bool
{ {
return isset($this->metadata[$offset]); 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) function offsetGet($offset)
{ {
return $this->metadata[$offset]; return $this->metadata[$offset];
} }
function offsetSet($offset, $value) function offsetSet($offset, $value): void
{ {
if ($offset === null) { if ($offset === null) {
if (isset($value[0])) { 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]); unset($this->metadata[$offset]);
} }
@@ -278,7 +280,7 @@ class PHP_LexerGenerator_Parser#line 171 "Parser.php"
$match = false; $match = false;
foreach ($yy_yymore_patterns[' . $this->token . '] as $index => $rule) { foreach ($yy_yymore_patterns[' . $this->token . '] as $index => $rule) {
if (preg_match(\'/\' . $rule . \'/' . $this->patternFlags . '\', 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 $yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
if ($match) { if ($match) {
if (strlen($yymatches[0]) > strlen($match[0][0])) { if (strlen($yymatches[0]) > strlen($match[0][0])) {
@@ -350,7 +352,7 @@ class PHP_LexerGenerator_Parser#line 171 "Parser.php"
$pattern . '\';' . "\n"); $pattern . '\';' . "\n");
fwrite($this->out, ' fwrite($this->out, '
do { do {
if (preg_match($yy_global_pattern,' . $this->input . ', $yymatches, null, ' . if (preg_match($yy_global_pattern,' . $this->input . ', $yymatches, 0, ' .
$this->counter . $this->counter .
')) { ')) {
$yysubmatches = $yymatches; $yysubmatches = $yymatches;
@@ -408,7 +410,7 @@ class PHP_LexerGenerator_Parser#line 171 "Parser.php"
} }
$yysubmatches = array(); $yysubmatches = array();
if (preg_match(\'/\' . $yy_yymore_patterns[' . $this->token . '][1] . \'/' . $this->patternFlags . '\', 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; $yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns $yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
next($yymatches); // skip global match next($yymatches); // skip global match

View File

@@ -187,7 +187,7 @@ require_once 'PHP/LexerGenerator/Exception.php';
$match = false; $match = false;
foreach ($yy_yymore_patterns[' . $this->token . '] as $index => $rule) { foreach ($yy_yymore_patterns[' . $this->token . '] as $index => $rule) {
if (preg_match(\'/\' . $rule . \'/' . $this->patternFlags . '\', 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 $yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
if ($match) { if ($match) {
if (strlen($yymatches[0]) > strlen($match[0][0])) { if (strlen($yymatches[0]) > strlen($match[0][0])) {
@@ -259,7 +259,7 @@ require_once 'PHP/LexerGenerator/Exception.php';
$pattern . '\';' . "\n"); $pattern . '\';' . "\n");
fwrite($this->out, ' fwrite($this->out, '
do { do {
if (preg_match($yy_global_pattern,' . $this->input . ', $yymatches, null, ' . if (preg_match($yy_global_pattern,' . $this->input . ', $yymatches, 0, ' .
$this->counter . $this->counter .
')) { ')) {
$yysubmatches = $yymatches; $yysubmatches = $yymatches;
@@ -317,7 +317,7 @@ require_once 'PHP/LexerGenerator/Exception.php';
} }
$yysubmatches = array(); $yysubmatches = array();
if (preg_match(\'/\' . $yy_yymore_patterns[' . $this->token . '][1] . \'/' . $this->patternFlags . '\', 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; $yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns $yymatches = array_filter($yymatches, \'strlen\'); // remove empty sub-patterns
next($yymatches); // skip global match 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(\\\\)/'; $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 { 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; $yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
if (!count($yymatches)) { if (!count($yymatches)) {
@@ -180,7 +180,7 @@ class PHP_LexerGenerator_Regex_Lexer
} }
$yysubmatches = array(); $yysubmatches = array();
if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/', if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/',
$this->input, $yymatches, null, $this->N)) { $this->input, $yymatches, 0, $this->N)) {
$yysubmatches = $yymatches; $yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
next($yymatches); // skip global match next($yymatches); // skip global match
@@ -360,7 +360,7 @@ class PHP_LexerGenerator_Regex_Lexer
$yy_global_pattern = '/\G(\\^)|\G(\\])|\G(.)/'; $yy_global_pattern = '/\G(\\^)|\G(\\])|\G(.)/';
do { 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; $yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
if (!count($yymatches)) { if (!count($yymatches)) {
@@ -410,7 +410,7 @@ class PHP_LexerGenerator_Regex_Lexer
} }
$yysubmatches = array(); $yysubmatches = array();
if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/', if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/',
$this->input, $yymatches, null, $this->N)) { $this->input, $yymatches, 0, $this->N)) {
$yysubmatches = $yymatches; $yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
next($yymatches); // skip global match 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(.)/'; $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 { 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; $yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
if (!count($yymatches)) { if (!count($yymatches)) {
@@ -555,7 +555,7 @@ class PHP_LexerGenerator_Regex_Lexer
} }
$yysubmatches = array(); $yysubmatches = array();
if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/', if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/',
$this->input, $yymatches, null, $this->N)) { $this->input, $yymatches, 0, $this->N)) {
$yysubmatches = $yymatches; $yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
next($yymatches); // skip global match 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(\\\\)/'; $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 { 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; $yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
if (!count($yymatches)) { if (!count($yymatches)) {
@@ -732,7 +732,7 @@ class PHP_LexerGenerator_Regex_Lexer
} }
$yysubmatches = array(); $yysubmatches = array();
if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/', if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/',
$this->input, $yymatches, null, $this->N)) { $this->input, $yymatches, 0, $this->N)) {
$yysubmatches = $yymatches; $yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
next($yymatches); // skip global match 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(.)/'; $yy_global_pattern = '/\G([imsxUX]+-[imsxUX]+|[imsxUX]+|-[imsxUX]+)|\G(:)|\G(\\))|\G(P<[^>]+>)|\G(<=)|\G(<!)|\G(=)|\G(!)|\G(>)|\G(\\(\\?)|\G(#[^)]+)|\G(R)|\G(.)/';
do { 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; $yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
if (!count($yymatches)) { if (!count($yymatches)) {
@@ -902,7 +902,7 @@ class PHP_LexerGenerator_Regex_Lexer
} }
$yysubmatches = array(); $yysubmatches = array();
if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/', if (preg_match('/' . $yy_yymore_patterns[$this->token][1] . '/',
$this->input, $yymatches, null, $this->N)) { $this->input, $yymatches, 0, $this->N)) {
$yysubmatches = $yymatches; $yysubmatches = $yymatches;
$yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
next($yymatches); // skip global match next($yymatches); // skip global match

View File

@@ -33,17 +33,19 @@ class PHP_LexerGenerator_Regex_yyToken implements ArrayAccess
return $this->_string; return $this->_string;
} }
function offsetExists($offset) function offsetExists($offset): bool
{ {
return isset($this->metadata[$offset]); 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) function offsetGet($offset)
{ {
return $this->metadata[$offset]; return $this->metadata[$offset];
} }
function offsetSet($offset, $value) function offsetSet($offset, $value): void
{ {
if ($offset === null) { if ($offset === null) {
if (isset($value[0])) { 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]); unset($this->metadata[$offset]);
} }

View File

@@ -575,6 +575,15 @@ class BinaryExpression extends Expression
case 'LIKE': case 'LIKE':
$sType = 'like'; $sType = 'like';
break; break;
case 'NOT LIKE':
$sType = 'notlike';
break;
case 'IN':
$sType = 'in';
break;
case 'NOT IN':
$sType = 'notin';
break;
default: default:
throw new Exception("Operator '$sOperator' not yet supported"); throw new Exception("Operator '$sOperator' not yet supported");
} }
@@ -639,7 +648,26 @@ class BinaryExpression extends Expression
case 'like': case 'like':
$sEscaped = preg_quote($mRight, '/'); $sEscaped = preg_quote($mRight, '/');
$sEscaped = str_replace(array('%', '_', '\\\\.*', '\\\\.'), array('.*', '.', '%', '_'), $sEscaped); $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; break;
} }
return $result; return $result;
@@ -2250,7 +2278,12 @@ class ListExpression extends Expression
*/ */
public function Evaluate(array $aArgs) 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; $match = false;
foreach ($yy_yymore_patterns[$this->token] as $index => $rule) { foreach ($yy_yymore_patterns[$this->token] as $index => $rule) {
if (preg_match('/' . $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 $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns
if ($match) { if ($match) {
if (strlen($yymatches[0]) > strlen($match[0][0])) { if (strlen($yymatches[0]) > strlen($match[0][0])) {

View File

@@ -36,7 +36,13 @@ class UnknownClassOqlException extends OqlNormalizeException
{ {
public function __construct($sInput, OqlName $oName, $aExpecting = null) 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() public function GetUserFriendlyDescription()

View File

@@ -48,6 +48,42 @@ class ormDocument
* @since 3.1.0 * @since 3.1.0
*/ */
public const DEFAULT_DOWNLOADS_COUNT = 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_data;
protected $m_sMimeType; protected $m_sMimeType;
@@ -76,6 +112,36 @@ class ormDocument
$this->m_iDownloadsCount = $iDownloadsCount; $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() public function __toString()
{ {
if($this->IsEmpty()) return ''; if($this->IsEmpty()) return '';
@@ -314,7 +380,7 @@ class ormDocument
'document' => $oDocument, 'document' => $oDocument,
'content_disposition' => $sContentDisposition, 'content_disposition' => $sContentDisposition,
); );
EventService::FireEvent(new EventData(EVENT_DOWNLOAD_DOCUMENT, $sClass, $aEventData)); EventService::FireEvent(new EventData(\EVENT_DOWNLOAD_DOCUMENT, $sClass, $aEventData));
$oPage->TrashUnexpectedOutput(); $oPage->TrashUnexpectedOutput();
$oPage->SetContentType($oDocument->GetMimeType()); $oPage->SetContentType($oDocument->GetMimeType());
$oPage->SetContentDisposition($sContentDisposition,$oDocument->GetFileName()); $oPage->SetContentDisposition($sContentDisposition,$oDocument->GetFileName());

View File

@@ -42,8 +42,8 @@ class iTopOwnershipToken extends DBObject
); );
MetaModel::Init_Params($aParams); MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes(); 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("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"=>'', "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 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 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()))); MetaModel::Init_AddAttribute(new AttributeString("token", array("allowed_values"=>null, "sql"=>'token', "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array())));

View File

@@ -44,6 +44,8 @@ class ObjectResult
* @var string * @var string
* @api * @api
*/ */
use SanitizeTrait;
public $message; public $message;
/** /**
* @var mixed|null * @var mixed|null
@@ -156,6 +158,19 @@ class ObjectResult
{ {
$this->fields[$sAttCode] = $this->MakeResultValue($oObject, $sAttCode, $bExtendedOutput); $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);
}
}
} }
@@ -221,6 +236,16 @@ class RestResultWithObjects extends RestResult
$sObjKey = get_class($oObject).'::'.$oObject->GetKey(); $sObjKey = get_class($oObject).'::'.$oObject->GetKey();
$this->objects[$sObjKey] = $oObjRes; $this->objects[$sObjKey] = $oObjRes;
} }
public function SanitizeContent()
{
parent::SanitizeContent();
foreach($this->objects as $sObjKey => $oObjRes)
{
$oObjRes->SanitizeContent();
}
}
} }
/** /**
@@ -308,9 +333,10 @@ class RestDelete
* *
* @package Core * @package Core
*/ */
class CoreServices implements iRestServiceProvider class CoreServices implements iRestServiceProvider, iRestInputSanitizer
{ {
/** use SanitizeTrait;
/**
* Enumerate services delivered by this class * Enumerate services delivered by this class
* *
* @param string $sVersion The version (e.g. 1.0) supported by the services * @param string $sVersion The version (e.g. 1.0) supported by the services
@@ -737,6 +763,33 @@ class CoreServices implements iRestServiceProvider
return $oResult; 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 * Helper for object deletion
*/ */
@@ -875,3 +928,50 @@ class CoreServices implements iRestServiceProvider
return $iLimit * max(0, $iPage - 1); return $iLimit * max(0, $iPage - 1);
} }
} }
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

@@ -1921,50 +1921,45 @@ class UserRights
*/ */
protected static function FindUser($sLogin, $sAuthentication = 'any', $bAllowDisabledUsers = false) protected static function FindUser($sLogin, $sAuthentication = 'any', $bAllowDisabledUsers = false)
{ {
if ($sAuthentication == 'any') if ($sAuthentication === 'any') {
{ $oUser = self::FindUser($sLogin, 'internal', $bAllowDisabledUsers);
$oUser = self::FindUser($sLogin, 'internal'); if ($oUser !== null) {
if ($oUser == null) return $oUser;
{
$oUser = self::FindUser($sLogin, 'external');
} }
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])) if (!isset(self::$m_aCacheUsers)) {
{ self::$m_aCacheUsers = [ 'internal' => [], 'external' => [] ];
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];
} }
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];
} }
/** /**

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -99,6 +99,28 @@ $ibo-color-pink-800: $common-color-pink-800 !default;
$ibo-color-pink-900: $common-color-pink-900 !default; $ibo-color-pink-900: $common-color-pink-900 !default;
$ibo-color-pink-950: $common-color-pink-950 !default; $ibo-color-pink-950: $common-color-pink-950 !default;
$ibo-color-yellow-100: $common-color-yellow-100 !default;
$ibo-color-yellow-200: $common-color-yellow-200 !default;
$ibo-color-yellow-300: $common-color-yellow-300 !default;
$ibo-color-yellow-400: $common-color-yellow-400 !default;
$ibo-color-yellow-500: $common-color-yellow-500 !default;
$ibo-color-yellow-600: $common-color-yellow-600 !default;
$ibo-color-yellow-700: $common-color-yellow-700 !default;
$ibo-color-yellow-800: $common-color-yellow-800 !default;
$ibo-color-yellow-900: $common-color-yellow-900 !default;
$ibo-color-yellow-950: $common-color-yellow-950 !default;
$ibo-color-purple-100: $common-color-purple-100 !default;
$ibo-color-purple-200: $common-color-purple-200 !default;
$ibo-color-purple-300: $common-color-purple-300 !default;
$ibo-color-purple-400: $common-color-purple-400 !default;
$ibo-color-purple-500: $common-color-purple-500 !default;
$ibo-color-purple-600: $common-color-purple-600 !default;
$ibo-color-purple-700: $common-color-purple-700 !default;
$ibo-color-purple-800: $common-color-purple-800 !default;
$ibo-color-purple-900: $common-color-purple-900 !default;
$ibo-color-purple-950: $common-color-purple-950 !default;
$ibo-colors: $common-colors; $ibo-colors: $common-colors;
/* CSS variables */ /* CSS variables */
@@ -196,4 +218,26 @@ $ibo-colors: $common-colors;
--ibo-color-pink-800: #{$ibo-color-pink-800}; --ibo-color-pink-800: #{$ibo-color-pink-800};
--ibo-color-pink-900: #{$ibo-color-pink-900}; --ibo-color-pink-900: #{$ibo-color-pink-900};
--ibo-color-pink-950: #{$ibo-color-pink-950}; --ibo-color-pink-950: #{$ibo-color-pink-950};
--ibo-color-yellow-100: #{$ibo-color-yellow-100};
--ibo-color-yellow-200: #{$ibo-color-yellow-200};
--ibo-color-yellow-300: #{$ibo-color-yellow-300};
--ibo-color-yellow-400: #{$ibo-color-yellow-400};
--ibo-color-yellow-500: #{$ibo-color-yellow-500};
--ibo-color-yellow-600: #{$ibo-color-yellow-600};
--ibo-color-yellow-700: #{$ibo-color-yellow-700};
--ibo-color-yellow-800: #{$ibo-color-yellow-800};
--ibo-color-yellow-900: #{$ibo-color-yellow-900};
--ibo-color-yellow-950: #{$ibo-color-yellow-950};
--ibo-color-purple-100: #{$ibo-color-purple-100};
--ibo-color-purple-200: #{$ibo-color-purple-200};
--ibo-color-purple-300: #{$ibo-color-purple-300};
--ibo-color-purple-400: #{$ibo-color-purple-400};
--ibo-color-purple-500: #{$ibo-color-purple-500};
--ibo-color-purple-600: #{$ibo-color-purple-600};
--ibo-color-purple-700: #{$ibo-color-purple-700};
--ibo-color-purple-800: #{$ibo-color-purple-800};
--ibo-color-purple-900: #{$ibo-color-purple-900};
--ibo-color-purple-950: #{$ibo-color-purple-950};
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -99,4 +99,26 @@ $common-color-pink-800: hsla(322, 60%, 37%, 1) !default;
$common-color-pink-900: hsla(318, 51%, 29%, 1) !default; $common-color-pink-900: hsla(318, 51%, 29%, 1) !default;
$common-color-pink-950: hsla(318, 51%, 21%, 1) !default; $common-color-pink-950: hsla(318, 51%, 21%, 1) !default;
$common-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pink', 'primary', 'secondary', 'information', 'success', 'warning', 'danger'); $common-color-yellow-100: hsla(60, 100%, 97.06%, 1) !default;
$common-color-yellow-200: hsla(58.1, 96.92%, 87.25%, 1) !default;
$common-color-yellow-300: hsla(54.69, 91.87%, 75.88%, 1) !default;
$common-color-yellow-400: hsla(51.32, 89.41%, 66.67%, 1) !default;
$common-color-yellow-500: hsla(46.96, 80.9%, 60.98%, 1) !default;
$common-color-yellow-600: hsla(40, 67.2%, 50.98%, 1) !default;
$common-color-yellow-700: hsla(35.53, 71.03%, 41.96%, 1) !default;
$common-color-yellow-800: hsla(31.63, 74.57%, 33.92%, 1) !default;
$common-color-yellow-900: hsla(30, 75.76%, 25.88%, 1) !default;
$common-color-yellow-950: hsla(29, 80%, 17%, 1) !default;
$common-color-purple-100: hsla(251.43, 91.3%, 95.49%, 1) !default;
$common-color-purple-200: hsla(250.5, 95.24%, 91.76%, 1) !default;
$common-color-purple-300: hsla(252.5, 94.74%, 85.1%, 1) !default;
$common-color-purple-400: hsla(255.14, 91.74%, 76.27%, 1) !default;
$common-color-purple-500: hsla(258.31, 89.53%, 66.27%, 1) !default;
$common-color-purple-600: hsla(262.12, 83.26%, 57.84%, 1) !default;
$common-color-purple-700: hsla(263.39, 69.96%, 50.39%, 1) !default;
$common-color-purple-800: hsla(263.36, 69.3%, 42.16%, 1) !default;
$common-color-purple-900: hsla(263.5, 67.42%, 34.9%, 1) !default;
$common-color-purple-950: hsla(263, 71%, 26%, 1) !default;
$common-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pink', 'yellow', 'purple', 'primary', 'secondary', 'information', 'success', 'warning', 'danger');

View File

@@ -9,8 +9,8 @@
/** /**
* *
*/ */
Dict::Add('CS CZ', 'Czech', 'Čeština', [ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'CAS:Error:UserNotAllowed' => 'Uživatel není oprávněn', 'CAS:Error:UserNotAllowed' => 'Uživatel není oprávněn',
'CAS:Login:SignIn' => 'Přihlásit se prostřednictvím CAS', 'CAS:Login:SignIn' => 'Přihlásit se prostřednictvím CAS',
'CAS:Login:SignInTooltip' => 'Klikni zde pro příhlášení prostřednictvím CAS serveru', 'CAS:Login:SignInTooltip' => 'Klikni zde pro příhlášení prostřednictvím CAS serveru',
]); ));

View File

@@ -9,8 +9,8 @@
/** /**
* *
*/ */
Dict::Add('DA DA', 'Danish', 'Dansk', [ Dict::Add('DA DA', 'Danish', 'Dansk', array(
'CAS:Error:UserNotAllowed' => 'User not allowed~~', 'CAS:Error:UserNotAllowed' => 'User not allowed~~',
'CAS:Login:SignIn' => 'Sign in with CAS~~', 'CAS:Login:SignIn' => 'Sign in with CAS~~',
'CAS:Login:SignInTooltip' => 'Click here to authenticate yourself with the CAS server~~', 'CAS:Login:SignInTooltip' => 'Click here to authenticate yourself with the CAS server~~',
]); ));

View File

@@ -9,8 +9,8 @@
/** /**
* *
*/ */
Dict::Add('DE DE', 'German', 'Deutsch', [ Dict::Add('DE DE', 'German', 'Deutsch', array(
'CAS:Error:UserNotAllowed' => 'Benutzer ist nicht zugelassen', 'CAS:Error:UserNotAllowed' => 'Benutzer ist nicht zugelassen',
'CAS:Login:SignIn' => 'Anmeldung mit CAS', 'CAS:Login:SignIn' => 'Anmeldung mit CAS',
'CAS:Login:SignInTooltip' => 'Hier klicken, um sich am CAS-Server zu authentifizieren', 'CAS:Login:SignInTooltip' => 'Hier klicken, um sich am CAS-Server zu authentifizieren',
]); ));

View File

@@ -0,0 +1,13 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
Dict::Add('EN GB', 'British English', 'British English', array(
'CAS:Error:UserNotAllowed' => 'User not allowed',
'CAS:Login:SignIn' => 'Sign in with CAS',
'CAS:Login:SignInTooltip' => 'Click here to authenticate yourself with the CAS server',
));

View File

@@ -7,8 +7,8 @@
* @author Miguel Turrubiates <miguel_tf@yahoo.com> * @author Miguel Turrubiates <miguel_tf@yahoo.com>
* @notas Utilizar codificación UTF-8 para mostrar acentos y otros caracteres especiales * @notas Utilizar codificación UTF-8 para mostrar acentos y otros caracteres especiales
*/ */
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'CAS:Error:UserNotAllowed' => 'Usuario no permitido', 'CAS:Error:UserNotAllowed' => 'Usuario no permitido',
'CAS:Login:SignIn' => 'Iniciar sesión con CAS', 'CAS:Login:SignIn' => 'Iniciar sesión con CAS',
'CAS:Login:SignInTooltip' => 'Click para autenticarse con servidor CAS', 'CAS:Login:SignInTooltip' => 'Click para autenticarse con servidor CAS',
]); ));

View File

@@ -9,8 +9,8 @@
/** /**
* *
*/ */
Dict::Add('FR FR', 'French', 'Français', [ Dict::Add('FR FR', 'French', 'Français', array(
'CAS:Error:UserNotAllowed' => 'Utilisateur non autorisé', 'CAS:Error:UserNotAllowed' => 'Utilisateur non autorisé',
'CAS:Login:SignIn' => 'S\'identifier avec CAS', 'CAS:Login:SignIn' => 'S\'identifier avec CAS',
'CAS:Login:SignInTooltip' => 'Cliquer ici pour s\'identifier avec le serveur CAS', 'CAS:Login:SignInTooltip' => 'Cliquer ici pour s\'identifier avec le serveur CAS',
]); ));

View File

@@ -9,8 +9,8 @@
/** /**
* *
*/ */
Dict::Add('HU HU', 'Hungarian', 'Magyar', [ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'CAS:Error:UserNotAllowed' => 'Nem engedélyezett felhasználó', 'CAS:Error:UserNotAllowed' => 'Nem engedélyezett felhasználó',
'CAS:Login:SignIn' => 'Bejelentkezés CAS szerverrel', 'CAS:Login:SignIn' => 'Bejelentkezés CAS szerverrel',
'CAS:Login:SignInTooltip' => 'Kattintson ide az azonosításhoz a CAS szerveren', 'CAS:Login:SignInTooltip' => 'Kattintson ide az azonosításhoz a CAS szerveren',
]); ));

View File

@@ -9,8 +9,8 @@
/** /**
* *
*/ */
Dict::Add('IT IT', 'Italian', 'Italiano', [ Dict::Add('IT IT', 'Italian', 'Italiano', array(
'CAS:Error:UserNotAllowed' => 'Utente non autorizzato', 'CAS:Error:UserNotAllowed' => 'Utente non autorizzato',
'CAS:Login:SignIn' => 'Accedi con CAS', 'CAS:Login:SignIn' => 'Accedi con CAS',
'CAS:Login:SignInTooltip' => 'Clicca qui per autenticarti con il server CAS', 'CAS:Login:SignInTooltip' => 'Clicca qui per autenticarti con il server CAS',
]); ));

View File

@@ -9,8 +9,8 @@
/** /**
* *
*/ */
Dict::Add('JA JP', 'Japanese', '日本語', [ Dict::Add('JA JP', 'Japanese', '日本語', array(
'CAS:Error:UserNotAllowed' => 'User not allowed~~', 'CAS:Error:UserNotAllowed' => 'User not allowed~~',
'CAS:Login:SignIn' => 'Sign in with CAS~~', 'CAS:Login:SignIn' => 'Sign in with CAS~~',
'CAS:Login:SignInTooltip' => 'Click here to authenticate yourself with the CAS server~~', 'CAS:Login:SignInTooltip' => 'Click here to authenticate yourself with the CAS server~~',
]); ));

View File

@@ -10,8 +10,8 @@
* @author Jeffrey Bostoen <info@jeffreybostoen.be> (2018 - 2022) * @author Jeffrey Bostoen <info@jeffreybostoen.be> (2018 - 2022)
* *
*/ */
Dict::Add('NL NL', 'Dutch', 'Nederlands', [ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
'CAS:Error:UserNotAllowed' => 'Gebruiker heeft onvoldoende rechten.', 'CAS:Error:UserNotAllowed' => 'Gebruiker heeft onvoldoende rechten.',
'CAS:Login:SignIn' => 'Inloggen met CAS', 'CAS:Login:SignIn' => 'Inloggen met CAS',
'CAS:Login:SignInTooltip' => 'Klik hier om aan te melden via de CAS-server', 'CAS:Login:SignInTooltip' => 'Klik hier om aan te melden via de CAS-server',
]); ));

View File

@@ -9,8 +9,8 @@
/** /**
* *
*/ */
Dict::Add('PL PL', 'Polish', 'Polski', [ Dict::Add('PL PL', 'Polish', 'Polski', array(
'CAS:Error:UserNotAllowed' => 'Użytkownik niedozwolony', 'CAS:Error:UserNotAllowed' => 'Użytkownik niedozwolony',
'CAS:Login:SignIn' => 'Zaloguj się za pomocą CAS', 'CAS:Login:SignIn' => 'Zaloguj się za pomocą CAS',
'CAS:Login:SignInTooltip' => 'Kliknij tutaj, aby uwierzytelnić się na serwerze CAS', 'CAS:Login:SignInTooltip' => 'Kliknij tutaj, aby uwierzytelnić się na serwerze CAS',
]); ));

View File

@@ -9,8 +9,8 @@
/** /**
* *
*/ */
Dict::Add('PT BR', 'Brazilian', 'Brazilian', [ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
'CAS:Error:UserNotAllowed' => 'Usuário não permitido', 'CAS:Error:UserNotAllowed' => 'Usuário não permitido',
'CAS:Login:SignIn' => 'Autenticar com CAS', 'CAS:Login:SignIn' => 'Autenticar com CAS',
'CAS:Login:SignInTooltip' => 'Clique aqui para se autenticar no servidor CAS', 'CAS:Login:SignInTooltip' => 'Clique aqui para se autenticar no servidor CAS',
]); ));

View File

@@ -10,8 +10,8 @@
* @author Vladimir Kunin <v.b.kunin@gmail.com> * @author Vladimir Kunin <v.b.kunin@gmail.com>
* *
*/ */
Dict::Add('RU RU', 'Russian', 'Русский', [ Dict::Add('RU RU', 'Russian', 'Русский', array(
'CAS:Error:UserNotAllowed' => 'Вход не разрешён', 'CAS:Error:UserNotAllowed' => 'Вход не разрешён',
'CAS:Login:SignIn' => 'Вход через CAS', 'CAS:Login:SignIn' => 'Вход через CAS',
'CAS:Login:SignInTooltip' => 'Нажмите здесь, чтобы войти через CAS сервер', 'CAS:Login:SignInTooltip' => 'Нажмите здесь, чтобы войти через CAS сервер',
]); ));

View File

@@ -9,8 +9,8 @@
/** /**
* *
*/ */
Dict::Add('SK SK', 'Slovak', 'Slovenčina', [ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
'CAS:Error:UserNotAllowed' => 'User not allowed~~', 'CAS:Error:UserNotAllowed' => 'User not allowed~~',
'CAS:Login:SignIn' => 'Sign in with CAS~~', 'CAS:Login:SignIn' => 'Sign in with CAS~~',
'CAS:Login:SignInTooltip' => 'Click here to authenticate yourself with the CAS server~~', 'CAS:Login:SignInTooltip' => 'Click here to authenticate yourself with the CAS server~~',
]); ));

View File

@@ -9,8 +9,8 @@
/** /**
* *
*/ */
Dict::Add('TR TR', 'Turkish', 'Türkçe', [ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
'CAS:Error:UserNotAllowed' => 'User not allowed~~', 'CAS:Error:UserNotAllowed' => 'User not allowed~~',
'CAS:Login:SignIn' => 'Sign in with CAS~~', 'CAS:Login:SignIn' => 'Sign in with CAS~~',
'CAS:Login:SignInTooltip' => 'Click here to authenticate yourself with the CAS server~~', 'CAS:Login:SignInTooltip' => 'Click here to authenticate yourself with the CAS server~~',
]); ));

View File

@@ -4,13 +4,9 @@
* *
* @copyright Copyright (C) 2010-2024 Combodo SAS * @copyright Copyright (C) 2010-2024 Combodo SAS
* @license https://opensource.org/licenses/AGPL-3.0 * @license https://opensource.org/licenses/AGPL-3.0
*
*/ */
/** Dict::Add('ZH CN', 'Chinese', '简体中文', array(
*
*/
Dict::Add('ZH CN', 'Chinese', '简体中文', [
'CAS:Error:UserNotAllowed' => '用户被禁止登录', 'CAS:Error:UserNotAllowed' => '用户被禁止登录',
'CAS:Login:SignIn' => '使用CAS登录', 'CAS:Login:SignIn' => '使用CAS登录',
'CAS:Login:SignInTooltip' => '点击这里使用CAS服务器认证', 'CAS:Login:SignInTooltip' => '点击这里使用CAS服务器认证',
]); ));

View File

@@ -5,7 +5,7 @@
SetupWebPage::AddModule( SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file __FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'authent-cas/3.2.0', 'authent-cas/3.2.1',
array( array(
// Identification // Identification
// //

View File

@@ -160,8 +160,7 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
private static function InitCASClient() private static function InitCASClient()
{ {
$bCASDebug = Config::Get('cas_debug'); $bCASDebug = Config::Get('cas_debug');
if ($bCASDebug) if ($bCASDebug) {
{
phpCAS::setLogger(new CASLogger(APPROOT.'log/cas.log')); phpCAS::setLogger(new CASLogger(APPROOT.'log/cas.log'));
} }
@@ -171,18 +170,17 @@ class CASLoginExtension extends AbstractLoginFSMExtension implements iLogoutExte
$iCASPort = Config::Get('cas_port'); $iCASPort = Config::Get('cas_port');
$sCASContext = Config::Get('cas_context'); $sCASContext = Config::Get('cas_context');
$sServiceBaseURL = Config::Get('service_base_url', self::GetServiceBaseURL()); $sServiceBaseURL = Config::Get('service_base_url', self::GetServiceBaseURL());
phpCAS::client($sCASVersion, $sCASHost, $iCASPort, $sCASContext, $sServiceBaseURL, false /* session already started */); if (!phpCAS::isInitialized()) {
phpCAS::client($sCASVersion, $sCASHost, $iCASPort, $sCASContext, $sServiceBaseURL, false /* session already started */);
}
$sCASCACertPath = Config::Get('cas_server_ca_cert_path'); $sCASCACertPath = Config::Get('cas_server_ca_cert_path');
if (empty($sCASCACertPath)) if (empty($sCASCACertPath)) {
{
// If no certificate authority is provided, do not attempt to validate // If no certificate authority is provided, do not attempt to validate
// the server's certificate // the server's certificate
// THIS SETTING IS NOT RECOMMENDED FOR PRODUCTION. // THIS SETTING IS NOT RECOMMENDED FOR PRODUCTION.
// VALIDATING THE CAS SERVER IS CRUCIAL TO THE SECURITY OF THE CAS PROTOCOL! // VALIDATING THE CAS SERVER IS CRUCIAL TO THE SECURITY OF THE CAS PROTOCOL!
phpCAS::setNoCasServerValidation(); phpCAS::setNoCasServerValidation();
} } else {
else
{
phpCAS::setCasServerCACert($sCASCACertPath); phpCAS::setCasServerCACert($sCASCACertPath);
} }
} }

View File

@@ -11,7 +11,7 @@
* @author Daniel Rokos <daniel.rokos@itopportal.cz> * @author Daniel Rokos <daniel.rokos@itopportal.cz>
* *
*/ */
Dict::Add('CS CZ', 'Czech', 'Čeština', [ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'Class:UserExternal' => 'Externí uživatel', 'Class:UserExternal' => 'Externí uživatel',
'Class:UserExternal+' => 'Uživatel definovaný mimo '.ITOP_APPLICATION_SHORT, 'Class:UserExternal+' => 'Uživatel definovaný mimo '.ITOP_APPLICATION_SHORT,
]); ));

View File

@@ -10,7 +10,7 @@
* @author Erik Bøg <erik@boegmoeller.dk> * @author Erik Bøg <erik@boegmoeller.dk>
* *
*/ */
Dict::Add('DA DA', 'Danish', 'Dansk', [ Dict::Add('DA DA', 'Danish', 'Dansk', array(
'Class:UserExternal' => 'Extern Bruger', 'Class:UserExternal' => 'Extern Bruger',
'Class:UserExternal+' => 'Bruger udenfor '.ITOP_APPLICATION_SHORT, 'Class:UserExternal+' => 'Bruger udenfor '.ITOP_APPLICATION_SHORT,
]); ));

View File

@@ -10,7 +10,7 @@
* @author ITOMIG GmbH <martin.raenker@itomig.de> * @author ITOMIG GmbH <martin.raenker@itomig.de>
* *
*/ */
Dict::Add('DE DE', 'German', 'Deutsch', [ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:UserExternal' => 'Externer Benutzer', 'Class:UserExternal' => 'Externer Benutzer',
'Class:UserExternal+' => 'Extern authentifizierter '.ITOP_APPLICATION_SHORT.'-Benutzer', 'Class:UserExternal+' => 'Extern authentifizierter '.ITOP_APPLICATION_SHORT.'-Benutzer',
]); ));

View File

@@ -0,0 +1,41 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2024 Combodo SAS
* @license https://opensource.org/licenses/AGPL-3.0
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// Class:<class_name>/Attribute:<attribute_code>+
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+
// Class:<class_name>/Stimulus:<stimulus_code>
// Class:<class_name>/Stimulus:<stimulus_code>+
//
// Class: UserExternal
//
Dict::Add('EN GB', 'British English', 'British English', array(
'Class:UserExternal' => 'External user',
'Class:UserExternal+' => 'User authentified outside of '.ITOP_APPLICATION_SHORT,
));

View File

@@ -7,7 +7,7 @@
* @author Miguel Turrubiates <miguel_tf@yahoo.com> * @author Miguel Turrubiates <miguel_tf@yahoo.com>
* @notas Utilizar codificación UTF-8 para mostrar acentos y otros caracteres especiales * @notas Utilizar codificación UTF-8 para mostrar acentos y otros caracteres especiales
*/ */
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:UserExternal' => 'Usuario externo', 'Class:UserExternal' => 'Usuario externo',
'Class:UserExternal+' => 'Usuario autenticado fuera de '.ITOP_APPLICATION_SHORT, 'Class:UserExternal+' => 'Usuario autenticado fuera de '.ITOP_APPLICATION_SHORT,
]); ));

View File

@@ -9,7 +9,7 @@
/** /**
* *
*/ */
Dict::Add('FR FR', 'French', 'Français', [ Dict::Add('FR FR', 'French', 'Français', array(
'Class:UserExternal' => 'Utilisateur externe à '.ITOP_APPLICATION_SHORT, 'Class:UserExternal' => 'Utilisateur externe à '.ITOP_APPLICATION_SHORT,
'Class:UserExternal+' => 'Utilisateur authentifié à l\'extérieur de '.ITOP_APPLICATION_SHORT, 'Class:UserExternal+' => 'Utilisateur authentifié à l\'extérieur de '.ITOP_APPLICATION_SHORT,
]); ));

View File

@@ -9,7 +9,7 @@
/** /**
* *
*/ */
Dict::Add('HU HU', 'Hungarian', 'Magyar', [ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'Class:UserExternal' => 'Külső felhasználó', 'Class:UserExternal' => 'Külső felhasználó',
'Class:UserExternal+' => '', 'Class:UserExternal+' => '',
]); ));

View File

@@ -9,7 +9,7 @@
/** /**
* *
*/ */
Dict::Add('IT IT', 'Italian', 'Italiano', [ Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:UserExternal' => 'Esterno utente', 'Class:UserExternal' => 'Esterno utente',
'Class:UserExternal+' => 'Utente autenticato al di fuori di '.ITOP_APPLICATION_SHORT, 'Class:UserExternal+' => 'Utente autenticato al di fuori di '.ITOP_APPLICATION_SHORT,
]); ));

View File

@@ -10,7 +10,7 @@
* @author Hirofumi Kosaka <kosaka@rworks.jp> * @author Hirofumi Kosaka <kosaka@rworks.jp>
* *
*/ */
Dict::Add('JA JP', 'Japanese', '日本語', [ Dict::Add('JA JP', 'Japanese', '日本語', array(
'Class:UserExternal' => '外部ユーザー', 'Class:UserExternal' => '外部ユーザー',
'Class:UserExternal+' => '外部認証ユーザー', 'Class:UserExternal+' => '外部認証ユーザー',
]); ));

View File

@@ -10,7 +10,7 @@
* @author Jeffrey Bostoen <info@jeffreybostoen.be> (2018 - 2022) * @author Jeffrey Bostoen <info@jeffreybostoen.be> (2018 - 2022)
* *
*/ */
Dict::Add('NL NL', 'Dutch', 'Nederlands', [ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
'Class:UserExternal' => 'Externe gebruiker', 'Class:UserExternal' => 'Externe gebruiker',
'Class:UserExternal+' => 'Gebruiker aangemeld via externe authenticatie', 'Class:UserExternal+' => 'Gebruiker aangemeld via externe authenticatie',
]); ));

View File

@@ -9,7 +9,7 @@
/** /**
* *
*/ */
Dict::Add('PL PL', 'Polish', 'Polski', [ Dict::Add('PL PL', 'Polish', 'Polski', array(
'Class:UserExternal' => 'Użytkownik zewnętrzny', 'Class:UserExternal' => 'Użytkownik zewnętrzny',
'Class:UserExternal+' => 'Użytkownik uwierzytelniony poza '.ITOP_APPLICATION_SHORT, 'Class:UserExternal+' => 'Użytkownik uwierzytelniony poza '.ITOP_APPLICATION_SHORT,
]); ));

View File

@@ -9,7 +9,7 @@
/** /**
* *
*/ */
Dict::Add('PT BR', 'Brazilian', 'Brazilian', [ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
'Class:UserExternal' => 'Usuário externo', 'Class:UserExternal' => 'Usuário externo',
'Class:UserExternal+' => '', 'Class:UserExternal+' => '',
]); ));

View File

@@ -10,7 +10,7 @@
* @author Vladimir Kunin <v.b.kunin@gmail.com> * @author Vladimir Kunin <v.b.kunin@gmail.com>
* *
*/ */
Dict::Add('RU RU', 'Russian', 'Русский', [ Dict::Add('RU RU', 'Russian', 'Русский', array(
'Class:UserExternal' => 'Внешний пользователь', 'Class:UserExternal' => 'Внешний пользователь',
'Class:UserExternal+' => 'Пользователь, аутентифицируемый вне '.ITOP_APPLICATION_SHORT, 'Class:UserExternal+' => 'Пользователь, аутентифицируемый вне '.ITOP_APPLICATION_SHORT,
]); ));

View File

@@ -9,7 +9,7 @@
/** /**
* *
*/ */
Dict::Add('SK SK', 'Slovak', 'Slovenčina', [ Dict::Add('SK SK', 'Slovak', 'Slovenčina', array(
'Class:UserExternal' => 'Externý užívateľ', 'Class:UserExternal' => 'Externý užívateľ',
'Class:UserExternal+' => 'User authentified outside of '.ITOP_APPLICATION_SHORT.'~~', 'Class:UserExternal+' => 'User authentified outside of '.ITOP_APPLICATION_SHORT.'~~',
]); ));

View File

@@ -10,7 +10,7 @@
* @author Izzet Sirin <izzet.sirin@htr.com.tr> * @author Izzet Sirin <izzet.sirin@htr.com.tr>
* *
*/ */
Dict::Add('TR TR', 'Turkish', 'Türkçe', [ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
'Class:UserExternal' => 'Harici kullanıcı', 'Class:UserExternal' => 'Harici kullanıcı',
'Class:UserExternal+' => ITOP_APPLICATION_SHORT.' dışında yetki kontrolü yapılan kullanıcı', 'Class:UserExternal+' => ITOP_APPLICATION_SHORT.' dışında yetki kontrolü yapılan kullanıcı',
]); ));

View File

@@ -9,8 +9,32 @@
/** /**
* @author Robert Deng <denglx@gmail.com> * @author Robert Deng <denglx@gmail.com>
* *
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/ */
Dict::Add('ZH CN', 'Chinese', '简体中文', [ // Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// Class:<class_name>/Attribute:<attribute_code>+
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+
// Class:<class_name>/Stimulus:<stimulus_code>
// Class:<class_name>/Stimulus:<stimulus_code>+
//
// Class: UserExternal
//
Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:UserExternal' => '外部用户', 'Class:UserExternal' => '外部用户',
'Class:UserExternal+' => '用户在'.ITOP_APPLICATION_SHORT.'外部验证身份', 'Class:UserExternal+' => '用户在'.ITOP_APPLICATION_SHORT.'外部验证身份',
]); ));

View File

@@ -27,7 +27,7 @@
SetupWebPage::AddModule( SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file __FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'authent-external/3.2.0', 'authent-external/3.2.1',
array( array(
// Identification // Identification
// //

View File

@@ -11,8 +11,17 @@
* @author Daniel Rokos <daniel.rokos@itopportal.cz> * @author Daniel Rokos <daniel.rokos@itopportal.cz>
* *
*/ */
Dict::Add('CS CZ', 'Czech', 'Čeština', [ Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'Class:UserLDAP' => 'LDAP uživatel', 'Class:UserLDAP' => 'LDAP uživatel',
'Class:UserLDAP+' => 'Uživatel ověřen přes LDAP', 'Class:UserLDAP+' => 'Uživatel ověřen přes LDAP',
'UserLDAP:server' => 'Specifika LDAP', 'UserLDAP:server' => 'Specifika LDAP',
]); ));
//
// Class: UserLDAP
//
Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'Class:UserLDAP/Attribute:ldap_server' => 'Ldap server~~',
'Class:UserLDAP/Attribute:ldap_server+' => '~~',
));

View File

@@ -10,8 +10,17 @@
* @author Erik Bøg <erik@boegmoeller.dk> * @author Erik Bøg <erik@boegmoeller.dk>
* *
*/ */
Dict::Add('DA DA', 'Danish', 'Dansk', [ Dict::Add('DA DA', 'Danish', 'Dansk', array(
'Class:UserLDAP' => 'LDAP-Bruger', 'Class:UserLDAP' => 'LDAP-Bruger',
'Class:UserLDAP+' => 'Bruger der godkendes via LDAP', 'Class:UserLDAP+' => 'Bruger der godkendes via LDAP',
'UserLDAP:server' => 'LDAP specifics~~', 'UserLDAP:server' => 'LDAP specifics~~',
]); ));
//
// Class: UserLDAP
//
Dict::Add('DA DA', 'Danish', 'Dansk', array(
'Class:UserLDAP/Attribute:ldap_server' => 'Ldap server~~',
'Class:UserLDAP/Attribute:ldap_server+' => '~~',
));

View File

@@ -10,10 +10,17 @@
* @author ITOMIG GmbH <martin.raenker@itomig.de> * @author ITOMIG GmbH <martin.raenker@itomig.de>
* *
*/ */
Dict::Add('DE DE', 'German', 'Deutsch', [ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:UserLDAP' => 'LDAP-Benutzer', 'Class:UserLDAP' => 'LDAP-Benutzer',
'Class:UserLDAP+' => 'Benutzer, der via LDAP authentifiziert wird', 'Class:UserLDAP+' => 'Benutzer, der via LDAP authentifiziert wird',
'UserLDAP:server' => 'LDAP-Einstellungen',
));
//
// Class: UserLDAP
//
Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:UserLDAP/Attribute:ldap_server' => 'LDAP-Server', 'Class:UserLDAP/Attribute:ldap_server' => 'LDAP-Server',
'Class:UserLDAP/Attribute:ldap_server+' => 'Optional: LDAP-Server, der zur Authentifizierung verwendet werden soll, falls mehrere LDAP-Server konfiguriert sind.', 'Class:UserLDAP/Attribute:ldap_server+' => 'Optional: LDAP-Server, der zur Authentifizierung verwendet werden soll, falls mehrere LDAP-Server konfiguriert sind.',
'UserLDAP:server' => 'LDAP-Einstellungen', ));
]);

View File

@@ -40,3 +40,12 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:UserLDAP+' => 'User authenticated by LDAP', 'Class:UserLDAP+' => 'User authenticated by LDAP',
'UserLDAP:server' => 'LDAP specifics', 'UserLDAP:server' => 'LDAP specifics',
)); ));
//
// Class: UserLDAP
//
Dict::Add('EN US', 'English', 'English', array(
'Class:UserLDAP/Attribute:ldap_server' => 'Ldap server',
'Class:UserLDAP/Attribute:ldap_server+' => '',
));

View File

@@ -0,0 +1,42 @@
<?php
/**
* Localized data
*
* @copyright Copyright (C) 2024 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
// Dictionnay conventions
// Class:<class_name>
// Class:<class_name>+
// Class:<class_name>/Attribute:<attribute_code>
// Class:<class_name>/Attribute:<attribute_code>+
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>
// Class:<class_name>/Attribute:<attribute_code>/Value:<value>+
// Class:<class_name>/Stimulus:<stimulus_code>
// Class:<class_name>/Stimulus:<stimulus_code>+
//
// Class: UserLDAP
//
Dict::Add('EN GB', 'British English', 'British English', array(
'Class:UserLDAP' => 'LDAP user',
'Class:UserLDAP+' => 'User authenticated by LDAP',
'UserLDAP:server' => 'LDAP specifics',
));

View File

@@ -7,8 +7,17 @@
* @author Miguel Turrubiates <miguel_tf@yahoo.com> * @author Miguel Turrubiates <miguel_tf@yahoo.com>
* @notas Utilizar codificación UTF-8 para mostrar acentos y otros caracteres especiales * @notas Utilizar codificación UTF-8 para mostrar acentos y otros caracteres especiales
*/ */
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', [ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:UserLDAP' => 'Usuario LDAP', 'Class:UserLDAP' => 'Usuario LDAP',
'Class:UserLDAP+' => 'Usuario autenticado vía LDAP', 'Class:UserLDAP+' => 'Usuario autenticado vía LDAP',
'UserLDAP:server' => 'Especificaciones LDAP', 'UserLDAP:server' => 'Especificaciones LDAP',
]); ));
//
// Class: UserLDAP
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:UserLDAP/Attribute:ldap_server' => 'Ldap server~~',
'Class:UserLDAP/Attribute:ldap_server+' => '~~',
));

View File

@@ -9,8 +9,17 @@
/** /**
* *
*/ */
Dict::Add('FR FR', 'French', 'Français', [ Dict::Add('FR FR', 'French', 'Français', array(
'Class:UserLDAP' => 'Utilisateur LDAP', 'Class:UserLDAP' => 'Utilisateur LDAP',
'Class:UserLDAP+' => 'Utilisateur authentifié par un serveur LDAP', 'Class:UserLDAP+' => 'Utilisateur authentifié par un serveur LDAP',
'UserLDAP:server' => 'Champs spécifiques pour LDAP', 'UserLDAP:server' => 'Champs spécifiques pour LDAP',
]); ));
//
// Class: UserLDAP
//
Dict::Add('FR FR', 'French', 'Français', array(
'Class:UserLDAP/Attribute:ldap_server' => 'Ldap server~~',
'Class:UserLDAP/Attribute:ldap_server+' => '~~',
));

View File

@@ -9,8 +9,17 @@
/** /**
* *
*/ */
Dict::Add('HU HU', 'Hungarian', 'Magyar', [ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'Class:UserLDAP' => 'LDAP felhasználó', 'Class:UserLDAP' => 'LDAP felhasználó',
'Class:UserLDAP+' => 'LDAP vagy AD felhasználó', 'Class:UserLDAP+' => 'LDAP vagy AD felhasználó',
'UserLDAP:server' => 'LDAP specifics~~', 'UserLDAP:server' => 'LDAP specifics~~',
]); ));
//
// Class: UserLDAP
//
Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'Class:UserLDAP/Attribute:ldap_server' => 'Ldap server~~',
'Class:UserLDAP/Attribute:ldap_server+' => '~~',
));

View File

@@ -9,8 +9,17 @@
/** /**
* *
*/ */
Dict::Add('IT IT', 'Italian', 'Italiano', [ Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:UserLDAP' => 'Utente LDAP', 'Class:UserLDAP' => 'Utente LDAP',
'Class:UserLDAP+' => 'Utente autenticato da LDAP', 'Class:UserLDAP+' => 'Utente autenticato da LDAP',
'UserLDAP:server' => 'Specifiche LDAP', 'UserLDAP:server' => 'Specifiche LDAP',
]); ));
//
// Class: UserLDAP
//
Dict::Add('IT IT', 'Italian', 'Italiano', array(
'Class:UserLDAP/Attribute:ldap_server' => 'Ldap server~~',
'Class:UserLDAP/Attribute:ldap_server+' => '~~',
));

View File

@@ -10,8 +10,17 @@
* @author Hirofumi Kosaka <kosaka@rworks.jp> * @author Hirofumi Kosaka <kosaka@rworks.jp>
* *
*/ */
Dict::Add('JA JP', 'Japanese', '日本語', [ Dict::Add('JA JP', 'Japanese', '日本語', array(
'Class:UserLDAP' => 'LDAP ユーザー', 'Class:UserLDAP' => 'LDAP ユーザー',
'Class:UserLDAP+' => 'LDAP認証ユーザー', 'Class:UserLDAP+' => 'LDAP認証ユーザー',
'UserLDAP:server' => 'LDAP specifics~~', 'UserLDAP:server' => 'LDAP specifics~~',
]); ));
//
// Class: UserLDAP
//
Dict::Add('JA JP', 'Japanese', '日本語', array(
'Class:UserLDAP/Attribute:ldap_server' => 'Ldap server~~',
'Class:UserLDAP/Attribute:ldap_server+' => '~~',
));

View File

@@ -6,13 +6,21 @@
* @license https://opensource.org/licenses/AGPL-3.0 * @license https://opensource.org/licenses/AGPL-3.0
* *
*/ */
/** /**
* @author Thomas Casteleyn <thomas.casteleyn@super-visions.com> * @author Thomas Casteleyn <thomas.casteleyn@super-visions.com>
* @author Jeffrey Bostoen <info@jeffreybostoen.be> (2018 - 2022) * @author Jeffrey Bostoen <info@jeffreybostoen.be> (2018 - 2022)
*/ */
Dict::Add('NL NL', 'Dutch', 'Nederlands', [ Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
'Class:UserLDAP' => 'LDAP-gebruiker', 'Class:UserLDAP' => 'LDAP-gebruiker',
'Class:UserLDAP+' => 'Gebruiker die aanmeldt via LDAP', 'Class:UserLDAP+' => 'Gebruiker die aanmeldt via LDAP',
'UserLDAP:server' => 'LDAP informatie', 'UserLDAP:server' => 'LDAP informatie',
]); ));
//
// Class: UserLDAP
//
Dict::Add('NL NL', 'Dutch', 'Nederlands', array(
'Class:UserLDAP/Attribute:ldap_server' => 'Ldap server~~',
'Class:UserLDAP/Attribute:ldap_server+' => '~~',
));

View File

@@ -9,8 +9,17 @@
/** /**
* *
*/ */
Dict::Add('PL PL', 'Polish', 'Polski', [ Dict::Add('PL PL', 'Polish', 'Polski', array(
'Class:UserLDAP' => 'Użytkownik LDAP', 'Class:UserLDAP' => 'Użytkownik LDAP',
'Class:UserLDAP+' => 'Użytkownik uwierzytelniony przez LDAP', 'Class:UserLDAP+' => 'Użytkownik uwierzytelniony przez LDAP',
'UserLDAP:server' => 'Serwer LDAP', 'UserLDAP:server' => 'Serwer LDAP',
]); ));
//
// Class: UserLDAP
//
Dict::Add('PL PL', 'Polish', 'Polski', array(
'Class:UserLDAP/Attribute:ldap_server' => 'Ldap server~~',
'Class:UserLDAP/Attribute:ldap_server+' => '~~',
));

View File

@@ -9,8 +9,17 @@
/** /**
* *
*/ */
Dict::Add('PT BR', 'Brazilian', 'Brazilian', [ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
'Class:UserLDAP' => 'Usuário externo via LDAP', 'Class:UserLDAP' => 'Usuário externo via LDAP',
'Class:UserLDAP+' => '', 'Class:UserLDAP+' => '',
'UserLDAP:server' => 'LDAP specifics~~', 'UserLDAP:server' => 'LDAP specifics~~',
]); ));
//
// Class: UserLDAP
//
Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
'Class:UserLDAP/Attribute:ldap_server' => 'Ldap server~~',
'Class:UserLDAP/Attribute:ldap_server+' => '~~',
));

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