Compare commits

...

933 Commits

Author SHA1 Message Date
denis.flaven@combodo.com
052e2a1a42 🔖 Prepare 3.1.2 version 2024-10-03 12:14:32 +02:00
Eric Espie
eb1ecff7d8 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-09-26 17:37:43 +02:00
Eric Espie
8141723869 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	approot.inc.php
#	css/css-variables.scss
#	datamodels/2.x/authent-cas/module.authent-cas.php
#	datamodels/2.x/authent-external/module.authent-external.php
#	datamodels/2.x/authent-ldap/module.authent-ldap.php
#	datamodels/2.x/authent-local/module.authent-local.php
#	datamodels/2.x/combodo-db-tools/module.combodo-db-tools.php
#	datamodels/2.x/itop-attachments/module.itop-attachments.php
#	datamodels/2.x/itop-backup/module.itop-backup.php
#	datamodels/2.x/itop-bridge-virtualization-storage/module.itop-bridge-virtualization-storage.php
#	datamodels/2.x/itop-change-mgmt-itil/module.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/module.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php
#	datamodels/2.x/itop-config/module.itop-config.php
#	datamodels/2.x/itop-core-update/module.itop-core-update.php
#	datamodels/2.x/itop-datacenter-mgmt/module.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-endusers-devices/module.itop-endusers-devices.php
#	datamodels/2.x/itop-files-information/module.itop-files-information.php
#	datamodels/2.x/itop-full-itil/module.itop-full-itil.php
#	datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/module.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-oauth-client/module.itop-oauth-client.php
#	datamodels/2.x/itop-portal-base/module.itop-portal-base.php
#	datamodels/2.x/itop-portal/module.itop-portal.php
#	datamodels/2.x/itop-problem-mgmt/module.itop-problem-mgmt.php
#	datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/module.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/module.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/module.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/module.itop-service-mgmt.php
#	datamodels/2.x/itop-sla-computation/module.itop-sla-computation.php
#	datamodels/2.x/itop-storage-mgmt/module.itop-storage-mgmt.php
#	datamodels/2.x/itop-tickets/module.itop-tickets.php
#	datamodels/2.x/itop-virtualization-mgmt/module.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-welcome-itil/module.itop-welcome-itil.php
#	datamodels/2.x/version.xml
2024-09-26 17:37:07 +02:00
denis.flaven@combodo.com
8cb701bda3 🔖 Prepare 2.7.11 version 2024-09-26 16:53:24 +02:00
jf-cbd
1b29746806 Rename github token 2024-09-23 17:14:41 +02:00
jf-cbd
fb9c317256 Add an action in the workflow to automatically add pull requests to the Combodo PRs dashboard 2024-09-23 14:43:33 +02:00
Molkobain
8c704951e1 N°7730 - Rename constant as it is actually for ID and class selectors, not only IDs 2024-08-19 18:41:32 +02:00
Eric Espie
24c23628d6 N°7730 - code hardening 2024-08-19 15:15:22 +02:00
jf-cbd
141c22ff67 Fix selectize bug when multiple selectize fields exist on the same page 2024-07-17 14:17:09 +02:00
Thomas Casteleyn
1e3c425e81 N°7645 - PHP 8.1: Fix usage of strpos() & str_replace() with null value when compiling empty dictionary (#600)
* fix(compiler): provide empty string instead of null value

* Apply review suggestions
2024-07-10 10:07:12 +02:00
Benjamin Dalsass
b2e3fb6354 N°7024 - Fix opening an object with abstract class indirect linked set in Portal 2024-07-05 09:10:19 +02:00
jf-cbd
45ce68e16e Merge remote-tracking branch 'refs/remotes/origin/support/3.0' into support/3.1 2024-07-04 13:55:11 +02:00
jf-cbd
1aef576403 N°7604 - Security hardening 2024-07-04 13:52:19 +02:00
jf-cbd
bfd9be8280 Merge remote-tracking branch 'refs/remotes/origin/support/3.0' into support/3.1 2024-07-04 10:59:57 +02:00
jf-cbd
96e1388dde N°7603 - Security hardening + UI blocks examples updated 2024-07-04 10:56:08 +02:00
Timothee
86b48b8980 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-07-03 17:00:22 +02:00
Timothee
69c8791fc5 Fix merge conflit resolution d3b9965283 2024-07-03 16:48:08 +02:00
Eric Espie
5fd8678a3a N°7619 - Object deletion not cascaded to legacy extensions 2024-07-02 10:38:25 +02:00
Eric Espie
86df9ac035 N°7619 - Object deletion not cascaded to legacy extensions 2024-07-02 10:37:36 +02:00
Molkobain
3f997b416f N°7313 - Revert "fix" as it breaks grouping on ext. keys, OQL functions and more
We keep the enhancements to the test framework though.
2024-06-28 14:54:51 +02:00
Eric Espie
cceb6809e7 Fix CI 2024-06-24 14:20:33 +02:00
Eric Espie
48b559472e Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php
2024-06-24 14:02:04 +02:00
Eric Espie
cddc452693 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-06-24 13:55:29 +02:00
Eric Espie
0904a21e3f Cleanup ItopTestCase 2024-06-24 11:50:37 +02:00
Timothee
66230199f8 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-06-21 12:41:17 +02:00
Timothee
1f1a2b660f N°7581 Improve error message readability during object creation/modification in the portal (regression introduced with N°7545) 2024-06-21 12:36:52 +02:00
Molkobain
4f36369ea1 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-06-21 11:32:21 +02:00
Molkobain
33a906f11a Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-06-21 11:29:18 +02:00
Molkobain
82d11eeb47 N°7127 - Upgrade handlebars.js to v4.7.8 2024-06-21 11:19:39 +02:00
Eric Espie
b8bf66031b Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-06-20 11:07:57 +02:00
Eric Espie
2596a150bf Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-06-20 11:07:36 +02:00
Eric Espie
142d6c8993 N°7533 - Detect and warns on Galera clusters 2024-06-20 11:06:57 +02:00
Timothee
d70e5dff45 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-06-17 16:55:26 +02:00
Timothee
c4fc0ed982 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-06-17 16:51:30 +02:00
Timothee
320922a13d N°7545 Correctly display error message 2024-06-17 16:49:33 +02:00
Eric Espie
f96d28a0a8 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-06-12 16:48:39 +02:00
Eric Espie
d3b9965283 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	core/cmdbsource.class.inc.php
2024-06-12 16:48:06 +02:00
Eric Espie
f03d731b1d N°7533 - Prevent installation of iTop on Galera clusters 2024-06-12 16:14:23 +02:00
Eric Espie
3e3ac0d83f N°7542 - code hardening 2024-05-30 12:04:36 +02:00
Eric Espie
32835f70b9 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-05-29 18:19:48 +02:00
Eric Espie
63cf78f64d Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	pages/preferences.php
2024-05-29 18:18:55 +02:00
Eric Espie
8be7628668 N°7548 - Code hardening 2024-05-29 18:11:36 +02:00
Eric Espie
5e64be8580 N°7491 - Fix unit tests 2024-05-24 09:48:07 +02:00
Eric Espie
a0b76a25be N°7491 - Fix Events during DBObject CRUD 2024-05-24 09:35:00 +02:00
Eric Espie
e2f2afad54 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-05-21 15:07:28 +02:00
Eric Espie
f632cf3155 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-05-21 15:07:08 +02:00
Eric Espie
13dfdc3d9d Merge remote-tracking branch 'origin/support/2.7' into support/3.1 2024-05-21 14:29:10 +02:00
Eric Espie
62caf16153 N°7364 - Code hardening 2024-05-21 14:20:30 +02:00
jf-cbd
020a37ead9 N°7526 - Profile input unfocus: Previously entered value still filters 2024-05-17 10:58:29 +02:00
odain
163a3afc0f N°7426 - no session created - replace php_sapi_name() by PHP_SAPI in unattended 2024-05-16 15:31:08 +02:00
odain
35265718c1 Merge branch 'support/3.0' into support/3.1 2024-05-16 14:37:41 +02:00
odain
d98e35d918 Merge branch 'support/2.7' into support/3.0 2024-05-16 14:13:24 +02:00
odain
f8b54be896 N°7426 - no session created - replace php_sapi_name() by PHP_SAPI 2024-05-16 14:10:54 +02:00
Romain Quetiez
a88ce075cc Merge branch 'support/3.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php
2024-05-16 14:07:36 +02:00
Romain Quetiez
c6f3e36451 Merge branch 'support/2.7' into support/3.0
# Conflicts:
#	tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php
#	tests/php-unit-tests/unitary-tests/core/iTopConfigParserTest.php
2024-05-16 10:09:11 +02:00
Romain Quetiez
53dc452d61 Avoid unnecessary custom test environment compilations (base compilation of file modification time) 2024-05-16 09:53:04 +02:00
Romain Quetiez
ccaf2dc5b7 Make the tests compatible with windows (and linux) 2024-05-16 09:53:04 +02:00
Molkobain
d9581a083d Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-05-07 10:41:04 +02:00
Molkobain
46738d4ba4 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-05-07 10:38:50 +02:00
Molkobain
5d5df5ad1a N°7255 - Fix misc. stylesheets not working in portal since N°7047 2024-05-07 10:37:39 +02:00
jf-cbd
8c46c99ecc N°7313 - Bad display of single quotes in charts 2024-04-30 15:04:28 +02:00
jf-cbd
02be397e8f Merge remote-tracking branch 'refs/remotes/origin/support/3.0' into support/3.1 2024-04-30 10:57:06 +02:00
jf-cbd
61469a28b9 N°7445 - Invalid Unicode escape sequence on dashlet Header with statistics 2024-04-30 10:56:09 +02:00
jf-cbd
9ff54cead8 Merge remote-tracking branch 'refs/remotes/origin/support/3.0' into support/3.1 2024-04-30 08:14:11 +02:00
jf-cbd
dbcbb187b2 N°7445 - Invalid Unicode escape sequence on dashlet Header with statistics 2024-04-30 08:13:37 +02:00
jf-cbd
71b4d672d4 Merge remote-tracking branch 'refs/remotes/origin/support/3.0' into support/3.1
# Conflicts:
#	pages/ajax.render.php
2024-04-30 08:04:45 +02:00
jf-cbd
93bba66323 N°7445 - Invalid Unicode escape sequence on dashlet Header with statistics 2024-04-30 08:03:14 +02:00
Molkobain
0a95aa385a Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/unitary-tests/application/applicationextension/Delta/application-extension-usages-in-snippets.xml
2024-04-29 14:03:16 +02:00
Molkobain
cab6394cba Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-04-29 13:58:13 +02:00
Molkobain
32140b360f Cherry pick fixes from 59a955f4 2024-04-29 11:45:09 +02:00
Benjamin Dalsass
06a8481511 N°7279 - AttributeClass defined in XML datamodel compilation issue 2024-04-25 11:39:54 +02:00
jf-cbd
59a955f491 🐛 N°7313 - Bad display of single quotes in charts (#627)
* 🐛 N°7313 - Bad display of single quotes in charts
* Fix and improve ItopCustomDatamodelTestCase
2024-04-24 17:03:47 +02:00
jf-cbd
2ffcfd2f57 Merge remote-tracking branch 'refs/remotes/origin/support/3.0' into support/3.1 2024-04-24 12:00:47 +02:00
jf-cbd
e657052d17 Merge remote-tracking branch 'refs/remotes/origin/support/2.7' into support/3.0 2024-04-24 11:58:13 +02:00
jf-cbd
d85767a838 Update test to run only on commmunity builds 2024-04-24 11:14:40 +02:00
jf-cbd
e56d5ed3aa Merge remote-tracking branch 'refs/remotes/origin/support/3.0' into support/3.1
# Conflicts:
#	datamodels/2.x/itop-hub-connector/launch.php
#	setup/wizardsteps.class.inc.php
2024-04-23 14:13:40 +02:00
jf-cbd
e5a8bd61b0 Merge remote-tracking branch 'refs/remotes/origin/support/2.7' into support/3.0
# Conflicts:
#	datamodels/2.x/itop-hub-connector/launch.php
2024-04-23 14:03:15 +02:00
jf-cbd
eeec57536b Security hardening 2024-04-23 11:55:39 +02:00
jf-cbd
7fba61ff35 N°7445 - Invalid Unicode escape sequence on dashlet Header with statistics 2024-04-19 15:21:48 +02:00
jf-cbd
1bf156bdda Revert "Merge branch 'refs/heads/support/3.0' into support/3.1"
This reverts commit 1164e757de, reversing
changes made to 1235452a1b.
2024-04-19 15:16:57 +02:00
jf-cbd
1164e757de Merge branch 'refs/heads/support/3.0' into support/3.1
# Conflicts:
#	pages/ajax.render.php
2024-04-19 11:24:56 +02:00
jf-cbd
514e0b80a5 N°7445 - Invalid Unicode escape sequence on dashlet Header with statistics 2024-04-19 11:17:09 +02:00
Molkobain
1235452a1b Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-04-19 09:24:21 +02:00
Molkobain
35f4ab4941 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-04-19 09:22:32 +02:00
Molkobain
16ff6341d0 N°7455 - Fix regression from 4c784886, wrong class tested 2024-04-19 09:14:53 +02:00
Molkobain
12ad3c2732 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-04-18 18:49:08 +02:00
Molkobain
ac826cb9f1 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-04-18 18:48:24 +02:00
Molkobain
9dab8679d6 N°7448 - Update dictionary entry 2024-04-18 18:44:20 +02:00
Molkobain
4f3b25aa46 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-04-18 18:17:41 +02:00
Molkobain
f737bcb9a0 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-04-18 18:16:24 +02:00
Molkobain
4c78488644 N°7455 - Ensure form renderer class extends FormRenderer 2024-04-18 18:15:02 +02:00
odain
b4cdf5cf6d Merge branch 'support/3.0' into support/3.1 2024-04-15 10:23:22 +02:00
odain
77cc4672b0 Merge branch 'support/2.7' into support/3.0 2024-04-15 10:20:49 +02:00
odain
b65e931c4c N°7439 - setup wizard broken on essential targets after fresh install via unattended CLI 2024-04-15 09:57:54 +02:00
odain
46b0dabb1d Merge branch 'support/3.0' into support/3.1 2024-04-12 17:18:44 +02:00
odain
dfbfab7005 N°7407 - adapt test to 3.0 test SDK evolutions 2024-04-12 17:17:03 +02:00
odain
aa831b632c Merge branch 'support/2.7' into support/3.0 2024-04-12 17:16:22 +02:00
odain
6cb3519308 N°7407 - test readability 2024-04-12 16:59:56 +02:00
odain
cfb9fae648 N°7407 - fix previous commit 2024-04-12 16:49:11 +02:00
odain
f4e791734f N°7407 - remove phpunit annotation 2024-04-12 14:49:20 +02:00
odain
6653ab0668 N°7407 - fix missing extension installation via unattended from production-modules or extension folder 2024-04-12 13:25:40 +02:00
odain
7ab258ba03 N°7439 - setup wizard broken on essential targets 2024-04-12 11:32:04 +02:00
odain
b5af30a93f N°7407 - refactor unattended tests to make work anywhere 2024-04-12 11:32:04 +02:00
Molkobain
2294fce5da Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-04-12 10:06:27 +02:00
Molkobain
ce5c05234d Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-04-12 10:02:19 +02:00
Molkobain
bbfa601ab1 N°7446 - Add temporary workaround to fix an issue due to DB views creation. 2024-04-12 09:59:14 +02:00
odain
c9a9ec2fed Merge branch 'support/3.0' into support/3.1 2024-04-11 18:55:25 +02:00
odain
83764deedb Merge branch 'support/2.7' into support/3.0 2024-04-11 18:55:00 +02:00
odain
172b1cb1ff N°7407 - add phpunit annotation to exclude tests on top of targets 2024-04-11 18:54:43 +02:00
Molkobain
5f35effad9 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-04-11 16:58:02 +02:00
Molkobain
e1374a0e6b Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-04-11 16:56:25 +02:00
Molkobain
ca356859a3 N°7446 - Add comment for better understanding 2024-04-11 16:55:48 +02:00
Molkobain
5efe294895 N°7446 - Fix custom datamodel test class not creating DB tables correctly 2024-04-11 16:50:27 +02:00
odain
e6fa589412 Merge branch 'support/3.0' into support/3.1 2024-04-10 14:14:28 +02:00
odain
18d0b88531 N°7407 - fix merge 2024-04-10 14:11:58 +02:00
odain
3139a0b610 Merge branch 'support/2.7' into support/3.0 2024-04-10 14:06:41 +02:00
odain
e0170ccc7e N°7407 - InstallationFileServiceTest relies on local installation.xml to reduce maintenance 2024-04-10 14:05:39 +02:00
jf-cbd
ffefa5495a Adding CSRF protection to simulation step in CSV import 2024-04-10 10:34:06 +02:00
odain
a371b7f03b N°7407 - fix ci itop installation 2024-04-09 21:21:46 +02:00
odain
de97a6263f N°7407 - fix test and itop install in ci envt 2024-04-09 16:52:06 +02:00
odain
f0c5264fc8 Merge branch 'support/3.0' into support/3.1 2024-04-09 11:06:38 +02:00
odain
367aac3e04 Merge branch 'support/2.7' into support/3.0 2024-04-09 11:06:17 +02:00
odain
3b78885f38 N°7407 - unattended test cleanup for PHP 8.x deprecations 2024-04-09 11:06:03 +02:00
odain
e6814d6860 N°7407 - adapt unattended test to 3.1 installation.xml choices 2024-04-09 10:55:05 +02:00
Molkobain
79198f51a3 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-04-09 10:52:00 +02:00
odain
f14b4c32be N°7407 - adapt unattended test to 3.0 installation.xml choices 2024-04-09 10:46:44 +02:00
Molkobain
6be5264fe9 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-04-09 10:41:08 +02:00
Molkobain
6a30d6caa9 N°7438 - Dashboard: Fix crash when closing the editor modal 2024-04-09 10:40:06 +02:00
odain
bc8543d88c Merge branch 'support/3.0' into support/3.1
# Conflicts:
#	setup/modulediscovery.class.inc.php
#	setup/runtimeenv.class.inc.php
#	setup/unattended-install/unattended-install.php
#	tests/setup_params/default-params.xml
2024-04-09 10:11:06 +02:00
odain
a51242dc36 N°7407 - remove 2.7 requires in unattended service 2024-04-09 10:08:43 +02:00
odain
64ba706083 Merge branch 'support/2.7' into support/3.0
# Conflicts:
#	setup/applicationinstaller.class.inc.php
#	setup/modulediscovery.class.inc.php
#	setup/runtimeenv.class.inc.php
#	setup/unattended-install/unattended-install.php
#	tests/setup_params/default-params.xml
2024-04-09 10:07:40 +02:00
odain
ed562c9f73 N°7407 - fix unattended test enhancement 2024-04-09 10:02:06 +02:00
odain-cbd
85c576a986 N°7407 - N°7306 - Ease iTop installation via unattended CLI by using installation.xml choices (#641)
* N°7306 - Use iTop configuration settings to run unattended installation (instead of XML file settings)

* 7306 - fix infinite loop with db_tls.ca null

* 7306 - complete fields to use from itop configuration instead of XML setup

* fix using default language from conf

* 6365 - temp work

* 6365 - add option to select modules from installation.xml

* 6365-select modules option in unattended install

* 6365 - pass env to service + debug failed test

* 6365 - debug ci again + separate process annotation

* 6365 - fix test + cleanup

* 6365 - ci using use_installation_xml mode

* 6365 - ci using use_installation_xml mode

* 6365 - pass selected_modules to unattended

* N°6365 - Compute selected modules based on selected extensions coming from XML setup

* switch constr parameters and fix call from unattended cli

* 6365 - use use_installation_xml for unattended install only when no selected modules already provided

* test ci XML setup including selected extensions but no modules

* test ci installing iTop without selected modules/extenesions: guess via installation.xml

* same but without even providing XML setup - comment it in ci_description.ini

* 7306 - cleanup requires

* use infra master

* N°6365 - make current unattended CLI work with any iTop version (CLIPage compatibility)

* N°6365 - log which modules will be installed during setup

* N°6365 - unattended documentation + bash helper

* 6365- fix warning due to copies index access

* 6365 - enhance traces feedback to understant which and how modules are computes

* 6365 - enhance bash CLI + doc

* 6365 - fix require clipage compatibility

* 6365 - add return for better cli ouput

* 6365 - enhance ouput messages

* Document the usage and harmonize argument names (still not perfect)

* 6365 - fix use of new param param-file

* 6365 - fix test + vardump cleanup

* N°6365 - use underscore for unattended install options as advices in the PR

* 6365 -enhance test by using PHP_BINARY

---------

Co-authored-by: Romain Quetiez <romain.quetiez@combodo.com>
2024-04-09 10:00:58 +02:00
Molkobain
3deb74a4d1 N°6964 - Allow folder creation to be recursive 2024-04-08 21:46:02 +02:00
jf-cbd
1f2469e1e4 Remove mixed type uses in itop 3.1 2024-04-08 17:24:51 +02:00
jf-cbd
22af1a8787 Remove namespace for 3.1 2024-04-08 17:10:05 +02:00
jf-cbd
cd34f8b5eb Removed namespaces in 3.1 2024-04-08 12:19:43 +02:00
jf-cbd
8357c86361 🐛 Fix php 7.4 compatibility in bulkchange.class.inc.php (for itop 3.1) 2024-04-08 11:10:12 +02:00
jf-cbd
b10d381dda N°7374 - Security hardening 2024-04-05 14:40:56 +02:00
Eric Espie
9dc7b6dc0c N°7405 - Fix unit tests 2024-04-05 11:29:41 +02:00
Eric Espie
1be67f349f N°7405 - Cache error message 2024-04-05 11:08:47 +02:00
odain
8647414a33 N°5120 - PR merge adaptation to CLIPage 3.0 2024-04-02 15:05:35 +02:00
odain
a58f0bfc0b Merge branch 'support/3.0' into support/3.1 2024-04-02 15:02:26 +02:00
odain
ff1305165e N°5120 - PR merge adaptation to CLIPage 2024-04-02 14:42:21 +02:00
Molkobain
a468bf8f7e Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-04-02 10:38:30 +02:00
Molkobain
65c706fdfe Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-04-02 10:37:08 +02:00
Stephen Abello
25f266ef66 N°4631 - Update fullscreen class 2024-04-02 10:13:28 +02:00
odain-cbd
5a34c76cc4 N°5120 - Unattended install : use cty toolkit version (#624)
* N°5120 - Unattended install : use cty toolkit version

* N°5120 - CLI mode validation

* 5120 - bring CI enhancements

* 5120 - bring back saas use-itop-config option: to read db settings from conf directly

* 5120 - move unattended script in setup folder/unattended-install

* 5120 - use-itop-config option: take db_tls_enabled and db_tls_ca into account

* 5120 - move test

* 5120 - put ci enhancements back - logs and conf preservation with install mode

* 5120 - keep PR simple - remove saas use-itop-config option for now

* 5120 - remove ci enhancement to preserve configuration

* Update setup/unattended-install/README.md

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

* 5120 - documentation

* 5120 - fix log level

* 5120 - fix test

* fix phpunit test comment

---------

Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>
2024-03-29 11:09:27 +01:00
Stephen Abello
5efb1a0872 N°4631 - Fix a display issue when description field is fullscreen white using vertical tabs 2024-03-28 17:01:45 +01:00
Eric Espie
7f9b60d66f N°5913 - Add Event Listener methods & enhance methods management 2024-03-28 11:02:55 +01:00
Eric Espie
1278c4b196 N°7165 - Error message on application upgrade fatal Error due to CSS compilation 2024-03-27 11:11:55 +01:00
Eric Espie
a7f3bf9a56 N°7294 - EVENT_ADD_ATTACHMENT_TO_OBJECT not triggered 2024-03-27 10:34:48 +01:00
Eric Espie
4eed5bdeb9 N°7165 - Error message on application upgrade fatal Error due to CSS compilation 2024-03-27 10:12:21 +01:00
Eric Espie
7b999cef50 Compilation issue 2024-03-26 11:57:08 +01:00
Eric Espie
90458f6048 N°6974 - Flatten classes in datamodel - Allow _delta="merge" in strict mode 2024-03-22 10:11:25 +01:00
Molkobain
3653a4bc90 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	css/setup.css
2024-03-21 15:52:57 +01:00
Molkobain
bdfd956825 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	css/setup.css
#	setup/setuppage.class.inc.php
2024-03-21 15:37:33 +01:00
Molkobain
da99a250bf N°7075 - Add check for Content Security Policies (CSP) in the setup 2024-03-21 14:20:22 +01:00
Molkobain
5554241877 N°7122 N°4164 - Fix wrong merge in b08b5297 2024-03-21 14:04:18 +01:00
Eric Espié
2f30a0146e N°6974 - Flatten classes in datamodel (#603)
* N°6974 - Flatten classes in datamodel

* N°6882 - LoadDelta() in lax/strict mode

* N°6974 - Flatten classes in datamodel

* N°6882 - LoadDelta() in lax/strict mode

* N°7186 - Code hardening

* N°6974 - Flatten classes in datamodel

* N°6660 and N°7318 - Support delete_if_exists and define_if_not_exists in XML injection

* N°6974 - Flatten classes in datamodel

* Apply suggestions from code review

* Update core/designdocument.class.inc.php

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2024-03-19 13:53:14 +01:00
Pierre Goiffon
1de9e704fc Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-03-18 18:19:52 +01:00
Pierre Goiffon
f4b9a9a5fe N°6455 Fix datepicker widget icon on button
The buttonText is now escaped

(cherry picked from commit fa5d03fc6e)
2024-03-18 18:19:30 +01:00
Pierre Goiffon
fa5d03fc6e N°6455 Fix datepicker widget icon on button
The buttonText is now escaped
2024-03-18 18:15:39 +01:00
jf-cbd
8ffcb01c43 Merge branch 'support/3.0' into support/3.1 2024-03-14 17:00:20 +01:00
jf-cbd
5c46b4ef4a Added modules to GetLtsCompatibleModulesList (Dictionnarie tests) 2024-03-14 16:59:32 +01:00
Pierre Goiffon
79cdb837b5 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	core/log.class.inc.php
2024-03-14 09:13:56 +01:00
Pierre Goiffon
a97935ca01 N°7336 DeprecatedCallsLog robustness (#632)
Was creating PHP notices when deprecated method caller wasn't a class/method
Found in iTop 3.2.0-dev with itop-object-copier copy.php : it is calling WebPage::add_linked_script directly inside the copy.php script (no class nor function)

Co-authored-by: odain <olivier.dain@combodo.com>
2024-03-13 15:38:21 +01:00
Molkobain
87181669d1 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-03-13 15:18:33 +01:00
Molkobain
efd7fb0f59 N°7331 - Fix wrong file extension in .htaccess file 2024-03-13 15:16:46 +01:00
Pierre Goiffon
71a0f8b87d Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-03-13 10:03:48 +01:00
Pierre Goiffon
a4edf8cb21 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	tests/php-unit-tests/unitary-tests/setup/SetupUtilsTest.php
2024-03-13 09:59:28 +01:00
Pierre Goiffon
dbd5ba0377 N°7302 Fix SetupUtilsTest::testHumanReadableSize 2024-03-13 09:56:31 +01:00
Pierre Goiffon
5d6f293956 N°7302 Report SetupUtilsTest::testHumanReadableSize in the 2.7 branch 2024-03-13 09:48:46 +01:00
Pierre Goiffon
65e6c84477 N°7302 Fix SetupUtilsTest::testHumanReadableSize 2024-03-13 09:37:34 +01:00
Pierre Goiffon
9cc7ccb1cd Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-03-13 09:27:39 +01:00
Pierre Goiffon
a337ef3d88 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	setup/modelfactory.class.inc.php
#	setup/setuputils.class.inc.php
2024-03-13 09:27:06 +01:00
Pierre Goiffon
986c24d777 N°7302 Fix unit name in \SetupUtils::HumanReadableSize (#626) 2024-03-12 18:09:29 +01:00
Pierre Goiffon
763112c179 N°7344 rest.php core/get : add try/catch around query execution (#622)
Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2024-03-12 18:08:04 +01:00
Pierre Goiffon
a5efd981d8 N°7343 Catch ParseError when loading dict files in setup (#615) 2024-03-12 18:05:38 +01:00
Pierre Goiffon
c0e4d04a1c Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/unitary-tests/core/DBObjectTest.php
2024-03-12 17:36:04 +01:00
Pierre Goiffon
289ca7b505 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-03-12 17:34:51 +01:00
Pierre Goiffon
2922b22478 \DBObject::GetSearchForUniquenessRule remove wrong @api 2024-03-12 17:34:28 +01:00
Pierre Goiffon
2af05a437e N°4314 - Fix Uniqueness rules not working with Silo
(cherry picked from commit e8c11f38d2)
2024-03-12 17:32:56 +01:00
vdumas
71d9536bc4 N°7268 Method SetComputedDate fails on Date only attribute
(cherry picked from commit b6caa51552)
(cherry picked from commit c8810708ef)
2024-03-12 17:29:04 +01:00
Molkobain
b08b5297cf N°7122 N°4164 - Adjustment to Symfony 5.4 2024-03-11 21:57:37 +01:00
Molkobain
3e6e096f62 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig
2024-03-11 21:27:59 +01:00
Molkobain
6377a738c5 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-03-11 21:18:53 +01:00
Molkobain
a9f8dcc5e8 N°7122 N°4164 - Portal: Hide log off button when user can't actually log off (eg. SSO using SAML or other providers) (#599)
* N°7122 N°4164 - Portal: Hide log off button when user can't actually log off (eg. SSO using SAML or other providers)

* N°7122 - Fix typo

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

---------

Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>
2024-03-11 21:13:09 +01:00
Pierre Goiffon
9750fec959 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-03-11 16:40:14 +01:00
Pierre Goiffon
969a301cbb Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-03-11 16:40:01 +01:00
Pierre Goiffon
c325294e17 N°6599 Update moment from 2.22.2 to 2.30.1 2024-03-11 16:39:05 +01:00
Benjamin Dalsass
af2c6d9ab8 N°7279 - AttributeClass defined in XML datamodel compilation issue
- make more_values property optional
2024-03-11 16:03:38 +01:00
Eric Espie
cf6e8ab22a N°6974 - Add unit tests for compatibility validation 2024-03-08 13:29:04 +01:00
Pierre Goiffon
9032dac314 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-03-08 11:13:43 +01:00
Pierre Goiffon
a29b0a8e33 N°6455 Update JQuery-UI from 1.12.1 to 1.13.2 2024-03-08 11:13:32 +01:00
Pierre Goiffon
c1efad7556 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-03-07 11:11:34 +01:00
Pierre Goiffon
f78b57521a Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-03-07 11:11:19 +01:00
Pierre Goiffon
da490739be 💡 Fix \CMDBSource::Query phpdoc block 2024-03-07 11:10:53 +01:00
Pierre Goiffon
bf4b07a366 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-03-04 08:41:25 +01:00
Pierre Goiffon
48de13b5cf Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-03-04 08:32:59 +01:00
Pierre Goiffon
b867faa355 Fix DBObjectTest
Error was "Class 'Combodo\iTop\Test\UnitTest\Core\DBObjectSet' not found"
2024-03-04 08:09:35 +01:00
Pierre Goiffon
849c495093 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-03-01 17:26:19 +01:00
Pierre Goiffon
e878938e25 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-03-01 17:26:05 +01:00
jf-cbd
7453cc184f ✏️ fix a typo 2024-03-01 17:25:08 +01:00
Eric Espie
3b9ca4ad18 N°7310 - New Event to handle available Transitions 2024-03-01 09:39:00 +01:00
Anne-Cath
eddc750ca8 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	pages/audit.php
#	tests/php-unit-tests/unitary-tests/core/DBSearchTest.php
2024-02-29 16:53:39 +01:00
Anne-Cath
f3abe1ff13 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	pages/audit.php
2024-02-29 16:50:04 +01:00
Anne-Catherine
473cf004b6 N°6968 - Audit duration : add of a rule multiplie by 4 the time of response (#575) 2024-02-29 16:33:04 +01:00
Pierre Goiffon
46273dfc12 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-02-28 16:43:51 +01:00
Pierre Goiffon
24f1cf8ca1 💡 Fix deprecated in JSDoc 2024-02-28 16:43:40 +01:00
Anne-Catherine
86bf6ba0b0 N°5547 - Object deletion fails if friendlyname too long (#529) 2024-02-27 17:59:37 +01:00
Anne-Catherine
82e19f6eca N°6543 - Fix display of AttributeText with width parameter (#521) 2024-02-27 17:42:59 +01:00
Eric Espie
8f1d8b1dc2 N°7294 - EVENT_ADD_ATTACHMENT_TO_OBJECT not triggered on the object 2024-02-27 17:39:53 +01:00
Molkobain
3cc3f0e4ff N°7288 - Fix page crash due to unescaped characters in relations row actions (#620) 2024-02-27 08:34:34 +01:00
Molkobain
b7f43ba22b Fix test case case when context tag is present more than once 2024-02-21 18:27:21 +01:00
Molkobain
b13e0c92a5 Fix test case dependant to setup option Change Management Simple vs ITIL 2024-02-21 18:13:27 +01:00
jf-cbd
aa81c94dad Improved test and test cleanup 2024-02-21 16:30:00 +01:00
Benjamin Dalsass
f41f2a063d N°7279 - AttributeClass defined in XML datamodel compilation issue 2024-02-21 10:41:47 +01:00
Pierre Goiffon
095c94a917 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-02-20 12:11:57 +01:00
Pierre Goiffon
102a4a0c75 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	core/attributedef.class.inc.php
2024-02-20 12:11:02 +01:00
Pierre Goiffon
f6fec506b1 💡 Some PHPDoc hints on value types to pass to DBObject::Set 2024-02-20 12:10:00 +01:00
Pierre Goiffon
7d0801f74f Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-02-20 09:27:26 +01:00
Pierre Goiffon
3b9f281afd N°7246 DictionariesConsistencyTest : remove combodo-approval-light
We have an invalid CS dict in the 1.2.3 module version
2024-02-20 09:27:15 +01:00
Pierre Goiffon
bcba666483 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-02-20 09:12:47 +01:00
Pierre Goiffon
ec465174f7 N°7246 DictionariesConsistencyTest : remove syntax incompatible with PHP < 7.3 2024-02-20 09:11:47 +01:00
Molkobain
9f675fef02 N°7231 - PHP 8.1: Migrate deprecated usages of rawurlencode() with null value 2024-02-19 20:19:36 +01:00
Molkobain
1d106eb33a Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-02-19 09:51:08 +01:00
Molkobain
31bd763b90 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-02-19 09:49:26 +01:00
Molkobain
5c12151c26 📝 Update PR template 2024-02-19 09:46:12 +01:00
vdumas
961315cf34 N°6826 - Add SQL value for file attribute in DocumentFile 2024-02-16 10:37:44 +01:00
Pierre Goiffon
95fef002a6 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-02-15 14:43:19 +01:00
Pierre Goiffon
9777ac1a5c N°7246 DictionariesConsistencyTest : don't test extensions modules in old iTop builds 2024-02-15 14:39:00 +01:00
Pierre Goiffon
ba0020fa63 N°7246 Fix dict files : translated keys with tildes in /dictionaries/** 2024-02-15 10:59:10 +01:00
Pierre Goiffon
1e83f2eb60 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	dictionaries/ui/layouts/navigation-menu/pt_br.dictionary.itop.navigation-menu.php
#	dictionaries/ui/layouts/navigation-menu/zh_cn.dictionary.itop.navigation-menu.php
2024-02-15 10:55:54 +01:00
Pierre Goiffon
dd27a3ebb4 N°7246 Fix dict files : translated keys with tildes in /dictionaries/** 2024-02-15 10:50:08 +01:00
Pierre Goiffon
54439ad529 N°7249 DictionariesConsistencyTest : also scan /dictionaries sub-directories
Since 3.0.0 we introduced sub directories
2024-02-15 10:46:42 +01:00
Pierre Goiffon
2c61ac4949 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-02-15 09:47:55 +01:00
Pierre Goiffon
8f7bf00551 N°7246 DictionariesConsistencyTest : add /extensions for local debugging 2024-02-15 09:46:14 +01:00
Molkobain
0483cea226 N°7212 - PHP 8.1: Migrate remaining usages of strlen() with null value (#607) 2024-02-14 19:29:26 +01:00
Molkobain
922a842e96 N°7213 - PHP 8.1: Migrate remaining usages of md5() with null value (#608)
* N°7213 - PHP 8.1: Migrate remaining usages of md5() with null value

* Update core/attributedef.class.inc.php
2024-02-14 19:27:14 +01:00
Pierre Goiffon
e4f068a518 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-02-14 15:17:14 +01:00
Pierre Goiffon
c020de59a7 📝 tests README : retrofit of the support/3.2 version 2024-02-14 15:16:58 +01:00
vdumas
3c2350961d N°7217 - Not able to create a link between "Audit Domain" and "Audit category" with an "Audit Manager" profile 2024-02-14 14:38:21 +01:00
Pierre Goiffon
391c78ae42 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	datamodels/2.x/combodo-db-tools/dictionaries/zh_cn.dict.combodo-db-tools.php
#	datamodels/2.x/itop-attachments/dictionaries/zh_cn.dict.itop-attachments.php
#	datamodels/2.x/itop-backup/dictionaries/da.dict.itop-backup.php
#	datamodels/2.x/itop-backup/dictionaries/it.dict.itop-backup.php
#	datamodels/2.x/itop-backup/dictionaries/ja.dict.itop-backup.php
#	datamodels/2.x/itop-backup/dictionaries/sk.dict.itop-backup.php
#	datamodels/2.x/itop-backup/dictionaries/tr.dict.itop-backup.php
#	datamodels/2.x/itop-config-mgmt/dictionaries/it.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config/dictionaries/da.dict.itop-config.php
#	datamodels/2.x/itop-config/dictionaries/it.dict.itop-config.php
#	datamodels/2.x/itop-config/dictionaries/ja.dict.itop-config.php
#	datamodels/2.x/itop-config/dictionaries/sk.dict.itop-config.php
#	datamodels/2.x/itop-config/dictionaries/tr.dict.itop-config.php
#	datamodels/2.x/itop-core-update/dictionaries/cs.dict.itop-core-update.php
#	datamodels/2.x/itop-core-update/dictionaries/da.dict.itop-core-update.php
#	datamodels/2.x/itop-core-update/dictionaries/it.dict.itop-core-update.php
#	datamodels/2.x/itop-core-update/dictionaries/ja.dict.itop-core-update.php
#	datamodels/2.x/itop-core-update/dictionaries/sk.dict.itop-core-update.php
#	datamodels/2.x/itop-core-update/dictionaries/tr.dict.itop-core-update.php
#	datamodels/2.x/itop-core-update/dictionaries/zh_cn.dict.itop-core-update.php
#	datamodels/2.x/itop-faq-light/dictionaries/zh_cn.dict.itop-faq-light.php
#	datamodels/2.x/itop-files-information/dictionaries/de.dict.itop-files-information.php
#	datamodels/2.x/itop-files-information/dictionaries/pl.dict.itop-files-information.php
#	datamodels/2.x/itop-files-information/dictionaries/zh_cn.dict.itop-files-information.php
#	datamodels/2.x/itop-hub-connector/dictionaries/pt_br.dict.itop-hub-connector.php
#	datamodels/2.x/itop-oauth-client/dictionaries/cs.dict.itop-oauth-client.php
#	datamodels/2.x/itop-oauth-client/dictionaries/da.dict.itop-oauth-client.php
#	datamodels/2.x/itop-oauth-client/dictionaries/es_cr.dict.itop-oauth-client.php
#	datamodels/2.x/itop-oauth-client/dictionaries/it.dict.itop-oauth-client.php
#	datamodels/2.x/itop-oauth-client/dictionaries/ja.dict.itop-oauth-client.php
#	datamodels/2.x/itop-oauth-client/dictionaries/nl.dict.itop-oauth-client.php
#	datamodels/2.x/itop-oauth-client/dictionaries/pt_br.dict.itop-oauth-client.php
#	datamodels/2.x/itop-oauth-client/dictionaries/ru.dict.itop-oauth-client.php
#	datamodels/2.x/itop-oauth-client/dictionaries/sk.dict.itop-oauth-client.php
#	datamodels/2.x/itop-oauth-client/dictionaries/tr.dict.itop-oauth-client.php
#	datamodels/2.x/itop-oauth-client/dictionaries/zh_cn.dict.itop-oauth-client.php
#	datamodels/2.x/itop-service-mgmt-provider/dictionaries/de.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-tickets/dictionaries/da.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/dictionaries/hu.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/dictionaries/it.dict.itop-tickets.php
#	datamodels/2.x/itop-tickets/dictionaries/ja.dict.itop-tickets.php
#	dictionaries/cs.dictionary.itop.ui.php
#	dictionaries/da.dictionary.itop.core.php
#	dictionaries/da.dictionary.itop.ui.php
#	dictionaries/de.dictionary.itop.ui.php
#	dictionaries/en.dictionary.itop.ui.php
#	dictionaries/es_cr.dictionary.itop.ui.php
#	dictionaries/fr.dictionary.itop.ui.php
#	dictionaries/hu.dictionary.itop.ui.php
#	dictionaries/it.dictionary.itop.core.php
#	dictionaries/it.dictionary.itop.ui.php
#	dictionaries/ja.dictionary.itop.core.php
#	dictionaries/ja.dictionary.itop.ui.php
#	dictionaries/nl.dictionary.itop.ui.php
#	dictionaries/pl.dictionary.itop.core.php
#	dictionaries/pl.dictionary.itop.ui.php
#	dictionaries/pt_br.dictionary.itop.core.php
#	dictionaries/pt_br.dictionary.itop.ui.php
#	dictionaries/ru.dictionary.itop.core.php
#	dictionaries/ru.dictionary.itop.ui.php
#	dictionaries/sk.dictionary.itop.core.php
#	dictionaries/sk.dictionary.itop.ui.php
#	dictionaries/tr.dictionary.itop.core.php
#	dictionaries/tr.dictionary.itop.ui.php
#	dictionaries/zh_cn.dictionary.itop.core.php
#	dictionaries/zh_cn.dictionary.itop.ui.php
#	tests/php-unit-tests/integration-tests/DictionariesConsistencyTest.php
2024-02-14 14:21:22 +01:00
Pierre Goiffon
aa53de467d Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-02-14 11:39:42 +01:00
Pierre Goiffon
bc6efc99ed N°7246 Fix dict files : remove keys defined multiple times in the same file 2024-02-14 11:20:02 +01:00
Pierre Goiffon
bb3ab76205 N°7246 Fix dict files : missing constants in dict labels 2024-02-14 11:20:02 +01:00
Pierre Goiffon
0b1bdfff55 N°7246 Fix dict files : translated keys with tildes
Note that there were some keys in EN files with tildes at the end
2024-02-14 11:20:02 +01:00
Pierre Goiffon
77c0cdf5aa N°7246 📝 test README : add markTestAsSkipped restrictions 2024-02-14 11:20:02 +01:00
Pierre Goiffon
af9fb74c54 N°7246 Add new tests methods in DictionariesConsistencyTest (#610)
Adding following checks:
* no duplicate key in the same file
* for each value different than its EN counterpart, no tildes at the end
* good use of iTop name constants (ITOP_APPLICATION_SHORT, ITOP_APPLICATION, ITOP_VERSION_NAME), eg `'my value ITOP_APPLICATION'` instead of `'my value '.ITOP_APPLICATION`
2024-02-14 11:20:02 +01:00
Pierre Goiffon
5d6c4939f6 N°7245 Bettor logs on RunTimeEnvironment::CallInstallerHandlers exceptions (#606) 2024-02-14 11:01:12 +01:00
Pierre Goiffon
5663c61e51 N°7179 Remove unused var in ActionEmail::PrepareMessageContent (#605)
Introduced in fed149d in 3.1.0
2024-02-14 10:32:23 +01:00
Pierre Goiffon
51d0d16a11 N°7052 synchro_import.php: fix undefined offset notices (#583)
Regression brought by #269
2024-02-14 09:53:31 +01:00
Pierre Goiffon
848528567e Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	dictionaries/en.dictionary.itop.ui.php
2024-02-09 13:41:23 +01:00
Pierre Goiffon
b0634c9fbc N°7232 Fix regression on UI:RunQuery:Error
In the UI:RunQuery:Error key, the placeholder was removed when we migrated run_query.php to using UiBlock and Panel.
But it was restored by mistake in f65c6904 (N°5491 in 3.0.4 / 3.1.1)
This was the only key modified for this bug in a EN file.
2024-02-09 13:30:47 +01:00
Pierre Goiffon
fabeea8dba Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-02-09 12:05:00 +01:00
Pierre Goiffon
c951a33646 📝 Update js/README.md file locations 2024-02-09 12:04:21 +01:00
Pierre Goiffon
8f250e82f4 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-02-08 11:34:32 +01:00
Pierre Goiffon
ed694b09b0 💡 Update test PHPDoc 2024-02-08 11:34:13 +01:00
Timmy38
18deb79068 N°7172 HTML markup for n:n set properties (#604)
* N°7172 HTML markup for n:n set properties
2024-01-29 15:35:17 +01:00
Molkobain
4d4cdf9e60 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-01-24 15:18:37 +01:00
Molkobain
3868d57d28 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-01-24 15:11:29 +01:00
Molkobain
1b3a2c8470 N°5775 - Show error message to user in case of issue during token generation 2024-01-24 14:49:51 +01:00
Dennis Lassiter
618d8e6468 N°5775 - Allow configuration of OAuth client on MS Azure with single tenant (#553)
* Add Tenant-Support for Azure OAuthClient

* Improvement: Make tenant required

* Improvment: Removed check for null-value

Since last commit, the "tenant"-field either set to a custom value or "common" by default. It is not allowed to be null

* Add field description

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2024-01-24 14:38:54 +01:00
odain
53a2a7a8e2 N°7147 - cleanup 2024-01-23 20:45:35 +01:00
odain
9a26c37ffd N°7147 - Error HTTP 500 due to access_token not URL decoded 2024-01-23 16:50:21 +01:00
Eric Espie
cc5e105d7f N°7070 - remove _delta attribute from datamodel and avoid _delta attributes under a node having _delta in [define, define_if_not_exist, redefine, force] in deltas 2024-01-17 15:12:08 +01:00
jf-cbd
7f62bd61e3 N°7134 - Can't retrieve the list of changes when editing URP_UserProfile 2024-01-17 15:05:30 +01:00
Molkobain
72bbc31cd9 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-01-16 19:48:33 +01:00
Molkobain
f54d1273c9 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-01-16 19:46:55 +01:00
Molkobain
01a955a16f 📝 Add PR template 2024-01-16 19:42:39 +01:00
Pierre Goiffon
669e6f93fa Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-01-16 11:00:52 +01:00
Pierre Goiffon
a5aac0caad N°7143 Woops removed forgotten echo 2024-01-16 10:25:28 +01:00
Pierre Goiffon
31e29506fa N°7143 DictionariesConsistencyTest::testNoDictFileInDatamodelsModuleRootDirectory Add exclusion list to remove modules that must be compatible with iTop 2.7 2024-01-16 10:18:50 +01:00
Anne-Cath
3005bc1234 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-01-15 16:34:01 +01:00
Anne-Cath
3f3b0cbe55 Fix merge support/2.7 to support/3.0 2024-01-15 16:25:10 +01:00
Anne-Catherine
cff50f8732 N°3448 - Framework field size check not correctly implemented for multibytes languages/strings (#528) 2024-01-15 16:10:23 +01:00
Anne-Cath
a8cd9566ac Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-01-15 16:01:48 +01:00
Anne-Cath
dab03e5b5d Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	application/cmdbabstract.class.inc.php
2024-01-15 15:56:24 +01:00
Anne-Catherine
87582a021b N°6993 - Error message on bulk transition on object containing a null blob (#596) 2024-01-15 15:49:19 +01:00
Pierre Goiffon
200cdefa1a Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	datamodels/2.x/itop-core-update/dictionaries/pt_br.dict.itop-core-update.php
#	datamodels/2.x/itop-hub-connector/dictionaries/pl.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/dictionaries/pt_br.dict.itop-hub-connector.php
#	datamodels/2.x/itop-hub-connector/dictionaries/zh_cn.dict.itop-hub-connector.php
#	datamodels/2.x/itop-welcome-itil/dictionaries/tr.dict.itop-welcome-itil.php
2024-01-15 15:44:48 +01:00
Pierre Goiffon
8ef04bedf2 N°7143 Move datamodels/2.x dict files to dictionaries subdir
New phpunit method to ensure it won't happen again
2024-01-15 15:35:15 +01:00
Pierre Goiffon
9eeb4b8751 N°7143 itop-hub-connector dic files: fix buggy br tags 2024-01-15 15:35:15 +01:00
Pierre Goiffon
ae3d0f9444 N°7143 Fix bytes unit in attachments EN dict 2024-01-15 15:35:15 +01:00
Molkobain
1fa4fe6d35 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-01-12 12:05:24 +01:00
Molkobain
2ed0666c3b N°7085 - Remove comment about annotation as on support/3.0 branch not using runTestsInSeparateProcesses is the standard 2024-01-12 10:53:24 +01:00
odain
9b5988133b Merge branch 'support/3.0' into support/3.1 2024-01-12 08:57:51 +01:00
odain
a61b117f71 Merge branch 'support/2.7' into support/3.0 2024-01-12 08:56:25 +01:00
odain
9830178a47 N°7085 - ci restore runTestsInSeparateProcesses 2024-01-12 08:36:15 +01:00
odain
83ac219ec9 Merge branch 'support/2.7' into support/3.0 2024-01-12 08:19:05 +01:00
odain-cbd
c140ebcb6b N°7085 - Fix infinite loop in login page until fatal error occurs (#592)
* N°7085 - login page infinite loop until fatal error- add Config->AddAllowedLoginTypes

* N°7085 - reproduce issue via a test

* N°7085-fix infinite loop

* N°7085 - ci: fix config file rights in tearDown

* N°7085 - ci: fix config file rights in tearDown (again)

* N°7085 - ci: fix config file content

* N°7085 - ci : add runTestsInSeparateProcesses

* Update core/config.class.inc.php

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

* N°7085 - exit -1 + enhance log message

* PR feedbacks from Romain regarding LoginTest annotations

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2024-01-12 08:13:40 +01:00
Molkobain
fddf9e36c7 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-01-11 22:16:10 +01:00
Molkobain
b181914d25 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2024-01-11 22:04:48 +01:00
Molkobain
7a0a4e377b N°7137 - Fix regression from 7fffbb60, missing "default_value" parameter 2024-01-11 16:40:40 +01:00
Thomas Casteleyn
7fffbb60e9 N°7137 - DataSynchro: Remove "Organization" as default value for SynchroReplica->dest_class (#551) 2024-01-11 15:38:02 +01:00
Benjamin Dalsass
03b095f246 N°7133 - Linkset displayed as property failed when filter contains single quote or new line 2024-01-10 16:50:03 +01:00
Pierre Goiffon
e8c371174c Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	approot.inc.php
#	datamodels/2.x/authent-cas/module.authent-cas.php
#	datamodels/2.x/authent-external/module.authent-external.php
#	datamodels/2.x/authent-ldap/module.authent-ldap.php
#	datamodels/2.x/authent-local/module.authent-local.php
#	datamodels/2.x/combodo-backoffice-darkmoon-theme/module.combodo-backoffice-darkmoon-theme.php
#	datamodels/2.x/combodo-db-tools/module.combodo-db-tools.php
#	datamodels/2.x/itop-attachments/module.itop-attachments.php
#	datamodels/2.x/itop-backup/module.itop-backup.php
#	datamodels/2.x/itop-bridge-cmdb-ticket/module.itop-bridge-cmdb-ticket.php
#	datamodels/2.x/itop-bridge-virtualization-storage/module.itop-bridge-virtualization-storage.php
#	datamodels/2.x/itop-change-mgmt-itil/module.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/module.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php
#	datamodels/2.x/itop-config/module.itop-config.php
#	datamodels/2.x/itop-core-update/module.itop-core-update.php
#	datamodels/2.x/itop-datacenter-mgmt/module.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-endusers-devices/module.itop-endusers-devices.php
#	datamodels/2.x/itop-faq-light/module.itop-faq-light.php
#	datamodels/2.x/itop-files-information/module.itop-files-information.php
#	datamodels/2.x/itop-full-itil/module.itop-full-itil.php
#	datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/module.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-oauth-client/module.itop-oauth-client.php
#	datamodels/2.x/itop-portal-base/module.itop-portal-base.php
#	datamodels/2.x/itop-portal/module.itop-portal.php
#	datamodels/2.x/itop-problem-mgmt/module.itop-problem-mgmt.php
#	datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/module.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/module.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/module.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/module.itop-service-mgmt.php
#	datamodels/2.x/itop-sla-computation/module.itop-sla-computation.php
#	datamodels/2.x/itop-storage-mgmt/module.itop-storage-mgmt.php
#	datamodels/2.x/itop-structure/module.itop-structure.php
#	datamodels/2.x/itop-themes-compat/module.itop-themes-compat.php
#	datamodels/2.x/itop-tickets/module.itop-tickets.php
#	datamodels/2.x/itop-virtualization-mgmt/module.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-welcome-itil/module.itop-welcome-itil.php
#	datamodels/2.x/version.xml
2024-01-05 17:48:19 +01:00
Pierre Goiffon
b8892e9651 🔖 Prepare 3.0.4 version 2024-01-05 17:34:39 +01:00
Pierre Goiffon
8cde0ce5c5 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	approot.inc.php
#	css/css-variables.scss
#	datamodels/2.x/authent-cas/module.authent-cas.php
#	datamodels/2.x/authent-external/module.authent-external.php
#	datamodels/2.x/authent-ldap/module.authent-ldap.php
#	datamodels/2.x/authent-local/module.authent-local.php
#	datamodels/2.x/combodo-db-tools/module.combodo-db-tools.php
#	datamodels/2.x/itop-attachments/module.itop-attachments.php
#	datamodels/2.x/itop-backup/module.itop-backup.php
#	datamodels/2.x/itop-bridge-virtualization-storage/module.itop-bridge-virtualization-storage.php
#	datamodels/2.x/itop-change-mgmt-itil/module.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/module.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php
#	datamodels/2.x/itop-config/module.itop-config.php
#	datamodels/2.x/itop-core-update/module.itop-core-update.php
#	datamodels/2.x/itop-datacenter-mgmt/module.itop-datacenter-mgmt.php
#	datamodels/2.x/itop-endusers-devices/module.itop-endusers-devices.php
#	datamodels/2.x/itop-files-information/module.itop-files-information.php
#	datamodels/2.x/itop-full-itil/module.itop-full-itil.php
#	datamodels/2.x/itop-hub-connector/module.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/module.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/module.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-oauth-client/module.itop-oauth-client.php
#	datamodels/2.x/itop-portal-base/module.itop-portal-base.php
#	datamodels/2.x/itop-portal/module.itop-portal.php
#	datamodels/2.x/itop-problem-mgmt/module.itop-problem-mgmt.php
#	datamodels/2.x/itop-profiles-itil/module.itop-profiles-itil.php
#	datamodels/2.x/itop-request-mgmt-itil/module.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/module.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/module.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/module.itop-service-mgmt.php
#	datamodels/2.x/itop-sla-computation/module.itop-sla-computation.php
#	datamodels/2.x/itop-storage-mgmt/module.itop-storage-mgmt.php
#	datamodels/2.x/itop-tickets/module.itop-tickets.php
#	datamodels/2.x/itop-virtualization-mgmt/module.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-welcome-itil/module.itop-welcome-itil.php
#	datamodels/2.x/version.xml
2024-01-05 17:26:28 +01:00
Pierre Goiffon
2fd9523c16 🔖 Prepare 2.7.10 version 2024-01-05 15:50:41 +01:00
Eric Espie
18887bf7f9 N°7118 - Act on guaranteed object deletion, with the object still accessible (EVENT_DB_ABOUT_TO_DELETE) 2024-01-05 11:21:59 +01:00
Pierre Goiffon
fad5571d9b Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2024-01-05 11:03:34 +01:00
Pierre Goiffon
48c4e2d13d Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	application/ajaxwebpage.class.inc.php
#	application/webpage.class.inc.php
#	application/xmlpage.class.inc.php
#	core/config.class.inc.php
2024-01-05 10:58:51 +01:00
Eric Espie
5b2b9c92e1 Merge branch 'support/3.1.1' into support/3.1 2024-01-05 10:51:21 +01:00
Pierre Goiffon
a4f6f6e877 N°4368 Fix CORB blocking regression (#598)
Don't send X-Content-Type-Options HTTP header for certain WebPage impl to workaround CORB blocking
To disable globally this new behavior introduced in 9865bf07, set the `security.enable_header_xcontent_type_options` config parameter to false

Thanks @Molkobain for the review !
2024-01-05 10:41:18 +01:00
Eric Espie
68fa3d0236 N°7080 - EVENT_DB_LINKS_CHANGED not fired when deleting a user 2024-01-05 10:36:37 +01:00
Pierre Goiffon
abb3ea3272 N°7080 - EVENT_DB_LINKS_CHANGED not fired when deleting a user
Fix regressions caused by deletion plan reorder
2024-01-04 18:07:16 +01:00
Eric Espie
1de390a24d N°7080 - EVENT_DB_LINKS_CHANGED not fired when deleting a user 2024-01-04 15:56:48 +01:00
jf-cbd
20478958ad Merge branch 'support/3.1.1' into support/3.1 2024-01-04 11:07:07 +01:00
jf-cbd
34499a719f Update test - broken with N°7079 fix 2024-01-04 10:59:51 +01:00
jf-cbd
ba99f8f5a4 Merge branch 'support/3.1.1' into support/3.1 2024-01-04 10:34:48 +01:00
jf-cbd
8092f566a5 N°7079 - EVENT_DB_LINKS_CHANGED not fired when creating/updating a user with profiles 2024-01-04 10:29:25 +01:00
Pierre Goiffon
379062f1f7 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
Most of the conflicts are caused by HU translations (N°7077 / #584)

# Conflicts:
#	css/setup.css
#	datamodels/2.x/authent-cas/dictionaries/hu.dict.authent-cas.php
#	datamodels/2.x/authent-ldap/dictionaries/hu.dict.authent-ldap.php
#	datamodels/2.x/authent-local/dictionaries/hu.dict.authent-local.php
#	datamodels/2.x/combodo-backoffice-darkmoon-theme/dictionaries/hu.dict.combodo-backoffice-darkmoon-theme.php
#	datamodels/2.x/combodo-db-tools/dictionaries/hu.dict.combodo-db-tools.php
#	datamodels/2.x/itop-attachments/dictionaries/hu.dict.itop-attachments.php
#	datamodels/2.x/itop-backup/dictionaries/hu.dict.itop-backup.php
#	datamodels/2.x/itop-bridge-cmdb-ticket/dictionaries/hu.dict.itop-bridge-cmdb-ticket.php
#	datamodels/2.x/itop-change-mgmt-itil/dictionaries/hu.dict.itop-change-mgmt-itil.php
#	datamodels/2.x/itop-change-mgmt/dictionaries/hu.dict.itop-change-mgmt.php
#	datamodels/2.x/itop-config-mgmt/dictionaries/hu.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-config/dictionaries/hu.dict.itop-config.php
#	datamodels/2.x/itop-core-update/dictionaries/hu.dict.itop-core-update.php
#	datamodels/2.x/itop-faq-light/dictionaries/hu.dict.itop-faq-light.php
#	datamodels/2.x/itop-hub-connector/dictionaries/hu.dict.itop-hub-connector.php
#	datamodels/2.x/itop-incident-mgmt-itil/dictionaries/hu.dict.itop-incident-mgmt-itil.php
#	datamodels/2.x/itop-knownerror-mgmt/dictionaries/hu.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-oauth-client/hu.dict.itop-oauth-client.php
#	datamodels/2.x/itop-portal-base/dictionaries/hu.dict.itop-portal-base.php
#	datamodels/2.x/itop-portal/dictionaries/hu.dict.itop-portal.php
#	datamodels/2.x/itop-problem-mgmt/dictionaries/hu.dict.itop-problem-mgmt.php
#	datamodels/2.x/itop-request-mgmt-itil/dictionaries/hu.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/dictionaries/hu.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-service-mgmt-provider/dictionaries/hu.dict.itop-service-mgmt-provider.php
#	datamodels/2.x/itop-service-mgmt/dictionaries/hu.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-structure/dictionaries/hu.dict.itop-structure.php
#	datamodels/2.x/itop-tickets/dictionaries/hu.dict.itop-tickets.php
#	dictionaries/hu.dictionary.itop.core.php
#	dictionaries/hu.dictionary.itop.ui.php
#	dictionaries/ui/components/breadcrumbs/hu.dictionary.itop.breadcrumbs.php
#	dictionaries/ui/components/datatable/hu.dictionary.itop.datatable.php
#	dictionaries/ui/components/field/hu.dictionary.itop.field.php
#	dictionaries/ui/components/global-search/hu.dictionary.itop.global-search.php
#	dictionaries/ui/components/input/hu.dictionary.itop.input.php
#	dictionaries/ui/components/quick-create/hu.dictionary.itop.quick-create.php
#	dictionaries/ui/layouts/activity-panel/hu.dictionary.itop.activity-panel.php
#	dictionaries/ui/layouts/navigation-menu/hu.dictionary.itop.navigation-menu.php
#	dictionaries/ui/layouts/object-details/hu.dictionary.itop.object-details.php
#	dictionaries/ui/layouts/page-content/hu.dictionary.itop.page-content.php
#	dictionaries/ui/layouts/tab-container/hu.dictionary.itop.tab-container.php
#	dictionaries/ui/pages/errorpage/hu.dictionary.itop.errorpage.php
#	dictionaries/ui/pages/preferences/hu.dictionary.itop.preferences.php
#	tests/php-unit-tests/unitary-tests/setup/DBBackupTest.php
2024-01-03 17:30:57 +01:00
Pierre Goiffon
6042e7f74d Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	tests/php-unit-tests/post-build-integration-tests/SetupCssIntegrityChecklistTest.php
#	tests/php-unit-tests/unitary-tests/core/CMDBSource/CMDBSourceTest.php
2024-01-03 10:43:41 +01:00
denis.flaven@combodo.com
5e233f4bec N°7017 - Prevent race conditions when rebuilding cache 2024-01-02 17:02:44 +01:00
Molkobain
19c93249e3 Merge remote-tracking branch 'origin/support/3.1.1' into support/3.1 2024-01-02 11:56:30 +01:00
denis.flaven@combodo.com
a1e2b648f4 N°7001 - Being admin to generate oAuth token is too restrictive 2024-01-02 11:38:43 +01:00
Molkobain
94c604a6af N°3062 - Fix setup.css compilation test to ensure that it is versioned correctly. 2023-12-21 12:00:26 +01:00
Pierre Goiffon
f84e2060be N°7077 Hungarian translations based on iTop 3.0.2-1 (#584)
Many thanks to Csaba TARJÁNYI (@tacsaby) !
2023-12-20 15:52:41 +01:00
Pierre Goiffon
6995a3c641 N°6889 backup mysqldump call : restore possibility to connect using socket protocol (#591)
With previous fix (N°6123) we forced to use the tcp protocol each time. This was blocking for users wanting to connect using the socket protocol on localhost.

Now for localhost we will : 
- send both port and protocol arguments if the `db_host` config parameter does contain a port
- don't send any of the port or protocol arguments if `db_host` doesn't contain a port
2023-12-20 15:19:50 +01:00
Pierre Goiffon
e007e09155 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	pages/ajax.render.php
2023-12-19 18:41:57 +01:00
Pierre Goiffon
4ee70cb95a Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	application/ajaxwebpage.class.inc.php
#	application/csvpage.class.inc.php
#	application/itopwebpage.class.inc.php
#	application/webpage.class.inc.php
#	application/xmlpage.class.inc.php
#	datamodels/2.x/itop-hub-connector/hubconnectorpage.class.inc.php
#	pages/ajax.document.php
#	pages/ajax.render.php
#	sources/application/TwigBase/Controller/Controller.php
#	webservices/export-v2.php
2023-12-19 18:38:45 +01:00
Pierre Goiffon
9865bf0779 N°4368 add sending X-Content-Type-Options HTTP header
Replace in consumers the \WebPage::add_xframe_options call by \WebPage::add_http_headers
2023-12-19 18:25:26 +01:00
Molkobain
2144933927 N°6816 - Search: Fix JS "me._onACSearchContainsSuccess is not a function" error (#556) 2023-12-14 10:57:53 +01:00
Molkobain
e2574a0738 Merge remote-tracking branch 'origin/support/3.1.0' into support/3.1 2023-12-13 18:47:00 +01:00
denis.flaven@combodo.com
3fb5c05467 N°7001 - Being admin to generate oAuth token is too restrictive 2023-12-13 13:57:44 +01:00
jf-cbd
17d131a9b1 N°7021 - Fix error log and useless compilation time due to SCSS file unnecessary compilation #579 2023-12-13 09:21:26 +01:00
Eric Espie
fc2be85c95 N°7054 - Rework the UpdateImpactedItems calls on Tickets 2023-12-12 11:25:27 +01:00
Eric Espie
e748d72c03 N°7054 - Rework the UpdateImpactedItems calls on Tickets 2023-12-12 10:40:48 +01:00
Pierre Goiffon
797aae58e3 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-12-08 17:21:01 +01:00
Pierre Goiffon
f63f3bb547 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-12-08 17:20:48 +01:00
Pierre Goiffon
d5449cca42 💡 iTopMutex: add link to mysql doc 2023-12-08 17:20:37 +01:00
Pierre Goiffon
3f8017f43d N°7046 Fix authent-cas: "CAS_ServiceBaseUrl_Static" not found
Regression in 3.1.0 brought by N°5270 (02afa2f1)
2023-12-08 10:41:04 +01:00
Molkobain
4846532368 N°7047 - Fix regression from 7b59df21, portal themes others than "bootstrap" and "portal" not loaded due to change in expected path (now relative) 2023-12-08 08:28:43 +01:00
Molkobain
90d28c1f90 N°7039 - Fix incorrect retrieval of SecurityHelper due to previous merge 2023-12-07 12:38:16 +01:00
Molkobain
8cc1970931 💚 Fix unit test not working on server with self-signed certificate 2023-12-07 11:18:51 +01:00
Molkobain
75732faf1d N°7039 - Fix warnings in the CI due to unnecessary use statement 2023-12-07 11:18:02 +01:00
Molkobain
c13d0a60e8 N°7039 - Fix placeholder :current_contact->id not working in OQL in iTop 3.1 2023-12-07 10:04:53 +01:00
Molkobain
66d1ffa00f Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-12-06 16:31:03 +01:00
Molkobain
181c180824 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-12-06 16:28:27 +01:00
Molkobain
5d38d22c50 N°7023 - Fix regression from the initial fix that throw exceptions even for ext. keys set programatically (eg. ComputeValues), which we still want to allow 2023-12-06 16:27:37 +01:00
Molkobain
c8d2cff0ff Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-12-04 23:19:22 +01:00
Molkobain
974c155855 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-12-04 23:16:35 +01:00
Pierre Goiffon
99d69493d1 N°7023 - Update tests so that we are now checking negative ext. keys 2023-12-04 22:36:26 +01:00
Molkobain
c9bb628c30 N°7023 - Improve debug message on portal \DBObject::CheckChangedExtKeysValues() call 2023-12-04 22:36:09 +01:00
Molkobain
08e8d15d78 N°7023 - Fix check to write error when adding a contact on a new user request on the end-users portal 2023-12-04 22:35:07 +01:00
Molkobain
be699b4358 🔖 Increase version number to 3.1.1 2023-11-28 19:56:14 +01:00
Eric Espie
ed12495ec5 N°6228 - Fix Unit tests due to logs added in CRUD 2023-11-28 15:16:48 +01:00
Stephen Abello
d79bbca8ce N°6874 - Make unit test more robust 2023-11-28 11:27:19 +01:00
Eric Espie
ad8922861b N°6531 - Fix refactor 2023-11-27 14:49:27 +01:00
odain
57671f336c N°6901 - Enable tracking of iTop active sessions (monitoring) 2023-11-27 12:34:19 +01:00
Stephen Abello
fdf48b8338 N°6874 - Add unit test 2023-11-27 12:15:07 +01:00
Stephen Abello
f0ea97850b Fix LogFile type emails 2023-11-27 12:14:33 +01:00
Stephen Abello
9b1cdd3581 N°6874 - Fix encoding issue in out-going emails 2023-11-27 10:37:13 +01:00
Molkobain
2d96cf34d4 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/public/css/portal.css
2023-11-24 17:25:11 +01:00
Molkobain
bed1db9c51 N°938 - Update compiled portal stylesheet in minified version 2023-11-24 17:07:02 +01:00
Molkobain
7e3e8e43a8 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/public/css/portal.css
#	datamodels/2.x/itop-portal-base/portal/templates/layout.html.twig
2023-11-24 16:45:04 +01:00
Molkobain
7b59df216b N°7005 - Fix portal stylesheets not being re-compiled when outdated
Stylesheets should remain as a relative path in the portal configuration, only when consumed by the TWIG should they become URLs

Note that if not absolute, URLs will be append to ITOP/pages/
2023-11-24 16:40:58 +01:00
Molkobain
cb5eab812e N°938 - Update compiled portal stylesheet 2023-11-24 16:22:36 +01:00
Anne-Cath
675affb7f2 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/public/css/portal.scss
2023-11-24 15:21:39 +01:00
Anne-Cath
484a0bb6b6 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-11-24 15:19:05 +01:00
Anne-Catherine
c9b73a7fe2 N°938 - Improve print of portal object page and portal dashboard page (#537)
N°938 - Improve print of portal object page and portal dashboard page
2023-11-24 15:17:42 +01:00
Pierre Goiffon
9266458cba Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-23 15:55:33 +01:00
Pierre Goiffon
6f1de11c59 N°6976 Fix DeprecatedCallsLog error handler never set (#576)
Caused by N°6274 (disabling error handler when running phpunit)
There is now a test testing the handler is really fixed when not in the phpunit context
Also a TRACE log is made on setting the handler
2023-11-23 15:54:10 +01:00
Molkobain
a18f50615a Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#    pages/exec.php
2023-11-23 15:07:11 +01:00
Molkobain
e02b6ee14a Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-11-23 14:25:16 +01:00
Anne-Catherine
0d388cb3e7 N°3465 - Fix attachment file name hardcoded to "uploaded-file" when imported from CSV import (#533)
N°3465 - Fix attachment file name hardcoded to "uploaded-file" when imported from CSV import
2023-11-23 14:15:59 +01:00
Anne-Catherine
5283df5ae9 N°3767 - Impact analysis: Display filtering box on CIs list and groups (#473)
N°3767 - Impact analysis: Display filtering box on CIs list and groups
2023-11-23 14:06:10 +01:00
Eric Espie
8e0d6d1f00 N°6228 - Refactor after review 2023-11-23 11:14:37 +01:00
Eric Espie
f72d8f4955 N°6531 - Trigger on Update on n-n fields not working when object updated in pop-up 2023-11-23 11:14:37 +01:00
Pierre Goiffon
ea845dc6eb N°6228 - CheckToWrite() propagation to target objects based on with_php_constraint property 2023-11-23 11:14:37 +01:00
Molkobain
3b2da39469 N°6989 - Security hardening 2023-11-22 18:02:50 +01:00
odain-cbd
9a59bc7890 N°6901 - Enable tracking of iTop active sessions (monitoring) (#568)
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
Co-authored-by: Romain Quetiez <romain.quetiez@combodo.com>
2023-11-22 17:46:00 +01:00
odain
88008b3925 Merge branch 'support/3.0' into support/3.1 2023-11-22 12:31:02 +01:00
odain
8484bee269 N°6949 - Run subset of itop core unit tests after the setup when validating a module github code 2023-11-22 11:16:03 +01:00
odain
c5b43f3157 Merge branch 'support/2.7' into support/3.0 2023-11-22 11:10:06 +01:00
odain
fc22d91232 N°6949 - Run subset of itop core unit tests after the setup when validating a module github code 2023-11-22 10:14:58 +01:00
Pierre Goiffon
cfe73699cf N°6967 Deprecate DBDeleteTracked_Internal methods 2023-11-21 18:05:29 +01:00
Pierre Goiffon
fa830bdd7f N°6966 Deprecate DBCloneTracked and DBCloneTracked_Internal methods 2023-11-21 18:04:05 +01:00
Pierre Goiffon
2c1df77f4d Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-21 11:01:31 +01:00
Pierre Goiffon
3068a6a360 N°6951 Fix autoloader 2023-11-21 11:01:16 +01:00
Stephen Abello
47e7c35c2b Merge branch 'support/3.0' into support/3.1
# Conflicts:
#	dictionaries/cs.dictionary.itop.core.php
#	dictionaries/da.dictionary.itop.core.php
#	dictionaries/de.dictionary.itop.core.php
#	dictionaries/es_cr.dictionary.itop.core.php
#	dictionaries/hu.dictionary.itop.core.php
#	dictionaries/it.dictionary.itop.core.php
#	dictionaries/ja.dictionary.itop.core.php
#	dictionaries/nl.dictionary.itop.core.php
#	dictionaries/pl.dictionary.itop.core.php
#	dictionaries/pt_br.dictionary.itop.core.php
#	dictionaries/ru.dictionary.itop.core.php
#	dictionaries/sk.dictionary.itop.core.php
#	dictionaries/tr.dictionary.itop.core.php
#	lib/composer/autoload_classmap.php
#	lib/composer/autoload_static.php
#	sources/Application/Helper/ExportHelper.php
2023-11-21 10:24:29 +01:00
Stephen Abello
083a0b79bf N°6951 - Security hardening 2023-11-21 10:08:46 +01:00
Stephen Abello
e22220b4fe Merge branch 'support/2.7' into support/3.0
# Conflicts:
#	core/csvbulkexport.class.inc.php
#	core/excelbulkexport.class.inc.php
#	lib/composer/autoload_classmap.php
#	lib/composer/autoload_files.php
#	lib/composer/autoload_real.php
#	lib/composer/autoload_static.php
2023-11-21 09:47:14 +01:00
Stephen Abello
b10bcb976d N°6951 - Security hardening 2023-11-21 09:42:11 +01:00
Pierre Goiffon
a2181b3478 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-21 09:25:21 +01:00
Pierre Goiffon
154a20b757 💡 Fix ItopCustomDatamodelTestCase since phpdoc
See https://docs.phpdoc.org/guide/references/phpdoc/tags/since.html#since
2023-11-21 09:21:30 +01:00
Pierre Goiffon
79c24dfc96 N°6975 Fix ItopCustomDatamodelTestCase removing symlinks flag (#574) 2023-11-21 09:20:11 +01:00
Molkobain
403c369ea0 N°6963 - Update comment 2023-11-20 09:20:09 +01:00
Molkobain
d5875047fa N°6963 - Update PHP min version prerequisite (8.1) for next iTop version (3.2.0) 2023-11-17 10:25:22 +01:00
Molkobain
6955f74c97 N°5281 - Fix methods name case 2023-11-17 08:48:51 +01:00
Molkobain
6bf9ce3237 Merge remote-tracking branch 'origin/release/designer-lot-3' into support/3.1 2023-11-16 16:33:17 +01:00
Molkobain
5726f9f81a N°6960 - Fix \MFDocument::GetNodes() adding a predicate on last expression only instead of the whole XPath 2023-11-16 16:31:47 +01:00
Pierre Goiffon
be93b41363 N°6458 Fix failing tests due to ItopDataTestCase modifications 2023-11-16 15:43:11 +01:00
Pierre Goiffon
d33d82bcd8 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-16 15:35:52 +01:00
Pierre Goiffon
b9d960e89e N°6458 Fix ItopDataTestCase::$bIsUsingSilo visibility 2023-11-16 15:35:04 +01:00
Pierre Goiffon
8540ec644a N°6458 Improve ItopDataTestCase tests speed
What was measured :
- 1'54 with previous code (always doing a reset in tearDown)
- 1'44 without any ResetMetaModelQueyCacheGetObject call (but 3 tests are failing)
- 1'44 with new optin mechanism + don't call Logoff if no current user logged
2023-11-16 15:23:18 +01:00
Pierre Goiffon
5b19593ede 📝 README tests : add prerequisites 2023-11-16 14:38:15 +01:00
Pierre Goiffon
57b70e48bd Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-16 12:02:20 +01:00
Pierre Goiffon
0915081f50 ItopDataTestCase fix cached SQL queries with silo 2023-11-16 11:57:29 +01:00
Pierre Goiffon
7798df0d53 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/src/BaseTestCase/ItopDataTestCase.php
2023-11-16 11:29:31 +01:00
Pierre Goiffon
a23d629e31 ItopDataTestCase fix using non existing EventService (added in 3.1.0) 2023-11-16 11:23:07 +01:00
Pierre Goiffon
47ccd7589f Fix TriggerTest relying on hardcoded admin user id 2023-11-16 11:16:59 +01:00
Pierre Goiffon
7521fc3006 N°6458 Tests : remove processIsolation
Was caused by cached User instances in UserRights + same login for each created User objects instances
2023-11-16 10:56:47 +01:00
Pierre Goiffon
8f61c02cbe N°6458 Fix portal errors 2023-11-16 09:23:19 +01:00
Pierre Goiffon
e3e8b25cfc Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	core/dbobject.class.php
#	datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php
#	lib/composer/autoload_files.php
#	lib/composer/autoload_real.php
#	lib/composer/autoload_static.php
#	pages/UI.php
#	tests/php-unit-tests/unitary-tests/core/DBObjectTest.php
2023-11-15 15:31:56 +01:00
Pierre Goiffon
c955fe00b7 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	application/cmdbabstract.class.inc.php
#	application/utils.inc.php
#	core/coreexception.class.inc.php
#	core/userrights.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php
#	tests/php-unit-tests/README.md
#	tests/php-unit-tests/unitary-tests/core/DBObjectTest.php
2023-11-15 15:03:04 +01:00
Pierre Goiffon
5a43448644 N°6458 Security hardening 2023-11-15 11:14:07 +01:00
Pierre Goiffon
f5a0b4bf8e Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-13 16:27:34 +01:00
Pierre Goiffon
a2b9583379 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-11-13 16:25:53 +01:00
Pierre Goiffon
77409eed99 🎨 DBObject small phpdoc fixes 2023-11-13 16:25:37 +01:00
Molkobain
eb7c971091 N°6948 - Upgrade "symfony/twig-bridge" to v5.4.31 2023-11-13 15:17:52 +01:00
Eric Espie
4d5a704d9a N°6062 - Fix unit tests 2023-11-13 15:14:29 +01:00
Eric Espie
b758113752 N°6436 - Integrate Performance Audit pre requisite (log events for extensions) 2023-11-13 15:02:54 +01:00
Stephen Abello
5465287089 Merge branch 'support/3.0' into support/3.1 2023-11-13 11:21:21 +01:00
Stephen Abello
09be84f69d N°6908 - Security hardening 2023-11-13 11:19:02 +01:00
Romain Quetiez
3b987f97eb Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-10 16:15:08 +01:00
Romain Quetiez
d9bdcfeae3 N°6658 - Fix regression: do not reset current user's profile cache 2023-11-10 15:57:08 +01:00
Pierre Goiffon
8df7c22464 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-10 15:21:10 +01:00
Pierre Goiffon
d725ba3d84 N°6765 Avoid behat scenario loading issues on portal modal (#569)
- New CombodoJsActivity API
- Replace existing calls in NiceWebPage (ready scripts)
- Add calls in ready block in portal object create template (used in both create and edit)
2023-11-10 15:10:37 +01:00
Molkobain
2d8ecd465b N°6903 - Fix crash when emptying file attribute (eg. picture of a contact) 2023-11-09 18:17:35 +01:00
Molkobain
34ba4fa0ce N°6917 - Security hardening 2023-11-09 16:43:37 +01:00
Molkobain
08d22219f4 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-08 14:59:56 +01:00
Molkobain
8a3d81c430 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-11-08 14:59:06 +01:00
Anne-Catherine
83a70daf68 N°6887 - Fix excessive OQL requests to display user's grant matrix (#564)
* N°6887 - Fix excessive OQL requests to display user's grant matrix

* N°6887 - Rename variable and add PHPDoc

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-11-08 14:57:28 +01:00
Molkobain
d5a8a3bb09 N°4756 - Fix PHPDoc 2023-11-07 12:00:19 +01:00
jf-cbd
eaeb114754 N°6852 - Use "email_default_sender_address" when "forgot_password_from" config param is empty 2023-11-06 10:47:40 +01:00
Molkobain
cfd32581b7 N°6861 - Add PHPDoc and return type hint 2023-11-03 09:20:15 +01:00
vdumas
85a6bd0a05 Typo in EN Dictionary - Allowed orgs class name 2023-11-02 17:43:48 +01:00
Stephen Abello
92cd1e3f19 N°6861 - Display warning message when creating/editing a mandatory blob in modal depending on edition mode and blob value 2023-11-02 14:58:46 +01:00
Stephen Abello
b54022e2ad N°6385 - Move Linkset flags and edit_when logic from UIBlock to DisplayBareRelations 2023-11-02 10:16:35 +01:00
odain-cbd
eaa80c5396 N°6824 - Notification with current_contact placeholder trigger hundred of email sent (#562)
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
Co-authored-by: Thomas Casteleyn <thomas.casteleyn@super-visions.com>
Co-authored-by: Romain Quetiez <romain.quetiez@combodo.com>
2023-10-31 16:17:57 +01:00
Molkobain
3aec6bff79 N°6866 - Fix regression from previous merge 2023-10-31 16:13:54 +01:00
Molkobain
83313ce1d5 N°6866 - Fix usages of hard-coded "listInObject" to \DisplayBlock::ENUM_STYLE_LIST_IN_OBJECT 2023-10-31 14:01:19 +01:00
Molkobain
8aa3dcdaa7 N°6866 - Fix regression from previous merge 2023-10-31 13:56:17 +01:00
Molkobain
306d8136ef Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php
2023-10-31 11:16:13 +01:00
Molkobain
cbb37f27d7 N°6866 - Fix issue when creating new fields in Request Template in French 2023-10-31 11:10:54 +01:00
odain-cbd
e78fa18359 N°6849 - Enhance setup error message in case of unmet module dependencies (#557)
* N°6849 - add cross/checkmark emoj to missing depenency message and remove unmet word

* N°6849 - PR advice to avoid modifying aDeps
2023-10-31 11:07:29 +01:00
Benjamin Dalsass
1385a3dc03 N°6774 - Risque d'erreur de chargement des relations dans le portail en visualisation 2023-10-31 10:18:21 +01:00
Benjamin Dalsass
789f0c826b N°6557 - PHP 8.1 : Deprecated Constant when adding a contact to ticket on ticket creation 2023-10-31 09:34:00 +01:00
Molkobain
ac070b0cbe Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-10-30 16:58:43 +01:00
Molkobain
e21dc4d21c Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-10-30 16:55:45 +01:00
Molkobain
a49a4e6c2b N°6886 - Add OAuth tests folder to removable directories list 2023-10-30 16:54:28 +01:00
Stephen Abello
95aa4afe75 N°6385 - Allow to disable LinkedSet (1:n & n:n) edition by XML in host edition 2023-10-30 16:29:25 +01:00
bdalsass
74004fa375 N°6651 - 3.1 bulk modify n:n like Tagset: checkbox ignored (#552)
- handle selectize enable/disable to update operation hidden input state
- don't execute elements click callback when input disabled
- merge current and initial values for the initial value
2023-10-30 13:57:41 +01:00
vdumas
d2fc87c6f9 N°6884 - Error on creating User from a Person edit screen 2023-10-30 12:22:07 +01:00
Romain Quetiez
5264a1f10d New test related to the build of SQL queries 2023-10-27 16:01:57 +02:00
Romain Quetiez
f0199a4cf2 Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-27 15:50:17 +02:00
Romain Quetiez
f8877ef3e7 Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-27 15:49:58 +02:00
Romain Quetiez
00b1156526 Optimize tests execution time (force cache usage and set the modification date in the past instead of waiting 1s) 2023-10-27 15:45:28 +02:00
Romain Quetiez
39d2ba8d1b Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-27 15:43:11 +02:00
Romain Quetiez
322adcb180 Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-27 14:27:00 +02:00
Romain Quetiez
77e7685c90 Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-27 14:24:31 +02:00
Romain Quetiez
3c14e5e032 Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-27 14:24:16 +02:00
Romain Quetiez
f58ec7e38e Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-27 14:22:20 +02:00
Romain Quetiez
73fd0b06b2 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/integration-tests/DictionariesConsistencyTest.php
#	tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php
#	tests/php-unit-tests/unitary-tests/core/CMDBSource/TransactionsTest.php
#	tests/php-unit-tests/unitary-tests/sources/Application/TwigBase/Twig/TwigTest.php
2023-10-27 14:12:06 +02:00
Romain Quetiez
8fa9336568 Fix regression introduced with the optimization done in 15148f7, and seen only in the context of the CI 2023-10-27 11:21:56 +02:00
Romain Quetiez
15148f7d1d Fix regression introduced with the optimization done in 798cd10, and seen only if APC is enabled 2023-10-27 10:43:13 +02:00
Romain Quetiez
7e8589ba95 Fix regression introduced with the optimization done in d641504. Cope with the fact that sometimes the admin account already exists, sometimes not. 2023-10-27 09:21:36 +02:00
Romain Quetiez
fba668207f Optimize tests execution time (test rework and defensive cleanup) 2023-10-26 21:35:52 +02:00
Romain Quetiez
798cd10d6b Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-26 21:23:47 +02:00
Romain Quetiez
442721bcb5 Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-26 21:22:54 +02:00
Romain Quetiez
1a9049d277 Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-26 21:16:24 +02:00
Romain Quetiez
c0931af91a Optimize tests execution time (no need for process isolation as long as we leave the premises clean, set file modification date instead of waiting for 1 second) 2023-10-26 21:15:57 +02:00
Romain Quetiez
29e9a06dc1 Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-26 21:10:47 +02:00
Romain Quetiez
d6415042ae Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-26 21:10:07 +02:00
Romain Quetiez
90006667fe Optimize tests execution time (copy fixture files only when necessary) 2023-10-26 20:58:26 +02:00
Romain Quetiez
fd351df08b Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-10-26 20:53:24 +02:00
Romain Quetiez
7419749ba6 Prerequisites for boosting tests 2023-10-26 20:51:28 +02:00
Romain Quetiez
73bed04555 Twig tests not executed 2023-10-26 10:47:11 +02:00
Romain Quetiez
8893cdac1d Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-26 10:44:37 +02:00
Romain Quetiez
7f245a15be Optimize tests execution time (suppress meaningless test and merge two test in one, while preserving test coverage) 2023-10-25 23:01:05 +02:00
Romain Quetiez
b5c46ccd4a Optimize tests execution time (x10 / no need for a systematic check of date formats, which was ok as a first approach) 2023-10-25 22:59:03 +02:00
Romain Quetiez
7fbc211c43 Optimize tests execution time (x50 / eval is way faster than exec) 2023-10-25 22:57:03 +02:00
Romain Quetiez
cf774cdb90 Explain why process isolation is a must 2023-10-25 22:18:05 +02:00
Romain Quetiez
722a58491c Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php
2023-10-25 22:08:08 +02:00
Romain Quetiez
037dfe1df6 Optimize tests execution time 2023-10-25 17:51:12 +02:00
Romain Quetiez
0b26d45014 Prerequisites for boosting tests 2023-10-25 17:50:41 +02:00
Eric Espie
44e826543d N°6870 - Community re-integration support 2023-10-25 14:30:16 +02:00
Eric Espie
50f5ab6be4 Fix ModelFactory tests 2023-10-25 14:03:26 +02:00
Eric Espie
941412a365 Community integration: changed default behaviour for datamodel XML nodes without _delta from 'must_exist' to 'merge' 2023-10-25 10:05:46 +02:00
Stephen Abello
a9bd62dc43 N°6385 - Allow to disable LinkedSet (1:n & n:n) edition by XML
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-10-24 11:09:12 +02:00
Stephen Abello
4f336abeb8 N°6861 - Display warning message when creating/editing a mandatory blob in modal 2023-10-23 15:51:38 +02:00
Molkobain
40d1ae0b1c Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-10-23 15:17:43 +02:00
Molkobain
b9c566238a Merge remote-tracking branch 'origin/support/2.7' into support/3.0
Remove PHPUnit annotations as from support/3.0 and newer they are no longer necessary
2023-10-23 15:09:49 +02:00
Molkobain
4fd8177165 N°3715 - Fix unit tests 2023-10-23 14:55:06 +02:00
Anne-Cath
c597c34e5c Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-10-20 17:06:11 +02:00
Anne-Cath
0cc0f39d9e Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-10-20 17:04:26 +02:00
Anne-Catherine
a2cdf214f0 N°3715 - Export above 1000 entries ignore obsolete data from user preference (#468)
* N°3715 - Export above 10000 entries ignore obsolete data from user preference
2023-10-20 17:02:16 +02:00
Anne-Cath
189fd1d9e3 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-10-20 16:55:41 +02:00
Anne-Cath
4f75d012e5 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-10-20 16:54:37 +02:00
Anne-Catherine
013173019f N°2909 - Search on Enum, Date, TagSet,... with index fails (#496) 2023-10-20 16:45:35 +02:00
Anne-Catherine
ed8d4df5ef N°5786 - Problem with text color in public log and in AttributeHTML (#527)
* N°5786 - Problem with text color in public log and in AttributeHTML
2023-10-17 12:09:52 +02:00
Pierre Goiffon
31a1370028 Merge branch 'support/3.1.0' into support/3.1
# Conflicts:
#	core/DbConnectionWrapper.php
#	tests/php-unit-tests/unitary-tests/core/CMDBSource/TransactionsTest.php
2023-10-17 11:13:29 +02:00
Pierre Goiffon
f36f3aa05b 💡 N°6848 Fix PHPDoc 2023-10-17 11:12:38 +02:00
Pierre Goiffon
d0d90d7c69 N°6848 Fix TransactionsTest::testTransactionOpenedNotClosed failing
Was caused by 239c51bb, which adds 65bb76b9 tests improvements but only partly : we were missing in TransactionsTest::tearDown the mysqli reset from mock to original mysqli cnx

Without this reset the rollback made in ItopTestCase::tearDown is throwing an exception (query() method on the DbConnectionWrapper cnx returns false in \CMDBSource::DBQuery) on PHP 8.1 and 8.2

Co-authored-by: Romain Quetiez <romain.queriez@combodo.com>
2023-10-17 10:53:26 +02:00
Stephen Abello
aa618468d1 Merge branch 'support/3.0' into support/3.1 2023-10-17 09:23:06 +02:00
Stephen Abello
fadfd94bac Merge branch 'support/2.7' into support/3.0 2023-10-17 09:19:16 +02:00
Stephen Abello
9469681a0c N°6777 - Security hardening 2023-10-17 09:12:40 +02:00
Pierre Goiffon
d4ab55dd9a Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	pages/ajax.render.php
2023-10-13 17:33:26 +02:00
Pierre Goiffon
da27ddba82 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	application/utils.inc.php
#	pages/ajax.render.php
2023-10-13 17:27:03 +02:00
Pierre Goiffon
c72cb7e70e N°6606 security hardening 2023-10-13 17:15:37 +02:00
Pierre Goiffon
9df92665e0 N°6606 Backport of utils::ENUM_SANITIZATION_FILTER_* constants
Were introduced in 3.0.0, but not added to the support/2.7 branch
2023-10-13 17:10:35 +02:00
Molkobain
819baa3951 Merge remote-tracking branch 'origin/support/3.1.0' into support/3.1 2023-10-06 17:47:52 +02:00
Eric Espie
c78024394e Better log of twig syntax errors 2023-10-06 15:25:27 +02:00
Eric Espie
4267f2b855 N°6747 - Customizing UserLDAP, generates presentation error messages in Designer during MTP 2023-10-06 09:53:26 +02:00
vdumas
ba8f18e1d4 N°6815 - DataModel : wrong attribute type for SLA.customercontracts_list 2023-10-05 18:08:46 +02:00
vdumas
ba13d24206 N°6814 - Datamodel remove lnkConnectableCIToNetworkDevice uniqueness rule 2023-10-05 18:08:46 +02:00
odain
239c51bb53 ci enhancement: complete tearDown to cleanup transactions and cmdb changes properly 2023-10-03 10:09:26 +02:00
Stephen Abello
ab3a4a2468 Merge branch 'support/3.0' into support/3.1
# Conflicts:
#	pages/ajax.render.php
2023-10-02 15:16:18 +02:00
Stephen Abello
3647291475 N°6778 - Security hardening 2023-10-02 15:06:17 +02:00
Molkobain
a472d83e3d Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	application/wizardhelper.class.inc.php
#	pages/ajax.render.php
2023-09-26 22:21:48 +02:00
Molkobain
6dc6392fab Merge remote-tracking branch 'origin/support/3.0.3' into support/3.0 2023-09-26 22:20:02 +02:00
Anne-Catherine
e793b02f8b N°6766 - Fix dependent fields not updated due to WizardHelper.UpdateFields() being triggered too early (#548)
* N°6766 - Javascript : function WizardHelper.UpdateFields triggered to early does not update fields

* N°6766 - Code review

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-09-26 12:25:56 +02:00
Pierre Goiffon
c5cb84f976 N°6733 Fix some attributes impl not prompted in transitions when mandatory 2023-09-26 11:32:06 +02:00
Molkobain
12c0edc530 N°6547 - Fix flags init being an hard-coded int instead of the corresponding constant 2023-09-21 18:25:44 +02:00
Stephen Abello
61565b25a3 N°6767 - Error in ajax request when there's dict to load and no onready scripts 2023-09-21 10:40:22 +02:00
Pierre Goiffon
7619d055dd N°6695 Handle multiline dict entries in portal tooltips (#542)
Report Tippy fix from admin console (/css/backoffice/vendors/_tippy.scss) to user portal.
Thanks DaveHeart  for the bug report !
2023-09-20 18:10:56 +02:00
Molkobain
f01997f2ad Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-09-20 16:16:53 +02:00
Molkobain
fc6e98b534 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-09-20 16:12:51 +02:00
Molkobain
8ecebee511 PHP unit tests: Fix typo for "final private" methods as they can't be both 2023-09-20 16:11:39 +02:00
Eric Espie
b5e26061e1 Merge branch 'issue/6667_trigger_on_state_enter' into support/3.1 2023-09-20 10:11:58 +02:00
Pierre Goiffon
14fa20b428 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	tests/ci_description.ini
2023-09-19 12:30:16 +02:00
Pierre Goiffon
2690fa3315 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-09-19 12:27:19 +02:00
Pierre Goiffon
35cd965360 N°6629 Update ci_description php_version 2023-09-19 12:25:40 +02:00
Pierre Goiffon
133fc29ad5 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-09-19 09:58:53 +02:00
Pierre Goiffon
83a5b98f82 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-09-19 09:58:40 +02:00
Pierre Goiffon
e5dd51f637 N°6600 Portal download attachment : don't display anymore SQL query on attachment not found error (#525) 2023-09-19 09:54:43 +02:00
Molkobain
2f6bcc3534 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-09-19 08:41:46 +02:00
Molkobain
4923418f58 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-09-19 08:39:41 +02:00
Molkobain
0a6c82dfe1 N°6752 - PHP unit tests: Fix typo in postbuild_integration.xml.dist 2023-09-19 08:37:46 +02:00
Molkobain
f89d843ab3 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-09-18 16:06:53 +02:00
Molkobain
2dd7f5cada Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-09-18 15:37:32 +02:00
Molkobain
24c0f4950f Add missing .htaccess / web.config files in .gitignore for /extensions folder 2023-09-18 15:26:06 +02:00
Molkobain
d4dbbc59d4 N°6754 - PHP unit tests: Add local PHPUnit XML files to .gitignore 2023-09-18 15:23:52 +02:00
Molkobain
dc0cd44c79 N°6752 - PHP unit tests: Migrate usages of unitestautoload.php to composer autoloader 2023-09-18 15:14:44 +02:00
Pierre Goiffon
d7df249586 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	application/ajaxwebpage.class.inc.php
#	application/capturewebpage.class.inc.php
#	application/clipage.class.inc.php
#	application/csvpage.class.inc.php
#	application/errorpage.class.inc.php
#	application/itopwebpage.class.inc.php
#	application/itopwizardwebpage.class.inc.php
#	application/nicewebpage.class.inc.php
#	application/pdfpage.class.inc.php
#	application/webpage.class.inc.php
#	application/xmlpage.class.inc.php
2023-09-18 15:10:19 +02:00
Pierre Goiffon
f3c4fcb0f5 💡 Pages files : add depreciation version 2023-09-18 15:07:32 +02:00
Pierre Goiffon
9a8e9a0b01 N°5491 Fix HU iTopUpdate:Error:MissingFile dict key 2023-09-18 08:45:45 +02:00
vdumas
f6a7e6b4e1 N°3506 - allow_target_creation based on UR_ACTION_MODIFY 2023-09-15 16:40:20 +02:00
Stephen Abello
2829fb291f Update contribution stickers with 2023 design (better late than never 🙃) 2023-09-15 15:43:50 +02:00
odain
6110abfc7f Merge branch 'support/3.0' into support/3.1 2023-09-15 10:08:37 +02:00
odain
6046f44f56 Merge branch 'support/2.7' into support/3.0 2023-09-15 10:08:08 +02:00
odain
6c6131ce03 N°5491 - test enhancement to reduce false positive 2023-09-15 10:07:42 +02:00
Stephen Abello
178c922407 Merge branch 'support/3.0' into support/3.1 2023-09-15 09:57:59 +02:00
Stephen Abello
343e87a8d4 N°6581 - Security hardening 2023-09-15 09:55:51 +02:00
Stephen Abello
d7e5d6fb7f N°6734 - Fix not being able to add dashlets to a dashboard when iBackofficeDictEntriesExtension is used and its JS files are loaded through ajax calls 2023-09-14 17:25:00 +02:00
Pierre Goiffon
993606f102 Merge branch 'support/3.1.0' into support/3.1
# Conflicts:
#	setup/itopdesignformat.class.inc.php
2023-09-14 16:54:28 +02:00
Pierre Goiffon
bdf0b4daa9 N°6562 Fix DOMNode->textContent write
This attribute is read only
Causes layout issues on PHP 8.1.21 and 8.2.8

(cherry picked from commit 734a788340)
(cherry picked from commit 7aa478d6ff)
2023-09-14 15:31:02 +02:00
Eric Espie
44c189223e Merge branch 'support/3.1.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/unitary-tests/core/DBObjectTest.php
2023-09-14 14:31:29 +02:00
Eric Espie
7fdbb59c30 N°6716 - High memory Consomption and performance issue 2023-09-14 14:09:05 +02:00
Eric Espie
5acf38ac36 Fix unit tests for MariaDB
(cherry picked from commit 61a9a4ac65)
2023-09-14 14:09:05 +02:00
odain-cbd
e76728b2bf N°5491 - Inconsistent dictionnary entries regarding arguments to pass to Dict::Format-test first (#545) 2023-09-13 12:02:49 +02:00
odain
3e258f32cc N°5491-fix redundant GetNonPublicStaticProperty 2023-09-13 10:30:56 +02:00
odain
3c51d6fb98 N°5491- fix dictionary test 2023-09-13 10:27:19 +02:00
odain
7cfe1389aa Merge branch 'support/3.0' into support/3.1 2023-09-13 10:20:38 +02:00
Stephen Abello
7292a8540b N°6547 - Disallow linkset edition when lnk attribute is readonly 2023-09-13 10:53:32 +02:00
odain
f65c690462 N°5491-fix test 2023-09-13 10:03:05 +02:00
odain
ecf8bc42fa Merge branch 'support/2.7' into support/3.0 2023-09-13 10:01:15 +02:00
vdumas
faba812fc1 N°6646 - Wrong dictionary entry for FR - Lnk Contact / Contrat 2023-09-12 12:48:33 +02:00
vdumas
add433d702 N°6706 - Missing dictionary entry for DE - Lnk Provider Contract / Service 2023-09-12 12:24:56 +02:00
vdumas
9c99cb35e5 N°6706 - Wrong dictionary entry for FR - Lnk Provider Contract / Service 2023-09-12 12:21:25 +02:00
Pierre Goiffon
9d392ad167 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-09-07 14:53:02 +02:00
Pierre Goiffon
ea8509db1f N°6709 Use ItopTestCase::RequireOnceCurrentModuleFile in GetAppRoot 2023-09-07 14:47:36 +02:00
Pierre Goiffon
df25ce76b6 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-09-07 14:43:29 +02:00
Pierre Goiffon
e946fc65fc N°6709 New ItopTestCase::RequireOnceCurrentModuleFile 2023-09-07 14:38:19 +02:00
Pierre Goiffon
d203e075a8 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-09-06 09:09:37 +02:00
Pierre Goiffon
dbe2f66539 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-09-06 09:07:45 +02:00
Pierre Goiffon
0d8ff7bbac N°6629 Set commit tests back to Mysql
For now we have perf issues on Jenkins with MariaDB (see N°6694)
2023-09-04 10:25:41 +02:00
Eric Espie
48eb022824 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-09-01 14:15:06 +02:00
Stephen Abello
03c9ffc033 N°6560 - Fix unescaped backtick in friendlyname breaking details page scripts 2023-09-01 09:41:23 +02:00
Eric Espie
61a9a4ac65 Fix unit tests for MariaDB 2023-09-01 09:29:21 +02:00
acognet
38962e68ee Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	pages/ajax.render.php
2023-08-31 16:22:01 +02:00
Pierre Goiffon
483dbb4a5d N°6658 Remove useless annotations
See comment for ItopTestCase::$preserveGlobalState
2023-08-31 16:06:34 +02:00
acognet
1f4dcc4f9e N°5136 - Relations: Fix "Select All objects" adding obsolete objects even if "show obsolete data" param. not activated - Merge from support/2.7 2023-08-31 16:04:03 +02:00
acognet
e86309669e Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	pages/ajax.render.php
2023-08-31 15:56:16 +02:00
Pierre Goiffon
6d6f55acf7 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/src/BaseTestCase/ItopDataTestCase.php
2023-08-31 15:40:56 +02:00
Pierre Goiffon
6ebcd44bb1 💡 N°6658 Add more comments and since tags 2023-08-31 15:34:44 +02:00
Anne-Catherine
f8fb51fea0 N°5145 - Fix attachments missing in new ticket when clone from an old ticket with object copier (#530) 2023-08-31 15:27:23 +02:00
Anne-Catherine
bf768311c2 N°5136 - "Select All objects" add obsolete objects even if the parameter show obsolete data is not activated (#467) 2023-08-31 15:13:20 +02:00
Anne-Catherine
d797436786 N°6555 - Add class description in tooltip of Dashlet badge (#504)
cheery pick from branch develop due to target branch error
2023-08-31 14:55:06 +02:00
Anne-Catherine
b508c0d983 N°6152 - Search: Criteria & object list loaded twice (#495) 2023-08-31 12:03:31 +02:00
Anne-Catherine
351893bbdd N°4494 - Fix auto-locking on log save and transition (#358) 2023-08-31 11:23:58 +02:00
vdumas
59e4bb028f N°6682 - Pbs with Audit classes and AccessRight 2023-08-29 16:41:27 +02:00
vdumas
6d895371ec N°6682 - AuditDomain XML meta declaration missing 2023-08-29 16:41:27 +02:00
Stephen Abello
ab91631e68 N°6677 - Ensure emails in test are never sent to cc'd and bcc'd addresses 2023-08-29 15:56:52 +02:00
Eric Espie
cc4af0a027 N°6667 - Ignore trigger on state entering with auto-dispatch 2023-08-22 14:30:18 +02:00
Eric Espie
3366bae0ab N°6061 - Add tests on Expression evaluation 2023-08-18 15:34:06 +02:00
Romain Quetiez
03b484c349 Tests: fix test not working on MariaDB (unexpected coma tolerated by MySQL) 2023-08-18 12:13:11 +02:00
Molkobain
70081ecf33 N°6436 - Add unit test for API introduced in 3.1 (\iFieldRendererMappingsExtension) 2023-08-18 10:31:05 +02:00
Molkobain
575ba1cd7b Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	core/metamodel.class.php
2023-08-18 10:24:50 +02:00
Molkobain
d130959692 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	core/metamodel.class.php
2023-08-18 10:14:51 +02:00
Molkobain
a8c689c6c0 N°6436 - Add unit test to ensure that we don't lose an API during merge between branches 2023-08-18 09:55:45 +02:00
Molkobain
1990ccb5d8 N°6436 - Move interfaces enumeration from 1 line to 1 line / interface (and re-ordered them) for easier merges in newer branches 2023-08-18 09:52:55 +02:00
Molkobain
e107be56e4 N°6097 - Tests: Fix missing hook entry in PHPUnit XML file that led to compiled environment being re-build for each test case 2023-08-18 09:51:15 +02:00
Romain Quetiez
0f8e87e001 Tests: allow execution of RouterTest alone, fix tool to execute each test class separately 2023-08-18 08:44:54 +02:00
Molkobain
d92d2b5e9e Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	core/metamodel.class.php
2023-08-17 21:36:19 +02:00
Romain Quetiez
ebd0136773 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php
2023-08-17 18:36:34 +02:00
Molkobain
f6653e1594 N°6436 - Restore 3.0 APIs lost during 6433678d merge 2023-08-17 17:47:46 +02:00
Romain Quetiez
65bb76b9e3 N°6658 - Boost PHPUnit tests execution 2023-08-17 17:27:55 +02:00
Molkobain
f238593966 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/src/BaseTestCase/ItopDataTestCase.php
#	tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php
2023-08-11 09:19:49 +02:00
Molkobain
d951d3b872 💚 Fix typo in extended class name 2023-08-11 09:05:30 +02:00
Molkobain
ccceb870e3 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	tests/php-unit-tests/src/BaseTestCase/ItopDataTestCase.php
#	tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php
2023-08-10 15:53:05 +02:00
Molkobain
ed6df77cbb N°6097 - Tests: Optimize performances by creating custom env. only once and re-using it across test classes 2023-08-10 15:45:39 +02:00
Molkobain
1ad28312ec N°6097 - Tests: Introduce autoloader for "utility" classes and move them to a sub-folder for better organization as folder was still messy
Note that unittestautoload.php is now useless. We just keep for now until everything is migrated (projects / branches / modules)
2023-08-10 15:45:39 +02:00
Molkobain
f002aa04cd N°6097 - Tests: Enable PHP unit tests on a custom DataModel 2023-08-10 15:45:39 +02:00
Molkobain
b86d70623e N°6097 - Tests: Temporarily add test case for the new ItopCustomDatamodelTestCase class 2023-08-10 15:45:39 +02:00
Molkobain
fe3467309d N°6097 - Tests: Refactor base test classes for better extensibility 2023-08-10 15:45:39 +02:00
Molkobain
851ab9c356 N°6097 - Add \utils::GetDataPath() method to avoid duplicating manual path build 2023-08-10 15:45:39 +02:00
Molkobain
aef3c2e609 N°6097 - Fix \CMDBSource::DropDB() not resetting cache like \CMDBSource::DropTable() which can lead to errors when trying to re-create it afterwards 2023-08-10 15:45:39 +02:00
Pierre Goiffon
5212e15cc4 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-08-10 14:41:05 +02:00
Pierre Goiffon
f04fc546b5 N°6643 Fix TypeError in \CMDBSource::LogDeadLock 2023-08-10 14:34:09 +02:00
Lars Kaltefleiter
caf3076b12 N°3441 - Portal: Fix failure to open an object containing a link to an archived object (#523)
* N°3441 - Portal : cannot open an object containing a link to an archived object

* N°3441 - Display fa-archive icon in portal

* Update sources/Renderer/Bootstrap/FieldRenderer/BsSelectObjectFieldRenderer.php

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

* Update sources/Renderer/Bootstrap/FieldRenderer/BsSelectObjectFieldRenderer.php

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

* Update sources/Renderer/Bootstrap/FieldRenderer/BsSelectObjectFieldRenderer.php

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

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-08-10 09:52:22 +02:00
Pierre Goiffon
c4c400d852 N°6638 💡 More explanations on CompiledDictionariesConsistencyTest::testImportCsvMessageStillOk 2023-08-09 14:54:19 +02:00
Pierre Goiffon
6cc4cc4fb6 📝 Version history : add 3.1.0-2 2023-08-09 10:20:27 +02:00
Pierre Goiffon
d7495af207 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-08-08 15:42:39 +02:00
Pierre Goiffon
13ad98b9b3 Add other integration tests in the beforeSetup group
All of those tests can be ran without a running iTop instance, and are blocking
2023-08-08 15:34:27 +02:00
Pierre Goiffon
4be54fdd65 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-08-08 15:33:36 +02:00
Pierre Goiffon
6d13397ba1 Add other integration tests in the beforeSetup group
All of those tests can be ran without a running iTop instance, and are blocking
2023-08-08 15:33:09 +02:00
Pierre Goiffon
48e7e0309a N°6638 Fix DictionariesConsistencyTest::testImportCsvMessageStillOk not run on Jenkins
Was contained in a class with a beforeSetup group annotation, whereas it tries to read files in env-production (!)
Plus the dataprovider was using APPROOT const + utils class, which aren't available by default :(
=> Fixed by moving in a dedicated class (CompiledDictionariesConsistencyTest) and removing the dataprovider
2023-08-08 15:30:01 +02:00
Pierre Goiffon
2ce9b2afaf Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-08-04 14:58:38 +02:00
Pierre Goiffon
d64a91d4ce Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	core/metamodel.class.php
2023-08-04 14:58:22 +02:00
Pierre Goiffon
c0c8a13864 💡 \MetaModel::GetObject : remove documented throw Exception 2023-08-04 14:55:38 +02:00
Pierre Goiffon
5ffa41bc16 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-08-03 11:09:14 +02:00
Pierre Goiffon
d2eef06276 AttributeURLTest : remove useless separateProcess annotations 2023-08-03 11:08:47 +02:00
Pierre Goiffon
77b14c516e Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-08-03 09:41:22 +02:00
Pierre Goiffon
880a824f2f N°6562 Replace new DOMText() by \DOMDocument::createTextNode
Because init using constructor outputs a read only node, see https://www.php.net/manual/en/domelement.construct.php
Thanks @Hipska
See conversation in 734a788
2023-08-03 09:40:39 +02:00
Molkobain
f7f1b5f399 Merge remote-tracking branch 'origin/support/3.1.0' into support/3.1 2023-08-02 15:27:17 +02:00
Molkobain
85f66f5e0c N°6618 - Router: Add protection against invalid routes cache 2023-08-02 11:44:20 +02:00
Molkobain
a5c980113b N°6618 - Router: Fix available routes cache being re-generated at each call 2023-08-02 11:44:20 +02:00
Pierre Goiffon
18efbfa803 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-08-02 10:39:51 +02:00
Pierre Goiffon
7aa478d6ff N°6562 💡 Fix comment
Thanks @Molkobain !
2023-08-02 10:35:30 +02:00
Pierre Goiffon
97700dbf15 N°6562 Re-enable failing tests
Conditional disabling was made in ea8e7c5
2023-08-01 14:27:57 +02:00
Pierre Goiffon
c25c69d746 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-08-01 14:27:41 +02:00
Pierre Goiffon
734a788340 N°6562 Fix DOMNode->textContent write
This attribute is read only
Causes layout issues on PHP 8.1.21 and 8.2.8
2023-08-01 14:22:56 +02:00
Eric Espie
eb1eb15791 N°6061 - Allow services to implement interfaces
N°6061 - allow local path from an arbitrary path

(cherry picked from commit 19e7fc9cb9)
(cherry picked from commit fb23bddeb2)
(cherry picked from commit 750ecd4804)
2023-07-28 10:46:06 +02:00
Pierre Goiffon
a84077782d N°4354 N°6587 Remove duplicated ItopDataTestCase::CreateContactlessUser method
Regression introduced by 26048150
2023-07-27 16:48:49 +02:00
Pierre Goiffon
26048150d3 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/ItopDataTestCase.php
2023-07-27 16:44:02 +02:00
Pierre Goiffon
e5b6e2eb8c N°4354 N°6587 Add test to cover $oUser->Get('profile_list') VS security.hide_administrators config param 2023-07-27 16:42:56 +02:00
Pierre Goiffon
87b6ea4def 📝 Version history : add missing 3.1.0-beta 2023-07-27 14:55:55 +02:00
purplegrape
72873a3343 🌐 Improve zh-cn dict
Manual merge for #516
Translation by @purplegrape, many thanks !
2023-07-27 11:17:09 +02:00
Pierre Goiffon
5ef25ccb77 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-07-26 12:07:50 +02:00
Pierre Goiffon
1682a85cc0 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-07-26 12:07:35 +02:00
Pierre Goiffon
cd9beec313 Merge remote-tracking branch 'origin/support/2.6' into support/2.7 2023-07-26 12:07:09 +02:00
Pierre Goiffon
8295eaed90 Merge remote-tracking branch 'origin/support/2.5' into support/2.6 2023-07-26 12:06:32 +02:00
Eric Espie
86a7cefa68 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-07-25 17:56:12 +02:00
Eric Espie
829b648dd2 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-07-25 17:55:45 +02:00
Eric Espie
5475b9fbbe N°3454 - MoveToProd in 2 steps - fix utils::GetCurrentModuleName() 2023-07-25 17:44:43 +02:00
Eric Espie
6f8e7c7002 N°3454 - MoveToProd in 2 steps - fix utils::GetCurrentModuleUrl() 2023-07-25 17:20:37 +02:00
Pierre Goiffon
67ca554261 📝 Version history : add 3.1.0-1 2023-07-25 17:07:30 +02:00
Pierre Goiffon
f89953f39e Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-07-24 15:39:09 +02:00
Pierre Goiffon
772368ef8a 💡 PHPDoc for object list panels 2023-07-24 15:38:57 +02:00
Pierre Goiffon
2e049aa244 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-07-24 11:59:57 +02:00
Pierre Goiffon
a57b6471c9 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-07-24 11:59:40 +02:00
Pierre Goiffon
bc7c1b4744 N°6590 Fix DictionariesConsistencyTest for PL dict files 2023-07-24 11:14:37 +02:00
Eric Espie
12c78697f4 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-07-19 15:20:15 +02:00
Eric Espie
046e857768 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	core/config.class.inc.php
2023-07-19 15:19:06 +02:00
Eric Espie
4d8246c4d8 N°6436 - Integrate Performance Audit pre requisite in iTop Pro 2.7.9 (changed config variable name) 2023-07-19 15:13:43 +02:00
Eric Espie
5c61d725e1 N°6436 - Integrate Performance Audit pre requisite in iTop Pro 2.7.9 (changed config variable name) 2023-07-19 15:06:00 +02:00
Eric Espie
0c7195f1a3 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	core/kpi.class.inc.php
2023-07-19 10:53:09 +02:00
Eric Espie
00b070b3cf Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	core/kpi.class.inc.php
2023-07-19 10:44:22 +02:00
Eric Espie
2c4cad4dac N°6436 - Integrate Performance Audit pre requisite in iTop Pro 2.7.9 (avoid unnecessary calls) 2023-07-19 10:37:41 +02:00
Eric Espie
9c37d5c23e Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	application/applicationextension.inc.php
#	application/cmdbabstract.class.inc.php
#	core/dbobject.class.php
#	core/kpi.class.inc.php
#	core/metamodel.class.php
#	lib/composer/autoload_classmap.php
#	lib/composer/autoload_static.php
#	tests/php-unit-tests/unitary-tests/setup/DBBackupTest.php
2023-07-19 09:26:46 +02:00
Stephen Abello
89145593ef N°6552 - Security hardening 2023-07-19 09:25:48 +02:00
Eric Espie
b2e80d37dd N°6436 - typo 2023-07-18 14:48:32 +02:00
Eric Espie
6432678de9 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	application/cmdbabstract.class.inc.php
#	application/utils.inc.php
#	bootstrap.inc.php
#	composer.json
#	core/MyHelpers.class.inc.php
#	core/cmdbsource.class.inc.php
#	core/config.class.inc.php
#	core/dbobject.class.php
#	core/kpi.class.inc.php
#	core/metamodel.class.php
#	lib/composer/autoload_classmap.php
#	lib/composer/autoload_real.php
#	lib/composer/autoload_static.php
2023-07-18 14:36:58 +02:00
Pierre Goiffon
952194b385 N°6570 Fix BulkChangeExtKeyTest errors
- transaction started but never stopped
- invalid value label typo
- urlencode on search url
2023-07-18 14:12:29 +02:00
Pierre Goiffon
bfb452dd69 N°6570 Rename BulkChangeExtKeyTest file so it is run in Jenkins
Was *Test.inc.php instead of default *Test.php
2023-07-18 14:12:29 +02:00
Pierre Goiffon
64baeba1c7 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-07-18 09:49:03 +02:00
Molkobain
71ed784c60 N°6532 - Fix missing "/" in path
(cherry picked from commit 32fd75bc4b)
2023-07-18 09:40:34 +02:00
Eric Espie
da45651121 Merge branch 'feature/6548_Hide_DBHost_and_DBUser_in_log' into support/2.7 2023-07-18 09:34:48 +02:00
Eric Espie
d388ce9a06 Merge branch 'feature/6548_Hide_DBHost_and_DBUser_in_log' into support/2.7 2023-07-18 09:17:40 +02:00
Eric Espie
47e71d8838 Merge branch 'feature/6436-Integrate_Performance_Audit_extensibility' into support/2.7 2023-07-18 09:17:05 +02:00
Stephen Abello
2b5973ec67 N°6436 - Integrate Performance Audit pre requisite in iTop Pro 2.7.9 2023-07-18 09:15:37 +02:00
Benjamin Dalsass
e58918f53e N°6546 - AttributeLinkedSetIndirect filter dosen't work 2023-07-18 08:53:26 +02:00
Molkobain
125715af3f N°6562 - Temporarily disable XML conversion unit tests failing in PHP 8.2.8 2023-07-14 21:09:37 +02:00
Molkobain
ea8e7c5131 N°6562 - Temporarily disable XML conversion unit tests failing in PHP 8.1.21 2023-07-13 10:11:56 +02:00
Eric Espie
06e5e0b102 Merge branch 'support/3.1.0' into support/3.1 2023-07-12 13:31:34 +02:00
Eric Espie
df1cb0b6e3 N°6408 - CRUD : Fix log consuming too much memory 2023-07-12 11:21:32 +02:00
Pierre Goiffon
5247f5b3ea 🔖 Prepare version 3.1.1 2023-07-11 09:56:59 +02:00
Eric Espie
78396d8e4a 6548 - [ER] Hide DBHost and DBUser in log 2023-07-10 17:37:27 +02:00
Pierre Goiffon
f1ee22cbed Merge branch 'release/3.1.0' into develop 2023-07-10 16:57:23 +02:00
Molkobain
9b409b117f N°6043 - Booking: Move \TemporaryObjectDescriptor to /core to keep compatibility with legacy/custom packages
Cherry picked from 39305468
2023-07-10 13:16:53 +02:00
Molkobain
39305468f8 N°6043 - Booking: Move \TemporaryObjectDescriptor to /core to keep compatibility with legacy/custom packages 2023-07-10 12:25:32 +02:00
Molkobain
32fd75bc4b N°6532 - Fix missing "/" in path 2023-07-10 09:43:28 +02:00
Pierre Goiffon
efadf2cc79 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-07-07 10:24:57 +02:00
Pierre Goiffon
40d63a2fa4 N°3663 💡 Fix depreciation comment in core/coreexception.class.inc.php 2023-07-07 10:24:15 +02:00
Pierre Goiffon
baa6dedbcf Merge remote-tracking branch 'origin/support/3.0' into develop 2023-07-07 09:32:14 +02:00
Pierre Goiffon
556b9ad89a N°6532 Fix "failed to open stream" error on require_once approot in coreexception.class.inc.php
Was occurring in TemplateFieldValueTest templates-base phpunit test
2023-07-07 09:31:34 +02:00
Stephen Abello
9afc22bd8f N°6123 - Add tests and comments 2023-07-07 09:29:15 +02:00
Pierre Goiffon
ef0b0f88c9 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	sources/Core/Email/EmailSwiftMailer.php
#	tests/php-unit-tests/integration-tests/DictionariesConsistencyTest.php
#	tests/php-unit-tests/postbuild_integration.xml.dist
2023-07-06 17:11:10 +02:00
Pierre Goiffon
a010239efb Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-07-06 15:48:42 +02:00
Pierre Goiffon
264a8cd70a N°6494 - Some tests are run twice, some never
(cherry picked from commit a2a0b2cd0b)

(cherry picked from commit 4c9ea0c9d4)

# Conflicts:
#	tests/php-unit-tests/integration-tests/DictionariesConsistencyTest.php
2023-07-06 15:45:09 +02:00
Pierre Goiffon
a2a0b2cd0b N°6494 Fix missing PHPUnit log in console log for postbuild tests 2023-07-06 15:28:32 +02:00
Stephen Abello
aa1834170b N°6427 - Fix SwiftMailer not retrieving sendmail path 2023-07-06 14:31:54 +02:00
Stephen Abello
f94d67ab35 N°6340 - Fix permission refused when sending an email and renewing OAuth token in synchronous mode 2023-07-06 10:28:10 +02:00
Stephen Abello
3048c8c41f N°5560 - Display an error when trying to regenerate an expired OAuth token 2023-07-06 09:52:00 +02:00
Stephen Abello
246e4a9f50 N°6123 - Fix warnings when launching a backup on MariaDB > v10.6.1 with localhost dbhost 2023-07-06 09:28:01 +02:00
odain
4c9ea0c9d4 N°6494 - Some tests are run twice, some never 2023-07-05 14:37:02 +02:00
Benjamin Dalsass
e654daf4a5 N°6516 - Add @experimental PHP annotation on temporary objects features 2023-07-05 14:24:05 +02:00
Molkobain
8292b16387 N°6502 - Fix AttributeImage::GetAsHtml() using download URL instead of display URL which triggers download listeners 2023-07-05 10:35:36 +02:00
Molkobain
340b982d77 N°6502 - Avoid new history entry on AttributeBlob download 2023-07-05 10:35:35 +02:00
Stephen Abello
3e187282b0 N°6498 - Fix str_replace crashing with null value 2023-07-05 09:18:56 +02:00
Molkobain
722a4a2c4d Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	js/jquery.ba-bbq.js
2023-07-05 08:59:47 +02:00
Molkobain
8907525b78 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-07-05 08:56:17 +02:00
Molkobain
6d58adb6dd N°6359 - Fix JS crash due to new version trying to detect MSIE browser through a dependency that we don't have.
Cherry-picked from f889c53d71
2023-07-05 08:41:00 +02:00
Benjamin Dalsass
03e7e0e79f N°6503 - Logs purge system delete index.php 2023-07-05 08:39:48 +02:00
Molkobain
83529da319 N°6500 - Fix user request approval freezing in the portal due to "Cannot read properties of undefined (reading 'validation')" 2023-07-04 21:36:34 +02:00
bdalsass
9d38b4d1d6 N°6043 - Booking: Add prerequisites in iTop core - CRUD extensibility (#520) 2023-07-04 16:22:53 +02:00
Stephen Abello
40dc3deabb N°6495 - Fix strlen crashing with null value in AttributePassword 2023-07-04 13:56:31 +02:00
Stephen Abello
56688fbbff Merge branch 'support/3.0' into develop 2023-07-03 14:30:20 +02:00
Stephen Abello
764a170cd0 N°6483 - Security hardening 2023-07-03 14:29:45 +02:00
Stephen Abello
c30da57818 N°6488 - Fix object keyboard shortcut not working anymore due to summary cards stealing its shortcuts 2023-07-03 11:50:12 +02:00
Pierre Goiffon
e36b9eb664 N°6414 Make AbstractSimpleField::Validate final
We don't have any overrides remaining anymore
2023-07-03 11:30:04 +02:00
Pierre Goiffon
6cc2d49cd5 N°6414 Move existing AbstractSimpleField::Validate impl to custom validators
- LinkedSetField
- SelectObjectField
- MultipleChoicesField (warning this hierarchy contains non multiple value fields like SelectField !)

Also change AbstractValidator::Validate signature : now we are returning an array of error messages, so that we can return multiple ones
2023-07-03 11:30:04 +02:00
Pierre Goiffon
d085f15b6d N°6414 Separate SubFormField and Scalar fields
This will help to set methods as final, as SubFormField is often the sole one to have a real custom impl
2023-07-03 11:30:04 +02:00
Pierre Goiffon
6606af71ff N°6414 Validator refactoring
New AbstractValidator class, with new method Validate
All existing validators are now children of AbstractRegexpValidator
Handle validators JS counterparts in renderers : only regexp validators are implemented client side
2023-07-03 11:30:04 +02:00
Pierre Goiffon
52049b7837 N°6414 Disable Field validation for untouched attcodes 2023-07-03 11:30:04 +02:00
Pierre Goiffon
f40674ec85 N°6414 Replace SelectObjectField::VerifyCurrentValue by ResetCurrentValueIfNotAmongAllowedValues 2023-07-03 11:30:04 +02:00
bdalsass
b46d4db8ff N°6476 - Revert datamodel / compiler changes from N°5563 (#515) 2023-06-29 15:11:30 +02:00
Molkobain
12dbd0ed3d N°6482 - Fix URP_UserProfile classes not flagged as link classes so they can benefit of the same mechanisms (eg. events) 2023-06-29 09:59:09 +02:00
Lars Kaltefleiter
0d9f33ec4c 🌐 Updated german translations for 3.1 (#511) 2023-06-29 09:05:27 +02:00
Molkobain
71704404d0 N°5945 - Apply same explicit value comparison as previous commit 2023-06-28 10:19:07 +02:00
Molkobain
3c8bea8921 N°5945 - Fix "menu" option in dashlet list having no effect since relations refactoring 2023-06-28 10:11:51 +02:00
Molkobain
b755f65a8a Improve PHPDoc 2023-06-27 23:35:57 +02:00
Molkobain
3bd3081359 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	synchro/synchro_exec.php
2023-06-27 18:19:48 +02:00
Molkobain
5a33fb7a6a N°6474 - Fix regression introduced by 70e6f707 2023-06-27 18:16:55 +02:00
Stephen Abello
29a90fe244 N°6474 - Fix undefined variable due to failed merge + refactoring 2023-06-27 16:55:25 +02:00
Molkobain
011116029b N°6478 - Add "edit_mode" node for AttributeLinkedSetIndirect (n:n) for future usages 2023-06-27 16:41:01 +02:00
Eric Espie
f95cb50002 N°6442 - comment 2023-06-27 15:49:39 +02:00
Eric Espie
2c8db92504 N°6442 - Error in Database integrity due to "translate placeholder in notification" 2023-06-27 15:23:28 +02:00
Eric Espie
98d4fa4331 N°6384 - fix typo 2023-06-27 14:13:33 +02:00
Anne-Catherine
4a39a5e51d N°6342 - Fix empty list when configuring a list adding a log attribute (#498)
* N°6342 - Empty list when configuring a list adding a case log attribute

* Update js/dataTables.settings.js

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-06-27 13:45:04 +02:00
Eric Espie
8373a03d82 N°6408 - CRUD fix tests 2023-06-27 12:11:36 +02:00
Eric Espie
befbe2dfa9 N°6408 - CRUD fix tests 2023-06-27 11:52:23 +02:00
Eric Espie
86ca7fcb7a N°6408 - CRUD : rework on DBUpdate reentrancy 2023-06-27 11:37:10 +02:00
Stephen Abello
ccf1d7ecfb N°6446 Fix OAuth landing page missing jQuery library needed in ajax calls 2023-06-27 11:24:06 +02:00
Stephen Abello
178e82a039 N°6446 Replace jQuery with vanilla JS in ajax dictionaries 2023-06-27 11:24:06 +02:00
Stephen Abello
452cc77168 N°6446 Prevent loaded js file constant from being missing if an ajax call is made from a page that's not a Webpage 2023-06-27 11:24:05 +02:00
Molkobain
7fc46fbc50 N°6472 - Fix empty tables in "Progress" tab of a survey 2023-06-27 10:36:30 +02:00
Benjamin Dalsass
f338d3bdc8 N°6326 - Magnifier pop-up search from ExtKey in Link edit in pop-up, fails 2023-06-27 09:42:12 +02:00
Benjamin Dalsass
d41e54bf34 N°6473 - Do not track QueryOQL statistic values 2023-06-27 08:42:44 +02:00
Eric Espie
aa75456e6e N°6271 - Fix drop-down list not refreshed when adding an external key with a friendlyname built on an external key 2023-06-27 08:41:20 +02:00
Eric Espie
db6e4137b1 N°6271 - Fix drop-down list not refreshed when adding an external key with a friendlyname built on an external key 2023-06-26 18:00:04 +02:00
Benjamin Dalsass
a206af1813 N°6005 - CSV import : import an object and a linkset fails
- Restore old get as html for linked set with display style tab
2023-06-26 15:11:41 +02:00
Eric Espie
77868e356b N°6408 - CRUD : Issue on DBlinkchange event when delete et create 2023-06-26 11:34:00 +02:00
Eric Espie
60e302162c N°6408 - CRUD : Issue on DBlinkchange event when delete et create 2023-06-26 11:31:53 +02:00
Eric Espie
f36c8c5cdd N°6408 - CRUD : Issue on DBlinkchange event when delete et create 2023-06-26 11:17:20 +02:00
Molkobain
d062ba8196 N°6456 - Code convention 2023-06-24 11:49:43 +02:00
Eric Espie
c3469e43bc N°6408 - Add logs, changed modification test of current object and prevent calling OnDBUpdate() when object is not modified 2023-06-23 17:02:31 +02:00
acognet
68ee12bf5e N°6456 - Empty list when configuring a list and cancel 2023-06-23 09:35:42 +02:00
Purple Grape
47400d94ba N°6468 update zh-cn dict (#512) 2023-06-23 09:03:49 +02:00
Molkobain
b642dbe3d6 N°6389 - Fix SQL error when saving an object with an emptied blob attribute 2023-06-22 20:43:56 +02:00
Molkobain
83564849e0 N°6356 - Search, SELECT on class with a single field display the count and an empty list 2023-06-22 20:18:18 +02:00
Molkobain
3e73038709 N°6380 - Fix exception "Failed to parse time string" with check_backup.php script 2023-06-22 13:51:01 +02:00
vdumas
dcfefc1588 N°1350 - Audit domains: add uniqueness rule on n:n with Category 2023-06-22 11:19:53 +02:00
vdumas
4db092ea52 N°6370 - Replace Audit Category menu by a dashboard only in module 2023-06-22 11:05:01 +02:00
Molkobain
96f1bd3646 N°6462 - Sort \AttributeApplicationLanguage alphabetically (eg. "Language" in ActionEmail class) 2023-06-21 18:33:12 +02:00
Molkobain
f889c53d71 N°6359 - Fix JS crash due to new version trying to detect MSIE browser through a dependency that we don't have.
It's far from perfect but as mentioned in the previous commit, we don't have much choices for now
2023-06-21 17:21:40 +02:00
Molkobain
814916fb21 N°6362 - Fix export of linkset tab not working due to JS error 2023-06-21 17:04:55 +02:00
Eric Espie
9e667c968e N°6408 - CRUD : Issue on DBlinkchange event when delete et create 2023-06-21 16:41:54 +02:00
Molkobain
a8a3385969 N°6460 - Sort \AttributeClassAttCodeSet alphabetically (eg. "Target fields" in Trigger class) 2023-06-21 16:06:22 +02:00
Molkobain
87e04547bd N°6448 - Restore sort order for \AttributeClass and \AttributeSetEnumPadded (eg. "Target class" and "Context" in DataSynchronisation and Trigger classes) 2023-06-21 15:58:11 +02:00
Eric Espie
6fe41796d9 N°5909 - Fix iApplicationExtension not called when attachment is added 2023-06-21 15:15:59 +02:00
Eric Espie
99a4e5e861 N°5909 - Fix iApplicationExtension not called when attachment is added 2023-06-21 14:39:04 +02:00
Molkobain
3d3a751072 N°6200 - Delete old redundant turkish dict file 2023-06-21 13:13:57 +02:00
Molkobain
216a1b95b1 N°6405 - Fix XML displayed corrupted when using \DesignerLongTextField due to usage of \utils::EscapeHtml() without double encoding 2023-06-21 11:33:18 +02:00
Benjamin Dalsass
2074a0fa0d N°5305 - CSV import ergonomy for SaaS
- Fix ext key displayable value
2023-06-21 11:21:40 +02:00
Eric Espie
bfd078c2a3 N°6281 - Rest API core/create key value is no more between quote 2023-06-20 17:25:13 +02:00
Benjamin Dalsass
85a879b587 N°5305 - CSV import ergonomy for SaaS
- Change checkbox error ugly font
- Improve errors messages (ambiguous, mismatch)
2023-06-20 17:21:39 +02:00
Eric Espie
9b9ba3c440 N°6281 - Rest API core/create key value is no more between quote 2023-06-20 16:58:16 +02:00
bdalsass
4d43d83b95 N°6000 - CSV Import with quote no more handled (#508) 2023-06-20 16:39:56 +02:00
Benjamin Dalsass
63487226a9 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	core/csvbulkexport.class.inc.php
2023-06-20 10:24:03 +02:00
vdumas
7b5ef52865 N°6422 - Improve french dictionary 2023-06-20 08:37:17 +02:00
Molkobain
4550dfb1d4 N°5388 - PHP 8.2: Fix dynamic property declaration 2023-06-19 16:27:45 +02:00
Molkobain
1a5ffc23ce N°6251 - Fix dynamic property declaration 2023-06-19 14:47:06 +02:00
vdumas
3a9198bf1c N°6422 - Finalize french dictionaries 2023-06-16 16:39:05 +02:00
vdumas
2242a80808 N°6428 - Portal : Hide role attribute in linkset Contact/Ticket 2023-06-16 16:19:04 +02:00
Benjamin Dalsass
86148ecf57 N°6415 - Linked set shown as tag set in csv import is ugly 2023-06-16 16:06:35 +02:00
Benjamin Dalsass
2c4a7c5a77 # Conflicts:
#	core/csvbulkexport.class.inc.php
2023-06-16 15:57:58 +02:00
Benjamin Dalsass
2cca57c7fa N°6431 - CSV bulk export text delimiter option not initialized correctly 2023-06-16 15:50:28 +02:00
Timothee
e52fa28089 Merge remote-tracking branch 'origin/release/3.1.0-beta' into develop 2023-06-16 15:21:51 +02:00
Pierre Goiffon
42dfa2c066 Merge branch 'develop' into release/3.1.0-beta 2023-06-16 10:47:37 +02:00
odain-cbd
49d3f4c2b2 N°6047 - Make iTop scripts work by HTTP via token with specific scopes (#494)
* N°6047 - Make iTop scripts work by HTTP via token with specific scopes

* Update core/contexttag.class.inc.php

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-06-15 15:36:35 +02:00
odain
d91eda1343 ci: enhance AddProfileToUser to work with any User 2023-06-15 15:07:09 +02:00
Timothee
e075bf8e89 Licenses update 2023-06-15 10:47:16 +02:00
Molkobain
40355c9442 N°6426 - Fix RenderAllUiBlocks test page not displaying 2023-06-15 10:20:41 +02:00
Timothee
f548dc2898 Dictionaries update for 3.1 beta 2023-06-15 09:47:51 +02:00
Benjamin Dalsass
0ece69112a N°6416 - In user portal, empty ext key drop down doesn't display error message anymore
Fix wrong jquery element resulting in select all form field instead of links elements
2023-06-15 08:37:03 +02:00
Molkobain
587b6a7148 N°6179 - Add visual test 2023-06-14 19:25:02 +02:00
Pierre Goiffon
baa4641795 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-06-14 19:03:02 +02:00
Pierre Goiffon
52820925b1 N°4725 DeprecatedCallsLog::NotifyDeprecatedFile remove throw ConfigException
Those exceptions are handled silently since 3.0.1
2023-06-14 19:01:50 +02:00
Benjamin Dalsass
578f2dfa8e N°3190 - Edit n:n LinkedSetIndirect in object details using a tagset-like widget
Add security if add option button js code isn't provided
2023-06-14 14:50:17 +02:00
Anne-Catherine
5185f721bd N°5594 - Dashboard Bug with Group by (table) - iTop 3.0 (#466) 2023-06-14 11:06:25 +02:00
Pierre Goiffon
add038d649 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-06-14 10:28:46 +02:00
Pierre Goiffon
1824111de8 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-06-14 10:28:30 +02:00
Pierre Goiffon
5a0b5364d6 N°4698 setup/phpinfo.php : if no iTop installation then display a proper message instead of an exception (#265) 2023-06-14 10:18:38 +02:00
Pierre Goiffon
76eed2eba0 N°6098 updateLicenses script : check availability of the required JQ command (#458)
This packaging script requires both bash and the JQ command when running on Windows.
If the later isn't available, it will run without throwing an error...

With this change the script will now check directly at launch for the JQ command availability, and exit in error if it isn't.
2023-06-14 10:17:00 +02:00
Anne-Catherine
c2309876fb N°6239 - Portail: Fix error message when trying to display object with missing icon (#485) 2023-06-14 09:42:43 +02:00
Molkobain
1d12ebd733 N°6398 - Improve PHPDoc 2023-06-14 09:38:05 +02:00
Eric Espie
5578147002 N°5906 - CRUD Event - fire event EVENT_DB_LINKS_CHANGED when an n-n link is created/updated/deleted 2023-06-14 09:32:19 +02:00
Eric Espie
a152c5db66 N°6351 - code hardening 2023-06-14 09:11:36 +02:00
Eric Espie
5d5589dd64 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-06-14 09:09:12 +02:00
Eric Espie
1ec671ef61 N°6351 - code hardening 2023-06-14 09:08:42 +02:00
vdumas
04e0950efb N°1350 - Audit selection: use absolute url 2023-06-14 08:13:48 +02:00
Molkobain
79d6cadfde N°4106 - Fix typo in the PHPDoc thanks to @piRGoif 2023-06-13 22:34:50 +02:00
Tarjányi Csaba
a1daef70e2 N°6419 - Update hungarian translations thanks to @tacsaby (#472)
* Update hu.dictionary.itop.ui.php

Some corrections

* Revert formatting changes

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-06-13 18:00:21 +02:00
Molkobain
eb7afd53a1 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	datamodels/2.x/itop-config-mgmt/dictionaries/nl.dict.itop-config-mgmt.php
2023-06-13 17:50:57 +02:00
Thomas Casteleyn
b8199a6e2c N°6418 - Fix dutch translations on impact relation view (#499)
* Fix author and copyrights
* Correct NL dict for impact relation view
2023-06-13 17:48:36 +02:00
Purple Grape
68e1f359d3 N°6417 - Update chinese translations thanks to @purplegrape (#500) 2023-06-13 17:36:27 +02:00
Eric Espie
e574fe3dc5 N°5906 - CRUD Event - fire event EVENT_DB_LINKS_CHANGED when an n-n link is created/updated/deleted 2023-06-13 17:12:35 +02:00
Molkobain
0fbef99875 N°4010 - Remove unnecessary single quote escape 2023-06-13 15:59:16 +02:00
Denis
9a5ad6681d Typo in en.dictionary.itop.core.php 2023-06-13 13:44:20 +02:00
Pierre Goiffon
d0cad4d829 N°1150 Fix regression : cannot add caselog in the user portal
Was caused by field value check added in MultipleChoicesField and SelectObjectField in b71cd218

With this commit the checks are now only done if we have a ContextTag::TAG_REST
2023-06-13 12:34:46 +02:00
Pierre Goiffon
b3ef634d21 N°1150 SelectObjectField : factorize DBObjectSet retrieval between Validate and VerifyCurrentValue 2023-06-13 12:34:46 +02:00
Molkobain
5915b2cc01 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-06-13 11:43:11 +02:00
Molkobain
9f38eec40a N°4106 - Reword PHPDoc to avoid confusion as the @internal was not accurate 2023-06-13 11:42:28 +02:00
Benjamin Dalsass
b2c2d5eaea N°6398 - Portal: Allow linkset visible attributes to be limited to attributes defined in a zlist 2023-06-13 10:51:05 +02:00
Denis
fed149dc66 N°918 - Translate placeholders in notifications (#506)
- Localization of date & time formats
- Use of DataLocalizer (if present)
- All placeholders can be used in the uploaded HTML template as well as in the notification "message"
2023-06-13 09:51:32 +02:00
Molkobain
58a20e9a11 N°1646 - Fix crash in case no value return (real fix about to be discussed) 2023-06-13 09:36:30 +02:00
Benjamin Dalsass
0a45039c5c N°6005 - CSV import : import an object and a linkset fails 2023-06-13 08:14:34 +02:00
Benjamin Dalsass
465388b5e3 N°803 - Allow display & edition of attributes on n:n relations on Portal
- Issue when selecting links rows with checkbox
- Change linked set field validation to prevent checking host id -1
2023-06-12 17:26:34 +02:00
vdumas
ad1836d028 N°1350 - Audit: propose domains selection as default 2023-06-12 14:15:57 +02:00
Eric Espie
1655674543 Merge remote-tracking branch 'origin/support/3.0' into develop 2023-06-12 11:39:39 +02:00
Eric Espie
5f5537b8b9 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	js/breadcrumb.js
2023-06-12 11:39:13 +02:00
Eric Espie
72716b7ec8 N°6396 - Protect URL display 2023-06-12 11:36:51 +02:00
Eric Espie
5084f7fccb N°6396 - Protect URL display 2023-06-12 11:33:33 +02:00
Molkobain
bd96247f97 N°1345 - Fix typo in conf. param. description 2023-06-12 09:56:57 +02:00
vdumas
01fb3466c8 N°1350 - Audit: propose domains selection as default 2023-06-12 09:22:34 +02:00
vdumas
b5a637fa11 N°1350 - Audit: Title, breadcrumb, back button and link to configure 2023-06-09 20:13:44 +02:00
Molkobain
4d6236cbea N°1345 - Restrict unit test iTop community only as it is based on the standard DM (thanks @piRGoif) 2023-06-09 17:55:54 +02:00
vdumas
2829068499 N°6381 - Add rank on Enums of default DataModel 2023-06-09 17:51:00 +02:00
Molkobain
8304e4df00 N°6394 - Force header statistics to sort [meta]enums pills according to the datamodel instead of the selection order 2023-06-09 17:49:20 +02:00
Pierre Goiffon
2824718189 N°6376 Fix french labels (requête => demande) 2023-06-09 16:35:21 +02:00
Pierre Goiffon
2d866b8f47 N°6320 Fix GetSelectFilterTest::testGetSelectFilter crashing when authent-token module installed 2023-06-09 15:47:19 +02:00
vdumas
36ad278b1f N°2199 - Request history tables without the Admin profile 2023-06-09 13:05:05 +02:00
Molkobain
74fec312d8 N°1345 - Add unit test 2023-06-09 13:01:59 +02:00
vdumas
05559a944b N°6381 - Add capital letter on enum values so rank by label works 2023-06-09 12:53:42 +02:00
Molkobain
508647fe0b N°1345 - Add possibility to sort transitions automatically 2023-06-09 12:49:42 +02:00
vdumas
c04c7e06ea N°6381 - Add capital letter on enum values so rank by label works 2023-06-09 11:48:14 +02:00
Benjamin Dalsass
2041a6ad3f N°803 - Allow display & edition of attributes on n:n relations on Portal
- Remove column mandatory information when object is in view mode
2023-06-09 10:40:24 +02:00
Benjamin Dalsass
c486aea299 N°803 - Allow display & edition of attributes on n:n relations on Portal
- Remove input when object is in view mode
- Improve form errors handling
- Prevent row selection when clicking input 
- Attach date time picker to table instead of input to prevent popup truncating (popup will be visible but not aligned on the input)
2023-06-09 10:34:19 +02:00
Eric Espie
1b7529fcb9 N°6384 - Flag LinkedSet to specify that it has CheckToWrite constraint (more specific parameter name) 2023-06-09 10:21:47 +02:00
Eric Espie
191742c2a9 N°6384 - Flag LinkedSet to specify that it has CheckToWrite constraint (more specific parameter name) 2023-06-09 10:17:22 +02:00
vdumas
d985ff860d N°2199 - Request history tables without the Admin profile 2023-06-09 09:20:41 +02:00
Molkobain
ae65856797 N°1646 - Fix unit tests to reflect new enum sort 2023-06-09 08:34:09 +02:00
vdumas
0fb770783d N°6381 - Add rank on Enums of default DataModel 2023-06-08 18:56:45 +02:00
Anne-Catherine
cc8c6ac027 N°3213 - Order transition attributes like in the "details" zlist instead of reordering them regarding the dependencies (#306)
N°3213 - Order transition attributes like in the "details" zlist instead of reordering them regarding the dependencies (#306)

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-06-08 17:29:53 +02:00
Eric Espie
99e84e86cd N°6384 - fix typo 2023-06-08 17:19:45 +02:00
Eric Espie
7ebf5a6dd4 N°6384 - Flag LinkedSet (Indirect) when CheckToWrite must be requested to Hosted Object 2023-06-08 17:07:01 +02:00
Vincent Dumas
55009b53e2 N°2199 - Request history tables without the Admin profile (#497) 2023-06-08 16:50:06 +02:00
vdumas
42304fb489 N°2199 - New History Group for access to other than Administrator 2023-06-08 16:38:09 +02:00
Eric Espie
f4fcfee03b N°6359 - Add comment 2023-06-08 15:52:08 +02:00
Eric Espie
a6e56e37c9 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	tests/php-unit-tests/ItopDataTestCase.php
2023-06-08 15:11:39 +02:00
Eric Espie
e288af4ddb Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	tests/setup_params/default-params.xml
2023-06-08 14:33:54 +02:00
Eric Espie
4f999de844 N°6359 - ⬆️ Update jQuery BBQ (from https://github.com/cee-chen/jquery-bbq) 2023-06-08 14:30:09 +02:00
denis.flaven@combodo.com
1809ea647f 💄 More explicit icon for the 'Add criterion' button 2023-06-08 11:55:08 +02:00
Anne-Catherine
f47133bc28 N°6125 - Issue with GetAttributeFlags and GetInitialStateAttributeFlags within iTop 3.0.2 (#474) 2023-06-08 11:12:22 +02:00
Pierre Goiffon
14d3eb6624 N°6238 Security hardening 2023-06-08 09:31:15 +02:00
odain
ea49c0a87c enable authent-cas in ci 2023-06-07 21:44:17 +02:00
odain
6cc971849b ci: enhance AddProfile 2023-06-07 21:44:00 +02:00
vdumas
68a1c0f0cb N°1350 - Audit result and audit configuration: add navigation from one to the other 2023-06-07 20:17:39 +02:00
Molkobain
edba3270a2 N°1646 - Update standard datamodel with enum sorted by rank 2023-06-07 19:53:12 +02:00
Molkobain
4d5d149079 N°1646 - Add unit tests 2023-06-07 19:53:12 +02:00
Molkobain
bac6cbc1d8 N°1646 - Add possibility to sort Attribute[Meta]Enum either by code (default), rank or label 2023-06-07 19:53:11 +02:00
Eric Espie
f886c78f24 Merge remote-tracking branch 'origin/support/3.0' into develop -> fix unit tests 2023-06-07 17:49:52 +02:00
Eric Espie
62ed5adbc5 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	application/loginwebpage.class.inc.php
#	composer.json
#	lib/composer/installed.json
#	lib/guzzlehttp/psr7/.github/workflows/ci.yml
#	lib/guzzlehttp/psr7/.github/workflows/integration.yml
#	lib/guzzlehttp/psr7/.github/workflows/static.yml
#	lib/guzzlehttp/psr7/CHANGELOG.md
#	lib/guzzlehttp/psr7/composer.json
#	lib/guzzlehttp/psr7/src/MessageTrait.php
#	tests/php-unit-tests/ItopDataTestCase.php
2023-06-07 17:45:50 +02:00
Eric Espie
0fbd41a884 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 -> fix syntax error 2023-06-07 17:30:39 +02:00
Eric Espie
70e6f707c4 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 -> composer autoloader 2023-06-07 17:23:47 +02:00
Eric Espie
e76ada641f Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	application/loginbasic.class.inc.php
#	application/loginexternal.class.inc.php
#	application/loginform.class.inc.php
#	application/loginurl.class.inc.php
#	application/loginwebpage.class.inc.php
#	composer.lock
#	datamodels/2.x/authent-cas/src/CASLoginExtension.php
#	lib/composer/autoload_real.php
#	lib/composer/installed.php
#	synchro/synchro_exec.php
#	synchro/synchro_import.php
#	tests/php-unit-tests/unitary-tests/application/utilsTest.php
2023-06-07 17:21:09 +02:00
Pierre Goiffon
2405810864 N°6238 Security hardening 2023-06-07 16:45:35 +02:00
Denis
120b8b55ed Typo 2023-06-07 15:53:52 +02:00
Eric Espie
fff46d99fc N°6358 - Login REST API - renamed test 2023-06-07 15:31:51 +02:00
Benjamin Dalsass
11c147574a N°803 - Allow display & edition of attributes on n:n relations on Portal 2023-06-07 15:30:08 +02:00
odain
3a891f707c ci: enhance AddProfile test method to work with any User (not only UserLocal) 2023-06-07 15:06:28 +02:00
odain
8b6ea43ebe N°6358 - Login REST API - fix cas + add tests 2023-06-07 15:05:32 +02:00
Eric Espie
90cf7502e8 N°6358 - Login REST API 2023-06-07 10:09:30 +02:00
Eric Espie
c596fa2967 N°6358 - Login API REST 2023-06-07 09:17:24 +02:00
Timothee
a45177410e N°6350 - Fixing phpunit test 2023-06-06 16:47:06 +02:00
vdumas
d778203f99 N°6370 - Replace Audit Category menu by a dashboard 2023-06-05 18:32:57 +02:00
vdumas
652a9f6e40 N°6370 - Replace Audit Category menu by a dashboard 2023-06-05 17:58:14 +02:00
Benjamin Dalsass
8eb1053daa N°803 - Allow display & edition of attributes on n:n relations on Portal 2023-06-05 16:19:06 +02:00
Eric Espie
519751faa1 N°6348 - Add unit test 2023-06-05 14:50:37 +02:00
Eric Espie
b99ff5e84a ✏️ typo 2023-06-05 08:22:29 +02:00
Benjamin Dalsass
4264123ef6 N°6219 - 1:n Read: tooltip, modal title and message on Add-Edit-Remove-Delete 2023-06-02 08:08:29 +02:00
vdumas
eb1ff95437 N°6347 - 1:n Add nice french dico entry on standard 1:n relationship 2023-06-01 17:12:47 +02:00
Eric Espie
cb7cbe9297 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	pages/UI.php
#	tests/php-unit-tests/unitary-tests/application/utilsTest.php
2023-06-01 16:59:59 +02:00
Eric Espie
b8f61362f5 N°6348 - Hardening code 2023-06-01 16:44:40 +02:00
Eric Espie
e3ba826e5d N°6349 - Hardening code 2023-06-01 16:36:56 +02:00
Eric Espie
17d22219d2 N°6350 - Fix unit tests 2023-06-01 16:30:09 +02:00
Eric Espie
a49025f371 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	application/utils.inc.php
2023-06-01 16:04:52 +02:00
Eric Espie
9e96ea2873 N°6350 - code hardening 2023-06-01 15:35:56 +02:00
Eric Espie
1172159745 N°6351 - code hardening 2023-06-01 15:12:50 +02:00
vdumas
c2fb0150b0 N°6347 - 1:n Add nice french dico entry on standard 1:n relationship 2023-06-01 11:56:34 +02:00
Molkobain
41bce359a0 N°6307 - Fix mentions not working in edition due to CRUD changes 2023-05-31 17:45:20 +02:00
Eric Espie
3e7c48d5c6 N°6357 - Prevent entering same password on change user password 2023-05-31 17:12:26 +02:00
vdumas
5e3b813da7 N°6339 - n:n Add nice french dico entry 2023-05-31 16:50:54 +02:00
Eric Espie
d680369733 N°5726 - loose data localizer language in console after impersonating a user with a different langage 2023-05-31 16:45:20 +02:00
vdumas
0af8f65a11 N°6339 - n:n Add nice french dico entry 2023-05-31 16:33:48 +02:00
Molkobain
f41879f0b1 N°6307 - Fix unappropriate confirm message when updating log on object edition
Proper fix as 345a8371 was temporary
2023-05-31 16:26:26 +02:00
Molkobain
eec9b027ac Revert "N°6307 Fix alert when caselog filled and submitting form"
This reverts commit 4b9b0a91a9.
2023-05-31 16:26:26 +02:00
Eric Espie
5f359b2b61 N°6151 - Allow to select classes on union outside the already selected classes 2023-05-31 14:40:13 +02:00
Benjamin Dalsass
31a96d24dd N°6219 - 1:n Read: tooltip, modal title and message on Add-Edit-Remove-Delete 2023-05-31 10:10:02 +02:00
Molkobain
d07bce3063 N°6352 - Portal: Fix autoloaders 2023-05-30 16:29:07 +02:00
Molkobain
c9beba0cea N°6352 - Portal: Fix "Combodo\iTop\Portal\Controller\AggregatePageBrickController" has no container set due to Symfony 5.4 migration 2023-05-30 16:09:58 +02:00
vdumas
4d9b054430 N°2639 - Add Tooltips EN/FR dico entries for Data Synchro 2023-05-30 15:42:42 +02:00
Eric Espie
2c4c69fcfc Fixed regression due to forcing camel case names in operation methods, now allow both camel case and snake case 2023-05-30 15:25:00 +02:00
Eric Espie
173960e717 N°6324 - CRUD Event for one time treatment before creation and before update 2023-05-30 11:54:15 +02:00
vdumas
79585c7128 N°6339 - n:n Add nice french dico entry on standard lnk 2023-05-30 11:34:33 +02:00
Eric Espie
c3bb995407 N°6324 - CRUD Event for one time treatment before creation and before update 2023-05-30 11:29:24 +02:00
vdumas
e3dd805f24 N°6338 - Add silo (org_id) and location on all final Interfaces, LogicalVolume & NASFileSystem 2023-05-26 13:49:49 +02:00
acognet
649457bfc2 N°6308 - issue on displaying a userrequest list with Service_details fields with PHP 8 2023-05-26 00:25:42 +02:00
vdumas
e933807b1c N°6203 - Complementary name on Ticket sub-classes 2023-05-24 18:41:02 +02:00
vdumas
52fd333922 N°6339 - n:n Add nice french dico entry on standard lnk 2023-05-24 18:17:58 +02:00
vdumas
91a9997480 N°6203 - Overcard & complementary on Ticket classes 2023-05-24 15:39:22 +02:00
vdumas
906ad70156 N°6331 - Add Service tab in Provider Contract and add fieldsets 2023-05-24 13:07:52 +02:00
Pierre Goiffon
0508608e78 N°6322 Delegate \AttributeCustomFields::CheckValue to the handler
This allows to move template_id checks from BuildForm (which is called in the UI to construct the form also) to a dedicated method only called for validation
2023-05-24 09:28:26 +02:00
vdumas
63fcf4e6f9 N°6203 - Add summary to CMDB classes 2023-05-23 20:00:57 +02:00
vdumas
f805c2f834 N°6203 - Add complementary name to CMDB classes 2023-05-22 18:30:33 +02:00
vdumas
976c9c1523 N°6311 - Add a Log on User Local, LDAP and External 2023-05-20 18:54:10 +02:00
vdumas
22a14a2de2 N°6323 - n:n Add filter on lnk ExtKey aligned to PrefillSearchForm 2023-05-20 17:21:10 +02:00
vdumas
bd04b2a24c N°6203 - Standard DM overcard, complementary name 2023-05-20 17:20:16 +02:00
Eric Espie
0188108a68 Add EVENT_DB_BEFORE_CREATE and EVENT_DB_BEFORE_UPDATE events in CRUD calls 2023-05-17 10:16:18 +02:00
Pierre Goiffon
4038d4d925 N°6314 N°1150 Fix cannot reopen or close a UR in the user portal
Since introduction of MultipleChoicesField and SelectObjectField value checks with b71cd218, when a portal user was trying to do an action on a resolved UR the agent_id field was rejected. The reason for the rejection is the corresponding SelectObjectField query is containing the portal user scopes (query generated in ObjectFormManager::Build), and of course the agent_id set isn't in this scope...

As a workaround we are disabling those checks for read only fields (values are set server side and possibly in a different context than the portal user)
2023-05-16 17:54:24 +02:00
acognet
ea88aa85df N°4517 - PHP 8.1 compatibility : Replace htmlentities(***, ENT_QUOTES,'UTF-8') with utils::EscapeHtml 2023-05-16 17:45:58 +02:00
Eric Espie
8f2814a590 Undo... Move event declaration where the object is defined 2023-05-16 15:59:50 +02:00
Eric Espie
ad25d8823f Add EVENT_DB_BEFORE_CREATE and EVENT_DB_BEFORE_UPDATE events in CRUD calls 2023-05-16 15:52:02 +02:00
Eric Espie
59cd489e72 N°6252 - Make designer itop 3.1 compatible - Fix deprecated calls of opendir() with null value 2023-05-16 14:06:01 +02:00
Pierre Goiffon
4b9b0a91a9 N°6307 Fix alert when caselog filled and submitting form
Regression was introduced in 8dc7d68b
Temporary fix, as the alert is caused by activity-panel.js which is disabled only if another handler is registered before 🤯 As a temporary fix I add an empty handler at the same place as the one that was there before, but we need a better way to disable activity-panel.js handler !
2023-05-16 12:14:26 +02:00
vdumas
a21def5329 N°4010 - Configuration file overwritten - fine tune messages 2023-05-16 12:06:26 +02:00
vdumas
55f8efe0db N°4010 - Configuration file overwritten - fine tune messages 2023-05-16 12:06:26 +02:00
Eric Espie
0cc28b42b2 Move EventService init to Metamodel::Startup() for unit tests 2023-05-16 10:23:01 +02:00
odain
0001e8ffc4 💚 use new ci validation 2020-10-09 10:13:51 +02:00
1282 changed files with 55298 additions and 32646 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

View File

@@ -62,6 +62,12 @@ gitGraph
commit id: "2022-12-28" tag: "2.7.8"
checkout support/3.0
commit id: "2023-04-12" tag: "3.0.3"
checkout develop
commit id: "2023-06-19" tag: "3.1.0-beta" type: REVERSE
commit id: "2023-07-26" tag: "3.1.0-1" type: HIGHLIGHT
branch support/3.1 order: 840
checkout support/3.1
commit id: "2023-08-09" tag: "3.1.0-2"
```
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).

83
.github/pull_request_template.md vendored Normal file
View File

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

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

@@ -0,0 +1,16 @@
name: Add PRs to Combodo PRs Dashboard
on:
pull_request_target:
types:
- opened
jobs:
add-to-project:
name: Add PR to Combodo Project
runs-on: ubuntu-latest
steps:
- uses: actions/add-to-project@v1.0.2
with:
project-url: https://github.com/orgs/Combodo/projects/5
github-token: ${{ secrets.PR_AUTOMATICALLY_ADD_TO_PROJECT }}

6
.gitignore vendored
View File

@@ -37,7 +37,9 @@ tests/*/vendor/*
# iTop extensions
/extensions/**
!/extensions/.htaccess
!/extensions/readme.txt
!/extensions/web.config
# all logs but listing prevention
/log/**
@@ -45,8 +47,10 @@ tests/*/vendor/*
!/log/index.php
!/log/web.config
# PHPUnit cache file
# PHPUnit: Cache file, local XML working copies
/tests/php-unit-tests/.phpunit.result.cache
/tests/php-unit-tests/phpunit.xml
/tests/php-unit-tests/postbuild_integration.xml
# Jetbrains

View File

@@ -27,7 +27,7 @@ $iTopFolder = __DIR__."/../../../";
require_once("$iTopFolder/approot.inc.php");
require_once(APPROOT."/application/utils.inc.php");
if (php_sapi_name() !== 'cli')
if (PHP_SAPI !== 'cli')
{
throw new \Exception('This script can only run from CLI');
}
@@ -48,4 +48,4 @@ if (!file_exists($sCssFile))
{
fwrite(STDERR, "Failed to compile $sCssFile, exiting.");
exit(1);
}
}

View File

@@ -26,7 +26,7 @@ $iTopFolder = __DIR__ . "/../../" ;
require_once ("$iTopFolder/approot.inc.php");
require_once (APPROOT."/setup/setuputils.class.inc.php");
if (php_sapi_name() !== 'cli')
if (PHP_SAPI !== 'cli')
{
throw new \Exception('This script can only run from CLI');
}
@@ -70,4 +70,4 @@ if (false === empty($aMissing)) {
echo "Some new tests dirs exists !\n"
.' They must be declared either in the allowed or denied list in '.iTopComposer::class." (see N°2651).\n"
.' List of dirs:'."\n".var_export($aMissing, true);
}
}

View File

@@ -19,17 +19,24 @@
* The target license file path is in `$xmlFilePath`
*/
$iTopFolder = __DIR__ . "/../../" ;
$xmlFilePath = $iTopFolder . "setup/licenses/community-licenses.xml";
$iTopFolder = __DIR__."/../../";
$xmlFilePath = $iTopFolder."setup/licenses/community-licenses.xml";
function get_scope($product_node)
{
$jqExec = shell_exec("jq -V"); // a param is mandatory otherwise the script will freeze
if ((null === $jqExec) || (false === $jqExec)) {
echo "/!\ JQ is required but cannot be launched :( \n";
echo "Check this script PHPDoc block for instructions\n";
die(-1);
}
function get_scope($product_node) {
$scope = $product_node->getAttribute("scope");
if ($scope === "")
{ //put iTop first
if ($scope === "") { //put iTop first
return "aaaaaaaaa";
}
return $scope;
}

View File

@@ -161,4 +161,4 @@ We have one sticker per contribution type. You might get multiple stickers with
Here is the design of each stickers for year 2022:
![iTop stickers 2022](.doc/contributing-guide/2022.contributing-stickers-side-by-side.png)
![iTop stickers 2023](.doc/contributing-guide/2023.contributing-stickers-side-by-side.png)

View File

@@ -107,7 +107,8 @@ We would like to give a special thank you 🤗 to the people from the community
- Rudner, Björn (a.k.a [@rudnerbjoern](https://github.com/rudnerbjoern))
- Seki, Shoji
- Shilov, Vladimir
- Stukalov, Ilya (a.k.a [@ilya](https://www.github.com/ilya)-stukalov)
- Stukalov, Ilya (a.k.a [@ilya](https://www.github.com/ilya-stukalov))
- Tarjányi, Csaba (a.k.a [@tacsaby](https://github.com/tacsaby))
- Tulio, Marco
- Turrubiates, Miguel

View File

@@ -228,6 +228,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
"db_table" => "priv_urp_userprofile",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => true, /** @since 3.1.0 N°6482 */
'uniqueness_rules' => array(
'no_duplicate' => array(
'attributes' => array(
@@ -445,6 +446,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
UR_ACTION_BULK_DELETE => 'bd',
);
/**
* @var array $aUsersProfilesList Cache of users' profiles. Hash array of user ID => [profile ID => profile friendlyname, profile ID => profile friendlyname, ...]
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6887
*/
private $aUsersProfilesList = [];
// Installation: create the very first user
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
{
@@ -501,6 +508,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
}
protected $m_aUserOrgs = array(); // userid -> array of orgid
protected $m_aAdministrators = null; // [user id]
// Built on demand, could be optimized if necessary (doing a query for each attribute that needs to be read)
protected $m_aObjectActionGrants = array();
@@ -557,6 +565,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Cache
$this->m_aObjectActionGrants = array();
$this->m_aAdministrators = null;
}
public function LoadCache()
@@ -699,12 +708,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
*/
private function GetAdministrators()
{
static $aAdministrators = null;
if ($aAdministrators === null)
if ($this->m_aAdministrators === null)
{
// Find all administrators
$aAdministrators = array();
$this->m_aAdministrators = array();
$oAdministratorsFilter = new DBObjectSearch('User');
$oLnkFilter = new DBObjectSearch('URP_UserProfile');
$oExpression = new FieldExpression('profileid', 'URP_UserProfile');
@@ -717,10 +724,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
$oSet->OptimizeColumnLoad(array('User' => array('login')));
while($oUser = $oSet->Fetch())
{
$aAdministrators[] = $oUser->GetKey();
$this->m_aAdministrators[] = $oUser->GetKey();
}
}
return $aAdministrators;
return $this->m_aAdministrators;
}
/**
@@ -757,8 +764,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
$sAction = self::$m_aActionCodes[$iActionCode];
$bStatus = null;
// Cache user's profiles
if(false === array_key_exists($iUser, $this->aUsersProfilesList)){
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
}
// Call the API of UserRights because it caches the list for us
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
{
$bGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
if (!is_null($bGrant))
@@ -884,11 +895,16 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Note: this code is VERY close to the code of IsActionAllowed()
$iUser = $oUser->GetKey();
// Cache user's profiles
if(false === array_key_exists($iUser, $this->aUsersProfilesList)){
$this->aUsersProfilesList[$iUser] = UserRights::ListProfiles($oUser);
}
// Note: The object set is ignored because it was interesting to optimize for huge data sets
// and acceptable to consider only the root class of the object set
$bStatus = null;
// Call the API of UserRights because it caches the list for us
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
{
$bGrant = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
if (!is_null($bGrant))
@@ -917,8 +933,9 @@ class UserRightsProfile extends UserRightsAddOnAPI
}
/**
* Find out which attribute is corresponding the the dimension 'owner org'
* returns null if no such attribute has been found (no filtering should occur)
* @param string $sClass
* @return string|null Find out which attribute is corresponding the dimension 'owner org'
* returns null if no such attribute has been found (no filtering should occur)
*/
public static function GetOwnerOrganizationAttCode($sClass)
{

View File

@@ -334,6 +334,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
"db_table" => "priv_urp_userprofile",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => true, /** @since 3.1.0 N°6482 */
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
@@ -579,10 +580,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
/**
* Read and cache organizations allowed to the given user
*
* @param $oUser
* @param $sClass (not used here but can be used in overloads)
* @param User $oUser
* @param string $sClass (not used here but can be used in overloads)
*
* @return array
* @return array keys of the User allowed org
* @throws \CoreException
* @throws \Exception
*/

View File

@@ -277,6 +277,7 @@ class URP_UserProfile extends UserRightsBaseClass
"db_table" => "priv_urp_userprofile",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => true, /** @since 3.1.0 N°6482 */
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/AjaxPage.php, now loadable using autoloader
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/AjaxPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -238,7 +238,7 @@ class ApplicationContext
{
$aContextInputBlocks = [];
foreach ($this->aValues as $sName => $sValue) {
$aContextInputBlocks[] = InputUIBlockFactory::MakeForHidden("c[$sName]", utils::EscapeHtml($sValue));
$aContextInputBlocks[] = InputUIBlockFactory::MakeForHidden("c[$sName]", $sValue);
}
return $aContextInputBlocks;
}

View File

@@ -335,7 +335,6 @@ abstract class AbstractPreferencesExtension implements iPreferencesExtension
* A recommended pattern is to cache data by the mean of static members.
*
* @api
* @deprecated 3.1.0 N°4756 use the new event service instead, see {@see DBObject::FireEvent()} method
* @package UIExtensibilityAPI
*/
interface iApplicationUIExtension
@@ -487,7 +486,6 @@ interface iApplicationUIExtension
* @api
* @package UIExtensibilityAPI
* @since 2.7.0
* @deprecated
*/
abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
{
@@ -560,6 +558,7 @@ abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
* or through the GUI.
*
* @api
* @deprecated 3.1.0 N°4756 use the new event service instead, see {@see DBObject::FireEvent()} method. More details on each method PHPDoc.
* @package ORMExtensibilityAPI
*/
interface iApplicationObjectExtension
@@ -574,6 +573,7 @@ interface iApplicationObjectExtension
* Otherwise, the answer is definitively "yes, the object has changed".
*
* @api
* @deprecated 3.1.0 N°4756 No alternative available, this API was unstable and is abandoned
* @param \cmdbAbstractObject $oObject The target object
*
* @return boolean True if something has changed for the target object
@@ -587,6 +587,7 @@ interface iApplicationObjectExtension
* Anyhow, this API can be called in other contexts such as the CSV import tool.
*
* @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_CHECK_TO_WRITE event instead
* @param \cmdbAbstractObject $oObject The target object
*
* @return string[] A list of errors message. An error message is made of one line and it can be displayed to the end-user.
@@ -601,6 +602,7 @@ interface iApplicationObjectExtension
* Please not that it is not possible to cascade deletion by this mean: only stopper issues can be handled.
*
* @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_CHECK_TO_DELETE event instead
* @param \cmdbAbstractObject $oObject The target object
*
* @return string[] A list of errors message. An error message is made of one line and it can be displayed to the end-user.
@@ -617,6 +619,7 @@ interface iApplicationObjectExtension
* * {@see DBObject::Get()} : for a given attribute the new value that was persisted
*
* @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_AFTER_WRITE event instead
* @param \cmdbAbstractObject $oObject The target object
* @param CMDBChange|null $oChange A change context. Since 2.0 it is fine to ignore it, as the framework does maintain this information
* once for all the changes made within the current page
@@ -633,6 +636,7 @@ interface iApplicationObjectExtension
* The method is called right <b>after</b> the object has been written to the database.
*
* @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_AFTER_WRITE event instead
* @param \cmdbAbstractObject $oObject The target object
* @param CMDBChange|null $oChange A change context. Since 2.0 it is fine to ignore it, as the framework does maintain this information
* once for all the changes made within the current page
@@ -647,6 +651,7 @@ interface iApplicationObjectExtension
* The method is called right <b>before</b> the object will be deleted from the database.
*
* @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_AFTER_DELETE event instead
* @param \cmdbAbstractObject $oObject The target object
* @param CMDBChange|null $oChange A change context. Since 2.0 it is fine to ignore it, as the framework does maintain this information
* once for all the changes made within the current page
@@ -660,6 +665,7 @@ interface iApplicationObjectExtension
* Extend this class instead of iApplicationObjectExtension if you don't need to overload all methods
*
* @api
* @deprecated 3.1.0 N°4756 use the new event service instead, see {@see DBObject::FireEvent()} method
* @package ORMExtensibilityAPI
* @since 2.7.0
*/
@@ -2009,6 +2015,8 @@ class RestUtils
*
* @return DBObject The object found
* @throws Exception If the input structure is not valid or it could not find exactly one object
*
* @see DBObject::CheckChangedExtKeysValues() generic method to check that we can access the linked object isn't used in that use case because values can be literal, OQL, friendlyname
*/
public static function FindObjectFromKey($sClass, $key, $bAllowNullValue = false)
{
@@ -2095,8 +2103,16 @@ class RestUtils
elseif (is_string($key))
{
// OQL
$oSearch = DBObjectSearch::FromOQL($key);
}
try {
$oSearch = DBObjectSearch::FromOQL($key);
} catch (Exception $e) {
throw new CoreOqlException('Query failed to execute', [
'query' => $key,
'exception_class' => get_class($e),
'exception_message' => $e->getMessage(),
]);
}
}
else
{
throw new Exception("Wrong format for key");
@@ -2246,3 +2262,27 @@ interface iModuleExtension
*/
public function __construct();
}
/**
* KPI logging extensibility point
*
* KPI Logger extension
*/
interface iKPILoggerExtension
{
/**
* Init the statistics collected
*
* @return void
*/
public function InitStats();
/**
* Add a new KPI to the stats
*
* @param \Combodo\iTop\Core\Kpi\KpiLogData $oKpiLogData
*
* @return mixed
*/
public function LogOperation($oKpiLogData);
}

View File

@@ -34,15 +34,15 @@ class AuditCategory extends cmdbAbstractObject
{
$aParams = array
(
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_auditcategory",
"db_key_field" => "id",
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_auditcategory",
"db_key_field" => "id",
"db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-folder.svg'),
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-folder.svg'),
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
@@ -74,15 +74,23 @@ class AuditCategory extends cmdbAbstractObject
public function GetReportColor($iTotal, $iErrors)
{
$sResult = 'red';
if ( ($iTotal == 0) || ($iErrors / $iTotal) <= ($this->Get('ok_error_tolerance') / 100) )
{
if ( ($iTotal == 0) || ($iErrors / $iTotal) <= ($this->Get('ok_error_tolerance') / 100) ) {
$sResult = 'green';
}
else if ( ($iErrors / $iTotal) <= ($this->Get('warning_error_tolerance') / 100) )
{
} else if (($iErrors / $iTotal) <= ($this->Get('warning_error_tolerance') / 100)) {
$sResult = 'orange';
}
return $sResult;
}
public static function GetShortcutActions($sFinalClass)
{
$aShortcutActions = parent::GetShortcutActions($sFinalClass);
if (!in_array('UI:Menu:RunAudit', $aShortcutActions)) {
$aShortcutActions[] = 'UI:Menu:RunAudit';
}
return $aShortcutActions;
}
}
?>

View File

@@ -35,7 +35,7 @@ class AuditDomain extends cmdbAbstractObject
{
$aParams = array
(
"category" => "application, grant_by_profile",
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"complementary_name_attcode" => array('description'),
@@ -47,19 +47,30 @@ class AuditDomain extends cmdbAbstractObject
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-album.svg'),
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeImage("icon", array("is_null_allowed"=>true, "depends_on"=>array(), "display_max_width"=>96, "display_max_height"=>96, "storage_max_width"=>256, "storage_max_height"=>256, "default_image"=>null, "always_load_in_tables"=>false)));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("categories_list", array("linked_class" => "lnkAuditCategoryToAuditDomain", "ext_key_to_me" => "domain_id", "ext_key_to_remote" => "category_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("name", array("description" => "Short name for this category", "allowed_values" => null, "sql" => "name", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => "", "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeImage("icon", array("is_null_allowed" => true, "depends_on" => array(), "display_max_width" => 96, "display_max_height" => 96, "storage_max_width" => 256, "storage_max_height" => 256, "default_image" => null, "always_load_in_tables" => false)));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("categories_list",
array("linked_class" => "lnkAuditCategoryToAuditDomain", "ext_key_to_me" => "domain_id", "ext_key_to_remote" => "category_id", "allowed_values" => null, "count_min" => 0, "count_max" => 0, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'icon', 'categories_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description', )); // Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('description',)); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description')); // Criteria of the default search form
}
public static function GetShortcutActions($sFinalClass)
{
$aShortcutActions = parent::GetShortcutActions($sFinalClass);
if (!in_array('UI:Menu:RunAudit', $aShortcutActions)) {
$aShortcutActions[] = 'UI:Menu:RunAudit';
}
return $aShortcutActions;
}
}
/**
@@ -75,15 +86,26 @@ class lnkAuditCategoryToAuditDomain extends cmdbAbstractObject
{
$aParams = array
(
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array('category_id', 'domain_id'),
"db_table" => "priv_link_audit_category_domain",
"db_key_field" => "id",
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "",
"state_attcode" => "",
"reconc_keys" => array('category_id', 'domain_id'),
"db_table" => "priv_link_audit_category_domain",
"db_key_field" => "id",
"db_finalclass_field" => "",
"is_link" => true,
"is_link" => true,
'uniqueness_rules' => array(
'no_duplicate' => array(
'attributes' => array(
0 => 'category_id',
1 => 'domain_id',
),
'filter' => '',
'disabled' => false,
'is_blocking' => true,
),
),
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", array("targetclass" => "AuditCategory", "jointype" => '', "allowed_values" => null, "sql" => "category_id", "is_null_allowed" => false, "on_target_delete" => DEL_AUTO, "depends_on" => array())));

View File

@@ -35,23 +35,23 @@ class AuditRule extends cmdbAbstractObject
{
$aParams = array
(
"category" => "application, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_auditrule",
"db_key_field" => "id",
"category" => "application,grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
"reconc_keys" => array('name'),
"db_table" => "priv_auditrule",
"db_key_field" => "id",
"db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit.svg'),
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit.svg'),
);
MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeOQL("query", array("allowed_values"=>null, "sql"=>"query", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("valid_flag", array("allowed_values"=>new ValueSetEnum('true,false'), "sql"=>"valid_flag", "default_value"=>"true", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", array("allowed_values"=>null, "sql"=>"category_id", "targetclass"=>"AuditCategory", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("category_name", array("allowed_values"=>null, "extkey_attcode"=> 'category_id', "target_attcode"=>"name")));
MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values" => null, "sql" => "name", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeString("description", array("allowed_values" => null, "sql" => "description", "default_value" => "", "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeOQL("query", array("allowed_values" => null, "sql" => "query", "default_value" => "", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("valid_flag", array("allowed_values" => new ValueSetEnum('true,false'), "sql" => "valid_flag", "default_value" => "true", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalKey("category_id", array("allowed_values" => null, "sql" => "category_id", "targetclass" => "AuditCategory", "is_null_allowed" => false, "on_target_delete" => DEL_MANUAL, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeExternalField("category_name", array("allowed_values" => null, "extkey_attcode" => 'category_id', "target_attcode" => "name")));
// Display lists
MetaModel::Init_SetZListItems('details', array('category_id', 'name', 'description', 'query', 'valid_flag')); // Attributes to be displayed for the complete details
@@ -60,5 +60,16 @@ class AuditRule extends cmdbAbstractObject
MetaModel::Init_SetZListItems('standard_search', array('category_id', 'name', 'description', 'valid_flag', 'query')); // Criteria of the std search form
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'category_id')); // Criteria of the advanced search form
}
public static function GetShortcutActions($sFinalClass)
{
$aShortcutActions = parent::GetShortcutActions($sFinalClass);
if (!in_array('UI:Menu:RunAudit', $aShortcutActions)) {
$aShortcutActions[] = 'UI:Menu:RunAudit';
}
return $aShortcutActions;
}
}
?>

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/CaptureWebPage.php, now loadable using autoloader
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/CaptureWebPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/CLIPage.php, now loadable using autoloader
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/CLIPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -47,6 +47,7 @@ use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
use Combodo\iTop\Renderer\Console\ConsoleFormRenderer;
use Combodo\iTop\Service\Links\LinkSetDataTransformer;
use Combodo\iTop\Service\Links\LinkSetModel;
use Combodo\iTop\Service\TemporaryObjects\TemporaryObjectHelper;
define('OBJECT_PROPERTIES_TAB', 'ObjectProperties');
@@ -133,6 +134,8 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
/** @var string */
public const ENUM_INPUT_TYPE_TAGSET = 'tagset';
/** @var string */
public const ENUM_INPUT_TYPE_TAGSET_LINKEDSET = 'tagset_linkedset';
/** @var string */
public const ENUM_INPUT_TYPE_RADIO = 'radio';
/** @var string */
public const ENUM_INPUT_TYPE_CHECKBOX = 'checkbox';
@@ -187,9 +190,6 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
/** @var array initial attributes flags cache [attcode]['flags'] */
protected $aInitialAttributesFlags;
protected $iUpdateLoopCount;
const MAX_UPDATE_LOOP_COUNT = 10;
/**
* @var array First level classname, second level id, value number of calls done
@@ -227,7 +227,6 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
$this->sDisplayMode = static::DEFAULT_DISPLAY_MODE;
$this->bAllowWrite = false;
$this->bAllowDelete = false;
$this->iUpdateLoopCount = 0;
}
/**
@@ -747,7 +746,13 @@ HTML
$oPage->SetCurrentTab($sTabCode, $oAttDef->GetLabel().$sCount, $sTabDescription);
$aArgs = array('this' => $this);
$bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE));
$sEditWhen = $oAttDef->GetEditWhen();
// Calculate if edit_when allows to edit based on current $bEditMode
$bIsEditableBasedOnEditWhen = ($sEditWhen === LINKSET_EDITWHEN_ALWAYS) ||
($bEditMode ? $sEditWhen === LINKSET_EDITWHEN_ON_HOST_EDITION : $sEditWhen === LINKSET_EDITWHEN_ON_HOST_DISPLAY);
$bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE)) || !$bIsEditableBasedOnEditWhen;
if ($bEditMode && (!$bReadOnly)) {
$sInputId = $this->m_iFormId.'_'.$sAttCode;
$sDisplayValue = ''; // not used
@@ -757,9 +762,9 @@ HTML
$oPage->add($sHTMLValue);
} else {
if ($oAttDef->IsIndirect()) {
$oBlockLinkSetViewTable = new BlockIndirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef);
$oBlockLinkSetViewTable = new BlockIndirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef, $bReadOnly);
} else {
$oBlockLinkSetViewTable = new BlockDirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef);
$oBlockLinkSetViewTable = new BlockDirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef, $bReadOnly);
}
$oPage->AddUiBlock($oBlockLinkSetViewTable);
}
@@ -1169,7 +1174,7 @@ HTML
/**
* @param \WebPage $oPage
* @param \CMDBObjectSet $oSet
* @param array $aExtraParams
* @param array $aExtraParams See possible values in {@see DataTableUIBlockFactory::RenderDataTable()}
*
* @throws \ApplicationException
* @throws \CoreException
@@ -2356,6 +2361,7 @@ EOF
case 'LinkedSet':
if ($oAttDef->GetDisplayStyle() === LINKSET_DISPLAY_STYLE_PROPERTY) {
$sInputType = self::ENUM_INPUT_TYPE_TAGSET_LINKEDSET;
if (array_key_exists('bulk_context', $aArgs)) {
$oTagSetBlock = LinkSetUIBlockFactory::MakeForBulkLinkSet($iId, $oAttDef, $value, $sWizardHelperJsVarName, $aArgs['bulk_context']);
} else {
@@ -2822,33 +2828,33 @@ JS
}
}
// Custom operation for the form ?
if (isset($aExtraParams['custom_operation'])) {
$sOperation = $aExtraParams['custom_operation'];
} else {
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
$sOperation = 'apply_modify';
} else {
$sOperation = 'apply_new';
}
}
if (isset($aExtraParams['custom_operation'])) {
$sOperation = $aExtraParams['custom_operation'];
} else {
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
$sOperation = 'apply_modify';
} else {
$sOperation = 'apply_new';
}
}
$oContentBlock = new UIContentBlock();
$oPage->AddUiBlock($oContentBlock);
$oContentBlock = new UIContentBlock();
$oPage->AddUiBlock($oContentBlock);
$oForm = new Form("form_{$this->m_iFormId}");
$oForm->SetAction($sFormAction);
$sOnSubmitForm = "let bOnSubmitForm = OnSubmit('form_{$this->m_iFormId}');";
if (isset($aExtraParams['js_handlers']['form_on_submit'])) {
$oForm->SetOnSubmitJsCode($sOnSubmitForm.$aExtraParams['js_handlers']['form_on_submit']);
} else {
$oForm->SetOnSubmitJsCode($sOnSubmitForm."return bOnSubmitForm;");
}
$oContentBlock->AddSubBlock($oForm);
$oForm = new Form("form_{$this->m_iFormId}");
$oForm->SetAction($sFormAction);
$sOnSubmitForm = "let bOnSubmitForm = OnSubmit('form_{$this->m_iFormId}');";
if (isset($aExtraParams['js_handlers']['form_on_submit'])) {
$oForm->SetOnSubmitJsCode($sOnSubmitForm . $aExtraParams['js_handlers']['form_on_submit']);
} else {
$oForm->SetOnSubmitJsCode($sOnSubmitForm . "return bOnSubmitForm;");
}
$oContentBlock->AddSubBlock($oForm);
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
// The object already exists in the database, it's a modification
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('id', $iKey, "{$sPrefix}_id"));
}
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
// The object already exists in the database, it's a modification
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('id', $iKey, "{$sPrefix}_id"));
}
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', $sOperation));
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $sClass));
@@ -2857,6 +2863,11 @@ JS
$oPage->SetTransactionId($iTransactionId);
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', $iTransactionId));
// Add temporary object watchdog (only on root form)
if (!utils::IsXmlHttpRequest()) {
$oPage->add_ready_script(TemporaryObjectHelper::GetWatchDogJS($iTransactionId));
}
// TODO 3.0.0: Is this (the if condition, not the code inside) still necessary?
if (isset($aExtraParams['wizard_container']) && $aExtraParams['wizard_container']) {
$sClassLabel = MetaModel::GetName($sClass);
@@ -2867,34 +2878,34 @@ JS
}
}
$oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null);
$oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null);
$oCancelButton = ButtonUIBlockFactory::MakeForCancel();
$oCancelButton->AddCSSClasses(['action', 'cancel']);
$oToolbarButtons->AddSubBlock($oCancelButton);
$oApplyButton = ButtonUIBlockFactory::MakeForPrimaryAction($sApplyButton, null, null, true);
$oApplyButton->AddCSSClass('action');
$oToolbarButtons->AddSubBlock($oApplyButton);
$bAreTransitionsHidden = isset($aExtraParams['hide_transitions']) && $aExtraParams['hide_transitions'] === true;
$aTransitions = $this->EnumTransitions();
if (!isset($aExtraParams['custom_operation']) && !$bAreTransitionsHidden && count($aTransitions)) {
// Transitions are displayed only for the standard new/modify actions, not for modify_all or any other case...
$oSetToCheckRights = DBObjectSet::FromObject($this);
$oCancelButton = ButtonUIBlockFactory::MakeForCancel();
$oCancelButton->AddCSSClasses(['action', 'cancel']);
$oToolbarButtons->AddSubBlock($oCancelButton);
$oApplyButton = ButtonUIBlockFactory::MakeForPrimaryAction($sApplyButton, null, null, true);
$oApplyButton->AddCSSClass('action');
$oToolbarButtons->AddSubBlock($oApplyButton);
$bAreTransitionsHidden = isset($aExtraParams['hide_transitions']) && $aExtraParams['hide_transitions'] === true;
$aTransitions = $this->EnumTransitions();
if (!isset($aExtraParams['custom_operation']) && !$bAreTransitionsHidden && count($aTransitions)) {
// Transitions are displayed only for the standard new/modify actions, not for modify_all or any other case...
$oSetToCheckRights = DBObjectSet::FromObject($this);
$oTransitionPopoverMenu = new PopoverMenu();
$sTPMSectionId = 'transitions';
$oTransitionPopoverMenu->AddSection($sTPMSectionId);
$aStimuli = Metamodel::EnumStimuli($sClass);
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass,
$sStimulusCode, $oSetToCheckRights) : UR_ALLOWED_NO;
switch ($iActionAllowed) {
case UR_ALLOWED_YES:
// Button to be displayed on its own on large screens
$oButton = ButtonUIBlockFactory::MakeForPrimaryAction($aStimuli[$sStimulusCode]->GetLabel(), 'next_action', $sStimulusCode, true);
$oButton->AddCSSClass('action');
$oButton->SetColor(Button::ENUM_COLOR_SCHEME_NEUTRAL);
$oToolbarButtons->AddSubBlock($oButton);
$oTransitionPopoverMenu = new PopoverMenu();
$sTPMSectionId = 'transitions';
$oTransitionPopoverMenu->AddSection($sTPMSectionId);
$aStimuli = Metamodel::EnumStimuli($sClass);
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass,
$sStimulusCode, $oSetToCheckRights) : UR_ALLOWED_NO;
switch ($iActionAllowed) {
case UR_ALLOWED_YES:
// Button to be displayed on its own on large screens
$oButton = ButtonUIBlockFactory::MakeForPrimaryAction($aStimuli[$sStimulusCode]->GetLabel(), 'next_action', $sStimulusCode, true);
$oButton->AddCSSClass('action');
$oButton->SetColor(Button::ENUM_COLOR_SCHEME_NEUTRAL);
$oToolbarButtons->AddSubBlock($oButton);
// Button to be displayed in a grouped button on smaller screens
$oTPMPopupMenuItem = new JSPopupMenuItem('next_action--'.$oButton->GetId(), $oButton->GetLabel(), "$(`#{$oButton->GetId()}`).trigger(`click`);");
@@ -2949,8 +2960,7 @@ JS
}
// Prepare blocker protection to avoid loosing data
$sBlockerId = uniqid($sClass.':'.$iKey.':', true);
$sConfirmationMessage = addslashes(Dict::S('UI:NavigateAwayConfirmationMessage'));
$sBlockerId = $sClass.':'.$iKey; // Important: This must have the synthax format as in js/layouts/activity-panel/activity-panel.js
$sJSToken = json_encode($sOwnershipToken);
$oPage->add_ready_script(<<<JS
// Try to release concurrent lock when leaving the page
@@ -3007,7 +3017,7 @@ JS
$oToolbarButtons->AddCSSClass('ibo-toolbar-top');
$oObjectDetails->AddToolbarBlock($oToolbarButtons);
// Allow form title customization
if (array_key_exists('form_title', $aExtraParams)) {
if (array_key_exists('form_title', $aExtraParams) && $aExtraParams['form_title'] !== null) {
$oObjectDetails->SetTitle($aExtraParams['form_title']);
}
}
@@ -3050,16 +3060,21 @@ JS
$oPage->SetCurrentTab('');
// Static fields values for wizard helper serialization
$aWizardHelperStaticValues = [];
// Add as hidden inputs values that we want displayed if they're readonly
if(isset($aExtraParams['forceFieldsSubmission'])){
$aExtraFlags = $aExtraParams['fieldsFlags'] ?? [];
foreach ($aExtraParams['forceFieldsSubmission'] as $sAttCode) {
if(FormHelper::GetAttributeFlagsForObject($this, $sAttCode, $aExtraFlags) & OPT_ATT_READONLY) {
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('attr_'.$sPrefix.$sAttCode, $this->Get($sAttCode)));
$aWizardHelperStaticValues[$sAttCode] = $this->Get($sAttCode);
}
}
}
$sWizardHelperStaticValues = json_encode($aWizardHelperStaticValues);
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $sClass));
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', $iTransactionId));
foreach ($aExtraParams as $sName => $value) {
@@ -3086,7 +3101,10 @@ JS
$iFieldsCount = count($aFieldsMap);
$sJsonFieldsMap = json_encode($aFieldsMap);
$sState = $this->GetState();
$sLifecycleStateForWizardHelper = '';
if (MetaModel::HasLifecycle($sClass)) {
$sLifecycleStateForWizardHelper = $this->GetState();
}
$sSessionStorageKey = $sClass.'_'.$iKey;
$sTempId = utils::GetUploadTempId($iTransactionId);
$oPage->add_ready_script(InlineImage::EnableCKEditorImageUpload($this, $sTempId));
@@ -3096,9 +3114,10 @@ JS
sessionStorage.removeItem('$sSessionStorageKey');
// Create the object once at the beginning of the page...
var oWizardHelper$sPrefix = new WizardHelper('$sClass', '$sPrefix', '$sState');
var oWizardHelper$sPrefix = new WizardHelper('$sClass', '$sPrefix', '$sLifecycleStateForWizardHelper');
oWizardHelper$sPrefix.SetFieldsMap($sJsonFieldsMap);
oWizardHelper$sPrefix.SetFieldsCount($iFieldsCount);
oWizardHelper$sPrefix.SetStaticValues($sWizardHelperStaticValues);
EOF
);
$oPage->add_ready_script(
@@ -3390,22 +3409,15 @@ EOF
];
// The list of candidate fields is made of the ordered list of "details" attributes + other attributes
$aAttributes = array();
foreach ($this->FlattenZList(MetaModel::GetZListItems($sClass, 'details')) as $sAttCode) {
$aAttributes[$sAttCode] = true;
}
// First attributes from the "details" zlist as they were sorted...
$aList = $this->FlattenZList(MetaModel::GetZListItems($sClass, 'details'));
// ... then append forgotten attributes
foreach (MetaModel::GetAttributesList($sClass) as $sAttCode) {
if (!array_key_exists($sAttCode, $aAttributes)) {
$aAttributes[$sAttCode] = true;
if (!in_array($sAttCode, $aList)) {
$aList[] = $sAttCode;
}
}
// Order the fields based on their dependencies, set the fields for which there is only one possible value
// and perform this in the order of dependencies to avoid dead-ends
$aDeps = array();
foreach ($aAttributes as $sAttCode => $trash) {
$aDeps[$sAttCode] = MetaModel::GetPrerequisiteAttributes($sClass, $sAttCode);
}
$aList = $this->OrderDependentFields($aDeps);
$bExistFieldToDisplay = false;
foreach ($aList as $sAttCode) {
@@ -3700,7 +3712,7 @@ HTML;
if ($oAttDef->GetEditClass() == 'Document') {
/** @var \ormDocument $oDocument */
$oDocument = $this->Get($sAttCode);
if (!$oDocument->IsEmpty()) {
if (is_object($oDocument) && !$oDocument->IsEmpty()) {
$sFieldAsHtml = $this->GetAsHTML($sAttCode);
$sDisplayLabel = Dict::S('UI:OpenDocumentInNewWindow_');
@@ -4518,16 +4530,12 @@ HTML;
*/
public function DBInsertNoReload()
{
$this->LogCRUDEnter(__METHOD__);
try {
$res = parent::DBInsertNoReload();
$this->SetWarningsAsSessionMessages('create');
// Invoke extensions after insertion (the object must exist, have an id, etc.)
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) {
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
}
} finally {
if (static::IsCrudStackEmpty()) {
// Avoid signaling the current object that links were modified
@@ -4535,9 +4543,25 @@ HTML;
static::FireEventDbLinksChangedForAllObjects();
}
}
$this->LogCRUDExit(__METHOD__);
return $res;
}
protected function PostInsertActions(): void
{
parent::PostInsertActions();
// Invoke extensions after insertion (the object must exist, have an id, etc.)
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins(iApplicationObjectExtension::class) as $oExtensionInstance) {
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBInsert()");
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
}
}
/**
* @inheritdoc
* Attaches InlineImages to the current object
@@ -4547,15 +4571,23 @@ HTML;
InlineImage::FinalizeInlineImages($this);
}
/**
* @deprecated 3.1.1 3.2.0 N°6966 We will have only one DBClone method in the future
*/
protected function DBCloneTracked_Internal($newKey = null)
{
$oNewObj = parent::DBCloneTracked_Internal($newKey);
/** @var cmdbAbstractObject $oNewObj */
$oNewObj = MetaModel::GetObject(get_class($this), parent::DBCloneTracked_Internal($newKey));
// Invoke extensions after insertion (the object must exist, have an id, etc.)
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBInsert()");
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBInsert($oNewObj, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
}
return $oNewObj;
@@ -4563,57 +4595,42 @@ HTML;
public function DBUpdate()
{
$this->LogCRUDEnter(__METHOD__);
$res = 0;
try {
if (count($this->ListChanges()) === 0) {
$this->LogCRUDExit(__METHOD__);
return $this->GetKey();
}
$res = parent::DBUpdate();
$this->SetWarningsAsSessionMessages('update');
// Protection against reentrance (e.g. cascading the update of ticket logs)
// Note: This is based on the fix made on r 3190 in DBObject::DBUpdate()
if (!MetaModel::StartReentranceProtection($this)) {
$sClass = get_class($this);
$sKey = $this->GetKey();
IssueLog::Debug("CRUD: DBUpdate $sClass::$sKey Rejected (reentrance)", LogChannels::DM_CRUD);
return $res;
}
try {
// Invoke extensions after the update (could be before)
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) {
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
}
}
finally {
MetaModel::StopReentranceProtection($this);
}
$aChanges = $this->ListChanges();
if (count($aChanges) != 0) {
$this->iUpdateLoopCount++;
if ($this->iUpdateLoopCount > self::MAX_UPDATE_LOOP_COUNT) {
$sClass = get_class($this);
$sKey = $this->GetKey();
$aPlugins = [];
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) {
$aPlugins[] = get_class($oExtensionInstance);
}
$sPlugins = implode(', ', $aPlugins);
IssueLog::Error("CRUD: DBUpdate $sClass::$sKey Update loop detected plugins: $sPlugins", LogChannels::DM_CRUD);
} else {
return $this->DBUpdate();
}
}
} finally {
if (static::IsCrudStackEmpty()) {
static::FireEventDbLinksChangedForAllObjects();
}
}
$this->LogCRUDExit(__METHOD__);
return $res;
}
protected function PostUpdateActions(array $aChanges): void
{
parent::PostUpdateActions($aChanges);
// Invoke extensions after the update (could be before)
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins(iApplicationObjectExtension::class) as $oExtensionInstance) {
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBUpdate()");
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBUpdate');
}
}
/**
* @param string $sMessageIdPrefix
*
@@ -4633,26 +4650,56 @@ HTML;
public function DBDelete(&$oDeletionPlan = null)
{
$this->LogCRUDEnter(__METHOD__);
try {
parent::DBDelete($oDeletionPlan);
} finally {
if (static::IsCrudStackEmpty()) {
// Avoid signaling the current object that links were modified
static::RemoveObjectAwaitingEventDbLinksChanged(get_class($this), $this->GetKey());
$this->LogCRUDDebug(__METHOD__, var_export(self::$aObjectsAwaitingEventDbLinksChanged, true));
static::FireEventDbLinksChangedForAllObjects();
}
}
$this->LogCRUDExit(__METHOD__);
return $oDeletionPlan;
}
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
final protected function PreDeleteActions(): void
{
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBDelete()");
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete');
}
parent::PreDeleteActions();
}
final protected function PostDeleteActions(): void
{
parent::PostDeleteActions();
}
/**
* @deprecated 3.1.1 3.2.0 N°6967 We will have only one DBDelete method in the future
*/
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
{
// Invoke extensions before the deletion (the deletion will do some cleanup and we might loose some information
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBDelete()");
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete');
}
return parent::DBDeleteTracked_Internal($oDeletionPlan);
@@ -4670,9 +4717,16 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
if ($oExtensionInstance->OnIsModified($this))
{
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified()");
$oKPI = new ExecutionKPI();
$bIsModified = $oExtensionInstance->OnIsModified($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnIsModified');
if ($bIsModified) {
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified() -> true");
return true;
} else {
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified() -> false");
}
}
@@ -4724,7 +4778,11 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnCheckToWrite()");
$oKPI = new ExecutionKPI();
$aNewIssues = $oExtensionInstance->OnCheckToWrite($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToWrite');
if (is_array($aNewIssues) && (count($aNewIssues) > 0)) // Some extensions return null instead of an empty array
{
$this->m_aCheckIssues = array_merge($this->m_aCheckIssues, $aNewIssues);
@@ -4772,7 +4830,11 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnCheckToDelete()");
$oKPI = new ExecutionKPI();
$aNewIssues = $oExtensionInstance->OnCheckToDelete($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToDelete');
if (is_array($aNewIssues) && count($aNewIssues) > 0)
{
$this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues);
@@ -5326,7 +5388,7 @@ EOF
$aErrors = $oObj->UpdateObjectFromPostedForm('');
$bResult = (count($aErrors) == 0);
if ($bResult) {
list($bResult, $aErrors) = $oObj->CheckToWrite();
[$bResult, $aErrors] = $oObj->CheckToWrite();
}
if ($bPreview) {
$sStatus = $bResult ? Dict::S('UI:BulkModifyStatusOk') : Dict::S('UI:BulkModifyStatusError');
@@ -5343,6 +5405,11 @@ EOF
'errors' => '<p>'.($bResult ? '' : implode('</p><p>', $aErrorsToDisplay)).'</p>',
);
if ($bResult && (!$bPreview)) {
// doing the check will load multiple times same objects :/
// but it shouldn't cost too much on execution time
// user can mitigate by selecting less extkeys/lnk to set and/or less objects to update 🤷‍♂️
$oObj->CheckChangedExtKeysValues();
$oObj->DBUpdate();
}
}
@@ -5871,39 +5938,28 @@ JS
*/
final protected function FireEventCheckToWrite(): void
{
$this->FireEvent(EVENT_DB_CHECK_TO_WRITE);
$this->FireEvent(EVENT_DB_CHECK_TO_WRITE, ['is_new' => $this->IsNew()]);
}
/**
* @return void
* @throws \CoreException
*
* @since 3.1.0
*/
final protected function FireEventCreateDone(): void
final protected function FireEventBeforeWrite()
{
$this->NotifyAttachedObjectsOnLinkClassModification();
$this->FireEventDbLinksChangedForCurrentObject();
$this->FireEvent(EVENT_DB_CREATE_DONE);
$this->FireEvent(EVENT_DB_BEFORE_WRITE, ['is_new' => $this->IsNew()]);
}
/////////////
/// UPDATE
///
/**
* @param array $aChanges
* @param bool $bIsNew
*
* @return void
* @throws \ArchivedObjectException
* @throws \CoreException
* @since 3.1.0
*/
final protected function FireEventUpdateDone(array $aChanges): void
final protected function FireEventAfterWrite(array $aChanges, bool $bIsNew): void
{
$this->NotifyAttachedObjectsOnLinkClassModification();
$this->FireEventDbLinksChangedForCurrentObject();
$this->FireEvent(EVENT_DB_UPDATE_DONE, ['changes' => $aChanges]);
$this->RemoveObjectAwaitingEventDbLinksChanged(get_class($this), $this->GetKey());
$this->FireEvent(EVENT_DB_AFTER_WRITE, ['is_new' => $bIsNew, 'changes' => $aChanges]);
}
//////////////
@@ -5925,53 +5981,79 @@ JS
/**
* @return void
* @throws \CoreException
*
* @since 3.1.0
* @since 3.1.2
*/
final protected function FireEventDeleteDone(): void
final protected function FireEventAboutToDelete(): void
{
$this->NotifyAttachedObjectsOnLinkClassModification();
$this->FireEventDbLinksChangedForCurrentObject();
$this->FireEvent(EVENT_DB_DELETE_DONE);
$this->FireEvent(EVENT_DB_ABOUT_TO_DELETE);
}
/**
* If the passed object is an instance of a link class, then will register each remote object for modification using {@see static::RegisterObjectAwaitingEventDbLinksChanged()}
* @return void
* @throws \CoreException
*
* @since 3.1.0
*/
final protected function FireEventAfterDelete(): void
{
$this->NotifyAttachedObjectsOnLinkClassModification();
$this->FireEvent(EVENT_DB_AFTER_DELETE);
}
/**
* Possibility for linked classes to be notified of current class modification
*
* If an external key was modified, register also the previous object that was linked previously.
*
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \Exception
* @uses static::RegisterObjectAwaitingEventDbLinksChanged()
*
* @since 3.1.0 N°5906
* @throws ArchivedObjectException
* @throws CoreException
* @throws Exception
*
* @since 3.1.0 N°5906 method creation
* @since 3.1.1 3.2.0 N°6228 now just notify attributes having `with_php_computation`
*/
final protected function NotifyAttachedObjectsOnLinkClassModification(): void
{
$sClass = get_class($this);
if (false === MetaModel::IsLinkClass($sClass)) {
return;
}
// previous values in case of link change
$aPreviousValues = $this->ListPreviousValuesForUpdatedAttributes();
$sClass = get_class($this);
$aClassExtKeyAttCodes = MetaModel::GetAttributesList($sClass, [AttributeExternalKey::class]);
foreach ($aClassExtKeyAttCodes as $sExternalKeyAttCode) {
/** @var AttributeExternalKey $oAttDef */
$oAttDef = MetaModel::GetAttributeDef($sClass, $sExternalKeyAttCode);
$aLnkClassExternalKeys = MetaModel::GetAttributesList($sClass, [AttributeExternalKey::class]);
foreach ($aLnkClassExternalKeys as $sExternalKeyAttCode) {
/** @var \AttributeExternalKey $oExternalKeyAttDef */
$oExternalKeyAttDef = MetaModel::GetAttributeDef($sClass, $sExternalKeyAttCode);
$sRemoteClassName = $oExternalKeyAttDef->GetTargetClass();
$sRemoteObjectId = $this->Get($sExternalKeyAttCode);
if ($sRemoteObjectId > 0) {
self::RegisterObjectAwaitingEventDbLinksChanged($sRemoteClassName, $sRemoteObjectId);
if (false === $this->DoesTargetObjectHavePhpComputation($oAttDef)) {
continue;
}
$sPreviousRemoteObjectId = $aPreviousValues[$sExternalKeyAttCode] ?? 0;
if ($sPreviousRemoteObjectId > 0) {
self::RegisterObjectAwaitingEventDbLinksChanged($sRemoteClassName, $sPreviousRemoteObjectId);
$sTargetObjectId = $this->Get($sExternalKeyAttCode);
$sTargetClass = $oAttDef->GetTargetClass();
if ($sTargetObjectId > 0) {
$this->LogCRUDDebug(__METHOD__, "Add $sTargetClass:$sTargetObjectId for DBLINKS_CHANGED");
self::RegisterObjectAwaitingEventDbLinksChanged($sTargetClass, $sTargetObjectId);
}
$sPreviousTargetObjectId = $aPreviousValues[$sExternalKeyAttCode] ?? 0;
if ($sPreviousTargetObjectId > 0) {
$this->LogCRUDDebug(__METHOD__, "Add $sTargetClass:$sPreviousTargetObjectId for DBLINKS_CHANGED");
self::RegisterObjectAwaitingEventDbLinksChanged($sTargetClass, $sPreviousTargetObjectId);
}
}
}
private function DoesTargetObjectHavePhpComputation(AttributeExternalKey $oAttDef): bool
{
/** @var AttributeLinkedSet $oAttDefMirrorLink */
$oAttDefMirrorLink = $oAttDef->GetMirrorLinkAttribute();
if (is_null($oAttDefMirrorLink) || false === $oAttDefMirrorLink->HasPHPComputation()){
return false;
}
return true;
}
/**
* Register one object for later EVENT_DB_LINKS_CHANGED event.
*
@@ -5989,26 +6071,6 @@ JS
}
}
/**
* Fire the EVENT_DB_LINKS_CHANGED event if current object is registered
*
* @return void
* @throws \ArchivedObjectException
* @throws \CoreException
*
* @since 3.1.0 N°5906
*/
final protected function FireEventDbLinksChangedForCurrentObject(): void
{
if (true === static::IsEventDBLinksChangedBlocked()) {
return;
}
$sClass = get_class($this);
$sId = $this->GetKey();
self::FireEventDbLinksChangedForClassId($sClass, $sId);
}
/**
* Fire the EVENT_DB_LINKS_CHANGED event if given object is registered, and unregister it
*
@@ -6037,16 +6099,23 @@ JS
// - we have a EVENT_DB_LINKS_CHANGED listener on Ticket that will update impacted items, so it will create new lnkApplicationSolutionToFunctionalCI
// We want to avoid launching the listener twice, first here, and secondly after saving the Ticket in the listener
// By disabling the event to be fired, we can remove the current object from the attribute !
/** @noinspection PhpRedundantOptionalArgumentInspection */
$oObject = MetaModel::GetObject($sClass, $sId, true);
self::SetEventDBLinksChangedBlocked(true);
MetaModel::StartReentranceProtection($oObject);
$oObject->FireEvent(EVENT_DB_LINKS_CHANGED);
MetaModel::StopReentranceProtection($oObject);
if ($oObject->IsModified()) {
$oObject->DBUpdate();
}
$oObject = MetaModel::GetObject($sClass, $sId, false);
self::FireEventDbLinksChangedForObject($oObject);
self::RemoveObjectAwaitingEventDbLinksChanged($sClass, $sId);
}
private static function FireEventDbLinksChangedForObject(DBObject $oObject)
{
self::SetEventDBLinksChangedBlocked(true);
// N°6408 The object can have been deleted
if (!is_null($oObject)) {
$oObject->FireEvent(EVENT_DB_LINKS_CHANGED);
// Update the object if needed
if (count($oObject->ListChanges()) !== 0) {
$oObject->DBUpdate();
}
}
cmdbAbstractObject::SetEventDBLinksChangedBlocked(false);
}

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/CSVPage.php, now loadable using autoloader
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/CSVPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -918,6 +918,11 @@ class RuntimeDashboard extends Dashboard
{
$bCustomized = false;
$sDashboardFileSanitized = utils::RealPath(APPROOT.$sDashboardFile, APPROOT);
if (false === $sDashboardFileSanitized) {
throw new SecurityException('Invalid dashboard file !');
}
// Search for an eventual user defined dashboard
$oUDSearch = new DBObjectSearch('UserDashboard');
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
@@ -929,7 +934,7 @@ class RuntimeDashboard extends Dashboard
$sDashboardDefinition = $oUserDashboard->Get('contents');
$bCustomized = true;
} else {
$sDashboardDefinition = @file_get_contents($sDashboardFile);
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
}
@@ -937,7 +942,7 @@ class RuntimeDashboard extends Dashboard
$oDashboard = new RuntimeDashboard($sDashBoardId);
$oDashboard->FromXml($sDashboardDefinition);
$oDashboard->SetCustomFlag($bCustomized);
$oDashboard->SetDefinitionFile($sDashboardFile);
$oDashboard->SetDefinitionFile($sDashboardFileSanitized);
} else {
$oDashboard = null;
}
@@ -1136,7 +1141,7 @@ JS
$oToolbar->AddSubBlock($oActionButton);
$aActions = array();
$sFile = addslashes($this->sDefinitionFile);
$sFile = addslashes(utils::LocalPath($this->sDefinitionFile));
$sJSExtraParams = json_encode($aExtraParams);
if ($this->HasCustomDashboard()) {
$oEdit = new JSPopupMenuItem('UI:Dashboard:Edit', Dict::S('UI:Dashboard:EditCustom'), "return EditDashboard('{$this->sId}', '$sFile', $sJSExtraParams)");
@@ -1259,12 +1264,12 @@ EOF
$sOkButtonLabel = Dict::S('UI:Button:Save');
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$sId = addslashes($this->sId);
$sLayoutClass = addslashes($this->sLayoutClass);
$sId = utils::HtmlEntities($this->sId);
$sLayoutClass = utils::HtmlEntities($this->sLayoutClass);
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
$sAutoReloadSec = (string) $this->iAutoReloadSec;
$sTitle = addslashes($this->sTitle);
$sFile = addslashes($this->GetDefinitionFile());
$sTitle = utils::HtmlEntities($this->sTitle);
$sFile = utils::HtmlEntities($this->GetDefinitionFile());
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
$sReloadURL = $this->GetReloadURL();

View File

@@ -667,7 +667,7 @@ class DashletUnknown extends Dashlet
*/
public function GetPropertiesFields(DesignerForm $oForm)
{
$oField = new DesignerLongTextField('xml', Dict::S('UI:DashletUnknown:Prop-XMLConfiguration'), $this->sOriginalDashletXML);
$oField = new DesignerXMLField('xml', Dict::S('UI:DashletUnknown:Prop-XMLConfiguration'), $this->sOriginalDashletXML);
$oForm->AddField($oField);
}
@@ -869,7 +869,7 @@ class DashletPlainText extends Dashlet
public function Render($oPage, $bEditMode = false, $aExtraParams = array())
{
$sText = $this->aProperties['text'];
$sText = utils::EscapeHtml($sText);
$sText = utils::EscapeHtml(Dict::S($sText));
$sText = str_replace(array("\r\n", "\n", "\r"), "<br/>", $sText);
$sId = 'plaintext_'.($bEditMode ? 'edit_' : '').$this->sId;

View File

@@ -186,8 +186,33 @@
</menu>
</menus>
<events>
<event id="EVENT_DB_BEFORE_WRITE" _delta="define">
<name>Before create or update</name>
<description><![CDATA[An object is about to be written into the database.
The object can be modified.]]></description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::OnInsert</replaces>
<event_data>
<event_datum id="object">
<description>The object inserted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="is_new">
<description>Creation flag</description>
<type>boolean</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_CHECK_TO_WRITE" _delta="define">
<description>Check an object before it is written into the database (no change possible). Call DBObject::AddCheckIssue() to signal an issue</description>
<name>Check to write</name>
<description><![CDATA[Check an object before it is written into the database (no change possible).
Call $this->AddCheckIssue() to signal an issue.]]></description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
@@ -197,14 +222,20 @@
<description>The object to check</description>
<type>DBObject</type>
</event_datum>
<event_datum id="is_new">
<description>Creation flag</description>
<type>boolean</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_CREATE_DONE" _delta="define">
<description>An object has been created into the database. The modifications can be propagated to other objects.</description>
<event id="EVENT_DB_AFTER_WRITE" _delta="define">
<name>After create or update</name>
<description><![CDATA[An object has been written into the database.
The modifications can be propagated to other objects.]]></description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
@@ -214,22 +245,13 @@
<description>The object inserted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
<event_datum id="is_new">
<description>Creation flag</description>
<type>boolean</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_UPDATE_DONE" _delta="define">
<description>An object has been updated into the database and reloaded.</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::AfterUpdate</replaces>
<event_data>
<event_datum id="object">
<description>The object updated</description>
<type>DBObject</type>
<event_datum id="changes">
<description><![CDATA[For updates, the list of changes done during this operation]]></description>
<type>array</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
@@ -238,7 +260,9 @@
</event_data>
</event>
<event id="EVENT_DB_CHECK_TO_DELETE" _delta="define">
<description>Check an object before it is deleted from the database. Call DBObject::AddDeleteIssue() to signal an issue</description>
<name>Check to delete</name>
<description><![CDATA[Check an object before it is deleted from the database.
Call $this->AddDeleteIssue() to signal an issue.]]></description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
@@ -254,8 +278,27 @@
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_DELETE_DONE" _delta="define">
<description>An object has been deleted into the database</description>
<event id="EVENT_DB_ABOUT_TO_DELETE" _delta="define">
<name>Before delete</name>
<description><![CDATA[An object is about to be deleted from the database]]></description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>cmdbAbstractObject::OnDelete</replaces>
<event_data>
<event_datum id="object">
<description>The object about to be deleted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_AFTER_DELETE" _delta="define">
<name>After delete</name>
<description><![CDATA[An object has been deleted into the database]]></description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
@@ -271,8 +314,10 @@
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_BEFORE_APPLY_STIMULUS" _delta="define">
<description>A stimulus is about to be applied to an object</description>
<event id="EVENT_ENUM_TRANSITIONS" _delta="define">
<name>Enum transitions</name>
<description><![CDATA[Manage the allowed transitions in current object state.
The only action allowed is to deny transitions with $this->DenyTransition()]]></description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
@@ -281,89 +326,9 @@
<description>The object where the stimulus is targeted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="stimulus">
<description>Current stimulus applied</description>
<type>string</type>
</event_datum>
<event_datum id="previous_state">
<description>Object previous state</description>
<type>string</type>
</event_datum>
<event_datum id="new_state">
<description>Object new state</description>
<type>string</type>
</event_datum>
<event_datum id="save_object">
<description>The object must be saved in the database</description>
<type>boolean</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_AFTER_APPLY_STIMULUS" _delta="define">
<description>A stimulus has been applied to an object</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<event_data>
<event_datum id="object">
<description>The object where the stimulus is targeted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="stimulus">
<description>Current stimulus applied</description>
<type>string</type>
</event_datum>
<event_datum id="previous_state">
<description>Object previous state</description>
<type>string</type>
</event_datum>
<event_datum id="new_state">
<description>Object new state</description>
<type>string</type>
</event_datum>
<event_datum id="save_object">
<description>The object is asked to be saved in the database</description>
<type>boolean</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_DB_APPLY_STIMULUS_FAILED" _delta="define">
<description>A stimulus has failed</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<event_data>
<event_datum id="action">
<description>The action that failed to apply the stimulus</description>
<type>string</type>
</event_datum>
<event_datum id="object">
<description>The object where the stimulus is targeted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="stimulus">
<description>Current stimulus applied</description>
<type>string</type>
</event_datum>
<event_datum id="previous_state">
<description>Object previous state</description>
<type>string</type>
</event_datum>
<event_datum id="new_state">
<description>Object new state</description>
<type>string</type>
</event_datum>
<event_datum id="save_object">
<description>The object must be saved in the database</description>
<type>boolean</type>
<event_datum id="allowed_stimuli">
<description>The list of available stimuli in the current state</description>
<type>array</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
@@ -372,7 +337,8 @@
</event_data>
</event>
<event id="EVENT_DB_LINKS_CHANGED" _delta="define">
<description>At least one link class was changed</description>
<name>Links on object have changed</name>
<description><![CDATA[At least one link class was changed]]></description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
@@ -388,7 +354,9 @@
</event_data>
</event>
<event id="EVENT_DB_OBJECT_RELOAD" _delta="define">
<description>An object has been re-loaded from the database</description>
<name>Object reload</name>
<internal>true</internal>
<description><![CDATA[An object has been re-loaded from the database]]></description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
@@ -404,7 +372,8 @@
</event_data>
</event>
<event id="EVENT_DB_COMPUTE_VALUES" _delta="define">
<description>An object needs to be recomputed after changes</description>
<name>Recompute object values</name>
<description><![CDATA[An object needs to be recomputed after changes]]></description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
@@ -421,7 +390,8 @@
</event_data>
</event>
<event id="EVENT_DB_ARCHIVE" _delta="define">
<description>An object has been archived</description>
<name>Object archived</name>
<description><![CDATA[An object has been archived]]></description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
@@ -437,7 +407,8 @@
</event_data>
</event>
<event id="EVENT_DB_UNARCHIVE" _delta="define">
<description>An object has been unarchived</description>
<name>Object unarchived</name>
<description><![CDATA[An object has been unarchived]]></description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
@@ -453,7 +424,9 @@
</event_data>
</event>
<event id="EVENT_DB_SET_ATTRIBUTES_FLAGS" _delta="define">
<description>Set object attributes flags. Call cmdbAbstractObject::AddAttributeFlags() for all the attributes to be set for this target state.</description>
<name>Set attributes flags</name>
<description><![CDATA[Set object attributes flags.
Call $this->AddAttributeFlags() for all the attributes to be set for this target state.]]></description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
@@ -464,7 +437,7 @@
</event_datum>
<event_datum id="target_state">
<description>The target state in which to evaluate the flags</description>
<type>array</type>
<type>string</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
@@ -473,7 +446,9 @@
</event_data>
</event>
<event id="EVENT_DB_SET_INITIAL_ATTRIBUTES_FLAGS" _delta="define">
<description>Set object initial attributes flags. Call cmdbAbstractObject::AddInitialAttributeFlags() for all the initial attributes to be set initially.</description>
<name>Set initial attributes flags</name>
<description><![CDATA[Set object initial attributes flags.
Call $this->AddInitialAttributeFlags() for all the initial attributes to be set initially.]]></description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
@@ -489,7 +464,11 @@
</event_data>
</event>
<event id="EVENT_DOWNLOAD_DOCUMENT" _delta="define">
<description>A document has been downloaded from the GUI</description>
<name>Document downloaded</name>
<description><![CDATA[A document has been downloaded from the GUI]]></description>
<sources>
<source id="Document">Document</source>
</sources>
<event_data>
<event_datum id="object">
<description>The object containing the document</description>
@@ -506,7 +485,9 @@
</event_data>
</event>
<event id="EVENT_LOGIN" _delta="define">
<description>Inform the listeners about the connection states</description>
<name>Login</name>
<internal>true</internal>
<description><![CDATA[Inform the listeners about the connection states]]></description>
<event_data>
<event_datum id="code">
<description>The login step result code (LoginWebPage::EXIT_CODE_...) </description>

View File

@@ -704,7 +704,7 @@ class DisplayBlock
if ($bDoSearch)
{
// Keep the table_id identifying this table if we're performing a search
$sTableId = utils::ReadParam('_table_id_', null, false, 'raw_data');
$sTableId = utils::ReadParam('_table_id_', null, false, utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER);
if ($sTableId != null)
{
$aExtraParams['table_id'] = $sTableId;
@@ -1083,6 +1083,7 @@ JS
$this->m_oSet = new CMDBObjectSet($this->m_oFilter, array(), $aQueryParams);
$this->m_oSet->SetShowObsoleteData($this->m_bShowObsoleteData);
}
// Summary details
$aCounts = array();
$aStateLabels = array();
@@ -1129,6 +1130,18 @@ JS
}
$oBlock = new UIContentBlockWithJSRefreshCallback(null, ["ibo-dashlet-header-dynamic--container"]);
// N°6394 Make sure to sort elements as defined in the datamodel
if (utils::IsNotNullOrEmptyString($sStateAttrCode)) {
$oAttDef = MetaModel::GetAttributeDef($sClass, $sStateAttrCode);
$aAllowedValues = $oAttDef->GetAllowedValues() ?? [];
$AllowedValuesKeys = array_keys($aAllowedValues);
uksort($aStateLabels, function ($sKey1, $sKey2) use ($AllowedValuesKeys) {
return array_search($sKey1, $AllowedValuesKeys) > array_search($sKey2, $AllowedValuesKeys) ? 1 : -1;
});
}
foreach ($aStateLabels as $sStateValue => $sStateLabel) {
$aCount = $aCounts[$sStateValue];
$sHyperlink = $aCount['link'];
@@ -1233,6 +1246,10 @@ JS
} else {
$oBlock = DashletFactory::MakeForDashletBadge($sClassIconUrl, $sHyperlink, $iCount, $sClassLabel, null, null, $aRefreshParams);
}
$sClassDescription = MetaModel::GetClassDescription($sClass);
if (utils::IsNotNullOrEmptyString($sClassDescription)) {
$oBlock->SetClassDescription($sClassDescription);
}
return $oBlock;
}
@@ -1858,7 +1875,13 @@ class MenuBlock extends DisplayBlock
/** @var array $aToolkitActions Any "legacy" toolkit menu item, which are now displayed in the same menu as the $aRegularActions, after them */
$aToolkitActions = [];
if (!isset($aExtraParams['selection_mode']) || ($aExtraParams['selection_mode'] == "")) {
// Display menu actions only if...
if (
// ... NOT in a selection mode
(!isset($aExtraParams['selection_mode']) || ($aExtraParams['selection_mode'] == ""))
// ... "menu" parameter is NOT EXPLICITLY disabled
&& (!isset($aExtraParams['menu']) || $aExtraParams['menu'] === "1" || $aExtraParams['menu'] === true)
) {
$oAppContext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
if (utils::IsNotNullOrEmptyString($sContext)) {
@@ -2019,7 +2042,7 @@ class MenuBlock extends DisplayBlock
}
//----------------------------------------------------
// Any style but NOT "listInObject" (linksets) actions
// Any style but NOT \DisplayBlock::ENUM_STYLE_LIST_IN_OBJECT (linksets) actions
//----------------------------------------------------
if ($this->m_sStyle !== static::ENUM_STYLE_LIST_IN_OBJECT) {
switch ($iSetCount) {
@@ -2321,10 +2344,15 @@ class MenuBlock extends DisplayBlock
false
);
// creation form title
if (array_key_exists('creation_in_modal_form_title', $aExtraParams) && $aExtraParams['creation_in_modal_form_title'] !== null) {
$oAddLinkActionButton->AddDataAttribute('modal-title', $aExtraParams['creation_in_modal_form_title']);
}
// - If we are used in a Datatable, 'datatable_' will be prefixed to our $sId, so we do the same here
$sRealId = $sId;
if(in_array($this->m_sStyle, [static::ENUM_STYLE_LIST, 'links', static::ENUM_STYLE_LIST_IN_OBJECT])){
$sRealId = 'datatable_' . $sId;
if (in_array($this->m_sStyle, [static::ENUM_STYLE_LIST, 'links', static::ENUM_STYLE_LIST_IN_OBJECT])) {
$sRealId = 'datatable_'.$sId;
}
$oAddLinkActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button'])
->SetOnClickJsCode("$('#$sRealId').trigger('open_creation_modal.object.itop');");

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/ErrorPage.php, now loadable using autoloader
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/ErrorPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

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

View File

@@ -0,0 +1,36 @@
<?php
/**
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6458 object creation
*/
class InvalidExternalKeyValueException extends CoreUnexpectedValue
{
private const ENUM_PARAMS_OBJECT = 'current_object';
private const ENUM_PARAMS_ATTCODE = 'attcode';
private const ENUM_PARAMS_ATTVALUE = 'attvalue';
private const ENUM_PARAMS_USER = 'current_user';
public function __construct($oObject, $sAttCode, $aContextData = null, $oPrevious = null)
{
$aContextData[self::ENUM_PARAMS_OBJECT] = get_class($oObject) . '::' . $oObject->GetKey();
$aContextData[self::ENUM_PARAMS_ATTCODE] = $sAttCode;
$aContextData[self::ENUM_PARAMS_ATTVALUE] = $oObject->Get($sAttCode);
$oCurrentUser = UserRights::GetUserObject();
if (false === is_null($oCurrentUser)) {
$aContextData[self::ENUM_PARAMS_USER] = get_class($oCurrentUser) . '::' . $oCurrentUser->GetKey();
}
parent::__construct('Attribute pointing to an object that is either non existing or not readable by the current user', $aContextData, '', $oPrevious);
}
public function GetAttCode(): string
{
return $this->getContextData()[self::ENUM_PARAMS_ATTCODE];
}
public function GetAttValue(): string
{
return $this->getContextData()[self::ENUM_PARAMS_ATTVALUE];
}
}

View File

@@ -1110,13 +1110,41 @@ $('#$sId').on('change keyup validate', function() { ValidateWithPattern('$sId',
}
EOF
);
$sValue = "<textarea $sCSSClasses id=\"$sId\" name=\"$sName\">".utils::EscapeHtml($this->defaultValue)."</textarea>";
$sValue = "<textarea $sCSSClasses id=\"$sId\" name=\"$sName\">".$this->PrepareValueForRendering()."</textarea>";
}
else {
$sValue = "<div $sCSSClasses id=\"$sId\">".utils::EscapeHtml($this->defaultValue)."</div>";
$sValue = "<div $sCSSClasses id=\"$sId\">".$this->PrepareValueForRendering()."</div>";
}
return array('label' => $this->sLabel, 'value' => $sValue);
}
/**
* @return string|null The value itself as expected for rendering. May it be encoded, escaped or else.
* @since 3.1.0 N°6405
*/
protected function PrepareValueForRendering(): ?string
{
return utils::EscapeHtml($this->defaultValue);
}
}
/**
* Class DesignerXMLField
*
* Field to display XML content
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @since 3.1.0 N°6405
*/
class DesignerXMLField extends DesignerLongTextField
{
/**
* @inheritDoc
*/
protected function PrepareValueForRendering(): ?string
{
return utils::EscapeHtml($this->defaultValue, true);
}
}
class DesignerIntegerField extends DesignerFormField

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/iTopWebPage.php, now loadable using autoloader
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/iTopWebPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/iTopWizardWebPage.php, now loadable using autoloader
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/iTopWizardWebPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -80,6 +80,11 @@ class LoginBasic extends AbstractLoginFSMExtension
{
if (Session::Get('login_mode') == 'basic')
{
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit === LoginWebPage::EXIT_RETURN)
{
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
LoginWebPage::HTTP401Error();
}
return LoginWebPage::LOGIN_FSM_CONTINUE;

View File

@@ -79,7 +79,7 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
{
self::ResetLoginSession();
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit == LoginWebPage::EXIT_RETURN)
if ($iOnExit === LoginWebPage::EXIT_RETURN)
{
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
@@ -95,6 +95,12 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
{
if (!Session::IsSet('login_mode'))
{
// N°6358 - if EXIT_RETURN was asked, send an error
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
// If no plugin validated the user, exit
self::ResetLoginSession();
exit();
@@ -113,6 +119,11 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
protected function OnConnected(&$iErrorCode)
{
Session::Unset('login_temp_auth_user');
if (is_null(UserRights::GetUserObject())){
//N°7085 avoid infinite loop
IssueLog::Error("No user logged in. exit");
exit(-1);
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
@@ -128,4 +139,4 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
}
}
}
}
}

View File

@@ -73,6 +73,11 @@ class LoginExternal extends AbstractLoginFSMExtension
{
if (Session::Get('login_mode') == 'external')
{
$iOnExit = LoginWebPage::getIOnExit();
if ($iOnExit === LoginWebPage::EXIT_RETURN)
{
return LoginWebPage::LOGIN_FSM_RETURN; // Error, exit FSM
}
LoginWebPage::HTTP401Error();
}
return LoginWebPage::LOGIN_FSM_CONTINUE;

View File

@@ -44,6 +44,10 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
exit;
}
if (LoginWebPage::getIOnExit() === LoginWebPage::EXIT_RETURN) {
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
// No credentials yet, display the form
$oPage = LoginWebPage::NewLoginWebPage();
$oPage->DisplayLoginForm($this->bForceFormOnError);

View File

@@ -90,7 +90,7 @@ class LoginWebPage extends NiceWebPage
parent::__construct($sTitle);
$this->SetStyleSheet();
$this->no_cache();
$this->add_xframe_options();
$this->add_http_headers();
}
public function SetStyleSheet()
@@ -248,6 +248,7 @@ class LoginWebPage extends NiceWebPage
$oEmail = new Email();
$oEmail->SetRecipientTO($sTo);
$sFrom = MetaModel::GetConfig()->Get('forgot_password_from');
$sFrom = utils::IsNullOrEmptyString($sFrom) ? MetaModel::GetConfig()->Get('email_default_sender_address') : $sFrom;
$oEmail->SetRecipientFrom($sFrom);
$oEmail->SetSubject(Dict::S('UI:ResetPwd-EmailSubject', $oUser->Get('login')));
$sResetUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?loginop=reset_pwd&auth_user='.urlencode($oUser->Get('login')).'&token='.urlencode($sToken);
@@ -391,6 +392,11 @@ class LoginWebPage extends NiceWebPage
Session::Unset('can_logoff');
Session::Unset('archive_mode');
Session::Unset('impersonate_user');
Session::Unset('PluginProperties');
Session::Unset('UrlMakerClass');
Session::Unset('itop_env');
Session::Unset('obj_messages');
Session::Unset('profile_list');
UserRights::_ResetSessionCache();
// If it's desired to kill the session, also delete the session cookie.
// Note: This will destroy the session, and not just the session data!

View File

@@ -103,7 +103,7 @@ class ApplicationMenu
{
self::$sFavoriteSiloQuery = $sOQL;
}
/**
* Get the query used to limit the list of displayed organizations in the drop-down menu
* @return string The OQL query returning a list of Organization objects
@@ -536,7 +536,7 @@ EOF
return -1;
}
/**
* Retrieves the currently active menu (if any, otherwise the first menu is the default)
* @return string The Id of the currently active menu
@@ -544,7 +544,7 @@ EOF
public static function GetActiveNodeId()
{
$oAppContext = new ApplicationContext();
$sMenuId = $oAppContext->GetCurrentValue('menu', null);
$sMenuId = $oAppContext->GetCurrentValue('menu', null);
if ($sMenuId === null)
{
$sMenuId = self::GetDefaultMenuId();
@@ -654,7 +654,7 @@ abstract class MenuNode
/**
* Stimulus to check: if the user can 'apply' this stimulus, then she/he can see this menu
*/
*/
protected $m_aEnableStimuli;
/**
@@ -814,7 +814,7 @@ abstract class MenuNode
{
return false;
}
/**
* Add a limiting display condition for the same menu node. The conditions will be combined with a AND
* @param $oMenuNode MenuNode Another definition of the same menu node, with potentially different access restriction
@@ -987,7 +987,7 @@ class TemplateMenuNode extends MenuNode
* @var string
*/
protected $sTemplateFile;
/**
* Create a menu item based on a custom template and inserts it into the application's main menu
* @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary)
@@ -1058,7 +1058,7 @@ class OQLMenuNode extends MenuNode
* @var bool|null
*/
protected $bSearchFormOpen;
/**
* Extra parameters to be passed to the display block to fine tune its appearence
*/
@@ -1091,7 +1091,7 @@ class OQLMenuNode extends MenuNode
// Enhancement: we could set as the "enable" condition that the user has enough rights to "read" the objects
// of the class specified by the OQL...
}
/**
* Set some extra parameters to be passed to the display block to fine tune its appearence
* @param array $aParams paramCode => value. See DisplayBlock::GetDisplay for the meaning of the parameters
@@ -1111,7 +1111,7 @@ class OQLMenuNode extends MenuNode
*/
public function RenderContent(WebPage $oPage, $aExtraParams = array())
{
ContextTag::AddContext(ContextTag::TAG_OBJECT_SEARCH);
$oTag = new ContextTag(ContextTag::TAG_OBJECT_SEARCH);
ApplicationMenu::CheckMenuIdEnabled($this->GetMenuId());
OQLMenuNode::RenderOQLSearch
(
@@ -1120,7 +1120,7 @@ class OQLMenuNode extends MenuNode
'Menu_'.$this->GetMenuId(),
$this->bSearch, // Search pane
$this->bSearchFormOpen, // Search open
$oPage,
$oPage,
array_merge($this->m_aParams, $aExtraParams),
true
);
@@ -1354,10 +1354,10 @@ class NewObjectMenuNode extends MenuNode
{
// Enable this menu, only if the current user has enough rights to create such an object, or an object of
// any child class
$aSubClasses = MetaModel::EnumChildClasses($this->sClass, ENUM_CHILD_CLASSES_ALL); // Including the specified class itself
$bActionIsAllowed = false;
foreach($aSubClasses as $sCandidateClass)
{
if (!MetaModel::IsAbstract($sCandidateClass) && (UserRights::IsActionAllowed($sCandidateClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES))
@@ -1366,7 +1366,7 @@ class NewObjectMenuNode extends MenuNode
break; // Enough for now
}
}
return $bActionIsAllowed;
return $bActionIsAllowed;
}
/**
@@ -1508,7 +1508,7 @@ class DashboardMenuNode extends MenuNode
throw new Exception("Error: failed to load dashboard file: '{$this->sDashboardFile}'");
}
}
}
/**
@@ -1549,7 +1549,7 @@ class ShortcutContainerMenuNode extends MenuNode
$sName = $this->GetMenuId().'_'.$oShortcut->GetKey();
new ShortcutMenuNode($sName, $oShortcut, $this->GetIndex(), $fRank++);
}
// Complete the tree
//
parent::PopulateChildMenus();

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/NiceWebPage.php, now loadable using autoloader
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/NiceWebPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/PDFPage.php, now loadable using autoloader
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/PDFPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -74,6 +74,7 @@ abstract class Query extends cmdbAbstractObject
"default_value" => 0,
"is_null_allowed" => false,
"depends_on" => array(),
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
)));
MetaModel::Init_AddAttribute(new AttributeDateTime("export_last_date", array(
@@ -82,6 +83,7 @@ abstract class Query extends cmdbAbstractObject
"default_value" => null,
"is_null_allowed" => true,
"depends_on" => array(),
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
)));
MetaModel::Init_AddAttribute(new AttributeExternalKey("export_last_user_id",
@@ -93,14 +95,16 @@ abstract class Query extends cmdbAbstractObject
"depends_on"=>array(),
"display_style"=>'select',
"always_load_in_tables"=>false,
"on_target_delete"=>DEL_SILENT
"on_target_delete"=>DEL_SILENT,
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
)));
MetaModel::Init_AddAttribute(new AttributeExternalField("export_last_user_contact",
array(
"allowed_values"=>null,
"extkey_attcode"=> "export_last_user_id",
"target_attcode"=>"contactid"
"target_attcode"=>"contactid",
"tracking_level" => ATTRIBUTE_TRACKING_NONE,
)));
// Display lists
@@ -292,7 +296,7 @@ class QueryOQL extends Query
}
catch
(OQLException $e) {
$oAlert = AlertUIBlockFactory::MakeForFailure(Dict::Format('UI:RunQuery:Error'), $e->getHtmlDesc())
$oAlert = AlertUIBlockFactory::MakeForFailure(Dict::S('UI:RunQuery:Error'), $e->getHtmlDesc())
->SetIsClosable(false)
->SetIsCollapsible(false);
$oAlert->AddCSSClass('mb-5');

View File

@@ -15,10 +15,7 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Application\EventRegister\ApplicationEvents;
use Combodo\iTop\Application\Helper\Session;
use Combodo\iTop\Service\Events\EventData;
use Combodo\iTop\Service\Events\EventService;
require_once(APPROOT.'core/cmdbobject.class.inc.php');
require_once(APPROOT.'application/utils.inc.php');
@@ -102,7 +99,10 @@ else
Session::Set('itop_env', ITOP_DEFAULT_ENV);
}
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
// Event service must be initialized after the MetaModel startup, otherwise it cannot discover classes implementing the iEventServiceSetup interface
EventService::InitService();
EventService::FireEvent(new EventData(ApplicationEvents::APPLICATION_EVENT_METAMODEL_STARTED));
try {
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
}
catch (MySQLException $e) {
IssueLog::Debug($e->getMessage());
throw new MySQLException('Could not connect to the DB server', []);
}

View File

@@ -163,13 +163,11 @@ class UIExtKeyWidget
$oPage->add_linked_script('../js/extkeywidget.js');
$oPage->add_linked_script('../js/forms-json-utils.js');
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation);
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_MODIFY) && $bAllowTargetCreation);
$bExtensions = true;
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
$sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
$sFilter = addslashes($oAllowedValues->GetFilter()->ToOQL());
if ($this->bSearchMode) {
$sWizHelper = 'null';
@@ -977,6 +975,10 @@ HTML
// Remove blob edition from creation form @see N°5863 to allow blob edition in modal context
FormHelper::DisableAttributeBlobInputs($this->sTargetClass, $aFormExtraParams);
if(FormHelper::HasMandatoryAttributeBlobInputs($oNewObj)){
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal(FormHelper::ENUM_MANDATORY_BLOB_MODE_CREATE));
}
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), $aFormExtraParams);
$oPage->add(<<<HTML
</div>
@@ -1070,18 +1072,27 @@ JS
{
$oObj = MetaModel::NewObject($this->sTargetClass);
$aErrors = $oObj->UpdateObjectFromPostedForm($this->iId);
if (count($aErrors) == 0)
{
$oObj->DBInsert();
if (count($aErrors) == 0) {
// Retrieve JSON data
$sJSON = utils::ReadParam('json', '{}', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA);
$oJSON = json_decode($sJSON);
$oObj->SetContextSection('temporary_objects', [
'create' => [
'transaction_id' => utils::ReadParam('root_transaction_id', '', false, utils::ENUM_SANITIZATION_FILTER_TRANSACTION_ID),
'host_class' => $oJSON->m_sClass,
'host_att_code' => $this->sAttCode,
],
]);
$oObj->DBInsertNoReload();
return array('name' => $oObj->GetName(), 'id' => $oObj->GetKey());
}
else
{
} else {
return array('error' => implode(' ', $aErrors), 'id' => 0);
}
}
catch(Exception $e)
{
catch (Exception $e) {
return array('error' => $e->getMessage(), 'id' => 0);
}
}

View File

@@ -71,15 +71,16 @@ class UIHTMLEditorWidget
// To change the default settings of the editor,
// a) edit the file /js/ckeditor/config.js
// b) or override some of the configuration settings, using the second parameter of ckeditor()
$sJSDefineWidth = '';
$aConfig = utils::GetCkeditorPref();
$sWidthSpec = addslashes(trim($this->m_oAttDef->GetWidth()));
if ($sWidthSpec != '')
{
$aConfig['width'] = $sWidthSpec;
if ($sWidthSpec != '') {
/*N°6543 - the function min allow to keep text inside the column when width is defined*/
$aConfig['width'] = "min($sWidthSpec,100%)";
$sJSDefineWidth = '$("#cke_'.$iId.' iframe").contents().find("body").css("width", "'.$sWidthSpec.'")';
}
$sHeightSpec = addslashes(trim($this->m_oAttDef->GetHeight()));
if ($sHeightSpec != '')
{
if ($sHeightSpec != '') {
$aConfig['height'] = $sHeightSpec;
}
$sConfigJS = json_encode($aConfig);
@@ -110,6 +111,7 @@ $('#$iId').on('update', function(evt){
else
{
oMe.data('ckeditorInstance').setReadOnly(oMe.prop('disabled'));
$sJSDefineWidth
}
};
setTimeout(delayedSetReadOnly, 50);

View File

@@ -143,6 +143,10 @@ JS
// Remove blob edition from creation form @see N°5863 to allow blob edition in modal context
FormHelper::DisableAttributeBlobInputs($sRealClass, $aFormExtraParams);
if(FormHelper::HasMandatoryAttributeBlobInputs($oObj)){
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal(FormHelper::ENUM_MANDATORY_BLOB_MODE_CREATE));
}
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), $aFormExtraParams);
}

View File

@@ -178,17 +178,18 @@ class UILinksWidget
$oDisplayBlock = new DisplayBlock($oFilter, 'search', false);
$oBlock->AddSubBlock($oDisplayBlock->GetDisplay($oPage, "SearchFormToAdd_{$sLinkedSetId}",
array(
'menu' => false,
[
'menu' => false,
'result_list_outer_selector' => "SearchResultsToAdd_{$sLinkedSetId}",
'table_id' => "add_{$sLinkedSetId}",
'table_inner_id' => "ResultsToAdd_{$sLinkedSetId}",
'selection_mode' => true,
'json' => $sJson,
'cssCount' => '#count_'.$this->m_sAttCode.$this->m_sNameSuffix,
'query_params' => $oFilter->GetInternalParams(),
'hidden_criteria' => $sAlreadyLinkedExpression,
)));
'table_id' => "add_{$sLinkedSetId}",
'table_inner_id' => "ResultsToAdd_{$sLinkedSetId}",
'selection_mode' => true,
'json' => $sJson,
'cssCount' => '#count_'.$this->m_sAttCode.$this->m_sNameSuffix,
'query_params' => $oFilter->GetInternalParams(),
'hidden_criteria' => $sAlreadyLinkedExpression,
'submit_on_load' => false,
]));
$oBlock->AddForm();
}

View File

@@ -20,6 +20,7 @@
use Combodo\iTop\Application\Helper\Session;
use Combodo\iTop\Application\UI\Base\iUIBlock;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use Combodo\iTop\Service\Module\ModuleService;
use ScssPhp\ScssPhp\Compiler;
use ScssPhp\ScssPhp\OutputStyle;
use ScssPhp\ScssPhp\ValueConverter;
@@ -51,22 +52,31 @@ class utils
{
/**
* @var string
* @since 3.0.0
* @since 2.7.10 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_INTEGER = 'integer';
/**
* Datamodel class
* @var string
* @since 3.0.0
* @since 2.7.10 3.0.0
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6606 update PHPDoc
* @uses MetaModel::IsValidClass()
*/
public const ENUM_SANITIZATION_FILTER_CLASS = 'class';
/**
* @var string
* @since 3.0.0
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6606
* @uses class_exists()
*/
public const ENUM_SANITIZATION_FILTER_PHP_CLASS = 'php_class';
/**
* @var string
* @since 2.7.10 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_STRING = 'string';
/**
* @var string
* @since 3.0.0
* @since 2.7.10 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_CONTEXT_PARAM = 'context_param';
/**
@@ -81,24 +91,29 @@ class utils
public const ENUM_SANITIZATION_FILTER_OPERATION = 'operation';
/**
* @var string
* @since 3.0.0
* @since 2.7.10 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_PARAMETER = 'parameter';
/**
* @var string
* @since 3.0.0
* @since 2.7.10 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_FIELD_NAME = 'field_name';
/**
* @var string
* @since 3.0.0
* @since 2.7.10 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_TRANSACTION_ID = 'transaction_id';
/**
* @var string For XML / HTML node identifiers
* @since 3.0.0
* @since 2.7.10 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER = 'element_identifier';
/**
* @var string For XML / HTML node id/class selector
* @since 3.1.2 3.2.1
*/
public const ENUM_SANITIZATION_FILTER_ELEMENT_SELECTOR = 'element_selector';
/**
* @var string For variables names
* @since 3.0.0
@@ -106,12 +121,13 @@ class utils
public const ENUM_SANITIZATION_FILTER_VARIABLE_NAME = 'variable_name';
/**
* @var string
* @since 3.0.0
* @since 2.7.10 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_RAW_DATA = 'raw_data';
/**
* @var string
* @since 3.0.2, 3.1.0 N°4899
* @since 3.0.2 3.1.0 N°4899
* @since 2.7.10 N°6606
*/
public const ENUM_SANITIZATION_FILTER_URL = 'url';
@@ -154,6 +170,8 @@ class utils
private static $iNextId = 0;
private static $m_sAppRootUrl = null;
protected static function LoadParamFile($sParamFile)
{
if (!file_exists($sParamFile)) {
@@ -225,13 +243,8 @@ class utils
public static function IsModeCLI()
{
$sSAPIName = php_sapi_name();
$sCleanName = strtolower(trim($sSAPIName));
if ($sCleanName == 'cli') {
return true;
} else {
return false;
}
$sCleanName = strtolower(trim(PHP_SAPI));
return ($sCleanName === 'cli');
}
/**
@@ -354,13 +367,13 @@ class utils
}
return self::Sanitize($retValue, $defaultValue, $sSanitizationFilter);
}
public static function ReadPostedParam($sName, $defaultValue = '', $sSanitizationFilter = 'parameter')
{
$retValue = isset($_POST[$sName]) ? $_POST[$sName] : $defaultValue;
return self::Sanitize($retValue, $defaultValue, $sSanitizationFilter);
}
public static function Sanitize($value, $defaultValue, $sSanitizationFilter)
{
if ($value === $defaultValue)
@@ -376,7 +389,7 @@ class utils
$retValue = $defaultValue;
}
}
return $retValue;
return $retValue;
}
/**
@@ -395,6 +408,10 @@ class utils
* @since 2.7.0 new 'element_identifier' filter
* @since 3.0.0 new utils::ENUM_SANITIZATION_* const
* @since 2.7.7, 3.0.2, 3.1.0 N°4899 - new 'url' filter
* @since 2.7.10 N°6606 use the utils::ENUM_SANITIZATION_* const
* @since 2.7.10 N°6606 new case for ENUM_SANITIZATION_FILTER_PHP_CLASS
*
* @link https://www.php.net/manual/en/filter.filters.sanitize.php PHP sanitization filters
*/
protected static function Sanitize_Internal($value, $sSanitizationFilter)
{
@@ -415,6 +432,13 @@ class utils
$retValue = filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
break;
case static::ENUM_SANITIZATION_FILTER_PHP_CLASS:
$retValue = $value;
if (!class_exists($value)) {
$retValue = false;
}
break;
case static::ENUM_SANITIZATION_FILTER_CONTEXT_PARAM:
case static::ENUM_SANITIZATION_FILTER_ROUTE:
case static::ENUM_SANITIZATION_FILTER_OPERATION:
@@ -470,8 +494,17 @@ class utils
}
break;
// For XML / HTML node identifiers
case static::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER:
$retValue = preg_replace('/[^a-zA-Z0-9_-]/', '', $value);
$retValue = filter_var($retValue, FILTER_VALIDATE_REGEXP,
['options' => ['regexp' => '/^[A-Za-z0-9][A-Za-z0-9_-]*$/']]);
break;
// For XML / HTML node id selector
case static::ENUM_SANITIZATION_FILTER_ELEMENT_SELECTOR:
$retValue = filter_var($value, FILTER_VALIDATE_REGEXP,
['options' => ['regexp' => '/^[#\.][A-Za-z0-9][A-Za-z0-9_-]*$/']]);
break;
case static::ENUM_SANITIZATION_FILTER_VARIABLE_NAME:
@@ -480,7 +513,8 @@ class utils
// For URL
case static::ENUM_SANITIZATION_FILTER_URL:
$retValue = filter_var($value, FILTER_SANITIZE_URL);
// N°6350 - returns only valid URLs
$retValue = filter_var($value, FILTER_VALIDATE_URL);
break;
default:
@@ -520,11 +554,11 @@ class utils
$sMimeType = self::GetFileMimeType($sTmpName);
$oDocument = new ormDocument($doc_content, $sMimeType, $sName);
break;
case UPLOAD_ERR_NO_FILE:
// no file to load, it's a normal case, just return an empty document
break;
case UPLOAD_ERR_FORM_SIZE:
case UPLOAD_ERR_INI_SIZE:
throw new FileUploadException(Dict::Format('UI:Error:UploadedFileTooBig', ini_get('upload_max_filesize')));
@@ -533,7 +567,7 @@ class utils
case UPLOAD_ERR_PARTIAL:
throw new FileUploadException(Dict::S('UI:Error:UploadedFileTruncated.'));
break;
case UPLOAD_ERR_NO_TMP_DIR:
throw new FileUploadException(Dict::S('UI:Error:NoTmpDir'));
break;
@@ -546,7 +580,7 @@ class utils
$sName = is_null($sIndex) ? $aFileInfo['name'] : $aFileInfo['name'][$sIndex];
throw new FileUploadException(Dict::Format('UI:Error:UploadStoppedByExtension_FileName', $sName));
break;
default:
throw new FileUploadException(Dict::Format('UI:Error:UploadFailedUnknownCause_Code', $sError));
break;
@@ -652,17 +686,17 @@ class utils
return $aSelectedObj;
}
public static function GetNewTransactionId()
{
return privUITransaction::GetNewTransactionId();
}
public static function IsTransactionValid($sId, $bRemoveTransaction = true)
{
return privUITransaction::IsTransactionValid($sId, $bRemoveTransaction);
}
public static function RemoveTransaction($sId)
{
return privUITransaction::RemoveTransaction($sId);
@@ -846,9 +880,9 @@ class utils
$aDateTokens = array_keys($aSpec);
$aDateRegexps = array_values($aSpec);
}
$sDateRegexp = str_replace($aDateTokens, $aDateRegexps, $sFormat);
if (preg_match('!^(?<head>)'.$sDateRegexp.'(?<tail>)$!', $sDate, $aMatches))
{
$sYear = isset($aMatches['year']) ? $aMatches['year'] : 0;
@@ -865,7 +899,7 @@ class utils
}
// http://www.spaweditor.com/scripts/regex/index.php
}
/**
* Convert an old date/time format specification (using % placeholders)
* to a format compatible with DateTime::createFromFormat
@@ -1023,7 +1057,7 @@ class utils
*/
public static function GetAbsoluteUrlAppRoot($bForceTrustProxy = false)
{
static $sUrl = null;
$sUrl = static::$m_sAppRootUrl;
if ($sUrl === null || $bForceTrustProxy)
{
$sUrl = self::GetConfig()->Get('app_root_url');
@@ -1044,8 +1078,9 @@ class utils
}
$sUrl = str_replace(SERVER_NAME_PLACEHOLDER, $sServerName, $sUrl);
}
static::$m_sAppRootUrl = $sUrl;
}
return $sUrl;
return static::$m_sAppRootUrl;
}
/**
@@ -1396,13 +1431,23 @@ class utils
return APPROOT . 'env-' . MetaModel::GetEnvironment() . '/';
}
/**
* @return string A path to the folder into which data can be written
* @internal
* @since N°6097 2.7.10 3.0.4 3.1.1
*/
public static function GetDataPath(): string
{
return APPROOT.'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
*/
public static function GetCachePath()
{
return APPROOT.'data/cache-'.MetaModel::GetEnvironment().'/';
return static::GetDataPath().'cache-'.MetaModel::GetEnvironment().'/';
}
/**
@@ -1452,7 +1497,7 @@ class utils
$aResult = [];
// 1st - add standard built-in menu items
//
//
switch($iMenuId)
{
case iPopupMenuExtension::MENU_OBJLIST_ACTIONS:
@@ -1836,7 +1881,7 @@ SQL;
return $sProposed;
}
}
/**
* Some characters cause troubles with jQuery when used inside DOM IDs, so let's replace them by the safe _ (underscore)
* @param string $sId The ID to sanitize
@@ -1846,7 +1891,7 @@ SQL;
{
return str_replace(array(':', '[', ']', '+', '-', ' '), '_', $sId);
}
/**
* Helper to execute an HTTP POST request, uses CURL PHP extension
*
@@ -1931,7 +1976,7 @@ SQL;
/**
* Get a standard list of character sets
*
*
* @param array $aAdditionalEncodings Additional values
* @return array of iconv code => english label, sorted by label
*/
@@ -1970,6 +2015,7 @@ SQL;
/**
* @param string $sValue
* @param bool $bDoubleEncode Whether to double encode the value or not
*
* @return string passed value with only characters having a special meaning in HTML escaped as entities
* Since 3.0.0 we were using for this {@link HtmlEntities} but it was overkill and leads to double escaping !
@@ -1977,14 +2023,15 @@ SQL;
* @uses \htmlspecialchars()
* @link https://www.php.net/manual/fr/function.htmlspecialchars.php
* @since 3.0.0 N°3623
* @since 3.1.0 N°6405 Add $bDoubleEncode parameter
*/
public static function EscapeHtml($sValue)
public static function EscapeHtml($sValue, bool $bDoubleEncode = false)
{
return htmlspecialchars(
$sValue ?? '',
ENT_QUOTES | ENT_DISALLOWED | ENT_HTML5,
WebPage::PAGES_CHARSET,
false
$bDoubleEncode
);
}
@@ -2036,7 +2083,7 @@ SQL;
return $e->getMessage();
}
}
/**
* Convert (?) plain text to some HTML markup by replacing newlines by <br/> tags
* and escaping HTML entities
@@ -2045,12 +2092,15 @@ SQL;
*/
public static function TextToHtml($sText)
{
if (static::IsNullOrEmptyString($sText)){
return '';
}
$sText = str_replace("\r\n", "\n", $sText);
$sText = str_replace("\r", "\n", $sText);
return str_replace("\n", '<br/>', utils::EscapeHtml($sText));
}
/**
* Eventually compiles the SASS (.scss) file into the CSS (.css) file
*
@@ -2164,7 +2214,7 @@ SQL;
case 'image/png':
$img = @imagecreatefromstring($oImage->GetData());
break;
default:
// Unsupported image type, return the image as-is
//throw new Exception("Unsupported image type: '".$oImage->GetMimeType()."'. Cannot resize the image, original image will be used.");
@@ -2178,14 +2228,14 @@ SQL;
else
{
// Let's scale the image, preserving the transparency for GIFs and PNGs
$fScale = min($iMaxImageWidth / $iWidth, $iMaxImageHeight / $iHeight);
$iNewWidth = $iWidth * $fScale;
$iNewHeight = $iHeight * $fScale;
$new = imagecreatetruecolor($iNewWidth, $iNewHeight);
// Preserve transparency
if(($oImage->GetMimeType() == "image/gif") || ($oImage->GetMimeType() == "image/png"))
{
@@ -2193,38 +2243,38 @@ SQL;
imagealphablending($new, false);
imagesavealpha($new, true);
}
imagecopyresampled($new, $img, 0, 0, 0, 0, $iNewWidth, $iNewHeight, $iWidth, $iHeight);
ob_start();
switch ($oImage->GetMimeType())
{
case 'image/gif':
imagegif($new); // send image to output buffer
break;
case 'image/jpeg':
imagejpeg($new, null, 80); // null = send image to output buffer, 80 = good quality
break;
case 'image/png':
imagepng($new, null, 5); // null = send image to output buffer, 5 = medium compression
break;
}
$oResampledImage = new ormDocument(ob_get_contents(), $oImage->GetMimeType(), $oImage->GetFileName());
@ob_end_clean();
imagedestroy($img);
imagedestroy($new);
return $oResampledImage;
}
}
/**
* Create a 128 bit UUID in the format: {########-####-####-####-############}
*
*
* Note: this method can be run from the command line as well as from the web server.
* Note2: this method is not cryptographically secure! If you need a cryptographically secure value
* consider using open_ssl or PHP 7 methods.
@@ -2260,26 +2310,9 @@ SQL;
*/
public static function GetCurrentModuleName($iCallDepth = 0)
{
$sCurrentModuleName = '';
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$sCallerFile = realpath($aCallStack[$iCallDepth]['file']);
foreach(GetModulesInfo() as $sModuleName => $aInfo)
{
if ($aInfo['root_dir'] !== '')
{
$sRootDir = realpath(APPROOT.$aInfo['root_dir']);
if(substr($sCallerFile, 0, strlen($sRootDir)) === $sRootDir)
{
$sCurrentModuleName = $sModuleName;
break;
}
}
}
return $sCurrentModuleName;
return ModuleService::GetInstance()->GetCurrentModuleName($iCallDepth + 1);
}
/**
* **Warning** : returned result can be invalid as we're using backtrace to find the module dir name
*
@@ -2299,24 +2332,7 @@ SQL;
*/
public static function GetCurrentModuleDir($iCallDepth)
{
$sCurrentModuleDir = '';
$aCallStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$sCallerFile = realpath($aCallStack[$iCallDepth]['file']);
foreach(GetModulesInfo() as $sModuleName => $aInfo)
{
if ($aInfo['root_dir'] !== '')
{
$sRootDir = realpath(APPROOT.$aInfo['root_dir']);
if(substr($sCallerFile, 0, strlen($sRootDir)) === $sRootDir)
{
$sCurrentModuleDir = basename($sRootDir);
break;
}
}
}
return $sCurrentModuleDir;
return ModuleService::GetInstance()->GetCurrentModuleDir($iCallDepth);
}
/**
@@ -2331,14 +2347,9 @@ SQL;
*/
public static function GetCurrentModuleUrl()
{
$sDir = static::GetCurrentModuleDir(1);
if ( $sDir !== '')
{
return static::GetAbsoluteUrlModulesRoot().'/'.$sDir;
}
return '';
return ModuleService::GetInstance()->GetCurrentModuleUrl(1);
}
/**
* @param string $sProperty The name of the property to retrieve
* @param mixed $defaultvalue
@@ -2346,24 +2357,18 @@ SQL;
*/
public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
{
$sModuleName = static::GetCurrentModuleName(1);
return MetaModel::GetModuleSetting($sModuleName, $sProperty, $defaultvalue);
return ModuleService::GetInstance()->GetCurrentModuleSetting($sProperty, $defaultvalue);
}
/**
* @param string $sModuleName
* @return string|NULL compiled version of a given module, as it was seen by the compiler
*/
public static function GetCompiledModuleVersion($sModuleName)
{
$aModulesInfo = GetModulesInfo();
if (array_key_exists($sModuleName, $aModulesInfo))
{
return $aModulesInfo[$sModuleName]['version'];
}
return null;
return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName);
}
/**
* Check if the given path/url is an http(s) URL
* @param string $sPath
@@ -2378,7 +2383,7 @@ SQL;
}
return $bRet;
}
/**
* 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
@@ -2427,7 +2432,7 @@ SQL;
}
return $result;
}
/**
* Read the content of a file (and retrieve its MIME type) from either:
* - an URL pointing to a blob (image/document) on the current iTop server
@@ -2471,7 +2476,7 @@ SQL;
'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
@@ -2503,15 +2508,17 @@ SQL;
$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)
{
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);
@@ -2527,7 +2534,7 @@ SQL;
}
$sExtension = strtolower(pathinfo($sPath, PATHINFO_EXTENSION));
$sFileName = basename($sPath);
if (array_key_exists($sExtension, $aKnownExtensions))
{
$sMimeType = $aKnownExtensions[$sExtension];
@@ -2541,7 +2548,7 @@ SQL;
}
return $oUploadedDoc;
}
protected static function ParseHeaders($aHeaders)
{
$aCleanHeaders = array();
@@ -2566,7 +2573,7 @@ SQL;
}
return $aCleanHeaders;
}
/**
* @return string a string based on compilation time or (if not available because the datamodel has not been loaded)
* the version of iTop. This string is useful to prevent browser side caching of content that may vary at each
@@ -2686,24 +2693,26 @@ SQL;
}
/**
* Returns the local path relative to the iTop installation of an existing file
* Returns the local path relative to the iTop installation (APPROOT or the given base path)
* Dir separator is changed to '/' for consistency among the different OS
*
* @param string $sAbsolutePath absolute path
* @param string $sBasePath Base path for the resulting local path (default APPROOT)
*
* @return false|string
* @return false|string The generated local path or false if absolute path is not under the base path
* @since 3.1.1 Added base path defaulted to previous version APPROOT
*/
final public static function LocalPath($sAbsolutePath)
final public static function LocalPath($sAbsolutePath, string $sBasePath = APPROOT)
{
$sRootPath = realpath(APPROOT);
$sRootPath = realpath($sBasePath);
$sFullPath = realpath($sAbsolutePath);
if (($sFullPath === false) || !self::StartsWith($sFullPath, $sRootPath))
{
return false;
}
$sLocalPath = substr($sFullPath, strlen($sRootPath.DIRECTORY_SEPARATOR));
$sLocalPath = str_replace(DIRECTORY_SEPARATOR, '/', $sLocalPath);
return $sLocalPath;
return str_replace(DIRECTORY_SEPARATOR, '/', $sLocalPath);
}
/**
@@ -2895,7 +2904,7 @@ HTML;
// Add already loaded classes
$aCurrentClasses = array_fill_keys(get_declared_classes(), '');
$aClassMap = array_merge($aClassMap, $aCurrentClasses);
$aClassMap = array_merge($aCurrentClasses, $aClassMap);
foreach ($aClassMap as $sPHPClass => $sPHPFile) {
$bSkipped = false;
@@ -2904,7 +2913,8 @@ HTML;
if ($sClassNameFilter !== '' && strpos($sPHPClass, $sClassNameFilter) === false) {
$bSkipped = true;
}
else {
// For some PHP classes we don't have their file path as they are already in memory, so we never filter on their paths
elseif (utils::IsNotNullOrEmptyString($sPHPFile)) {
$sPHPFile = self::LocalPath($sPHPFile);
if ($sPHPFile !== false) {
$sPHPFile = '/'.$sPHPFile; // for regex
@@ -2919,11 +2929,12 @@ HTML;
$bSkipped = true; // file not found
}
}
if(!$bSkipped){
try {
$oRefClass = new ReflectionClass($sPHPClass);
if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable()) {
if ($oRefClass->implementsInterface($sInterface) &&
!$oRefClass->isInterface() && !$oRefClass->isAbstract() && !$oRefClass->isTrait()) {
$aMatchingClasses[] = $sPHPClass;
}
} catch (Exception $e) {
@@ -3029,6 +3040,7 @@ HTML;
*
* @return bool if string null or empty
* @since 3.0.2 N°5302
* @since 2.7.10 N°6458 add method in the 2.7 branch
*/
public static function IsNullOrEmptyString(?string $sString): bool
{
@@ -3044,6 +3056,7 @@ HTML;
*
* @return bool if string is not null and not empty
* @since 3.0.2 N°5302
* @since 2.7.10 N°6458 add method in the 2.7 branch
*/
public static function IsNotNullOrEmptyString(?string $sString): bool
{
@@ -3368,5 +3381,22 @@ HTML;
{
return in_array($sTrait, self::TraitsUsedByClass($sClass, true));
}
/**
* Get stack trace as string array.
*
* @return array
* @since 3.1.0
*/
public static function GetStackTraceAsArray(): array
{
$e = new Exception();
$aTrace = explode("\n", $e->getTraceAsString());
// Remove call to this method
array_shift($aTrace);
// Remove Main
array_pop($aTrace);
return $aTrace;
}
}

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/WebPage.php, now loadable using autoloader
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/WebPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -351,6 +351,7 @@ class WizardHelper
/**
* @return string JS code to be executed for fields update
* @since 3.0.0 N°3198
* @deprecated 3.0.3-2 3.0.4 3.1.1 3.2.0 Use {@see \WizardHelper::AddJsForUpdateFields()} instead
*/
public function GetJsForUpdateFields()
{
@@ -363,6 +364,32 @@ class WizardHelper
JS;
}
/**
* Add necessary JS snippets (to the page) to be executed for fields update
*
* @param \WebPage $oPage
* @return void
* @since 3.0.3-2 3.0.4 3.1.1 3.2.0 N°6766
*/
public function AddJsForUpdateFields(WebPage $oPage)
{
$sWizardHelperJsVar = (!is_null($this->m_aData['m_sWizHelperJsVarName'])) ? utils::Sanitize($this->m_aData['m_sWizHelperJsVarName'], '', utils::ENUM_SANITIZATION_FILTER_PARAMETER) : 'oWizardHelper'.$this->GetFormPrefix();
$sWizardHelperJson = $this->ToJSON();
$oPage->add_script(<<<JS
{$sWizardHelperJsVar}.m_oData = {$sWizardHelperJson};
{$sWizardHelperJsVar}.UpdateFields();
JS
);
$oPage->add_ready_script(<<<JS
if ({$sWizardHelperJsVar}.m_oDependenciesUpdatedPromiseResolve !== null){
{$sWizardHelperJsVar}.m_oDependenciesUpdatedPromiseResolve();
}
JS
);
}
/*
* Function with an old pattern of code
* @deprecated 3.1.0
@@ -371,11 +398,9 @@ JS;
{
$aSet = json_decode($sJsonSet, true); // true means hash array instead of object
$oSet = CMDBObjectSet::FromScratch($sLinkClass);
foreach ($aSet as $aLinkObj)
{
foreach ($aSet as $aLinkObj) {
$oLink = MetaModel::NewObject($sLinkClass);
foreach ($aLinkObj as $sAttCode => $value)
{
foreach ($aLinkObj as $sAttCode => $value) {
$oAttDef = MetaModel::GetAttributeDef($sLinkClass, $sAttCode);
if (($oAttDef->IsExternalKey()) && ($value != '') && ($value > 0))
{

View File

@@ -1,6 +1,6 @@
<?php
/**
* @deprecated will be removed in 3.1.0 - moved to sources/Application/WebPage/XMLPage.php, now loadable using autoloader
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/Application/WebPage/XMLPage.php, now loadable using autoloader
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2023 Combodo SARL
*/

View File

@@ -23,11 +23,13 @@ define('ITOP_DESIGN_LATEST_VERSION', '3.1');
* @used-by utils::GetItopVersionWikiSyntax()
* @used-by iTopModulesPhpVersionIntegrationTest
*/
define('ITOP_CORE_VERSION', '3.1.0');
define('ITOP_CORE_VERSION', '3.1.2');
/**
* @since 3.0.4 N°6274 Allow to test if PHPUnit is currently running. Starting with PHPUnit 9.5 we'll be able to replace it with $GLOBALS['phpunit_version']
* @var string
* @since 3.0.4 3.1.0 3.2.0 N°6274 Allow to test if PHPUnit is currently running. Starting with PHPUnit 9.5 we'll be able to replace it with $GLOBALS['phpunit_version']
* @since 3.0.4 3.1.1 3.2.0 N°6976 Fix constant name (DeprecatedCallsLog error handler was never set)
*/
define('ITOP_PHPUNIT_RUNNING_CONSTANT_NAME', 'ITOP_PHPUNIT_RUNNING');
const ITOP_PHPUNIT_RUNNING_CONSTANT_NAME = 'ITOP_PHPUNIT_RUNNING';
require_once APPROOT.'bootstrap.inc.php';

View File

@@ -45,6 +45,7 @@ define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance');
define('READONLY_MODE_FILE', APPROOT.'data/.readonly');
$fItopStarted = microtime(true);
$iItopInitialMemory = memory_get_usage(true);
if (!isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false) {
require_once APPROOT.'/lib/autoload.php';
@@ -67,7 +68,7 @@ if (file_exists(MAINTENANCE_MODE_FILE) && !$bBypassMaintenance)
http_response_code(503);
// Display message depending on the request
include(APPROOT.'application/maintenancemsg.php');
$sSAPIName = strtoupper(trim(php_sapi_name()));
$sSAPIName = strtoupper(trim(PHP_SAPI));
switch (true)
{

View File

@@ -12,10 +12,10 @@
"ext-json": "*",
"ext-mysqli": "*",
"ext-soap": "*",
"apereo/phpcas" : "~1.6.0",
"apereo/phpcas": "~1.6.0",
"combodo/tcpdf": "~6.4.4",
"firebase/php-jwt": "~6.4.0",
"guzzlehttp/guzzle": "^7.4.5",
"guzzlehttp/guzzle": "^7.5.1",
"laminas/laminas-mail": "^2.11",
"laminas/laminas-servicemanager": "^3.5",
"league/oauth2-google": "^3.0",

150
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "69db9dbdea61a588fa2058724d91a579",
"content-hash": "cb3883f141f50e1bfbc957880ab93be1",
"packages": [
{
"name": "apereo/phpcas",
@@ -217,22 +217,22 @@
},
{
"name": "guzzlehttp/guzzle",
"version": "7.4.5",
"version": "7.7.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "1dd98b0564cb3f6bd16ce683cb755f94c10fbd82"
"reference": "fb7566caccf22d74d1ab270de3551f72a58399f5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/1dd98b0564cb3f6bd16ce683cb755f94c10fbd82",
"reference": "1dd98b0564cb3f6bd16ce683cb755f94c10fbd82",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/fb7566caccf22d74d1ab270de3551f72a58399f5",
"reference": "fb7566caccf22d74d1ab270de3551f72a58399f5",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/promises": "^1.5",
"guzzlehttp/psr7": "^1.9 || ^2.4",
"guzzlehttp/promises": "^1.5.3 || ^2.0",
"guzzlehttp/psr7": "^1.9.1 || ^2.4.5",
"php": "^7.2.5 || ^8.0",
"psr/http-client": "^1.0",
"symfony/deprecation-contracts": "^2.2 || ^3.0"
@@ -241,10 +241,11 @@
"psr/http-client-implementation": "1.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.4.1",
"bamarni/composer-bin-plugin": "^1.8.1",
"ext-curl": "*",
"php-http/client-integration-tests": "^3.0",
"phpunit/phpunit": "^8.5.5 || ^9.3.5",
"php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999",
"php-http/message-factory": "^1.1",
"phpunit/phpunit": "^8.5.29 || ^9.5.23",
"psr/log": "^1.1 || ^2.0 || ^3.0"
},
"suggest": {
@@ -254,8 +255,9 @@
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "7.4-dev"
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"autoload": {
@@ -321,7 +323,7 @@
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/7.4.5"
"source": "https://github.com/guzzle/guzzle/tree/7.7.0"
},
"funding": [
{
@@ -337,38 +339,37 @@
"type": "tidelift"
}
],
"time": "2022-06-20T22:16:13+00:00"
"time": "2023-05-21T14:04:53+00:00"
},
{
"name": "guzzlehttp/promises",
"version": "1.5.1",
"version": "2.0.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da"
"reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
"reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da",
"url": "https://api.github.com/repos/guzzle/promises/zipball/3a494dc7dc1d7d12e511890177ae2d0e6c107da6",
"reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6",
"shasum": ""
},
"require": {
"php": ">=5.5"
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^4.4 || ^5.1"
"bamarni/composer-bin-plugin": "^1.8.1",
"phpunit/phpunit": "^8.5.29 || ^9.5.23"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.5-dev"
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"autoload": {
"files": [
"src/functions_include.php"
],
"psr-4": {
"GuzzleHttp\\Promise\\": "src/"
}
@@ -405,7 +406,7 @@
],
"support": {
"issues": "https://github.com/guzzle/promises/issues",
"source": "https://github.com/guzzle/promises/tree/1.5.1"
"source": "https://github.com/guzzle/promises/tree/2.0.0"
},
"funding": [
{
@@ -421,26 +422,26 @@
"type": "tidelift"
}
],
"time": "2021-10-22T20:56:57+00:00"
"time": "2023-05-21T13:50:22+00:00"
},
{
"name": "guzzlehttp/psr7",
"version": "2.4.0",
"version": "2.5.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "13388f00956b1503577598873fffb5ae994b5737"
"reference": "b635f279edd83fc275f822a1188157ffea568ff6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/13388f00956b1503577598873fffb5ae994b5737",
"reference": "13388f00956b1503577598873fffb5ae994b5737",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/b635f279edd83fc275f822a1188157ffea568ff6",
"reference": "b635f279edd83fc275f822a1188157ffea568ff6",
"shasum": ""
},
"require": {
"php": "^7.2.5 || ^8.0",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.0",
"psr/http-message": "^1.1 || ^2.0",
"ralouphie/getallheaders": "^3.0"
},
"provide": {
@@ -448,17 +449,18 @@
"psr/http-message-implementation": "1.0"
},
"require-dev": {
"bamarni/composer-bin-plugin": "^1.4.1",
"bamarni/composer-bin-plugin": "^1.8.1",
"http-interop/http-factory-tests": "^0.9",
"phpunit/phpunit": "^8.5.8 || ^9.3.10"
"phpunit/phpunit": "^8.5.29 || ^9.5.23"
},
"suggest": {
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.4-dev"
"bamarni-bin": {
"bin-links": true,
"forward-command": false
}
},
"autoload": {
@@ -520,7 +522,7 @@
],
"support": {
"issues": "https://github.com/guzzle/psr7/issues",
"source": "https://github.com/guzzle/psr7/tree/2.4.0"
"source": "https://github.com/guzzle/psr7/tree/2.5.0"
},
"funding": [
{
@@ -536,7 +538,7 @@
"type": "tidelift"
}
],
"time": "2022-06-20T21:43:11+00:00"
"time": "2023-04-17T16:11:26+00:00"
},
{
"name": "laminas/laminas-loader",
@@ -1668,21 +1670,21 @@
},
{
"name": "psr/http-client",
"version": "1.0.1",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-client.git",
"reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621"
"reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
"reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621",
"url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31",
"reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31",
"shasum": ""
},
"require": {
"php": "^7.0 || ^8.0",
"psr/http-message": "^1.0"
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
"extra": {
@@ -1702,7 +1704,7 @@
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP clients",
@@ -1714,27 +1716,27 @@
"psr-18"
],
"support": {
"source": "https://github.com/php-fig/http-client/tree/master"
"source": "https://github.com/php-fig/http-client/tree/1.0.2"
},
"time": "2020-06-29T06:28:15+00:00"
"time": "2023-04-10T20:12:12+00:00"
},
{
"name": "psr/http-factory",
"version": "1.0.1",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-factory.git",
"reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be"
"reference": "e616d01114759c4c489f93b099585439f795fe35"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
"reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be",
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
"reference": "e616d01114759c4c489f93b099585439f795fe35",
"shasum": ""
},
"require": {
"php": ">=7.0.0",
"psr/http-message": "^1.0"
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
"extra": {
@@ -1754,7 +1756,7 @@
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interfaces for PSR-7 HTTP message factories",
@@ -1769,31 +1771,31 @@
"response"
],
"support": {
"source": "https://github.com/php-fig/http-factory/tree/master"
"source": "https://github.com/php-fig/http-factory/tree/1.0.2"
},
"time": "2019-04-30T12:38:16+00:00"
"time": "2023-04-10T20:10:41+00:00"
},
{
"name": "psr/http-message",
"version": "1.0.1",
"version": "2.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message.git",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
"php": "^7.2 || ^8.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
"dev-master": "2.0.x-dev"
}
},
"autoload": {
@@ -1808,7 +1810,7 @@
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
"homepage": "https://www.php-fig.org/"
}
],
"description": "Common interface for HTTP messages",
@@ -1822,9 +1824,9 @@
"response"
],
"support": {
"source": "https://github.com/php-fig/http-message/tree/master"
"source": "https://github.com/php-fig/http-message/tree/2.0"
},
"time": "2016-08-06T14:39:51+00:00"
"time": "2023-04-04T09:54:51+00:00"
},
{
"name": "psr/log",
@@ -4474,16 +4476,16 @@
},
{
"name": "symfony/twig-bridge",
"version": "v5.4.11",
"version": "v5.4.31",
"source": {
"type": "git",
"url": "https://github.com/symfony/twig-bridge.git",
"reference": "63b8a50d48c9fe3d04e77307d4f1771dd848baa8"
"reference": "fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/63b8a50d48c9fe3d04e77307d4f1771dd848baa8",
"reference": "63b8a50d48c9fe3d04e77307d4f1771dd848baa8",
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942",
"reference": "fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942",
"shasum": ""
},
"require": {
@@ -4496,22 +4498,22 @@
"phpdocumentor/reflection-docblock": "<3.2.2",
"phpdocumentor/type-resolver": "<1.4.0",
"symfony/console": "<5.3",
"symfony/form": "<5.3",
"symfony/form": "<5.4.21|>=6,<6.2.7",
"symfony/http-foundation": "<5.3",
"symfony/http-kernel": "<4.4",
"symfony/translation": "<5.2",
"symfony/workflow": "<5.2"
},
"require-dev": {
"doctrine/annotations": "^1.12",
"egulias/email-validator": "^2.1.10|^3",
"doctrine/annotations": "^1.12|^2",
"egulias/email-validator": "^2.1.10|^3|^4",
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"symfony/asset": "^4.4|^5.0|^6.0",
"symfony/console": "^5.3|^6.0",
"symfony/dependency-injection": "^4.4|^5.0|^6.0",
"symfony/expression-language": "^4.4|^5.0|^6.0",
"symfony/finder": "^4.4|^5.0|^6.0",
"symfony/form": "^5.3|^6.0",
"symfony/form": "^5.4.21|^6.2.7",
"symfony/http-foundation": "^5.3|^6.0",
"symfony/http-kernel": "^4.4|^5.0|^6.0",
"symfony/intl": "^4.4|^5.0|^6.0",
@@ -4575,7 +4577,7 @@
"description": "Provides integration for Twig with various Symfony components",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/twig-bridge/tree/v5.4.11"
"source": "https://github.com/symfony/twig-bridge/tree/v5.4.31"
},
"funding": [
{
@@ -4591,7 +4593,7 @@
"type": "tidelift"
}
],
"time": "2022-07-20T13:00:38+00:00"
"time": "2023-11-09T21:19:08+00:00"
},
{
"name": "symfony/twig-bundle",
@@ -5274,5 +5276,5 @@
"platform-overrides": {
"php": "7.4.0"
},
"plugin-api-version": "2.1.0"
"plugin-api-version": "2.6.0"
}

View File

@@ -59,9 +59,17 @@ class DbConnectionWrapper
* Use this to register a mock that will handle {@see mysqli::query()}
*
* @param \mysqli|null $oMysqli
* @since 3.0.4 3.1.1 3.2.0 Param $oMysqli becomes nullable
* @since 3.1.0-4 N°6848 backport of restoring cnx on null parameter value
*/
public static function SetDbConnectionMockForQuery(?mysqli $oMysqli): void
public static function SetDbConnectionMockForQuery(?mysqli $oMysqli = null): void
{
static::$oDbCnxMockableForQuery = $oMysqli;
if (is_null($oMysqli)) {
// Reset to standard connection
static::$oDbCnxMockableForQuery = static::$oDbCnxStandard;
}
else {
static::$oDbCnxMockableForQuery = $oMysqli;
}
}
}

View File

@@ -419,6 +419,7 @@ class MyHelpers
//}
return $sOutput;
}
}
/**
@@ -523,5 +524,3 @@ class Str
return (strtolower($sString) == $sString);
}
}
?>

View File

@@ -0,0 +1,101 @@
<?php
/*
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* Class TemporaryObjectDescriptor
*
* Descriptor to track a temporary object.
*
* @experimental do not use, this feature will be part of a future version
*
* @since 3.1
*/
class TemporaryObjectDescriptor extends DBObject
{
public static function Init()
{
$aParams = array(
'category' => 'core',
'key_type' => 'autoincrement',
'name_attcode' => array('item_class', 'temp_id'),
'image_attcode' => '',
'state_attcode' => '',
'reconc_keys' => array(''),
'db_table' => 'priv_temporary_object_descriptor',
'db_key_field' => 'id',
'db_finalclass_field' => '',
'style' => new ormStyle(null, null, null, null, null, null),
'indexes' => array(
1 =>
array(
0 => 'temp_id',
),
2 =>
array(
0 => 'item_class',
1 => 'item_id',
),
),
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeDateTime('expiration_date', array('sql' => 'expiration_date', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeString('temp_id', array('sql' => 'temp_id', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeString('item_class', array('sql' => 'item_class', 'is_null_allowed' => false, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeObjectKey('item_id', array('class_attcode' => 'item_class', 'sql' => 'item_id', 'is_null_allowed' => true, 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeDateTime('creation_date', array('sql' => 'creation_date', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeString('host_class', array('sql' => 'host_class', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeObjectKey('host_id', array('class_attcode' => 'host_class', 'sql' => 'host_id', 'is_null_allowed' => true, 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeString('host_att_code', array('sql' => 'host_att_code', 'is_null_allowed' => true, 'default_value' => '', 'allowed_values' => null, 'depends_on' => array(), 'always_load_in_tables' => false)));
MetaModel::Init_AddAttribute(new AttributeEnum("operation", array("allowed_values" => new ValueSetEnum('create,delete'), "sql" => "operation", "default_value" => "create", "is_null_allowed" => true, "depends_on" => array())));
MetaModel::Init_SetZListItems('details', array(
0 => 'temp_id',
1 => 'item_class',
2 => 'item_id',
3 => 'creation_date',
4 => 'expiration_date',
5 => 'meta',
));
MetaModel::Init_SetZListItems('standard_search', array(
0 => 'temp_id',
1 => 'item_class',
2 => 'item_id',
));
MetaModel::Init_SetZListItems('list', array(
0 => 'temp_id',
1 => 'item_class',
2 => 'item_id',
3 => 'creation_date',
4 => 'expiration_date',
));;
}
public function DBInsertNoReload()
{
$this->SetCurrentDateIfNull('creation_date');
return parent::DBInsertNoReload();
}
/**
* Set/Update all of the '_item' fields
*
* @param object $oItem Container item
*
* @return void
*/
public function SetItem($oItem, $bUpdateOnChange = false)
{
$sClass = get_class($oItem);
$iItemId = $oItem->GetKey();
$this->Set('item_class', $sClass);
$this->Set('item_id', $iItemId);
}
}

View File

@@ -16,6 +16,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Application\TwigBase\Twig\TwigHelper;
/**
* Persistent classes (internal): user defined actions
@@ -225,7 +226,22 @@ class ActionEmail extends ActionNotification
* @since 3.0.1
*/
const ENUM_HEADER_NAME_REFERENCES = 'References';
/**
* @var string
* @since 3.1.0
*/
const TEMPLATE_BODY_CONTENT = '$content$';
/**
* Wraps the 'body' of the message for previewing inside an IFRAME -- i.e. without any of the iTop stylesheets being applied
* @var string
* @since 3.1.0
*/
const CONTENT_HIGHLIGHT = '<div style="border:2px dashed #6800ff;position:relative;padding:2px;margin-top:14px;"><div style="background-color:#6800ff;color:#fff;font-family:Courier New, sans-serif;font-size:14px;line-height:16px;padding:3px;display:block;position:absolute;top:-22px;right:0;">$content$</div>%s</div>';
/**
* Wraps a placeholder of the email's body for previewing inside an IFRAME -- i.e. without any of the iTop stylesheets being applied
* @var string
*/
const FIELD_HIGHLIGHT = '<span style="background-color:#6800ff;color:#fff;font-size:smaller;font-family:Courier New, sans-serif;padding:2px;">\\$$1\\$</span>';
/**
* @inheritDoc
*/
@@ -257,6 +273,10 @@ class ActionEmail extends ActionNotification
MetaModel::Init_AddAttribute(new AttributeTemplateString("subject", array("allowed_values" => null, "sql" => "subject", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeTemplateHTML("body", array("allowed_values" => null, "sql" => "body", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("importance", array("allowed_values" => new ValueSetEnum('low,normal,high'), "sql" => "importance", "default_value" => 'normal', "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeApplicationLanguage("language", array("sql"=>"language", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeBlob("html_template", array("is_null_allowed"=>true, "depends_on"=>array(), "always_load_in_tables"=>false)));
MetaModel::Init_AddAttribute(new AttributeEnum("ignore_notify", array("allowed_values" => new ValueSetEnum('yes,no'), "sql" => "ignore_notify", "default_value" => 'yes', "is_null_allowed" => false, "depends_on" => array())));
// Display lists
// - Attributes to be displayed for the complete details
@@ -266,8 +286,10 @@ class ActionEmail extends ActionNotification
0 => 'name',
1 => 'description',
2 => 'status',
3 => 'subject',
4 => 'body',
3 => 'language',
4 => 'html_template',
5 => 'subject',
6 => 'body',
// 5 => 'importance', not handled when sending the mail, better hide it then
),
'fieldset:ActionEmail:trigger' => array(
@@ -281,20 +303,21 @@ class ActionEmail extends ActionNotification
2 => 'reply_to',
3 => 'reply_to_label',
4 => 'test_recipient',
5 => 'to',
6 => 'cc',
7 => 'bcc',
5 => 'ignore_notify',
6 => 'to',
7 => 'cc',
8 => 'bcc',
),
),
));
// - Attributes to be displayed for a list
MetaModel::Init_SetZListItems('list', array('status', 'to', 'subject'));
MetaModel::Init_SetZListItems('list', array('status', 'to', 'subject', 'language'));
// Search criteria
// - Standard criteria of the search
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'status', 'subject'));
MetaModel::Init_SetZListItems('standard_search', array('name', 'description', 'status', 'subject', 'language'));
// - Default criteria for the search
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status', 'subject'));
MetaModel::Init_SetZListItems('default_search', array('name', 'description', 'status', 'subject', 'language'));
}
// count the recipients found
@@ -324,6 +347,15 @@ class ActionEmail extends ActionNotification
try
{
$oSearch = DBObjectSearch::FromOQL($sOQL);
if ($this->Get('ignore_notify') === 'no') {
// In theory it is possible to notify *any* kind of object,
// as long as there is an email attribute in the class
// So let's not assume that the selected class is a Person
$sFirstSelectedClass = $oSearch->GetClass();
if (MetaModel::IsValidAttCode($sFirstSelectedClass, 'notify')) {
$oSearch->AddCondition('notify', 'yes');
}
}
$oSearch->AllowAllData();
}
catch (OQLException $e)
@@ -448,114 +480,27 @@ class ActionEmail extends ActionNotification
*/
protected function _DoExecute($oTrigger, $aContextArgs, &$oLog)
{
$sPreviousUrlMaker = ApplicationContext::SetUrlMakerClass();
try
{
$this->m_iRecipients = 0;
$this->m_aMailErrors = array();
// Determine recipients
//
$sTo = $this->FindRecipients('to', $aContextArgs);
$sCC = $this->FindRecipients('cc', $aContextArgs);
$sBCC = $this->FindRecipients('bcc', $aContextArgs);
$sFrom = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
$sFromLabel = MetaModel::ApplyParams($this->Get('from_label'), $aContextArgs);
$sReplyTo = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
$sReplyToLabel = MetaModel::ApplyParams($this->Get('reply_to_label'), $aContextArgs);
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
$oObj = $aContextArgs['this->object()'];
$sMessageId = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_MESSAGE_ID);
$sReference = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_REFERENCES);
}
catch (Exception $e) {
/** @noinspection PhpUnhandledExceptionInspection */
throw $e;
}
finally {
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
}
if (!is_null($oLog)) {
// Note: we have to secure this because those values are calculated
// inside the try statement, and we would like to keep track of as
// many data as we could while some variables may still be undefined
if (isset($sTo)) {
$oLog->Set('to', $sTo);
}
if (isset($sCC)) {
$oLog->Set('cc', $sCC);
}
if (isset($sBCC)) {
$oLog->Set('bcc', $sBCC);
}
if (isset($sFrom)) {
$oLog->Set('from', $sFrom);
}
if (isset($sSubject)) {
$oLog->Set('subject', $sSubject);
}
if (isset($sBody)) {
$oLog->Set('body', $sBody);
}
}
$sStyles = file_get_contents(APPROOT.'css/email.css');
$sStyles .= MetaModel::GetConfig()->Get('email_css');
$oEmail = new EMail();
if ($this->IsBeingTested()) {
$oEmail->SetSubject('TEST['.$sSubject.']');
$sTestBody = $sBody;
$sTestBody .= "<div style=\"border: dashed;\">\n";
$sTestBody .= "<h1>Testing email notification ".$this->GetHyperlink()."</h1>\n";
$sTestBody .= "<p>The email should be sent with the following properties\n";
$sTestBody .= "<ul>\n";
$sTestBody .= "<li>TO: $sTo</li>\n";
$sTestBody .= "<li>CC: $sCC</li>\n";
$sTestBody .= "<li>BCC: $sBCC</li>\n";
$sTestBody .= empty($sFromLabel) ? "<li>From: $sFrom</li>\n" : "<li>From: $sFromLabel &lt;$sFrom&gt;</li>\n";
$sTestBody .= empty($sReplyToLabel) ? "<li>Reply-To: $sReplyTo</li>\n" : "<li>Reply-To: $sReplyToLabel &lt;$sReplyTo&gt;</li>\n";
$sTestBody .= "<li>References: $sReference</li>\n";
$sTestBody .= "</ul>\n";
$sTestBody .= "</p>\n";
$sTestBody .= "</div>\n";
$oEmail->SetBody($sTestBody, 'text/html', $sStyles);
$oEmail->SetRecipientTO($this->Get('test_recipient'));
$oEmail->SetRecipientFrom($sFrom, $sFromLabel);
$oEmail->SetReferences($sReference);
$oEmail->SetMessageId($sMessageId);
// Note: N°4849 We pass the "References" identifier instead of the "Message-ID" on purpose as we want notifications emails to group around the triggering iTop object, not just the users' replies to the notification
$oEmail->SetInReplyTo($sReference);
} else {
$oEmail->SetSubject($sSubject);
$oEmail->SetBody($sBody, 'text/html', $sStyles);
$oEmail->SetRecipientTO($sTo);
$oEmail->SetRecipientCC($sCC);
$oEmail->SetRecipientBCC($sBCC);
$oEmail->SetRecipientFrom($sFrom, $sFromLabel);
$oEmail->SetRecipientReplyTo($sReplyTo, $sReplyToLabel);
$oEmail->SetReferences($sReference);
$oEmail->SetMessageId($sMessageId);
// Note: N°4849 We pass the "References" identifier instead of the "Message-ID" on purpose as we want notifications emails to group around the triggering iTop object, not just the users' replies to the notification
$oEmail->SetInReplyTo($sReference);
$aEmailContent = $this->PrepareMessageContent($aContextArgs, $oLog);
$oEmail->SetSubject($aEmailContent['subject']);
$oEmail->SetBody($aEmailContent['body'], 'text/html', $sStyles);
$oEmail->SetRecipientTO($aEmailContent['to']);
$oEmail->SetRecipientCC($aEmailContent['cc']);
$oEmail->SetRecipientBCC($aEmailContent['bcc']);
$oEmail->SetRecipientFrom($aEmailContent['from'], $aEmailContent['from_label']);
$oEmail->SetRecipientReplyTo($aEmailContent['reply_to'], $aEmailContent['reply_to_label']);
$oEmail->SetReferences($aEmailContent['references']);
$oEmail->SetMessageId($aEmailContent['message_id']);
$oEmail->SetInReplyTo($aEmailContent['in_reply_to']);
foreach($aEmailContent['attachments'] as $aAttachment) {
$oEmail->AddAttachment($aAttachment['data'], $aAttachment['filename'], $aAttachment['mime_type']);
}
if (isset($aContextArgs['attachments']))
{
$aAttachmentReport = array();
foreach($aContextArgs['attachments'] as $oDocument)
{
$oEmail->AddAttachment($oDocument->GetData(), $oDocument->GetFileName(), $oDocument->GetMimeType());
$aAttachmentReport[] = array($oDocument->GetFileName(), $oDocument->GetMimeType(), strlen($oDocument->GetData()));
}
$oLog->Set('attachments', $aAttachmentReport);
}
if (empty($this->m_aMailErrors))
{
if ($this->m_iRecipients == 0)
@@ -564,6 +509,7 @@ class ActionEmail extends ActionNotification
}
else
{
$aErrors = [];
$iRes = $oEmail->Send($aErrors, false, $oLog); // allow asynchronous mode
switch ($iRes)
{
@@ -588,13 +534,148 @@ class ActionEmail extends ActionNotification
}
}
/**
* @param array $aContextArgs
* @param \EventNotification $oLog
*
* @return array
* @throws \CoreException
* @throws \Exception
* @since 3.1.0 N°918
*/
protected function PrepareMessageContent($aContextArgs, &$oLog): array
{
$aMessageContent = [
'to' => '',
'cc' => '',
'bcc' => '',
'from' => '',
'from_label' => '',
'reply_to' => '',
'reply_to_label' => '',
'subject' => '',
'body' => '',
'references' => '',
'message_id' => '',
'in_reply_to' => '',
'attachments' => [],
];
$sPreviousUrlMaker = ApplicationContext::SetUrlMakerClass();
$sPreviousLanguage = Dict::GetUserLanguage();
$aPreviousPluginProperties = ApplicationContext::GetPluginProperties('QueryLocalizerPlugin');
if ($this->Get('language') !== '') {
// If a language is specified for this action, force this language
// when rendering all placeholders inside this message
Dict::SetUserLanguage($this->Get('language'));
AttributeDateTime::LoadFormatFromConfig();
ApplicationContext::SetPluginProperty('QueryLocalizerPlugin', 'language_code', $this->Get('language'));
}
try
{
$this->m_iRecipients = 0;
$this->m_aMailErrors = array();
// Determine recipients
//
$aMessageContent['to'] = $this->FindRecipients('to', $aContextArgs);
$aMessageContent['cc'] = $this->FindRecipients('cc', $aContextArgs);
$aMessageContent['bcc'] = $this->FindRecipients('bcc', $aContextArgs);
$aMessageContent['from'] = MetaModel::ApplyParams($this->Get('from'), $aContextArgs);
$aMessageContent['from_label'] = MetaModel::ApplyParams($this->Get('from_label'), $aContextArgs);
$aMessageContent['reply_to'] = MetaModel::ApplyParams($this->Get('reply_to'), $aContextArgs);
$aMessageContent['reply_to_label'] = MetaModel::ApplyParams($this->Get('reply_to_label'), $aContextArgs);
$aMessageContent['subject'] = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
$sBody = $this->BuildMessageBody(false);
$aMessageContent['body'] = MetaModel::ApplyParams($sBody, $aContextArgs);
$oObj = $aContextArgs['this->object()'];
$aMessageContent['message_id'] = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_MESSAGE_ID);
$aMessageContent['references'] = $this->GenerateIdentifierForHeaders($oObj, static::ENUM_HEADER_NAME_REFERENCES);
}
catch (Exception $e) {
/** @noinspection PhpUnhandledExceptionInspection */
throw $e;
}
finally {
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
Dict::SetUserLanguage($sPreviousLanguage);
AttributeDateTime::LoadFormatFromConfig();
ApplicationContext::SetPluginProperty('QueryLocalizerPlugin', 'language_code', $aPreviousPluginProperties['language_code'] ?? null);
}
if (!is_null($oLog)) {
// Note: we have to secure this because those values are calculated
// inside the try statement, and we would like to keep track of as
// many data as we could while some variables may still be undefined
if (isset($aMessageContent['to'])) {
$oLog->Set('to', $aMessageContent['to']);
}
if (isset($aMessageContent['cc'])) {
$oLog->Set('cc', $aMessageContent['cc']);
}
if (isset($aMessageContent['bcc'])) {
$oLog->Set('bcc', $aMessageContent['bcc']);
}
if (isset($aMessageContent['from'])) {
$oLog->Set('from', $aMessageContent['from']);
}
if (isset($aMessageContent['subject'])) {
$oLog->Set('subject', $aMessageContent['subject']);
}
if (isset($aMessageContent['body'])) {
$oLog->Set('body', HTMLSanitizer::Sanitize($aMessageContent['body']));
}
}
if ($this->IsBeingTested()) {
$sTestBody = $aMessageContent['body'];
$sTestBody .= "<div style=\"border: dashed;\">\n";
$sTestBody .= "<h1>Testing email notification ".$this->GetHyperlink()."</h1>\n";
$sTestBody .= "<p>The email should be sent with the following properties\n";
$sTestBody .= "<ul>\n";
$sTestBody .= "<li>TO: {$aMessageContent['to']}</li>\n";
$sTestBody .= "<li>CC: {$aMessageContent['cc']}</li>\n";
$sTestBody .= "<li>BCC: {$aMessageContent['bcc']}</li>\n";
$sTestBody .= empty($aMessageContent['from_label']) ? "<li>From: {$aMessageContent['from']}</li>\n" : "<li>From: {$aMessageContent['from_label']} &lt;{$aMessageContent['from']}&gt;</li>\n";
$sTestBody .= empty($aMessageContent['reply_to_label']) ? "<li>Reply-To: {$aMessageContent['reply_to']}</li>\n" : "<li>Reply-To: {$aMessageContent['reply_to_label']} &lt;{$aMessageContent['reply_to']}&gt;</li>\n";
$sTestBody .= "<li>References: {$aMessageContent['references']}</li>\n";
$sTestBody .= "</ul>\n";
$sTestBody .= "</p>\n";
$sTestBody .= "</div>\n";
$aMessageContent['subject'] = 'TEST['.$aMessageContent['subject'].']';
$aMessageContent['body'] = $sTestBody;
$aMessageContent['to'] = $this->Get('test_recipient');
// N°6677 Ensure emails in test are never sent to cc'd and bcc'd addresses
$aMessageContent['cc'] = '';
$aMessageContent['bcc'] = '';
}
// Note: N°4849 We pass the "References" identifier instead of the "Message-ID" on purpose as we want notifications emails to group around the triggering iTop object, not just the users' replies to the notification
$aMessageContent['in_reply_to'] = $aMessageContent['references'];
if (isset($aContextArgs['attachments']))
{
$aAttachmentReport = array();
foreach($aContextArgs['attachments'] as $oDocument)
{
$aMessageContent['attachments'][] = ['data' => $oDocument->GetData(), 'filename' => $oDocument->GetFileName(), 'mime_type' => $oDocument->GetMimeType()];
$aAttachmentReport[] = array($oDocument->GetFileName(), $oDocument->GetMimeType(), strlen($oDocument->GetData()));
}
$oLog->Set('attachments', $aAttachmentReport);
}
return $aMessageContent;
}
/**
* @param \DBObject $oObject
* @param string $sHeaderName {@see \ActionEmail::ENUM_HEADER_NAME_REFERENCES}, {@see \ActionEmail::ENUM_HEADER_NAME_MESSAGE_ID}
*
* @return string The formatted identifier for $sHeaderName based on $oObject
* @throws \Exception
* @since 3.0.1 N°4849
* @since 3.1.0 N°4849
*/
protected function GenerateIdentifierForHeaders(DBObject $oObject, string $sHeaderName): string
{
@@ -629,4 +710,65 @@ class ActionEmail extends ActionNotification
]);
throw new Exception($sErrorMessage);
}
/**
* Compose the body of the message from the 'body' attribute and the HTML template (if any)
* @since 3.1.0 N°4849
* @param bool $bHighlightPlaceholders If true add some extra HTML around placeholders to highlight them
* @return string
*/
protected function BuildMessageBody(bool $bHighlightPlaceholders = false): string
{
$sBody = $this->Get('body');
/** @var ormDocument $oHtmlTemplate */
$oHtmlTemplate = $this->Get('html_template');
if ($oHtmlTemplate && !$oHtmlTemplate->IsEmpty()) {
$sHtmlTemplate = $oHtmlTemplate->GetData();
if (false !== mb_strpos($sHtmlTemplate, static::TEMPLATE_BODY_CONTENT)) {
if ($bHighlightPlaceholders) {
// Highlight the $content$ block
$sBody = sprintf(static::CONTENT_HIGHLIGHT, $sBody);
}
$sBody = str_replace(static::TEMPLATE_BODY_CONTENT, $sBody, $oHtmlTemplate->GetData()); // str_replace is ok as long as both strings are well-formed UTF-8
} else {
$sBody = $oHtmlTemplate->GetData();
}
}
if($bHighlightPlaceholders) {
// Highlight all placeholders
$sBody = preg_replace('/\\$([^$]+)\\$/', static::FIELD_HIGHLIGHT, $sBody);
}
return $sBody;
}
/**
* @since 3.1.0 N°4849
* @inheritDoc
* @see cmdbAbstractObject::DisplayBareRelations()
*/
public function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
{
parent::DisplayBareRelations($oPage, false);
if (!$bEditMode) {
$oPage->SetCurrentTab('action_email__preview', Dict::S('ActionEmail:preview_tab'), Dict::S('ActionEmail:preview_tab+'));
$sBody = $this->BuildMessageBody(true);
TwigHelper::RenderIntoPage($oPage, APPROOT.'/', 'templates/datamodel/ActionEmail/email-notification-preview', ['iframe_content' => $sBody]);
}
}
/**
* @since 3.1.0
* @inheritDoc
* @see cmdbAbstractObject::DoCheckToWrite()
*/
public function DoCheckToWrite()
{
parent::DoCheckToWrite();
$oHtmlTemplate = $this->Get('html_template');
if ($oHtmlTemplate && !$oHtmlTemplate->IsEmpty()) {
if (false === mb_strpos($oHtmlTemplate->GetData(), static::TEMPLATE_BODY_CONTENT)) {
$this->m_aCheckWarnings[] = Dict::Format('ActionEmail:content_placeholder_missing', static::TEMPLATE_BODY_CONTENT, Dict::S('Class:ActionEmail/Attribute:body'));
}
}
}
}

View File

@@ -60,14 +60,15 @@ function apc_store($key, $var = NULL, $ttl = 0)
*/
function apc_fetch($key)
{
if (is_array($key))
{
$aResult = array();
foreach($key as $sKey)
{
if (is_array($key)) {
$aResult = [];
foreach ($key as $sKey) {
$aResult[$sKey] = apcFile::FetchOneFile($sKey);
}
return $aResult;
} elseif (is_null($key)) {
return false;
}
return apcFile::FetchOneFile($key);
}
@@ -211,21 +212,16 @@ class apcFile
*/
static public function StoreOneFile($sKey, $value, $iTTL)
{
if (empty($sKey))
{
if (empty($sKey)) {
return false;
}
if (is_file(self::GetCacheFileName($sKey)))
{
if (is_file(self::GetCacheFileName($sKey))) {
@unlink(self::GetCacheFileName($sKey));
}
if (is_file(self::GetCacheFileName('-'.$sKey)))
{
if (is_file(self::GetCacheFileName('-'.$sKey))) {
@unlink(self::GetCacheFileName('-'.$sKey));
}
if ($iTTL > 0)
{
if ($iTTL > 0) {
// hint for ttl management
$sKey = '-'.$sKey;
}
@@ -233,15 +229,14 @@ class apcFile
$sFilename = self::GetCacheFileName($sKey);
// try to create the folder
$sDirname = dirname($sFilename);
if (!file_exists($sDirname))
{
if (!@mkdir($sDirname, 0755, true))
{
if (!is_dir($sDirname)) {
if (!@mkdir($sDirname, 0755, true)) {
return false;
}
}
$bRes = !(@file_put_contents($sFilename, serialize($value), LOCK_EX) === false);
self::AddFile($sFilename);
return $bRes;
}
@@ -325,19 +320,15 @@ class apcFile
*/
static protected function ReadCacheLocked($sFilename)
{
if (!is_file($sFilename))
{
return false;
}
$sContent = false;
$file = @fopen($sFilename, 'r');
if ($file === false)
{
return false;
if ($file !== false) {
if (flock($file, LOCK_SH)) {
$sContent = file_get_contents($sFilename);
flock($file, LOCK_UN);
}
fclose($file);
}
flock($file, LOCK_SH);
$sContent = @fread($file, @filesize($sFilename));
flock($file, LOCK_UN);
fclose($file);
return $sContent;
}

View File

@@ -9,8 +9,7 @@ use Combodo\iTop\Application\UI\Links\Set\BlockLinkSetDisplayAsProperty;
use Combodo\iTop\Form\Field\LabelField;
use Combodo\iTop\Form\Field\TextAreaField;
use Combodo\iTop\Form\Form;
use Combodo\iTop\Form\Validator\NotEmptyExtKeyValidator;
use Combodo\iTop\Form\Validator\Validator;
use Combodo\iTop\Form\Validator\CustomRegexpValidator;
use Combodo\iTop\Renderer\BlockRenderer;
use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
use Combodo\iTop\Service\Links\LinkSetModel;
@@ -92,8 +91,11 @@ define('LINKSET_EDITMODE_ACTIONS', 2); // Show the usual 'Actions' popup menu
define('LINKSET_EDITMODE_INPLACE', 3); // The "linked" objects can be created/modified/deleted in place
define('LINKSET_EDITMODE_ADDREMOVE', 4); // The "linked" objects can be added/removed in place
define('LINKSET_RELATIONTYPE_PROPERTY', 'property');
define('LINKSET_RELATIONTYPE_LINK', 'link');
define('LINKSET_EDITWHEN_NEVER', 0); // The linkset cannot be edited at all from inside this object
define('LINKSET_EDITWHEN_ON_HOST_EDITION', 1); // The only possible action is to open a new window to create a new object
define('LINKSET_EDITWHEN_ON_HOST_DISPLAY', 2); // Show the usual 'Actions' popup menu
define('LINKSET_EDITWHEN_ALWAYS', 3); // Show the usual 'Actions' popup menu
define('LINKSET_DISPLAY_STYLE_PROPERTY', 'property');
define('LINKSET_DISPLAY_STYLE_TAB', 'tab');
@@ -795,7 +797,7 @@ abstract class AttributeDefinition
public function HasAValue($proposedValue): bool
{
// Default implementation, we don't really know what type $proposedValue will be
return is_null($proposedValue);
return !(is_null($proposedValue));
}
/**
@@ -1125,7 +1127,7 @@ abstract class AttributeDefinition
// Validation pattern
if ($this->GetValidationPattern() !== '') {
$oFormField->AddValidator(new Validator($this->GetValidationPattern()));
$oFormField->AddValidator(new CustomRegexpValidator($this->GetValidationPattern()));
}
// Description
@@ -1154,6 +1156,13 @@ abstract class AttributeDefinition
$oFormField->AddMetadata('value-raw', (string)$oObject->Get($this->GetCode()));
}
// We don't want to invalidate field because of old untouched values that are no longer valid
$aModifiedAttCodes = $oObject->ListChanges();
$bAttributeHasBeenModified = array_key_exists($this->GetCode(), $aModifiedAttCodes);
if (false === $bAttributeHasBeenModified) {
$oFormField->SetValidationDisabled(true);
}
return $oFormField;
}
@@ -1696,20 +1705,19 @@ class AttributeLinkedSet extends AttributeDefinition
/**
* @return string see LINKSET_EDITMODE_* constants
* @since 3.1.0 N°5563 relations are edited using new attributes in details mode, but as nothing changed in edit mode we are still using edit_mode attribute
*/
public function GetEditMode()
{
return $this->GetOptional('edit_mode', LINKSET_EDITMODE_ACTIONS);
}
}
/**
* @return string see LINKSET_RELATIONTYPE_* constants
* @since 3.1.0 N°5563
* @return int see LINKSET_EDITWHEN_* constants
* @since 3.1.1 3.2.0 N°6385
*/
public function GetRelationType()
public function GetEditWhen(): int
{
return $this->GetOptional('relation_type', LINKSET_RELATIONTYPE_LINK);
return $this->GetOptional('edit_when', LINKSET_EDITWHEN_ALWAYS);
}
/**
@@ -1727,14 +1735,24 @@ class AttributeLinkedSet extends AttributeDefinition
}
/**
* @return boolean
* @since 3.1.0 N°5563
* Indicates if the current Attribute has constraints (php constraints or datamodel constraints)
* @return bool true if Attribute has constraints
* @since 3.1.0 N°6228
*/
public function GetReadOnly()
public function HasPHPConstraint(): bool
{
return $this->GetOptional('read_only', false);
return $this->GetOptional('with_php_constraint', false);
}
/**
* @return bool true if Attribute has computation (DB_LINKS_CHANGED event propagation, `with_php_computation` attribute xml property), false otherwise
* @since 3.1.1 3.2.0 N°6228
*/
public function HasPHPComputation(): bool
{
return $this->GetOptional('with_php_computation', false);
}
public function GetLinkedClass()
{
return $this->Get('linked_class');
@@ -1761,12 +1779,58 @@ class AttributeLinkedSet extends AttributeDefinition
}
/** @inheritDoc * */
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true): string
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
if($this->GetDisplayStyle() === LINKSET_DISPLAY_STYLE_TAB){
return $this->GetAsHTMLForTab($sValue, $oHostObject, $bLocalize);
}
else{
return $this->GetAsHTMLForProperty($sValue, $oHostObject, $bLocalize);
}
}
public function GetAsHTMLForTab($sValue, $oHostObject = null, $bLocalize = true)
{
if (is_object($sValue) && ($sValue instanceof ormLinkSet))
{
$sValue->Rewind();
$aItems = array();
while ($oObj = $sValue->Fetch())
{
// Show only relevant information (hide the external key to the current object)
$aAttributes = array();
foreach(MetaModel::ListAttributeDefs($this->GetLinkedClass()) as $sAttCode => $oAttDef)
{
if ($sAttCode == $this->GetExtKeyToMe())
{
continue;
}
if ($oAttDef->IsExternalField())
{
continue;
}
$sAttValue = $oObj->GetAsHTML($sAttCode);
if (strlen($sAttValue) > 0)
{
$aAttributes[] = $sAttValue;
}
}
$sAttributes = implode(', ', $aAttributes);
$aItems[] = $sAttributes;
}
return implode('<br/>', $aItems);
}
return null;
}
public function GetAsHTMLForProperty($sValue, $oHostObject = null, $bLocalize = true): string
{
try {
/** @var ormLinkSet $sValue */
if ($sValue->Count() === 0) {
if (is_null($sValue) || $sValue->Count() === 0) {
return '';
}
@@ -2385,43 +2449,61 @@ class AttributeLinkedSet extends AttributeDefinition
{
if ($oFormField === null)
{
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
$sFormFieldClass = static::GetFormFieldClass();
$oFormField = new $sFormFieldClass($this->GetCode());
}
// Setting target class
// Setting target class
if (!$this->IsIndirect()) {
$sTargetClass = $this->GetLinkedClass();
} else {
/** @var \AttributeExternalKey $oRemoteAttDef */
/** @var \AttributeLinkedSetIndirect $this */
$oRemoteAttDef = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToRemote());
$sTargetClass = $oRemoteAttDef->GetTargetClass();
$sTargetClass = $this->GetLinkedClass();
} else {
/** @var \AttributeExternalKey $oRemoteAttDef */
/** @var \AttributeLinkedSetIndirect $this */
$oRemoteAttDef = MetaModel::GetAttributeDef($this->GetLinkedClass(), $this->GetExtKeyToRemote());
$sTargetClass = $oRemoteAttDef->GetTargetClass();
/** @var \AttributeLinkedSetIndirect $this */
$oFormField->SetExtKeyToRemote($this->GetExtKeyToRemote());
}
$oFormField->SetTargetClass($sTargetClass);
$oFormField->SetIndirect($this->IsIndirect());
// Setting attcodes to display
$aAttCodesToDisplay = MetaModel::FlattenZList(MetaModel::GetZListItems($sTargetClass, 'list'));
// - Adding friendlyname attribute to the list is not already in it
$sTitleAttCode = MetaModel::GetFriendlyNameAttributeCode($sTargetClass);
if (($sTitleAttCode !== null) && !in_array($sTitleAttCode, $aAttCodesToDisplay)) {
$aAttCodesToDisplay = array_merge(array($sTitleAttCode), $aAttCodesToDisplay);
}
// - Adding attribute labels
$aAttributesToDisplay = array();
foreach ($aAttCodesToDisplay as $sAttCodeToDisplay) {
$oAttDefToDisplay = MetaModel::GetAttributeDef($sTargetClass, $sAttCodeToDisplay);
$aAttributesToDisplay[$sAttCodeToDisplay] = $oAttDefToDisplay->GetLabel();
}
$oFormField->SetAttributesToDisplay($aAttributesToDisplay);
/** @var \AttributeLinkedSetIndirect $this */
$oFormField->SetExtKeyToRemote($this->GetExtKeyToRemote());
}
$oFormField->SetTargetClass($sTargetClass);
$oFormField->SetLinkedClass($this->GetLinkedClass());
$oFormField->SetIndirect($this->IsIndirect());
// Setting attcodes to display
$aAttCodesToDisplay = MetaModel::FlattenZList(MetaModel::GetZListItems($sTargetClass, 'list'));
// - Adding friendlyname attribute to the list is not already in it
$sTitleAttCode = MetaModel::GetFriendlyNameAttributeCode($sTargetClass);
if (($sTitleAttCode !== null) && !in_array($sTitleAttCode, $aAttCodesToDisplay)) {
$aAttCodesToDisplay = array_merge(array($sTitleAttCode), $aAttCodesToDisplay);
}
// - Adding attribute properties
$aAttributesToDisplay = array();
foreach ($aAttCodesToDisplay as $sAttCodeToDisplay) {
$oAttDefToDisplay = MetaModel::GetAttributeDef($sTargetClass, $sAttCodeToDisplay);
$aAttributesToDisplay[$sAttCodeToDisplay] = [
'att_code' => $sAttCodeToDisplay,
'label' => $oAttDefToDisplay->GetLabel(),
];
}
$oFormField->SetAttributesToDisplay($aAttributesToDisplay);
parent::MakeFormField($oObject, $oFormField);
// Append lnk attributes (filtered from zlist)
if ($this->IsIndirect()) {
$aLnkAttDefToDisplay = MetaModel::GetZListAttDefsFilteredForIndirectLinkClass($this->m_sHostClass, $this->m_sCode);
$aLnkAttributesToDisplay = array();
foreach ($aLnkAttDefToDisplay as $oLnkAttDefToDisplay) {
$aLnkAttributesToDisplay[$oLnkAttDefToDisplay->GetCode()] = [
'att_code' => $oLnkAttDefToDisplay->GetCode(),
'label' => $oLnkAttDefToDisplay->GetLabel(),
'mandatory' => !$oLnkAttDefToDisplay->IsNullAllowed(),
];
}
$oFormField->SetLnkAttributesToDisplay($aLnkAttributesToDisplay);
}
return $oFormField;
}
parent::MakeFormField($oObject, $oFormField);
return $oFormField;
}
public function IsPartOfFingerprint()
{
@@ -2527,15 +2609,6 @@ class AttributeLinkedSetIndirect extends AttributeLinkedSet
return $this->GetOptional("duplicates", false);
} // The same object may be linked several times... or not...
/**
* @return boolean
* @since 3.1.0 N°5563
*/
public function GetReadOnly()
{
return $this->GetOptional('read_only', false);
}
public function GetTrackingLevel()
{
return $this->GetOptional('tracking_level',
@@ -2988,6 +3061,11 @@ class AttributeObjectKey extends AttributeDBFieldVoid
return ((int) $proposedValue) !== 0;
}
/**
* @inheritDoc
*
* @param int|DBObject $proposedValue Object key or valid ({@see MetaModel::IsValidObject()}) datamodel object
*/
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue))
@@ -3000,7 +3078,6 @@ class AttributeObjectKey extends AttributeDBFieldVoid
}
if (MetaModel::IsValidObject($proposedValue))
{
/** @var \DBObject $proposedValue */
return $proposedValue->GetKey();
}
@@ -3120,7 +3197,7 @@ class AttributeDecimal extends AttributeDBField
$iPrecision = $this->Get('decimals');
$iNbIntegerDigits = $iNbDigits - $iPrecision - 1; // -1 because the first digit is treated separately in the pattern below
return "^[-+]?[0-9]\d{0,$iNbIntegerDigits}(\.\d{0,$iPrecision})?$";
return "^[\-\+]?[0-9]\d{0,$iNbIntegerDigits}(\.\d{0,$iPrecision})?$";
}
public function GetBasicFilterOperators()
@@ -3842,6 +3919,12 @@ class AttributeApplicationLanguage extends AttributeString
{
$aLanguageCodes[$sLangCode] = $aInfo['description'].' ('.$aInfo['localized_description'].')';
}
// N°6462 This should be sorted directly in \Dict during the compilation but we can't for 2 reasons:
// - Additional languages can be added on the fly even though it is not recommended
// - Formatting is done at run time (just above)
natcasesort($aLanguageCodes);
$aParams["allowed_values"] = new ValueSetEnum($aLanguageCodes);
parent::__construct($sCode, $aParams);
}
@@ -4132,7 +4215,7 @@ class AttributePassword extends AttributeString implements iAttributeNoGroupBy
public function GetAsHTML($sValue, $oHostObject = null, $bLocalize = true)
{
if (strlen($sValue) == 0)
if (utils::IsNullOrEmptyString($sValue))
{
return '';
}
@@ -4451,7 +4534,6 @@ class AttributeText extends AttributeString
$sStyle = '';
if (count($aStyles) > 0)
{
$aStyles[] = 'overflow:auto';
$sStyle = 'style="'.implode(';', $aStyles).'"';
}
@@ -5900,6 +5982,14 @@ class AttributeEnum extends AttributeString
$aLocalizedValues[$sKey] = $this->GetValueLabel($sKey);
}
// Sort by label only if necessary
// See N°1646 and {@see \MFCompiler::CompileAttributeEnumValues()} for complete information as for why sort on labels is done at runtime while other sorting are done at compile time
/** @var \ValueSetEnum $oValueSetDef */
$oValueSetDef = $this->GetValuesDef();
if ($oValueSetDef->IsSortedByValues()) {
asort($aLocalizedValues);
}
return $aLocalizedValues;
}
@@ -6175,7 +6265,7 @@ class AttributeDateTime extends AttributeDBField
/**
* Load the 3 settings: date format, time format and data_time format from the configuration
*/
protected static function LoadFormatFromConfig()
public static function LoadFormatFromConfig()
{
$aFormats = MetaModel::GetConfig()->Get('date_and_time_format');
$sLang = Dict::GetUserLanguage();
@@ -6423,6 +6513,11 @@ class AttributeDateTime extends AttributeDBField
}
}
/**
* @inheritDoc
*
* @param int|string $proposedValue timestamp ({@see DateTime::getTimestamp()) or date as string, following the {@see GetInternalFormat} format.
*/
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue))
@@ -7157,14 +7252,27 @@ class AttributeExternalKey extends AttributeDBFieldVoid
{
return 0;
}
if (MetaModel::IsValidObject($proposedValue))
{
if (MetaModel::IsValidObject($proposedValue)) {
return $proposedValue->GetKey();
}
return (int)$proposedValue;
}
/** @inheritdoc @since 3.1 */
public function WriteExternalValues(DBObject $oHostObject): void
{
$sTargetKey = $oHostObject->Get($this->GetCode());
$oFilter = DBSearch::FromOQL('SELECT `'.TemporaryObjectDescriptor::class.'` WHERE item_class=:class AND item_id=:id');
$oSet = new DBObjectSet($oFilter, [], ['class' => $this->GetTargetClass(), 'id' => $sTargetKey]);
while ($oTemporaryObjectDescriptor = $oSet->Fetch()) {
$oTemporaryObjectDescriptor->Set('host_class', get_class($oHostObject));
$oTemporaryObjectDescriptor->Set('host_id', $oHostObject->GetKey());
$oTemporaryObjectDescriptor->Set('host_att_code', $this->GetCode());
$oTemporaryObjectDescriptor->DBUpdate();
}
}
public function GetMaximumComboLength()
{
return $this->GetOptional('max_combo_length', MetaModel::GetConfig()->Get('max_combo_length'));
@@ -7228,6 +7336,7 @@ class AttributeExternalKey extends AttributeDBFieldVoid
public function MakeFormField(DBObject $oObject, $oFormField = null)
{
/** @var \Combodo\iTop\Form\Field\Field $oFormField */
if ($oFormField === null) {
// Later : We should check $this->Get('display_style') and create a Radio / Select / ... regarding its value
$sFormFieldClass = static::GetFormFieldClass();
@@ -7258,19 +7367,12 @@ class AttributeExternalKey extends AttributeDBFieldVoid
}
});
}
else
{
else {
$oSearch = DBSearch::FromOQL($this->GetValuesDef()->GetFilterExpression());
$oSearch->SetInternalParams(array('this' => $oObject));
$oFormField->SetSearch($oSearch);
}
// If ExtKey is mandatory, we add a validator to ensure that the value 0 is not selected
if ($oObject->GetAttributeFlags($this->GetCode()) & OPT_ATT_MANDATORY)
{
$oFormField->AddValidator(new NotEmptyExtKeyValidator());
}
parent::MakeFormField($oObject, $oFormField);
return $oFormField;
@@ -7896,6 +7998,17 @@ class AttributeExternalField extends AttributeDefinition
return $oExtAttDef->MakeRealValue($proposedValue, $oHostObj);
}
/**
* @inheritDoc
* @since 3.1.0 N°6271 Delegate to remote attribute to ensure cascading computed values
*/
public function GetSQLValues($value)
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->GetSQLValues($value);
}
public function ScalarToSQL($value)
{
// This one could be used in case of filtering only
@@ -8194,9 +8307,9 @@ class AttributeBlob extends AttributeDefinition
}
/**
* Users can provide the document from an URL (including an URL on iTop itself)
* for CSV import. Administrators can even provide the path to a local file
* {@inheritDoc}
* {@inheritDoc}
*
* @param string $proposedValue Can be an URL (including an URL to iTop itself), or a local path (CSV import)
*
* @see AttributeDefinition::MakeRealValue()
*/
@@ -8304,7 +8417,7 @@ class AttributeBlob extends AttributeDefinition
$aValues[$this->GetCode().'_data'] = '';
$aValues[$this->GetCode().'_mimetype'] = '';
$aValues[$this->GetCode().'_filename'] = '';
$aValues[$this->GetCode().'_downloads_count'] = ''; // Note: Should this be set to \ormDocument::DEFAULT_DOWNLOADS_COUNT ?
$aValues[$this->GetCode().'_downloads_count'] = ormDocument::DEFAULT_DOWNLOADS_COUNT;
}
return $aValues;
@@ -8437,7 +8550,7 @@ class AttributeBlob extends AttributeDefinition
$sFingerprint = '';
if ($value instanceOf ormDocument)
{
$sFingerprint = md5($value->GetData());
$sFingerprint = $value->GetSignature();
}
return $sFingerprint;
@@ -8494,6 +8607,22 @@ class AttributeBlob extends AttributeDefinition
return utils::IsNotNullOrEmptyString($proposedValue->GetData()) && utils::IsNotNullOrEmptyString($proposedValue->GetFileName());
}
/**
* @inheritDoc
* @param \ormDocument $original
* @param \ormDocument $value
* @since N°6502
*/
public function RecordAttChange(DBObject $oObject, $original, $value): void
{
// N°6502 Don't record history if only the download count has changed
if ((null !== $original) && (null !== $value) && $original->EqualsExceptDownloadsCount($value)) {
return;
}
parent::RecordAttChange($oObject, $original, $value);
}
protected function GetChangeRecordAdditionalData(CMDBChangeOp $oMyChangeOp, DBObject $oObject, $original, $value): void
{
if (is_null($original)) {
@@ -8653,7 +8782,7 @@ class AttributeImage extends AttributeBlob
return 'data:'.$value->GetMimeType().';base64,'.base64_encode($value->GetData());
}
return $value->GetDownloadURL(get_class($oHostObject), $oHostObject->GetKey(), $this->GetCode());
return $value->GetDisplayURL(get_class($oHostObject), $oHostObject->GetKey(), $this->GetCode());
}
public static function GetFormFieldClass()
@@ -8680,8 +8809,11 @@ class AttributeImage extends AttributeBlob
}
else
{
$oFormField->SetDownloadUrl($this->Get('default_image'));
$oFormField->SetDisplayUrl($this->Get('default_image'));
$oDefaultImage = $this->Get('default_image');
if (is_object($oDefaultImage) && !$oDefaultImage->IsEmpty()) {
$oFormField->SetDownloadUrl($oDefaultImage);
$oFormField->SetDisplayUrl($oDefaultImage);
}
}
return $oFormField;
@@ -11260,6 +11392,9 @@ class AttributeClassAttCodeSet extends AttributeSet
}
$aAllowedAttributes[$sAttCode] = $sLabel;
}
// N°6460 Always sort on the labels, not on the datamodel definition order
natcasesort($aAllowedAttributes);
return $aAllowedAttributes;
}
@@ -11373,6 +11508,11 @@ class AttributeClassAttCodeSet extends AttributeSet
}
return '<span class="'.implode(' ', $this->aCSSClasses).'">'.$value.'</span>';
}
public function IsNull($proposedValue)
{
return (empty($proposedValue));
}
}
class AttributeQueryAttCodeSet extends AttributeSet
@@ -13245,27 +13385,11 @@ class AttributeCustomFields extends AttributeDefinition
*/
public function CheckValue(DBObject $oHostObject, $value)
{
try
{
try {
$oHandler = $this->GetHandler($value->GetValues());
$oHandler->BuildForm($oHostObject, '');
$oForm = $oHandler->GetForm();
$oForm->Validate();
if ($oForm->GetValid())
{
$ret = true;
}
else
{
$aMessages = array();
foreach($oForm->GetErrorMessages() as $sFieldId => $aFieldMessages)
{
$aMessages[] = $sFieldId.': '.implode(', ', $aFieldMessages);
}
$ret = 'Invalid value: '.implode(', ', $aMessages);
}
} catch (Exception $e)
{
$ret = $oHandler->Validate($oHostObject);
} catch (Exception $e) {
$ret = $e->getMessage();
}

View File

@@ -35,6 +35,7 @@ MetaModel::IncludeModule('synchro/synchrodatasource.class.inc.php');
MetaModel::IncludeModule('core/backgroundtask.class.inc.php');
MetaModel::IncludeModule('core/inlineimage.class.inc.php');
MetaModel::IncludeModule('core/counter.class.inc.php');
MetaModel::IncludeModule('core/TemporaryObjectDescriptor.php');
MetaModel::IncludeModule('webservices/webservices.basic.php');

View File

@@ -32,23 +32,57 @@ abstract class CellChangeSpec
return $this->m_proposedValue;
}
public function GetDisplayableValue()
/**
* @throws \Exception
* @since 3.2.0
*/
public function GetCLIValue(bool $bLocalizedValues = false): string
{
if (is_object($this->m_proposedValue)) {
if ($this->m_proposedValue instanceof ReportValue) {
return $this->m_proposedValue->GetAsCSV($bLocalizedValues, ',', '"');
}
throw new Exception('Unexpected class : '. get_class($this->m_proposedValue));
}
return $this->m_proposedValue;
}
/**
* @throws \Exception
* @since 3.2.0
*/
public function GetHTMLValue(bool $bLocalizedValues = false): string
{
if (is_object($this->m_proposedValue)) {
if ($this->m_proposedValue instanceof ReportValue) {
return $this->m_proposedValue->GetAsHTML($bLocalizedValues);
}
throw new Exception('Unexpected class : '. get_class($this->m_proposedValue));
}
return utils::EscapeHtml($this->m_proposedValue);
}
/**
* @since 3.1.0 N°5305
*/
public function SetDisplayableValue(string $sDisplayableValue)
{
$this->m_proposedValue = $sDisplayableValue;
}
public function GetOql()
{
return $this->m_sOql;
}
/**
* @since 3.1.0 N°5305
* @since 3.2.0
*/
public function GetDisplayableValueAndDescription(): string
public function GetCLIValueAndDescription(): string
{
return sprintf("%s%s",
$this->GetDisplayableValue(),
$this->GetCLIValue(),
$this->GetDescription()
);
}
@@ -97,13 +131,25 @@ class CellStatus_Issue extends CellStatus_Modify
parent::__construct($proposedValue, $previousValue);
}
public function GetDisplayableValue()
public function GetCLIValue(bool $bLocalizedValues = false): string
{
if (is_null($this->m_proposedValue)) {
return Dict::Format('UI:CSVReport-Value-SetIssue');
}
return Dict::Format('UI:CSVReport-Value-ChangeIssue',$this->m_proposedValue);
}
public function GetHTMLValue(bool $bLocalizedValues = false): string
{
if (is_null($this->m_proposedValue))
{
return Dict::Format('UI:CSVReport-Value-SetIssue');
}
return Dict::Format('UI:CSVReport-Value-ChangeIssue', \utils::EscapeHtml($this->m_proposedValue));
if ($this->m_proposedValue instanceof ReportValue)
{
return Dict::Format('UI:CSVReport-Value-ChangeIssue', $this->m_proposedValue->GetAsHTML($bLocalizedValues));
}
return Dict::Format('UI:CSVReport-Value-ChangeIssue',utils::EscapeHtml($this->m_proposedValue));
}
public function GetDescription()
@@ -111,12 +157,12 @@ class CellStatus_Issue extends CellStatus_Modify
return $this->m_sReason;
}
/*
* @since 3.1.0 N°5305
* @since 3.2.0
*/
public function GetDisplayableValueAndDescription(): string
public function GetCLIValueAndDescription(): string
{
return sprintf("%s. %s",
$this->GetDisplayableValue(),
$this->GetCLIValue(),
$this->GetDescription()
);
}
@@ -136,6 +182,12 @@ class CellStatus_SearchIssue extends CellStatus_Issue
/** @var string|null $m_sTargetClass */
private $m_sTargetClass;
/**
* @since 3.1.0 N°5305
* @var string $sAllowedValuesSearch
*/
private $sAllowedValuesSearch;
/**
* CellStatus_SearchIssue constructor.
* @since 3.1.0 N°5305
@@ -144,16 +196,18 @@ class CellStatus_SearchIssue extends CellStatus_Issue
* @param string $sReason : main message
* @param null $sClass : used for additional message that provides allowed values for current class $sClass
* @param null $sAllowedValues : used for additional message that provides allowed values $sAllowedValues for current class
* @param string|null $sAllowedValuesSearch : used to search all allowed values
*/
public function __construct($sSerializedSearch, $sReason, $sClass=null, $sAllowedValues=null)
public function __construct($sSerializedSearch, $sReason, $sClass = null, $sAllowedValues = null, string $sAllowedValuesSearch = null)
{
parent::__construct(null, null, $sReason);
$this->sSerializedSearch = $sSerializedSearch;
$this->m_sAllowedValues = $sAllowedValues;
$this->m_sTargetClass = $sClass;
$this->sAllowedValuesSearch = $sAllowedValuesSearch;
}
public function GetDisplayableValue()
public function GetCLIValue(bool $bLocalizedValues = false): string
{
if (null === $this->m_sReason) {
return Dict::Format('UI:CSVReport-Value-NoMatch', '');
@@ -162,6 +216,15 @@ class CellStatus_SearchIssue extends CellStatus_Issue
return $this->m_sReason;
}
public function GetHTMLValue(bool $bLocalizedValues = false): string
{
if (null === $this->m_sReason) {
return Dict::Format('UI:CSVReport-Value-NoMatch', '');
}
return utils::EscapeHtml($this->m_sReason);
}
public function GetDescription()
{
if (\utils::IsNullOrEmptyString($this->m_sAllowedValues) ||
@@ -179,7 +242,18 @@ class CellStatus_SearchIssue extends CellStatus_Issue
public function GetSearchLinkUrl()
{
return sprintf("UI.php?operation=search&filter=%s",
rawurlencode($this->sSerializedSearch)
rawurlencode($this->sSerializedSearch ?? "")
);
}
/**
* @since 3.1.0 N°5305
* @return null|string
*/
public function GetAllowedValuesLinkUrl(): ?string
{
return sprintf("UI.php?operation=search&filter=%s",
rawurlencode($this->sAllowedValuesSearch ?? "")
);
}
}
@@ -197,6 +271,42 @@ class CellStatus_NullIssue extends CellStatus_Issue
}
}
/**
* Class to differ formatting depending on the caller
*/
class ReportValue
{
protected DBObject $oObject;
protected string $sAttCode;
protected bool $bOriginal;
/**
* @param DBObject $oObject
* @param string $sAttCode
* @param bool $bOriginal
*/
public function __construct(DBObject $oObject, string $sAttCode, bool $bOriginal)
{
$this->oObject = $oObject;
$this->sAttCode = $sAttCode;
$this->bOriginal = $bOriginal;
}
public function GetAsHTML(bool $bLocalizedValues)
{
if ($this->bOriginal) {
return $this->oObject->GetOriginalAsHTML($this->sAttCode, $bLocalizedValues);
}
return $this->oObject->GetAsHTML($this->sAttCode, $bLocalizedValues);
}
public function GetAsCSV (bool $bLocalizedValues, string $sCsvSep, string $sCsvDelimiter) {
if ($this->bOriginal) {
return $this->oObject->GetOriginalAsCSV($this->sAttCode, $sCsvSep, $sCsvDelimiter, $bLocalizedValues);
}
return $this->oObject->GetAsCSV($this->sAttCode, $sCsvSep, $sCsvDelimiter, $bLocalizedValues);
}
}
class CellStatus_Ambiguous extends CellStatus_Issue
{
@@ -235,7 +345,7 @@ class CellStatus_Ambiguous extends CellStatus_Issue
public function GetSearchLinkUrl()
{
return sprintf("UI.php?operation=search&filter=%s",
rawurlencode($this->sSerializedSearch)
rawurlencode($this->sSerializedSearch ?? "")
);
}
}
@@ -383,22 +493,6 @@ class BulkChange
$this->m_aExtKeysMappingCache = array();
}
protected $m_bReportHtml = false;
protected $m_sReportCsvSep = ',';
protected $m_sReportCsvDelimiter = '"';
public function SetReportHtml()
{
$this->m_bReportHtml = true;
}
public function SetReportCsv($sSeparator = ',', $sDelimiter = '"')
{
$this->m_bReportHtml = false;
$this->m_sReportCsvSep = $sSeparator;
$this->m_sReportCsvDelimiter = $sDelimiter;
}
protected function ResolveExternalKey($aRowData, $sAttCode, &$aResults)
{
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
@@ -416,7 +510,7 @@ class BulkChange
$value = $oForeignAtt->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
}
$oReconFilter->AddCondition($sReconKeyAttCode, $value, '=');
$aResults[$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
}
$oExtObjects = new CMDBObjectSet($oReconFilter);
@@ -440,7 +534,7 @@ class BulkChange
}
/**
* @param \DBObject $oTargetObj
* @param DBObject $oTargetObj
* @param array $aRowData
* @param array $aErrors
*
@@ -503,7 +597,7 @@ class BulkChange
}
$aCacheKeys[] = $value;
$oReconFilter->AddCondition($sReconKeyAttCode, $value, '=');
$aResults[$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
$aResults[$iCol] = new CellStatus_Void($aRowData[$iCol]);
}
$sCacheKey = implode('_|_', $aCacheKeys); // Unique key for this query...
$iForeignKey = null;
@@ -572,7 +666,7 @@ class BulkChange
foreach ($aReconKeys as $sReconKeyAttCode => $iCol)
{
// Report the change on reconciliation values as well
$aResults[$iCol] = new CellStatus_Modify(utils::HtmlEntities($aRowData[$iCol]));
$aResults[$iCol] = new CellStatus_Modify($aRowData[$iCol]);
}
}
}
@@ -650,50 +744,32 @@ class BulkChange
// Reporting on fields
//
$aChangedFields = $oTargetObj->ListChanges();
foreach ($this->m_aAttList as $sAttCode => $iCol)
{
if ($sAttCode == 'id')
{
$aResults[$iCol]= new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
foreach ($this->m_aAttList as $sAttCode => $iCol) {
if ($sAttCode == 'id') {
$aResults[$iCol]= new CellStatus_Void($aRowData[$iCol]);
}
else
{
if ($this->m_bReportHtml)
{
$sCurValue = $oTargetObj->GetAsHTML($sAttCode, $this->m_bLocalizedValues);
$sOrigValue = $oTargetObj->GetOriginalAsHTML($sAttCode, $this->m_bLocalizedValues);
else {
$sCurValue = new ReportValue($oTargetObj, $sAttCode, false);
$sOrigValue = new ReportValue($oTargetObj, $sAttCode, true);
if (isset($aErrors[$sAttCode])) {
$aResults[$iCol]= new CellStatus_Issue($aRowData[$iCol], $sOrigValue, $aErrors[$sAttCode]);
}
else
{
$sCurValue = $oTargetObj->GetAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter, $this->m_bLocalizedValues);
$sOrigValue = $oTargetObj->GetOriginalAsCSV($sAttCode, $this->m_sReportCsvSep, $this->m_sReportCsvDelimiter, $this->m_bLocalizedValues);
}
if (isset($aErrors[$sAttCode]))
{
$aResults[$iCol]= new CellStatus_Issue(utils::HtmlEntities($aRowData[$iCol]), $sOrigValue, $aErrors[$sAttCode]);
}
elseif (array_key_exists($sAttCode, $aChangedFields))
{
if ($oTargetObj->IsNew())
{
elseif (array_key_exists($sAttCode, $aChangedFields)){
if ($oTargetObj->IsNew()) {
$aResults[$iCol]= new CellStatus_Void($sCurValue);
}
else
{
else {
$aResults[$iCol]= new CellStatus_Modify($sCurValue, $sOrigValue);
}
}
else
{
else {
// By default... nothing happens
$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
if ($oAttDef instanceof AttributeDateTime)
{
if ($oAttDef instanceof AttributeDateTime) {
$aResults[$iCol]= new CellStatus_Void($oAttDef->GetFormat()->Format($aRowData[$iCol]));
}
else
{
$aResults[$iCol]= new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
else {
$aResults[$iCol]= new CellStatus_Void($aRowData[$iCol]);
}
}
}
@@ -745,6 +821,7 @@ class BulkChange
$oDbSearchWithoutAnyCondition->AllowAllData(false);
$oExtObjectSetWithCurrentUserPermissions = new CMDBObjectSet($oDbSearchWithoutAnyCondition);
$iCurrentUserRightsObjectCount = $oExtObjectSetWithCurrentUserPermissions->Count();
$sAllowedValuesOql = $oDbSearchWithoutAnyCondition->serialize();
if ($iCurrentUserRightsObjectCount === 0){
// No objects visible by current user
@@ -757,7 +834,7 @@ class BulkChange
// Possibles values are displayed to UI user. we have to limit the amount of displayed values
$oExtObjectSetWithCurrentUserPermissions->SetLimit(4);
for($i = 0; $i < 3; $i++){
/** @var \DBObject $oVisibleObject */
/** @var DBObject $oVisibleObject */
$oVisibleObject = $oExtObjectSetWithCurrentUserPermissions->Fetch();
if (is_null($oVisibleObject)){
break;
@@ -785,7 +862,7 @@ class BulkChange
if ($iAllowAllDataObjectCount != $iCurrentUserRightsObjectCount) {
// No match and some objects NOT visible by current user. including current search maybe...
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch-SomeObjectNotVisibleForCurrentUser', $oDbSearchWithConditions->GetClass());
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues);
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues, $sAllowedValuesOql);
}
// No match. This is not linked to any right issue
@@ -796,7 +873,7 @@ class BulkChange
}
$value =implode(" ", $aCurrentValueFields);
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch', $value);
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues);
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues, $sAllowedValuesOql);
}
protected function PrepareMissingObject(&$oTargetObj, &$aErrors)
@@ -1124,7 +1201,7 @@ class BulkChange
if (!preg_match($sRegExp, $sValue))
{
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
$aResult[$iRow][$iCol] = new CellStatus_Issue(utils::HtmlEntities($sValue), null, $sErrorMsg);
$aResult[$iRow][$iCol] = new CellStatus_Issue($sValue, null, $sErrorMsg);
}
else
@@ -1137,6 +1214,7 @@ class BulkChange
}
else
{
// almost impossible ti reproduce since even incorrect dates with correct formats are formated and $oDate will not be false
// Leave the cell unchanged
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
$aResult[$iRow][$iCol] = new CellStatus_Issue($sValue, null, $sErrorMsg);
@@ -1276,7 +1354,7 @@ class BulkChange
{
if (!array_key_exists($iCol, $aResult[$iRow]))
{
$aResult[$iRow][$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
$aResult[$iRow][$iCol] = new CellStatus_Void($aRowData[$iCol]);
}
}
foreach($this->m_aExtKeys as $sAttCode => $aForeignAtts)
@@ -1290,7 +1368,7 @@ class BulkChange
if (!array_key_exists($iCol, $aResult[$iRow]))
{
// The foreign attribute is one of our reconciliation key
$aResult[$iRow][$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
$aResult[$iRow][$iCol] = new CellStatus_Void($aRowData[$iCol]);
}
}
}

View File

@@ -149,7 +149,9 @@ abstract class BulkExport
$this->oSearch = null;
$this->iChunkSize = 0;
$this->sFormatCode = null;
$this->aStatusInfo = array();
$this->aStatusInfo = [
'show_obsolete_data' => utils::ShowObsoleteData(),
];
$this->oBulkExportResult = null;
$this->sTmpFile = '';
$this->bLocalizeOutput = false;
@@ -203,15 +205,17 @@ abstract class BulkExport
if ($oInfo && ($oInfo->Get('user_id') == UserRights::GetUserId()))
{
$sFormatCode = $oInfo->Get('format');
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
$aStatusInfo = json_decode($oInfo->Get('status_info'),true);
$oSearch = DBObjectSearch::unserialize($oInfo->Get('search'));
$oSearch->SetShowObsoleteData($aStatusInfo['show_obsolete_data']);
$oBulkExporter = self::FindExporter($sFormatCode, $oSearch);
if ($oBulkExporter)
{
$oBulkExporter->SetFormat($sFormatCode);
$oBulkExporter->SetObjectList($oSearch);
$oBulkExporter->SetChunkSize($oInfo->Get('chunk_size'));
$oBulkExporter->SetStatusInfo(json_decode($oInfo->Get('status_info'), true));
$oBulkExporter->SetStatusInfo($aStatusInfo);
$oBulkExporter->SetLocalizeOutput($oInfo->Get('localize_output'));
@@ -289,6 +293,7 @@ abstract class BulkExport
*/
public function SetObjectList(DBSearch $oSearch)
{
$oSearch->SetShowObsoleteData($this->aStatusInfo['show_obsolete_data']);
$this->oSearch = $oSearch;
}

View File

@@ -19,17 +19,17 @@ class CMDBChange extends DBObject
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "date",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_change",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "date",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_change",
"db_key_field" => "id",
"db_finalclass_field" => "",
'indexes' => array(
'indexes' => array(
array('origin'),
)
),
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();

View File

@@ -52,17 +52,17 @@ class CMDBChangeOp extends DBObject implements iCMDBChangeOp
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "autoincrement",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "autoincrement",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop",
"db_key_field" => "id",
"db_finalclass_field" => "optype",
'indexes' => array(
'indexes' => array(
array('objclass', 'objkey'),
)
),
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
@@ -121,13 +121,13 @@ class CMDBChangeOpCreate extends CMDBChangeOp
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_create",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_create",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -157,13 +157,13 @@ class CMDBChangeOpDelete extends CMDBChangeOp
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_delete",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_delete",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -198,13 +198,13 @@ class CMDBChangeOpSetAttribute extends CMDBChangeOp
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -231,13 +231,13 @@ class CMDBChangeOpSetAttributeScalar extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_scalar",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_scalar",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -290,14 +290,14 @@ class CMDBChangeOpSetAttributeTagSet extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_tagset",
"db_key_field" => "id",
"db_finalclass_field" => "",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_tagset",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
@@ -350,7 +350,7 @@ class CMDBChangeOpSetAttributeURL extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
@@ -419,13 +419,13 @@ class CMDBChangeOpSetAttributeBlob extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_data",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_data",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -498,13 +498,13 @@ class CMDBChangeOpSetAttributeOneWayPassword extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_pwd",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_pwd",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -561,13 +561,13 @@ class CMDBChangeOpSetAttributeEncrypted extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_encrypted",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_encrypted",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -627,13 +627,13 @@ class CMDBChangeOpSetAttributeText extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_text",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_text",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -695,13 +695,13 @@ class CMDBChangeOpSetAttributeLongText extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_longtext",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_longtext",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -760,13 +760,13 @@ class CMDBChangeOpSetAttributeHTML extends CMDBChangeOpSetAttributeLongText
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_html",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_html",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -824,13 +824,13 @@ class CMDBChangeOpSetAttributeCaseLog extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_log",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_log",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -903,13 +903,13 @@ class CMDBChangeOpPlugin extends CMDBChangeOp
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_plugin",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_plugin",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -944,13 +944,13 @@ abstract class CMDBChangeOpSetAttributeLinks extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -977,13 +977,13 @@ class CMDBChangeOpSetAttributeLinksAddRemove extends CMDBChangeOpSetAttributeLin
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links_addremove",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links_addremove",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -1044,13 +1044,13 @@ class CMDBChangeOpSetAttributeLinksTune extends CMDBChangeOpSetAttributeLinks
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links_tune",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_links_tune",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
@@ -1134,13 +1134,13 @@ class CMDBChangeOpSetAttributeCustomFields extends CMDBChangeOpSetAttribute
{
$aParams = array
(
"category" => "core/cmdb",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_custfields",
"db_key_field" => "id",
"category" => "core/cmdb, grant_by_profile",
"key_type" => "",
"name_attcode" => "change",
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "priv_changeop_setatt_custfields",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);

View File

@@ -335,7 +335,7 @@ abstract class CMDBObject extends DBObject
$oMyChangeOp->Set("objclass", MetaModel::GetRootClass(get_class($this)));
$oMyChangeOp->Set("objkey", $objkey);
$oMyChangeOp->Set("fclass", get_class($this));
$oMyChangeOp->Set("fname", substr($this->GetRawName(), 0, 255)); // Protect against very long friendly names
$oMyChangeOp->SetTrim("fname", $this->GetRawName()); // Protect against very long friendly names
$iId = $oMyChangeOp->DBInsertNoReload();
}
@@ -438,7 +438,8 @@ abstract class CMDBObject extends DBObject
}
/**
* @deprecated 3.1.0 N°5232 not used
* @deprecated 3.1.0 N°5232 N°6966 simply use {@see DBObject::DBClone()} instead, that will automatically create and persist a CMDBChange object.
* If you need to persist your own, call {@see CMDBObject::SetCurrentChange()} before.
*/
public function DBCloneTracked(CMDBChange $oChange, $newKey = null)
{
@@ -446,6 +447,9 @@ abstract class CMDBObject extends DBObject
$this->DBCloneTracked_Internal($newKey);
}
/**
* @deprecated 3.1.1 3.2.0 N°6966 We will have only one DBClone method in the future
*/
protected function DBCloneTracked_Internal($newKey = null)
{
$newKey = parent::DBClone($newKey);
@@ -469,21 +473,14 @@ abstract class CMDBObject extends DBObject
*/
public function DBDelete(&$oDeletionPlan = null)
{
return $this->DBDeleteTracked_Internal($oDeletionPlan);
$this->LogCRUDEnter(__METHOD__);
$oDeletionPlan = parent::DBDelete($oDeletionPlan);
$this->LogCRUDExit(__METHOD__);
return $oDeletionPlan;
}
/**
* @param null $oDeletionPlan
*
* @return \DeletionPlan|null
* @throws \ArchivedObjectException
* @throws \CoreCannotSaveObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DeleteException
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @deprecated 3.1.1 3.2.0 N°6967 We will have only one DBDelete method in the future
*/
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
{
@@ -519,15 +516,6 @@ abstract class CMDBObject extends DBObject
utils::PopArchiveMode();
}
}
public function DBUpdate()
{
if (count($this->ListChanges()) === 0) {
$this->InitPreviousValuesForUpdatedAttributes();
return $this->GetKey();
}
return parent::DBUpdate(); // TODO: Change the autogenerated stub
}
}

View File

@@ -3,7 +3,7 @@
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -42,6 +42,12 @@ class CMDBSource
const ENUM_DB_VENDOR_MARIADB = 'MariaDB';
const ENUM_DB_VENDOR_PERCONA = 'Percona';
/**
* @since 2.7.10 3.0.4 3.1.2 3.0.2 N°6889 constant creation
* @internal will be removed in a future version
*/
const MYSQL_DEFAULT_PORT = 3306;
/**
* Error: 1205 SQLSTATE: HY000 (ER_LOCK_WAIT_TIMEOUT)
* Message: Lock wait timeout exceeded; try restarting transaction
@@ -212,16 +218,19 @@ class CMDBSource
/**
* @param string $sDbHost initial value ("p:domain:port" syntax)
* @param string $sServer server variable to update
* @param int $iPort port variable to update
* @param int|null $iPort port variable to update, will return null if nothing is specified in $sDbHost
*
* @since 2.7.10 3.0.4 3.1.2 3.2.0 N°6889 will return null in $iPort if port isn't present in $sDbHost. Use {@see MYSQL_DEFAULT_PORT} if needed
*
* @link http://php.net/manual/en/mysqli.persistconns.php documentation for the "p:" prefix (persistent connexion)
*/
public static function InitServerAndPort($sDbHost, &$sServer, &$iPort)
{
$aConnectInfo = explode(':', $sDbHost);
$bUsePersistentConnection = false;
if (strcasecmp($aConnectInfo[0], 'p') == 0)
if (strcasecmp($aConnectInfo[0], 'p') === 0)
{
// we might have "p:" prefix to use persistent connections (see http://php.net/manual/en/mysqli.persistconns.php)
$bUsePersistentConnection = true;
$sServer = $aConnectInfo[0].':'.$aConnectInfo[1];
}
@@ -239,10 +248,6 @@ class CMDBSource
{
$iPort = (int)($aConnectInfo[1]);
}
else
{
$iPort = 3306;
}
}
/**
@@ -380,7 +385,7 @@ class CMDBSource
public static function GetDBVendor()
{
$sDBVendor = static::ENUM_DB_VENDOR_MYSQL;
$sVersionComment = static::GetServerVariable('version') . ' - ' . static::GetServerVariable('version_comment');
if(preg_match('/mariadb/i', $sVersionComment) === 1)
{
@@ -390,7 +395,7 @@ class CMDBSource
{
$sDBVendor = static::ENUM_DB_VENDOR_PERCONA;
}
return $sDBVendor;
}
@@ -431,6 +436,7 @@ class CMDBSource
{
self::$m_sDBName = '';
}
self::_TablesInfoCacheReset(); // reset the table info cache!
}
public static function CreateTable($sQuery)
@@ -543,10 +549,9 @@ class CMDBSource
/**
* @param string $sSQLQuery
*
* @return \mysqli_result|null
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \CoreException
* @return mysqli_result|null
* @throws MySQLException
* @throws MySQLHasGoneAwayException
*
* @since 2.7.0 N°679 handles nested transactions
*/
@@ -607,8 +612,9 @@ class CMDBSource
{
self::LogDeadLock($e, true);
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
}
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
} finally {
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
}
if ($oResult === false) {
$aContext = array('query' => $sSql);
@@ -626,18 +632,24 @@ class CMDBSource
}
/**
* @param \Exception $e
* @param Exception $e
* @param bool $bForQuery to get the proper DB connection
* @param bool $bCheckMysqliErrno if false won't try to check for mysqli::errno value
*
* @since 2.7.1
* @since 3.0.0 N°4325 add new optional parameter to use the correct DB connection
* @since 3.0.4 3.1.1 3.2.0 N°6643 new bCheckMysqliErrno parameter as a workaround for mysqli::errno cannot be mocked
*/
private static function LogDeadLock(Exception $e, $bForQuery = false)
private static function LogDeadLock(Exception $e, $bForQuery = false, $bCheckMysqliErrno = true)
{
// checks MySQL error code
$iMySqlErrorNo = DbConnectionWrapper::GetDbConnection($bForQuery)->errno;
if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK))) {
return;
if ($bCheckMysqliErrno) {
$iMySqlErrorNo = DbConnectionWrapper::GetDbConnection($bForQuery)->errno;
if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK))) {
return;
}
} else {
$iMySqlErrorNo = "N/A";
}
// Get error info
@@ -664,7 +676,10 @@ class CMDBSource
);
DeadLockLog::Info($sMessage, $iMySqlErrorNo, $aLogContext);
IssueLog::Error($sMessage, LogChannels::DEADLOCK, $e->getMessage());
IssueLog::Error($sMessage, LogChannels::DEADLOCK, [
'exception.class' => get_class($e),
'exception.message' => $e->getMessage(),
]);
}
/**
@@ -923,7 +938,7 @@ class CMDBSource
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
}
while ($aRow = $oResult->fetch_array($iMode))
{
$aData[] = $aRow;
@@ -1077,7 +1092,7 @@ class CMDBSource
if (!array_key_exists($iKey, $aTableInfo["Fields"])) return false;
$aFieldData = $aTableInfo["Fields"][$iKey];
if (!array_key_exists("Key", $aFieldData)) return false;
return ($aFieldData["Key"] == "PRI");
return ($aFieldData["Key"] == "PRI");
}
public static function IsAutoIncrement($sTable, $sField)
@@ -1088,7 +1103,7 @@ class CMDBSource
$aFieldData = $aTableInfo["Fields"][$sField];
if (!array_key_exists("Extra", $aFieldData)) return false;
//MyHelpers::debug_breakpoint($aFieldData);
return (strstr($aFieldData["Extra"], "auto_increment"));
return (strstr($aFieldData["Extra"], "auto_increment"));
}
public static function IsField($sTable, $sField)
@@ -1154,8 +1169,8 @@ class CMDBSource
*/
public static function IsSameFieldTypes($sItopGeneratedFieldType, $sDbFieldType)
{
list($sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
list($sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sDbFieldType);
[$sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
[$sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sDbFieldType);
if (strcasecmp($sItopFieldDataType, $sDbFieldDataType) !== 0)
{
@@ -1355,13 +1370,13 @@ class CMDBSource
public static function GetTableFieldsList($sTable)
{
assert(!empty($sTable));
$aTableInfo = self::GetTableInfo($sTable);
if (empty($aTableInfo)) return array(); // #@# or an error ?
return array_keys($aTableInfo["Fields"]);
}
// Cache the information about existing tables, and their fields
private static $m_aTablesInfo = array();
private static function _TablesInfoCacheReset($sTableName = null)
@@ -1494,7 +1509,7 @@ class CMDBSource
{
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
}
$aRows = array();
while ($aRow = $oResult->fetch_array(MYSQLI_ASSOC))
{
@@ -1503,7 +1518,7 @@ class CMDBSource
$oResult->free();
return $aRows;
}
/**
* Returns the value of the specified server variable
* @param string $sVarName Name of the server variable
@@ -1519,7 +1534,7 @@ class CMDBSource
/**
* Returns the privileges of the current user
* @return string privileges in a raw format
*/
*/
public static function GetRawPrivileges()
{
try
@@ -1545,8 +1560,8 @@ class CMDBSource
/**
* Determine the slave status of the server
* @return bool true if the server is slave
*/
* @return bool true if the server is slave
*/
public static function IsSlaveServer()
{
try
@@ -1588,7 +1603,19 @@ class CMDBSource
return false;
}
/**
public static function GetClusterNb()
{
$result = 0;
$sSql = "SHOW STATUS LIKE 'wsrep_cluster_size';";
$aRows = self::QueryToArray($sSql);
if (count($aRows) > 0)
{
$result = $aRows[0]['Value'];
}
return intval($result);
}
/**
* @see https://dev.mysql.com/doc/refman/5.7/en/charset-database.html
* @return string query to upgrade database charset and collation if needed, null if not
* @throws \MySQLException

View File

@@ -29,7 +29,7 @@ define('ITOP_APPLICATION_SHORT', 'iTop');
*
* @see ITOP_CORE_VERSION to get iTop core version
*/
define('ITOP_VERSION', '3.1.0-dev');
define('ITOP_VERSION', '3.1.1-dev');
define('ITOP_VERSION_NAME', 'Fullmoon');
define('ITOP_REVISION', 'svn');
@@ -137,7 +137,7 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'log_purge.max_keep_days' => [
'log_purge.max_keep_days' => [
'type' => 'integer',
'description' => 'Optional purge number of days to keep logs.',
'default' => 365,
@@ -145,7 +145,7 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'event_service.debug.filter_events' => [
'event_service.debug.filter_events' => [
'type' => 'array',
'description' => 'List of events name to filter Event Service debug messages',
'default' => [],
@@ -153,7 +153,7 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'event_service.debug.filter_sources' => [
'event_service.debug.filter_sources' => [
'type' => 'array',
'description' => 'List of event sources to filter Event Service debug messages',
'default' => '',
@@ -161,6 +161,38 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'temporary_object.force_creation' => [
'type' => 'bool',
'description' => 'If true, all the objects created by the external key are temporary',
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'temporary_object.lifetime' => [
'type' => 'integer',
'description' => 'Seconds for temporary objects created',
'default' => 300,
'value' => 300,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'temporary_object.watchdog_interval' => [
'type' => 'integer',
'description' => 'Seconds between watchdog signals',
'default' => 60,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'temporary_object.garbage_interval' => [
'type' => 'integer',
'description' => 'Seconds between garbage collections',
'default' => 60,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'app_env_label' => [
'type' => 'string',
'description' => 'Label displayed to describe the current application environment, defaults to the environment name (e.g. "production")',
@@ -185,7 +217,7 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'db_host' => [
'db_host' => [
'type' => 'string',
'default' => null,
'value' => '',
@@ -624,22 +656,22 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'email_transport_smtp.allow_self_signed' => array(
'email_transport_smtp.allow_self_signed' => [
'type' => 'bool',
'description' => 'Allow self signed peer certificates',
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'email_transport_smtp.verify_peer' => array(
],
'email_transport_smtp.verify_peer' => [
'type' => 'bool',
'description' => 'Verify peer certificate',
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
),
],
'email_css' => [
'type' => 'string',
'description' => 'CSS that will override the standard stylesheet used for the notifications',
@@ -946,6 +978,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'lifecycle.transitions_sort_type' => [
'type' => 'string',
'description' => 'How transitions will be sorted in the GUI. Possible values are "xml", "alphabetical", "fixed" or "relative"',
'default' => DBObject::DEFAULT_TRANSITIONS_SORT_TYPE,
'value' => DBObject::DEFAULT_TRANSITIONS_SORT_TYPE,
'source_of_value' => '',
'show_in_conf_sample' => true,
],
'url_validation_pattern' => [
'type' => 'string',
'description' => 'Regular expression to validate/detect the format of an URL (URL attributes and Wiki formatting for Text attributes)',
@@ -1029,6 +1069,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'log_kpi_generate_legacy_report' => [
'type' => 'bool',
'description' => 'Generate the legacy KPI report (kpi.html)',
'default' => true,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'max_linkset_output' => [
'type' => 'integer',
'description' => 'Maximum number of items shown when getting a list of related items in an email, using the form $this->some_list$. 0 means no limit.',
@@ -1145,6 +1193,30 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'sessions_tracking.enabled' => [
'type' => 'bool',
'description' => 'Whether or not the whole mechanism to track active sessions is enabled. See PHP session.gc_maxlifetime setting to configure session expiration.',
'default' => false,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'sessions_tracking.gc_threshold' => [
'type' => 'integer',
'description' => 'fallback in case cron is not active: probability in percent that session files are cleanup during any itop request (100 means always)',
'default' => 1,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'sessions_tracking.gc_duration_in_seconds' => [
'type' => 'integer',
'description' => 'fallback in case cron is not active: when a cleanup is triggered cleanup duration will not exceed this duration (in seconds).',
'default' => 1,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'transaction_storage' => [
'type' => 'string',
'description' => 'The type of mechanism to use for storing the unique identifiers for transactions (Session|File).',
@@ -1571,6 +1643,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'security.enable_header_xcontent_type_options' => [
'type' => 'bool',
'description' => 'If set to false, iTop will stop sending the X-Content-Type-Options HTTP header. This header could trigger CORB protection on certain resources (JSON, XML, HTML, text) therefore blocking them.',
'default' => true,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'security.disable_inline_documents_sandbox' => [
'type' => 'bool',
'description' => 'If true then the sandbox for documents displayed in a browser tab will be disabled; enabling scripts and other interactive content. Note that setting this to true will open the application to potential XSS attacks!',
@@ -1630,8 +1710,8 @@ class Config
'audit.enable_selection_landing_page' => [
'type' => 'bool',
'description' => 'If true audit categories must be selected before results are computed (use this setting in case of a lot of audit categories)',
'default' => false,
'value' => false,
'default' => true,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
@@ -2241,6 +2321,24 @@ class Config
$this->m_sAllowedLoginTypes = implode('|', $aAllowedLoginTypes);
}
/**
* @since 2.7.11 N°7085
* Add login mode if not configured already
* @param string $sLoginMode
*
* @return void
*/
public function AddAllowedLoginTypes($sLoginMode)
{
$aAllowedLoginTypes = $this->GetAllowedLoginTypes();
if (in_array($sLoginMode, $aAllowedLoginTypes)){
return;
}
$aAllowedLoginTypes[] = $sLoginMode;
$this->SetAllowedLoginTypes($aAllowedLoginTypes);
}
public function SetExternalAuthenticationVariable($sExtAuthVariable)
{
$this->m_sExtAuthVariable = $sExtAuthVariable;

View File

@@ -3,7 +3,7 @@
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -58,6 +58,16 @@ class ContextTag
public const TAG_SETUP = 'Setup';
public const TAG_SYNCHRO = 'Synchro';
public const TAG_REST = 'REST/JSON';
/**
* @since 3.1.0 N°6047
*/
public const TAG_IMPORT = 'Import';
/**
* @since 3.1.0 N°6047
*/
public const TAG_EXPORT = 'Export';
/**
* @var string
* @since 3.1.0 N°3200
@@ -75,7 +85,7 @@ class ContextTag
{
static::$aStack[] = $sTag;
}
public static function AddContext($sTag)
{
static::$aStack[] = $sTag;

View File

@@ -1,8 +1,11 @@
<?php
/**
* This file is only here for compatibility issues. Will be removed in iTop 3.1.0 (N°3664)
* This file is only here for compatibility reasons.
* It will be removed in future iTop versions (N°6533)
*
* @deprecated 3.0.0 N°3663 Exception classes were moved to `/application/exceptions`, use autoloader instead of require !
*/
require_once '../approot.inc.php';
DeprecatedCallsLog::NotifyDeprecatedFile('Classes were moved to /application/exceptions');
require_once __DIR__ . '/../approot.inc.php';
DeprecatedCallsLog::NotifyDeprecatedFile('Classes were moved to /application/exceptions and can be used directly with the autoloader');

View File

@@ -188,8 +188,8 @@ final class ItopCounter
if (!$hDBLink)
{
throw new Exception("Could not connect to the DB server (host=$sDBHost, user=$sDBUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
}
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
}
return $hDBLink;
}

View File

@@ -12,6 +12,7 @@ use Combodo\iTop\Application\UI\Base\Component\Input\SelectUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\ColumnUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\MultiColumnUIBlockFactory;
use Combodo\iTop\Application\Helper\ExportHelper;
/**
* Bulk export: CSV export
@@ -114,6 +115,7 @@ class CSVBulkExport extends TabularBulkExport
case 'csv_options':
$oPanel = PanelUIBlockFactory::MakeNeutral(Dict::S('Core:BulkExport:CSVOptions'));
$oPanel->AddSubBlock(ExportHelper::GetAlertForExcelMaliciousInjection());
$oMulticolumn = MultiColumnUIBlockFactory::MakeStandard();
$oPanel->AddSubBlock($oMulticolumn);
@@ -137,7 +139,7 @@ class CSVBulkExport extends TabularBulkExport
$aSep['other'] = Dict::S('UI:CSVImport:SeparatorOther').' <input type="text" size="3" name="other-separator" value="'.utils::EscapeHtml($sOtherSeparator).'"/>';
foreach ($aSep as $sVal => $sLabel) {
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "separator", utils::EscapeHtml($sVal), $sLabel, "radio");
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "separator", $sVal, $sLabel, "radio");
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawSeparator));
$oRadio->SetBeforeInput(false);
$oRadio->GetInput()->AddCSSClass('ibo-input--label-right');
@@ -163,8 +165,8 @@ class CSVBulkExport extends TabularBulkExport
$aQualifiers['other'] = Dict::S('UI:CSVImport:QualifierOther').' <input type="text" size="3" name="other-text-qualifier" value="'.utils::EscapeHtml($sOtherQualifier).'"/>';
foreach ($aQualifiers as $sVal => $sLabel) {
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "text-qualifier", utils::EscapeHtml($sVal), $sLabel, "radio");
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawSeparator));
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "text-qualifier", $sVal, $sLabel, "radio");
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawQualifier));
$oRadio->SetBeforeInput(false);
$oRadio->GetInput()->AddCSSClass('ibo-input--label-right');
$oRadio->GetInput()->AddCSSClass('ibo-input-checkbox');

View File

@@ -47,20 +47,47 @@ abstract class CustomFieldsHandler {
*
* @param $sAttCode
*/
final public function __construct($sAttCode)
{
final public function __construct($sAttCode) {
$this->sAttCode = $sAttCode;
$this->aValues = null;
}
abstract public function BuildForm(DBObject $oHostObject, $sFormId);
/**
* @returns true|string true if no error found, error message otherwise
* @throws \ApplicationException if {@link static::$oForm} attribute not initialized yet
* @since 3.1.0 N°6322 N°1150 Add template_id checks
*/
public function Validate(DBObject $oHostObject) {
if (false === isset($this->oForm)) {
throw new ApplicationException('oForm attribute not init yet. You must call BuildForm before this method !');
}
try {
$this->oForm->Validate();
if ($this->oForm->GetValid()) {
$ret = true;
}
else {
$aMessages = array();
foreach ($this->oForm->GetErrorMessages() as $sFieldId => $aFieldMessages) {
$aMessages[] = $sFieldId.': '.implode(', ', $aFieldMessages);
}
$ret = 'Invalid value: '.implode(', ', $aMessages);
}
} catch (Exception $e) {
$ret = $e->getMessage();
}
return $ret;
}
/**
*
* @return \Combodo\iTop\Form\Form
*/
public function GetForm()
{
public function GetForm() {
return $this->oForm;
}
@@ -69,16 +96,14 @@ abstract class CustomFieldsHandler {
$this->aValues = $aValues;
}
static public function GetPrerequisiteAttributes($sClass = null)
{
public static function GetPrerequisiteAttributes($sClass = null) {
return array();
}
/**
* List the available verbs for 'GetForTemplate'
*/
static public function EnumTemplateVerbs()
{
public static function EnumTemplateVerbs() {
return array();
}

View File

@@ -58,6 +58,7 @@
<parent>cmdbAbstractObject</parent>
<properties>
<category>addon/userrights,grant_by_profile</category>
<is_link>1</is_link>
</properties>
<fields>
<field id="userid" xsi:type="AttributeExternalKey">
@@ -218,6 +219,19 @@
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
</fields>
</class>
<class id="AuditDomain" _delta="define">
<parent>cmdbAbstractObject</parent>
<properties>
<category>application, grant_by_profile</category>
</properties>
<fields>
<field id="name" xsi:type="AttributeString"/>
<field id="description" xsi:type="AttributeString"/>
<field id="icon" xsi:type="AttributeImage"/>
<field id="categories_list" xsi:type="AttributeLinkedSet"/>
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
</fields>
</class>
<class id="Query" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php -->
<parent>cmdbAbstractObject</parent>
@@ -482,6 +496,24 @@
<type>boolean</type>
<default>true</default>
</property>
<property id="with_php_constraint">
<php_param>with_php_constraint</php_param>
<mandatory>false</mandatory>
<type>boolean</type>
<default>false</default>
</property>
<property id="with_php_computation">
<php_param>with_php_computation</php_param>
<mandatory>false</mandatory>
<type>boolean</type>
<default>false</default>
</property>
<property id="create_temporary_object">
<php_param>create_temporary_object</php_param>
<mandatory>false</mandatory>
<type>boolean</type>
<default>false</default>
</property>
<property id="on_target_delete">
<php_param>on_target_delete</php_param>
<mandatory>false</mandatory>

File diff suppressed because it is too large Load Diff

View File

@@ -767,7 +767,10 @@ class DBObjectSet implements iDBObjectSetIterator
try
{
$oKPI = new ExecutionKPI();
$this->m_oSQLResult = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, $this->GetRealSortOrder(), $this->m_iLimitCount, $this->m_iLimitStart, false);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
} catch (MySQLException $e)
{
// 1116 = ER_TOO_MANY_TABLES
@@ -847,8 +850,11 @@ class DBObjectSet implements iDBObjectSetIterator
{
if (is_null($this->m_iNumTotalDBRows))
{
$oKPI = new ExecutionKPI();
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true);
$resQuery = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), 0, 0, true);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if (!$resQuery) return 0;
$aRow = CMDBSource::FetchArray($resQuery);
@@ -859,6 +865,42 @@ class DBObjectSet implements iDBObjectSetIterator
return $this->m_iNumTotalDBRows + count($this->m_aAddedObjects); // Does it fix Trac #887 ??
}
/**
* @param \DBSearch $oFilter
* @param array $aOrder
* @param int $iLimitCount
* @param int $iLimitStart
* @param bool $bCount
*
* @return string
*/
private function GetPseudoOQL($oFilter, $aOrder, $iLimitCount, $iLimitStart, $bCount)
{
$sOQL = '';
if ($bCount) {
$sOQL .= 'COUNT ';
}
$sOQL .= $oFilter->ToOQL();
if ($iLimitCount > 0) {
$sOQL .= ' LIMIT ';
if ($iLimitStart > 0) {
$sOQL .= "$iLimitStart, ";
}
$sOQL .= "$iLimitCount";
}
if (count($aOrder) > 0) {
$sOQL .= ' ORDER BY ';
$aOrderBy = [];
foreach ($aOrder as $sAttCode => $bAsc) {
$aOrderBy[] = $sAttCode.' '.($bAsc ? 'ASC' : 'DESC');
}
$sOQL .= implode(', ', $aOrderBy);
}
return $sOQL;
}
/**
* Check if the count exceeds a given limit
*
@@ -875,8 +917,11 @@ class DBObjectSet implements iDBObjectSetIterator
{
if (is_null($this->m_iNumTotalDBRows))
{
$oKPI = new ExecutionKPI();
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
$resQuery = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if ($resQuery)
{
$aRow = CMDBSource::FetchArray($resQuery);
@@ -887,7 +932,7 @@ class DBObjectSet implements iDBObjectSetIterator
{
$iCount = 0;
}
}
}
else
{
$iCount = $this->m_iNumTotalDBRows;
@@ -912,8 +957,11 @@ class DBObjectSet implements iDBObjectSetIterator
{
if (is_null($this->m_iNumTotalDBRows))
{
$oKPI = new ExecutionKPI();
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
$resQuery = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if ($resQuery)
{
$aRow = CMDBSource::FetchArray($resQuery);
@@ -924,7 +972,7 @@ class DBObjectSet implements iDBObjectSetIterator
{
$iCount = 0;
}
}
}
else
{
$iCount = $this->m_iNumTotalDBRows;

View File

@@ -851,11 +851,11 @@ abstract class DBSearch
return;
}
if (count($aColumns) == 0)
{
$aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass()));
// Add the standard id (as first column)
array_unshift($aColumns, 'id');
if (count($aColumns) == 0)
{
$aColumns = array_keys(MetaModel::ListAttributeDefs($this->GetClass()));
// Add the standard id (as first column)
array_unshift($aColumns, 'id');
}
$aQueryCols = CMDBSource::GetColumns($resQuery, $sSQL);
@@ -885,6 +885,55 @@ abstract class DBSearch
return $aRes;
}
/**
* Selects a column ($sAttCode) from the specified class ($sClassAlias - default main class) of the DBsearch object and gives the result as an array
* @param string $sAttCode
* @param string|null $sClassAlias
*
* @return array
* @throws ConfigException
* @throws CoreException
* @throws MissingQueryArgument
* @throws MySQLException
* @throws MySQLHasGoneAwayException
*/
public function SelectAttributeToArray(string $sAttCode, ?string $sClassAlias = null):array
{
if(is_null($sClassAlias)) {
$sClassAlias = $this->GetClassAlias();
}
$sClass = $this->GetClass();
if($sAttCode === 'id'){
$aAttToLoad[$sClassAlias]=[];
} else {
$aAttToLoad[$sClassAlias][$sAttCode] = MetaModel::GetAttributeDef($sClass, $sAttCode);
}
$sSQL = $this->MakeSelectQuery([], [], $aAttToLoad);
$resQuery = CMDBSource::Query($sSQL);
if (!$resQuery)
{
return [];
}
$sColName = $sClassAlias.$sAttCode;
$aRes = [];
while ($aRow = CMDBSource::FetchArray($resQuery))
{
$aMappedRow = array();
if($sAttCode === 'id') {
$aMappedRow[$sAttCode] = $aRow[$sColName];
} else {
$aMappedRow[$sAttCode] = $aAttToLoad[$sClassAlias][$sAttCode]->FromSQLToValue($aRow, $sColName);
}
$aRes[] = $aMappedRow;
}
CMDBSource::FreeResult($resQuery);
return $aRes;
}
////////////////////////////////////////////////////////////////////////////
//
// Construction of the SQL queries

View File

@@ -224,6 +224,10 @@ class DBUnionSearch extends DBSearch
}
/**
* Set the selected classes for this query.
* The selected classes can be either in the selected classes of all the queries,
* or they should exist in all the sub-queries of the union.
*
* @param array $aSelectedClasses array of aliases
*
* @throws \Exception
@@ -236,22 +240,31 @@ class DBUnionSearch extends DBSearch
$aAliasesToColumn = array_flip(array_keys($oFirstSearch->GetSelectedClasses()));
foreach ($aSelectedClasses as $sSelectedAlias) {
if (!isset($aAliasesToColumn[$sSelectedAlias])) {
throw new CoreException("SetSelectedClasses: Invalid class alias $sSelectedAlias");
// The selected class is not in the selected classes of the union,
// try to delegate the feature to the sub-queries
$aSelectedColumns = [];
break;
}
$aSelectedColumns[] = $aAliasesToColumn[$sSelectedAlias];
}
// 1 - change for each search
foreach ($this->aSearches as $iPos => $oSearch)
{
foreach ($this->aSearches as $iPos => $oSearch) {
$aCurrentSelectedAliases = [];
foreach ($aSelectedColumns as $iColumn) {
$aCurrentSelectedAliases[] = $this->aColumnToAliases[$iColumn][$iPos];
if (count($aSelectedColumns) === 0) {
// Default to the list of aliases given
$aCurrentSelectedAliases = $aSelectedClasses;
} else {
// Map the aliases for each query
foreach ($aSelectedColumns as $iColumn) {
$aCurrentSelectedAliases[] = $this->aColumnToAliases[$iColumn][$iPos];
}
}
// Throws an exception if not valid
$oSearch->SetSelectedClasses($aCurrentSelectedAliases);
}
// 2 - update the lowest common ancestors
$this->ComputeSelectedClasses();
}

View File

@@ -28,8 +28,13 @@ namespace Combodo\iTop;
use DOMDocument;
use DOMFormatException;
use DOMNode;
use DOMNodeList;
use DOMXPath;
use Exception;
use IssueLog;
use LogAPI;
use MFElement;
use utils;
/**
@@ -41,6 +46,11 @@ use utils;
*/
class DesignDocument extends DOMDocument
{
/** To fix DOMNode::getLineNo() ref https://www.php.net/manual/en/domnode.getlineno.php */
public const XML_PARSE_BIG_LINES = 4194304;
/**
* @throws \Exception
*/
@@ -69,10 +79,12 @@ class DesignDocument extends DOMDocument
*/
public function load($filename, $options = null)
{
libxml_clear_errors();
if (parent::load($filename, LIBXML_NOBLANKS) === false) {
$aErrors = libxml_get_errors();
IssueLog::Error("Error loading $filename", LogAPI::CHANNEL_DEFAULT, $aErrors);
if (is_file($filename)) {
libxml_clear_errors();
if (parent::load($filename, LIBXML_NOBLANKS | LIBXML_BIGLINES | LIBXML_PARSEHUGE | self::XML_PARSE_BIG_LINES) === false) {
$aErrors = libxml_get_errors();
IssueLog::Error("Error loading $filename", LogAPI::CHANNEL_DEFAULT, $aErrors);
}
}
}
@@ -227,6 +239,56 @@ class DesignElement extends \DOMElement
return '';
}
/**
* Compatibility with PHP8.0
*
* @return \DOMElement|null
*
* @since 3.1.2
*/
public function GetFirstElementChild()
{
if (property_exists($this, 'firstElementChild')) {
return $this->firstElementChild;
}
$oChildNode = $this->firstChild;
while (!is_null($oChildNode)) {
if ($oChildNode instanceof \DOMElement) {
return $oChildNode;
}
$oChildNode = $oChildNode->nextSibling;
}
return null;
}
/**
* Compatibility with PHP8.0
*
* @return \DOMElement|null
*
* @since 3.1.2
*/
public function GetNextElementSibling()
{
if (property_exists($this, 'nextElementSibling')) {
return $this->nextElementSibling;
}
$oSibling = $this->nextSibling;
while (!is_null($oSibling)) {
if ($oSibling instanceof \DOMElement) {
return $oSibling;
}
$oSibling = $oSibling->nextSibling;
}
return null;
}
/**
* Returns the node directly under the given node
* @param $sTagName
@@ -307,4 +369,146 @@ class DesignElement extends \DOMElement
}
return $sRet;
}
/**
* Check that the current node is actually a class node, under classes
* @since 3.1.2 3.2.0 N°6974
*/
public function IsClassNode(): bool
{
if ($this->tagName == 'class') {
// Beware: classes/class also exists in the group definition
if (($this->parentNode->tagName == 'classes') && ($this->parentNode->parentNode->tagName == 'itop_design')) {
return true;
}
}
return false;
}
/**
* True if the node is contained in a _delta="merge" tree
* @return bool
*/
public function IsInSpecifiedMerge(): bool
{
// Iterate through the parents: reset the flag if any of them has a flag set
for ($oParent = $this; $oParent instanceof MFElement; $oParent = $oParent->parentNode) {
$sDeltaSpec = $oParent->getAttribute('_delta');
if ($sDeltaSpec === 'merge') {
return true;
}
if (in_array($sDeltaSpec, ['define', 'define_if_not_exists', 'force', 'redefine'])) {
return false;
}
}
return false;
}
/**
* Find the child node matching the given node.
* UNSAFE: may return nodes marked as _alteration="removed"
* A method with the same signature MUST exist in MFDocument for the recursion to work fine
*
* @param DesignElement $oRefNode The node to search for
* @param null|string $sSearchId substitutes to the value of the 'id' attribute
*
* @return DesignElement|null
* @throws \Exception
* @since 3.1.2 3.2.0 N°6974
*/
public function _FindChildNode(DesignElement $oRefNode, $sSearchId = null): ?DesignElement
{
return self::_FindNode($this, $oRefNode, $sSearchId);
}
/**
* Find the child node matching the given node.
* UNSAFE: may return nodes marked as _alteration="removed"
* A method with the same signature MUST exist in MFDocument for the recursion to work fine
*
* @param DesignElement $oRefNode The node to search for
*
* @return DesignElement|null
* @throws \Exception
* @since 3.1.2 3.2.0 N°6974
*/
public function _FindChildNodes(DesignElement $oRefNode): ?DesignElement
{
return self::_FindNodes($this, $oRefNode);
}
/**
* Find the child node matching the given node under the specified parent.
* UNSAFE: may return nodes marked as _alteration="removed"
*
* @param \DOMNode $oParent
* @param DesignElement $oRefNode
* @param string|null $sSearchId
*
* @return DesignElement|null
* @throws Exception
* @since 3.1.2 3.2.0 N°6974
*/
public static function _FindNode(DOMNode $oParent, DesignElement $oRefNode, string $sSearchId = null): ?DesignElement
{
$oNodes = self::_FindNodes($oParent, $oRefNode, $sSearchId);
if ($oNodes instanceof DOMNodeList) {
/** @var DesignElement $oNode */
$oNode = $oNodes->item(0);
return $oNode;
}
return null;
}
/**
* Find the child node matching the given node under the specified parent.
* UNSAFE: may return nodes marked as _alteration="removed"
*
* @param \DOMNode $oParent
* @param DesignElement $oRefNode
* @param string|null $sSearchId
*
* @return \DOMNodeList|false|mixed
* @since 3.1.2 3.2.0 N°6974
*/
public static function _FindNodes(DOMNode $oParent, DesignElement $oRefNode, string $sSearchId = null)
{
if ($oParent instanceof DOMDocument)
{
$oDoc = $oParent->firstChild->ownerDocument;
$oRoot = $oParent;
}
else
{
$oDoc = $oParent->ownerDocument;
$oRoot = $oParent;
}
$oXPath = new DOMXPath($oDoc);
if ($oRefNode->hasAttribute('id'))
{
// Find the elements having the same tag name and id
if (!$sSearchId)
{
$sSearchId = $oRefNode->getAttribute('id');
}
$sXPath = './'.$oRefNode->tagName."[@id='$sSearchId']";
$oRes = $oXPath->query($sXPath, $oRoot);
}
else
{
// Get the elements having the same tag name
$sXPath = './'.$oRefNode->tagName;
$oRes = $oXPath->query($sXPath, $oRoot);
}
return $oRes;
}
}

View File

@@ -3,7 +3,7 @@
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -56,10 +56,11 @@ class Dict
* @param $sLanguageCode
*
* @throws \DictExceptionUnknownLanguage
* @since 3.0.4 3.1.1 3.2.0 Param $sLanguageCode becomes nullable
*/
public static function SetUserLanguage($sLanguageCode)
public static function SetUserLanguage($sLanguageCode = null)
{
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
if (!is_null($sLanguageCode) && !array_key_exists($sLanguageCode, self::$m_aLanguages))
{
throw new DictExceptionUnknownLanguage($sLanguageCode);
}
@@ -106,42 +107,59 @@ class Dict
}
/**
* Returns a localised string from the dictonary
* Returns a localised string from the dictionary
*
* @param string $sStringCode The code identifying the dictionary entry
* @param string $sDefault Default value if there is no match in the dictionary
* @param string $sDefault Default value if there is no match in the dictionary, if no default provided, returns $sStringCode unchanged
* @param bool $bUserLanguageOnly False to allow the use of the default language as a fallback, true otherwise
*
* @return string
*/
public static function S($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
{
$aInfo = self::GetLabelAndLangCode($sStringCode, $sDefault, $bUserLanguageOnly);
return $aInfo['label'];
}
/**
* Returns a localised string from the dictonary with its associated lang code
*
* @param string $sStringCode The code identifying the dictionary entry
* @param string $sDefault Default value if there is no match in the dictionary
* @param bool $bUserLanguageOnly True to allow the use of the default language as a fallback, false otherwise
*
* @return array{
* lang: string, label: string
* } with localized label string and used lang code
*/
private static function GetLabelAndLangCode($sStringCode, $sDefault = null, $bUserLanguageOnly = false)
{
// Attempt to find the string in the user language
//
$sLangCode = self::GetUserLanguage();
self::InitLangIfNeeded($sLangCode);
if (!array_key_exists($sLangCode, self::$m_aData))
if (! array_key_exists($sLangCode, self::$m_aData))
{
IssueLog::Warning("Cannot find $sLangCode in dictionnaries. default labels displayed");
IssueLog::Warning("Cannot find $sLangCode in all registered dictionaries.");
// It may happen, when something happens before the dictionaries get loaded
return $sStringCode;
return [ 'label' => $sStringCode, 'lang' => $sLangCode ];
}
$aCurrentDictionary = self::$m_aData[$sLangCode];
if (is_array($aCurrentDictionary) && array_key_exists($sStringCode, $aCurrentDictionary))
{
return $aCurrentDictionary[$sStringCode];
return [ 'label' => $aCurrentDictionary[$sStringCode], 'lang' => $sLangCode ];
}
if (!$bUserLanguageOnly)
{
// Attempt to find the string in the default language
//
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
$aDefaultDictionary = self::$m_aData[self::$m_sDefaultLanguage];
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
{
return $aDefaultDictionary[$sStringCode];
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => self::$m_sDefaultLanguage ];
}
// Attempt to find the string in english
//
@@ -150,17 +168,17 @@ class Dict
$aDefaultDictionary = self::$m_aData['EN US'];
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
{
return $aDefaultDictionary[$sStringCode];
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => 'EN US' ];
}
}
// Could not find the string...
//
if (is_null($sDefault))
{
return $sStringCode;
return [ 'label' => $sStringCode, 'lang' => null ];
}
return $sDefault;
return [ 'label' => $sDefault, 'lang' => null ];
}
@@ -176,19 +194,25 @@ class Dict
*/
public static function Format($sFormatCode /*, ... arguments ... */)
{
$sLocalizedFormat = self::S($sFormatCode);
['label' => $sLocalizedFormat, 'lang' => $sLangCode] = self::GetLabelAndLangCode($sFormatCode);
$aArguments = func_get_args();
array_shift($aArguments);
if ($sLocalizedFormat == $sFormatCode)
{
// Make sure the information will be displayed (ex: an error occuring before the dictionary gets loaded)
return $sFormatCode.' - '.implode(', ', $aArguments);
}
return vsprintf($sLocalizedFormat, $aArguments);
try{
return vsprintf($sLocalizedFormat, $aArguments);
} catch(\Throwable $e){
\IssueLog::Error("Cannot format dict key", null, ["sFormatCode" => $sFormatCode, "sLangCode" => $sLangCode, 'exception_msg' => $e->getMessage() ]);
return $sFormatCode.' - '.implode(', ', $aArguments);
}
}
/**
* Initialize a the entries for a given language (replaces the former Add() method)
* @param string $sLanguageCode Code identifying the language i.e. 'FR-FR', 'EN-US'
@@ -198,7 +222,7 @@ class Dict
{
self::$m_aData[$sLanguageCode] = $aEntries;
}
/**
* Set the list of available languages
* @param hash $aLanguagesList
@@ -259,7 +283,7 @@ class Dict
{
$sDictFile = APPROOT.'env-'.utils::GetCurrentEnvironment().'/dictionaries/'.str_replace(' ', '-', strtolower($sLangCode)).'.dict.php';
require_once($sDictFile);
if (self::GetApcService()->function_exists('apc_store')
&& (self::$m_sApplicationPrefix !== null))
{
@@ -269,7 +293,7 @@ class Dict
}
return $bResult;
}
/**
* Enable caching (cached using APC)
* @param string $sApplicationPrefix The prefix for uniquely identiying this iTop instance
@@ -312,14 +336,14 @@ class Dict
}
}
}
public static function MakeStats($sLanguageCode, $sLanguageRef = 'EN US')
{
$aMissing = array(); // Strings missing for the target language
$aUnexpected = array(); // Strings defined for the target language, but not found in the reference dictionary
$aNotTranslated = array(); // Strings having the same value in both dictionaries
$aOK = array(); // Strings having different values in both dictionaries
foreach (self::$m_aData[$sLanguageRef] as $sStringCode => $sValue)
{
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageCode]))
@@ -327,7 +351,7 @@ class Dict
$aMissing[$sStringCode] = $sValue;
}
}
foreach (self::$m_aData[$sLanguageCode] as $sStringCode => $sValue)
{
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageRef]))
@@ -350,7 +374,7 @@ class Dict
}
return array($aMissing, $aUnexpected, $aNotTranslated, $aOK);
}
public static function Dump()
{
MyHelpers::var_dump_html(self::$m_aData);
@@ -373,7 +397,7 @@ class Dict
// No need to actually load the strings since it's only used to know the list of languages
// at setup time !!
}
/**
* Export all the dictionary entries - of the given language - whose code matches the given prefix
* missing entries in the current language will be replaced by entries in the default language
@@ -386,7 +410,7 @@ class Dict
self::InitLangIfNeeded(self::$m_sDefaultLanguage);
$aEntries = array();
$iLength = strlen($sStartingWith);
// First prefill the array with entries from the default language
foreach(self::$m_aData[self::$m_sDefaultLanguage] as $sCode => $sEntry)
{
@@ -395,7 +419,7 @@ class Dict
$aEntries[$sCode] = $sEntry;
}
}
// Now put (overwrite) the entries for the user language
foreach(self::$m_aData[self::GetUserLanguage()] as $sCode => $sEntry)
{

View File

@@ -16,8 +16,11 @@
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
use Combodo\iTop\Application\Helper\WebResourcesHelper;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
use Combodo\iTop\Application\UI\Base\Component\MedallionIcon\MedallionIcon;
use Combodo\iTop\Application\UI\Base\Component\Panel\Panel;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
use Combodo\iTop\Renderer\BlockRenderer;
/**
@@ -59,7 +62,7 @@ class DisplayableNode extends GraphNode
public function GetWidth()
{
return max(32, 5*strlen($this->GetProperty('label'))); // approximation of the text's bounding box
return max(32, 5 * mb_strlen($this->GetProperty('label'))); // approximation of the text's bounding box
}
public function GetHeight()
@@ -488,7 +491,7 @@ class DisplayableNode extends GraphNode
if ($bNoLabel)
{
// simulate a fake label with the approximate same size as the true label
$sLabel = str_repeat('x',strlen($this->GetProperty('label', $this->GetId())));
$sLabel = str_repeat('x', mb_strlen($this->GetProperty('label', $this->GetId())));
$sDot = 'label="'.$sLabel.'"';
}
else
@@ -1412,6 +1415,8 @@ class DisplayableGraph extends SimpleGraph
/**
* Display the graph inside the given page, with the "filter" drawer above it
*
* @deprecated 3.1.1 3.2.0 N°3767 Use \DisplayableGraph::DisplayFilterBox() and \DisplayableGraph::DisplayGraph() instead
*
* @param WebPage $oP
* @param array $aResults
* @param string $sRelation
@@ -1425,10 +1430,35 @@ class DisplayableGraph extends SimpleGraph
*
* @throws \CoreException
* @throws \DictExceptionMissingString
*
*/
function Display(WebPage $oP, $aResults, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects, $sObjClass, $iObjKey, $sContextKey, $aContextParams = array(), bool $bLazyLoading = false)
{
list($aExcludedByClass, $aAdditionalContexts) = $this->DisplayFiltering($sContextKey, $aContextParams, $aExcludedObjects, $oP, $aResults, $bLazyLoading);
$oP->AddSubBlock($this->DisplayFilterBox($oP, $aResults, $bLazyLoading));
$this->DisplayGraph($oP, $sRelation, $oAppContext, $aExcludedObjects, $sObjClass, $iObjKey, $sContextKey, $aContextParams, $bLazyLoading);
}
/**
* Display only the graph inside the given page, with the parameters of filter box draw with DisplayFilterBox
*
* @param WebPage $oP
* @param string $sRelation
* @param ApplicationContext $oAppContext
* @param array $aExcludedObjects
* @param string $sObjClass
* @param int $iObjKey
* @param string $sContextKey
* @param array $aContextParams
* @param bool $bLazyLoading
*
* @throws \CoreException
* @throws \DictExceptionMissingString
*
* @since 3.1.1 3.2.0 N°3767
*/
function DisplayGraph(WebPage $oP, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects, $sObjClass, $iObjKey, $sContextKey, $aContextParams = array(), bool $bLazyLoading = false): void
{
list($aExcludedByClass, $aAdditionalContexts) = $this->GetFilteringData($sContextKey, $aContextParams, $aExcludedObjects);
$iGroupingThreshold = utils::ReadParam('g', 5);
@@ -1513,12 +1543,10 @@ class DisplayableGraph extends SimpleGraph
$oP->add_ready_script(" $('#$sId').simple_graph(".json_encode($aParams).");");
} else {
$oP->add_script("function Load(){var aExcluded = []; $('input[name^=excluded]').each( function() {if (!$(this).prop('checked')) { aExcluded.push($(this).val()); }} ); var params= $.extend(".json_encode($aParams).", {excluded_classes: aExcluded}); $('#$sId').simple_graph(params);}");
$oP->add_ready_script("$('#impacted_objects_lists').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');$('#impacted_groups').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');");
$oP->add_ready_script("$('#graph').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');$('#impacted_objects_lists').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');$('#impacted_groups').html('".utils::TextToHtml(Dict::S('Relation:impacts/NoFilteredData'))."');");
}
}
catch(Exception $e)
{
catch (Exception $e) {
$oP->add('<div>'.$e->getMessage().'</div>');
}
$oP->add_script(
@@ -1563,23 +1591,41 @@ EOF
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
*
* @deprecated 3.1.1 3.2.0 N°3767 Use \DisplayableGraph::DisplayFilterBox() and \DisplayableGraph::GetFilteringData() instead
*/
public function DisplayFiltering(string $sContextKey, array $aContextParams, array $aExcludedObjects, WebPage $oP, array $aResults, bool $bLazyLoading = false): array
{
$aContextDefs = static::GetContextDefinitions($sContextKey, true, $aContextParams);
$aExcludedByClass = array();
foreach ($aExcludedObjects as $oObj) {
if (!array_key_exists(get_class($oObj), $aExcludedByClass)) {
$aExcludedByClass[get_class($oObj)] = array();
}
$aExcludedByClass[get_class($oObj)][] = $oObj->GetKey();
}
$oP->Add($this->DisplayFilterBox($oP, $aResults, $bLazyLoading));
return $this->GetFilteringData($sContextKey, $aContextParams, $aExcludedObjects);
}
/**
* @param \WebPage $oP
* @param array $aResults
* @param bool $bLazyLoading
*
* @return UIContentBlock
* @throws \CoreException
* @throws \DictExceptionMissingString
* @throws \ReflectionException
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
*
* @since 3.1.1 3.2.0 N°3767
*/
public function DisplayFilterBox(WebPage $oP, array $aResults, bool $bLazyLoading = false): UIContentBlock
{
$sSftShort = Dict::S('UI:ElementsDisplayed');
$oP->add("<div class=\"not-printable\">\n");
$oBlock = UIContentBlockUIBlockFactory::MakeStandard(null, ['not-printable']);
$oUiSearchBlock = new Panel($sSftShort, [], Panel::ENUM_COLOR_SCHEME_CYAN, 'dh_flash');
$oUiSearchBlock->SetCSSClasses(["ibo-search-form-panel", "display_block"]);
$oUiSearchBlock->SetIsCollapsible(true);
$oUiHtmlBlock = new Combodo\iTop\Application\UI\Base\Component\Html\Html(
$oUiSearchBlock->SetCSSClasses(["ibo-search-form-panel", "display_block"])
->SetIsCollapsible(true);
$oUiHtmlBlock = new Html(
<<<EOF
<div id="ds_flash" class="search_box ibo-display-graph--search-box">
@@ -1626,11 +1672,23 @@ EOF
$oUiHtmlBlock->AddHtml("<button type=\"button\" id=\"ReloadMovieBtn\" class=\"ibo-button ibo-is-neutral ibo-is-regular\" onClick=\"$sOnCLick\">".Dict::S('UI:Button:Refresh')."</button></div></form>");
}
$oUiHtmlBlock->AddHtml("</div>\n");
$oUiHtmlBlock->AddHtml("</div>\n"); // class="not-printable"
$oUiSearchBlock->AddSubBlock($oUiHtmlBlock);
$oP->AddUiBlock($oUiSearchBlock);
$oBlock->AddSubBlock($oUiSearchBlock);
return $oBlock;
}
public function GetFilteringData(string $sContextKey, array $aContextParams, array $aExcludedObjects): array
{
$aContextDefs = static::GetContextDefinitions($sContextKey, true, $aContextParams);
$aExcludedByClass = array();
foreach ($aExcludedObjects as $oObj) {
if (!array_key_exists(get_class($oObj), $aExcludedByClass)) {
$aExcludedByClass[get_class($oObj)] = array();
}
$aExcludedByClass[get_class($oObj)][] = $oObj->GetKey();
}
$aAdditionalContexts = array();
foreach ($aContextDefs as $sKey => $aDefinition) {
$aAdditionalContexts[] = array('key' => $sKey, 'label' => Dict::S($aDefinition['dict']), 'oql' => $aDefinition['oql'], 'default' => (array_key_exists('default', $aDefinition) && ($aDefinition['default'] == 'yes')));

View File

@@ -242,44 +242,33 @@ class EventIssue extends Event
{
if (is_string($sValue))
{
if (strlen($sValue) < 256)
{
if (mb_strlen($sValue) < 256) {
$aPost[$sKey] = $sValue;
} else {
$aPost[$sKey] = "!long string: ".mb_strlen($sValue)." chars";
}
else
{
$aPost[$sKey] = "!long string: ".strlen($sValue). " chars";
}
}
else
{
} else {
// Not a string (avoid warnings in case the value cannot be easily casted into a string)
$aPost[$sKey] = @(string) $sValue;
$aPost[$sKey] = @(string)$sValue;
}
}
$this->Set('arguments_post', $aPost);
}
else
{
} else {
$this->Set('arguments_post', array());
}
$sLength = strlen($this->Get('issue'));
if ($sLength > 255)
{
$this->Set('issue', substr($this->Get('issue'), 0, 200)." -truncated ($sLength chars)");
$sLength = mb_strlen($this->Get('issue'));
if ($sLength > 255) {
$this->Set('issue', mb_substr($this->Get('issue'), 0, 210)." -truncated ($sLength chars)");
}
$sLength = strlen($this->Get('impact'));
if ($sLength > 255)
{
$this->Set('impact', substr($this->Get('impact'), 0, 200)." -truncated ($sLength chars)");
$sLength = mb_strlen($this->Get('impact'));
if ($sLength > 255) {
$this->Set('impact', mb_substr($this->Get('impact'), 0, 210)." -truncated ($sLength chars)");
}
$sLength = strlen($this->Get('page'));
if ($sLength > 255)
{
$this->Set('page', substr($this->Get('page'), 0, 200)." -truncated ($sLength chars)");
$sLength = mb_strlen($this->Get('page'));
if ($sLength > 255) {
$this->Set('page', mb_substr($this->Get('page'), 0, 210)." -truncated ($sLength chars)");
}
}
}

View File

@@ -10,6 +10,7 @@ use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\Column\ColumnUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\MultiColumn\MultiColumnUIBlockFactory;
use Combodo\iTop\Application\Helper\ExportHelper;
require_once(APPROOT.'application/xlsxwriter.class.php');
@@ -82,6 +83,7 @@ class ExcelBulkExport extends TabularBulkExport
case 'xlsx_options':
$oPanel = PanelUIBlockFactory::MakeNeutral(Dict::S('Core:BulkExport:XLSXOptions'));
$oPanel->AddSubBlock(ExportHelper::GetAlertForExcelMaliciousInjection());
$oMulticolumn = MultiColumnUIBlockFactory::MakeStandard();
$oPanel->AddSubBlock($oMulticolumn);

View File

@@ -101,7 +101,7 @@ EOF;
EOF;
SetupUtils::builddir(dirname($sFilePath));
file_put_contents($sFilePath, $content);
file_put_contents($sFilePath, $content, LOCK_EX);
}
}
Dict::SetUserLanguage($sUserLang);

View File

@@ -1,27 +1,15 @@
<?php
// Copyright (C) 2010-2023 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Core\Kpi\KpiLogData;
use Combodo\iTop\Service\Module\ModuleService;
/**
* Measures operations duration, memory usage, etc. (and some other KPIs)
*
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ExecutionKPI
@@ -30,6 +18,8 @@ class ExecutionKPI
static protected $m_bEnabled_Memory = false;
static protected $m_bBlameCaller = false;
static protected $m_sAllowedUser = '*';
static protected $m_bGenerateLegacyReport = true;
static protected $m_fSlowQueries = 0;
static protected $m_aStats = []; // Recurrent operations
static protected $m_aExecData = []; // One shot operations
@@ -86,14 +76,39 @@ class ExecutionKPI
return false;
}
static public function SetGenerateLegacyReport($bReportExtensionsOnly)
{
self::$m_bGenerateLegacyReport = $bReportExtensionsOnly;
}
static public function SetSlowQueries($fSlowQueries)
{
self::$m_fSlowQueries = $fSlowQueries;
}
static public function GetDescription()
{
$aFeatures = array();
if (self::$m_bEnabled_Duration) $aFeatures[] = 'Duration';
if (self::$m_bEnabled_Memory) $aFeatures[] = 'Memory usage';
$sFeatures = implode(', ', $aFeatures);
$sFeatures = 'Measures: '.implode(', ', $aFeatures);
$sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'";
return "KPI logging is active for $sFor. Measures: $sFeatures";
$sSlowQueries = '';
if (self::$m_fSlowQueries > 0) {
$sSlowQueries = ". Slow Queries: ".self::$m_fSlowQueries."s";
}
$aExtensions = [];
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$aExtensions[] = ModuleService::GetInstance()->GetModuleNameFromObject($oExtensionInstance);
}
$sExtensions = '';
if (count($aExtensions) > 0) {
$sExtensions = '. KPI Extensions: ['.implode(', ', $aExtensions).']';
}
return "KPI logging is active for $sFor. $sFeatures$sSlowQueries$sExtensions";
}
static public function ReportStats()
@@ -101,7 +116,28 @@ class ExecutionKPI
if (!self::IsEnabled()) return;
global $fItopStarted;
global $iItopInitialMemory;
$sExecId = microtime(); // id to differentiate the hrefs!
$sRequest = $_SERVER['REQUEST_URI'].' ('.$_SERVER['REQUEST_METHOD'].')';
if (isset($_POST['operation'])) {
$sRequest .= ' operation: '.$_POST['operation'];
}
$fStop = MyHelpers::getmicrotime();
if (($fStop - $fItopStarted) > self::$m_fSlowQueries) {
// Invoke extensions to log the KPI operation
/** @var \iKPILoggerExtension $oExtensionInstance */
$iCurrentMemory = self::memory_get_usage();
$iPeakMemory = self::memory_get_peak_usage();
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$oKPILogData = new KpiLogData(KpiLogData::TYPE_REQUEST, 'Page', $sRequest, $fItopStarted, $fStop, '', $iItopInitialMemory, $iCurrentMemory, $iPeakMemory);
$oExtensionInstance->LogOperation($oKPILogData);
}
}
if (!self::$m_bGenerateLegacyReport) {
return;
}
$aBeginTimes = array();
foreach (self::$m_aExecData as $aOpStats)
@@ -114,9 +150,9 @@ class ExecutionKPI
$sHtml = "<hr/>";
$sHtml .= "<div style=\"background-color: grey; padding: 10px;\">";
$sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - ".$_SERVER['REQUEST_URI']." (".$_SERVER['REQUEST_METHOD'].")</h3>";
$sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - $sRequest</h3>";
$oStarted = DateTime::createFromFormat('U.u', $fItopStarted);
$sHtml .= "<p>".$oStarted->format('Y-m-d H:i:s.u')."</p>";
$sHtml .= '<p>'.$oStarted->format('Y-m-d H:i:s.u').'</p>';
$sHtml .= "<p>log_kpi_user_id: ".UserRights::GetUserId()."</p>";
$sHtml .= "<div>";
$sHtml .= "<table border=\"1\" style=\"$sTableStyle\">";
@@ -257,7 +293,7 @@ class ExecutionKPI
$sTotalInter = round($fTotalInter, 3);
$sMinInter = round($fMinInter, 3);
$sMaxInter = round($fMaxInter, 3);
if (($fTotalInter >= $fSlowQueries))
if (($fTotalInter >= self::$m_fSlowQueries))
{
if ($bDisplayHeader)
{
@@ -285,37 +321,19 @@ class ExecutionKPI
self::Report($sHtml);
}
public static function InitStats()
{
// Invoke extensions to initialize the KPI statistics
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$oExtensionInstance->InitStats();
}
}
public function __construct()
{
$this->ResetCounters();
self::Push($this);
}
/**
* Stack executions to remove children duration from stats
*
* @param \ExecutionKPI $oExecutionKPI
*/
private static function Push(ExecutionKPI $oExecutionKPI)
{
self::$m_aExecutionStack[] = $oExecutionKPI;
}
/**
* Pop current child and count its duration in its parent
*
* @param float|int $fChildDuration
*/
private static function Pop(float $fChildDuration = 0)
{
array_pop(self::$m_aExecutionStack);
// Update the parent's children duration
$oPrevExecutionKPI = end(self::$m_aExecutionStack);
if ($oPrevExecutionKPI) {
$oPrevExecutionKPI->m_fChildrenDuration += $fChildDuration;
}
}
}
// Get the duration since startup, and reset the counter for the next measure
//
@@ -323,9 +341,15 @@ class ExecutionKPI
{
global $fItopStarted;
if (!self::IsEnabled()) {
return;
}
$aNewEntry = null;
if (self::$m_bEnabled_Duration) {
$fStarted = $this->m_fStarted;
$fStopped = $this->m_fStarted;
if (self::$m_bEnabled_Duration) {
$fStopped = MyHelpers::getmicrotime();
$aNewEntry = array(
'op' => $sOperationDesc,
@@ -336,6 +360,9 @@ class ExecutionKPI
$this->m_fStarted = $fStopped;
}
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
$iCurrentMemory = 0;
$iPeakMemory = 0;
if (self::$m_bEnabled_Memory)
{
$iCurrentMemory = self::memory_get_usage();
@@ -345,40 +372,118 @@ class ExecutionKPI
}
$aNewEntry['mem_begin'] = $this->m_iInitialMemory;
$aNewEntry['mem_end'] = $iCurrentMemory;
if (function_exists('memory_get_peak_usage'))
{
$aNewEntry['mem_peak'] = memory_get_peak_usage();
}
$iPeakMemory = self::memory_get_peak_usage();
$aNewEntry['mem_peak'] = $iPeakMemory;
// Reset for the next operation (if the object is recycled)
$this->m_iInitialMemory = $iCurrentMemory;
}
if (!is_null($aNewEntry))
if (self::$m_bEnabled_Duration || self::$m_bEnabled_Memory) {
// Invoke extensions to log the KPI operation
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance)
{
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
$oKPILogData = new KpiLogData(
KpiLogData::TYPE_REPORT,
'Step',
$sOperationDesc,
$fStarted,
$fStopped,
$sExtension,
$iInitialMemory,
$iCurrentMemory,
$iPeakMemory);
$oExtensionInstance->LogOperation($oKPILogData);
}
}
if (!is_null($aNewEntry) && self::$m_bGenerateLegacyReport)
{
self::$m_aExecData[] = $aNewEntry;
}
$this->ResetCounters();
}
/**
* Compute statistics for a call to an extension
* Note: not working in dev mode (with links to env-production)
*
* @param object|string $object object called
* @param string $sMethod method called on the object
* @param string $sMessage additional message
*
* @return bool true if an extension was found for this object::method
* @throws \ReflectionException
*/
public function ComputeStatsForExtension($object, string $sMethod, string $sMessage = ''): bool
{
if (!self::IsEnabled()) {
return true;
}
$sSignature = ModuleService::GetInstance()->GetModuleMethodSignature($object, $sMethod);
if (utils::StartsWith($sSignature, '[')) {
$this->ComputeStats('Extension', "$sSignature $sMessage");
return true;
}
return false;
}
public function ComputeStats($sOperation, $sArguments)
{
if (!self::IsEnabled()) {
return;
}
$fDuration = 0;
if (self::$m_bEnabled_Duration) {
$fStopped = MyHelpers::getmicrotime();
$fDuration = $fStopped - $this->m_fStarted;
$fSelfDuration = $fDuration - $this->m_fChildrenDuration;
if (self::$m_bBlameCaller) {
self::$m_aStats[$sOperation][$sArguments][] = array(
'time' => $fSelfDuration,
'callers' => MyHelpers::get_callstack(1),
);
} else {
self::$m_aStats[$sOperation][$sArguments][] = array(
'time' => $fSelfDuration,
);
}
}
self::Pop($fDuration);
$aCallstack = [];
if (self::$m_bGenerateLegacyReport) {
if (self::$m_bBlameCaller) {
$aCallstack = MyHelpers::get_callstack(1);
self::$m_aStats[$sOperation][$sArguments][] = [
'time' => $fDuration,
'callers' => $aCallstack,
];
} else {
self::$m_aStats[$sOperation][$sArguments][] = [
'time' => $fDuration
];
}
}
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
$iCurrentMemory = 0;
$iPeakMemory = 0;
if (self::$m_bEnabled_Memory)
{
$iCurrentMemory = self::memory_get_usage();
$iPeakMemory = self::memory_get_peak_usage();
}
// Invoke extensions to log the KPI operation
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
$oKPILogData = new KpiLogData(
KpiLogData::TYPE_STATS,
$sOperation,
$sArguments,
$this->m_fStarted,
$fStopped,
$sExtension,
$iInitialMemory,
$iCurrentMemory,
$iPeakMemory,
$aCallstack);
$oExtensionInstance->LogOperation($oKPILogData);
}
}
}
protected function ResetCounters()
@@ -408,35 +513,7 @@ class ExecutionKPI
static protected function memory_get_usage()
{
if (function_exists('memory_get_usage'))
{
return memory_get_usage(true);
}
// Copied from the PHP manual
//
//If its Windows
//Tested on Win XP Pro SP2. Should work on Win 2003 Server too
//Doesn't work for 2000
//If you need it to work for 2000 look at http://us2.php.net/manual/en/function.memory-get-usage.php#54642
if (substr(PHP_OS,0,3) == 'WIN')
{
$output = array();
exec('tasklist /FI "PID eq ' . getmypid() . '" /FO LIST', $output);
return preg_replace( '/[\D]/', '', $output[5] ) * 1024;
}
else
{
//We now assume the OS is UNIX
//Tested on Mac OS X 10.4.6 and Linux Red Hat Enterprise 4
//This should work on most UNIX systems
$pid = getmypid();
exec("ps -eo%mem,rss,pid | grep $pid", $output);
$output = explode(" ", $output[0]);
//rss is given in 1024 byte units
return $output[1] * 1024;
}
return memory_get_usage(true);
}
static public function memory_get_peak_usage($bRealUsage = false)

View File

@@ -3,7 +3,7 @@
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
@@ -576,6 +576,11 @@ class LogChannels
public const DATATABLE = 'Datatable';
public const DEADLOCK = 'DeadLock';
/**
* @var string Everything related to PHP sessions tracking
* @since 3.1.1 3.2.0 N°6901
*/
public const SESSIONTRACKER = 'SessionTracker';
/**
* @var string Everything related to the datamodel CRUD
@@ -583,6 +588,12 @@ class LogChannels
*/
public const DM_CRUD = 'DMCRUD';
/**
* @var string Everything related to the datamodel CRUD
* @since 3.1.0
*/
public const WEB_REQUEST = 'WebRequest';
/**
* @var string Everything related to the event service
* @since 3.1.0
@@ -606,6 +617,8 @@ class LogChannels
public const PORTAL = 'portal';
public const TEMPORARY_OBJECTS = 'TemporaryObjects';
/**
* @var string
* @since 3.1.0
@@ -1130,9 +1143,13 @@ class DeprecatedCallsLog extends LogAPI
parent::Enable($sTargetFile);
if (
(false === defined(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME))
(
(false === defined(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME))
|| (defined(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME) && (constant(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME) !== true))
)
&& static::IsLogLevelEnabledSafe(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD)
) {
IssueLog::Trace('Setting '.static::class.' error handler to catch DEPRECATED', static::ENUM_CHANNEL_PHP_LIBMETHOD);
set_error_handler([static::class, 'DeprecatedNoticesErrorHandler'], E_DEPRECATED | E_USER_DEPRECATED);
}
}
@@ -1230,7 +1247,9 @@ class DeprecatedCallsLog extends LogAPI
}
/**
* @throws \ConfigException
* @since 3.0.1 3.1.0 N°4725 silently handles ConfigException
* @since 3.0.4 3.1.0 N°4725 remove forgotten throw PHPDoc annotation
*
* @link https://www.php.net/debug_backtrace
* @uses \debug_backtrace()
*/
@@ -1282,19 +1301,7 @@ class DeprecatedCallsLog extends LogAPI
}
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
$iStackDeprecatedMethodLevel = 1; // level 0 = current method, level 1 = method containing the `NotifyDeprecatedPhpMethod` call
$sDeprecatedObject = $aStack[$iStackDeprecatedMethodLevel]['class'];
$sDeprecatedMethod = $aStack[$iStackDeprecatedMethodLevel]['function'];
$sCallerFile = $aStack[$iStackDeprecatedMethodLevel]['file'];
$sCallerLine = $aStack[$iStackDeprecatedMethodLevel]['line'];
$sMessage = "Call to {$sDeprecatedObject}::{$sDeprecatedMethod} in {$sCallerFile}#L{$sCallerLine}";
$iStackCallerMethodLevel = $iStackDeprecatedMethodLevel + 1; // level 2 = caller of the deprecated method
if (array_key_exists($iStackCallerMethodLevel, $aStack)) {
$sCallerObject = $aStack[$iStackCallerMethodLevel]['class'];
$sCallerMethod = $aStack[$iStackCallerMethodLevel]['function'];
$sMessage .= " ({$sCallerObject}::{$sCallerMethod})";
}
$sMessage = self::GetMessageFromStack($aStack);
if (!is_null($sAdditionalMessage)) {
$sMessage .= ' : '.$sAdditionalMessage;
@@ -1303,6 +1310,45 @@ class DeprecatedCallsLog extends LogAPI
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_METHOD);
}
/**
* @param array $aDebugBacktrace data from {@see debug_backtrace()}
*
* @return string message to print to the log
*/
private static function GetMessageFromStack(array $aDebugBacktrace): string
{
// level 0 = current method
// level 1 = deprecated method, containing the `NotifyDeprecatedPhpMethod` call
$sMessage = 'Call'.self::GetMessageForCurrentStackLevel($aDebugBacktrace[1], " to ");
// level 2 = caller of the deprecated method
if (array_key_exists(2, $aDebugBacktrace)) {
$sMessage .= ' (from ';
$sMessage .= self::GetMessageForCurrentStackLevel($aDebugBacktrace[2]);
$sMessage .= ')';
}
return $sMessage;
}
private static function GetMessageForCurrentStackLevel(array $aCurrentLevelDebugTrace, ?string $sPrefix = ""): string
{
$sMessage = "";
if (array_key_exists('class', $aCurrentLevelDebugTrace)) {
$sDeprecatedObject = $aCurrentLevelDebugTrace['class'];
$sDeprecatedMethod = $aCurrentLevelDebugTrace['function'] ?? "";
$sMessage = "{$sPrefix}{$sDeprecatedObject}::{$sDeprecatedMethod} in ";
}
if (array_key_exists('file', $aCurrentLevelDebugTrace)) {
$sCallerFile = $aCurrentLevelDebugTrace['file'];
$sCallerLine = $aCurrentLevelDebugTrace['line'] ?? "";
$sMessage .= "{$sCallerFile}#L{$sCallerLine}";
}
return $sMessage;
}
/**
* @param string|null $sAdditionalMessage
* @since 3.1.0
@@ -1404,7 +1450,7 @@ class LogFileRotationProcess implements iScheduledProcess
$iMaxDays = MetaModel::GetConfig()->Get(LogAPI::ENUM_CONFIG_PARAM_PURGE_MAX_KEEP_DAYS);
// Files iterator (*.*)
$oIterator = new \GlobIterator(APPROOT.'log'.DIRECTORY_SEPARATOR.'/*.*');
$oIterator = new \GlobIterator(APPROOT.'log'.DIRECTORY_SEPARATOR.'*.*');
$aLogFiles = iterator_to_array($oIterator);
// Reference date
@@ -1416,6 +1462,11 @@ class LogFileRotationProcess implements iScheduledProcess
// File real path
$sFileRealPath = $oLogFile->getRealPath();
// Check file extension
if(!in_array($oLogFile->getExtension(), ['log','sql','xml'])){
continue;
}
// Compute number of days since last modification
$oDateFileLastModification = new DateTime();
$oDateFileLastModification->setTimestamp($oLogFile->getMTime());
@@ -1656,6 +1707,8 @@ class ExceptionLog extends LogAPI
*/
private static function GetLastEventIssue()
{
return self::$oLastEventIssue;
$oRet = self::$oLastEventIssue;
self::$oLastEventIssue = null;
return $oRet;
}
}

View File

@@ -17,7 +17,10 @@
// along with iTop. If not, see <http://www.gnu.org/licenses/>
//
use Combodo\iTop\Application\EventRegister\ApplicationEvents;
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
use Combodo\iTop\Service\Events\EventData;
use Combodo\iTop\Service\Events\EventService;
require_once APPROOT.'core/modulehandler.class.inc.php';
require_once APPROOT.'core/querymodifier.class.inc.php';
@@ -1238,7 +1241,7 @@ abstract class MetaModel
}
$sTable = self::DBGetTable($sClass);
// Could be completed later with all the classes that are using a given table
// Could be completed later with all the classes that are using a given table
if (!array_key_exists($sTable, $aTables)) {
$aTables[$sTable] = array();
}
@@ -1442,8 +1445,10 @@ abstract class MetaModel
*
* @return AttributeDefinition[]
* @throws \CoreException
*
* @see GetAttributesList for attcode list
*/
final static public function ListAttributeDefs($sClass)
final public static function ListAttributeDefs($sClass)
{
self::_check_subclass($sClass);
return self::$m_aAttribDefs[$sClass];
@@ -1456,8 +1461,10 @@ abstract class MetaModel
* @param string[] $aDesiredAttTypes Array of AttributeDefinition classes to filter the list on
* @param string|null $sListCode If provided, attributes will be limited to those in this zlist
*
* @return array
* @return string[] list of attcodes
* @throws \CoreException
*
* @see ListAttributeDefs to get AttributeDefinition array instead
*/
final public static function GetAttributesList(string $sClass, array $aDesiredAttTypes = [], ?string $sListCode = null)
{
@@ -2924,52 +2931,7 @@ abstract class MetaModel
}
self::$m_sTablePrefix = $sTablePrefix;
// Build the list of available extensions
//
$aInterfaces = [
'iApplicationUIExtension',
'iPreferencesExtension',
'iApplicationObjectExtension',
'iLoginFSMExtension',
'iLoginUIExtension',
'iLogoutExtension',
'iQueryModifier',
'iOnClassInitialization',
'iPopupMenuExtension',
'iPageUIExtension',
'iPageUIBlockExtension',
'iBackofficeLinkedScriptsExtension',
'iBackofficeEarlyScriptExtension',
'iBackofficeScriptExtension',
'iBackofficeInitScriptExtension',
'iBackofficeReadyScriptExtension',
'iBackofficeLinkedStylesheetsExtension',
'iBackofficeStyleExtension',
'iBackofficeDictEntriesExtension',
'iBackofficeDictEntriesPrefixesExtension',
'iPortalUIExtension',
'ModuleHandlerApiInterface',
'iNewsroomProvider',
'iModuleExtension',
];
foreach($aInterfaces as $sInterface)
{
self::$m_aExtensionClassNames[$sInterface] = array();
}
foreach(get_declared_classes() as $sPHPClass)
{
$oRefClass = new ReflectionClass($sPHPClass);
$oExtensionInstance = null;
foreach($aInterfaces as $sInterface)
{
if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable())
{
self::$m_aExtensionClassNames[$sInterface][$sPHPClass] = $sPHPClass;
}
}
}
self::InitExtensions();
// Initialize the classes (declared attributes, etc.)
//
@@ -3564,7 +3526,7 @@ abstract class MetaModel
}
// Set the "host class" as soon as possible, since HierarchicalKeys use it for their 'target class' as well
// and this needs to be know early (for Init_IsKnowClass 19 lines below)
// and this needs to be know early (for Init_IsKnowClass 19 lines below)
$oAtt->SetHostClass($sTargetClass);
// Some attributes could refer to a class
@@ -3606,7 +3568,7 @@ abstract class MetaModel
self::$m_aAttribDefs[$sTargetClass][$oAtt->GetCode()] = $oAtt;
self::$m_aAttribOrigins[$sTargetClass][$oAtt->GetCode()] = $sTargetClass;
// Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used
// Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used
}
/**
@@ -3618,8 +3580,7 @@ abstract class MetaModel
{
MyHelpers::CheckKeyInArray('list code', $sListCode, self::$m_aListInfos);
if (!$sTargetClass)
{
if (!$sTargetClass) {
$sTargetClass = self::GetCallersPHPClass("Init");
}
@@ -3807,7 +3768,7 @@ abstract class MetaModel
self::$m_aStimuli[$sTargetClass][$oStimulus->GetCode()] = $oStimulus;
// I wanted to simplify the syntax of the declaration of objects in the biz model
// Therefore, the reference to the host class is set there
// Therefore, the reference to the host class is set there
$oStimulus->SetHostClass($sTargetClass);
}
@@ -4262,40 +4223,78 @@ abstract class MetaModel
}
else
{
$aCurrentUser = array();
$aCurrentContact = array();
$aCurrentUser = [];
$aCurrentContact = [];
foreach ($aExpectedArgs as $expression)
{
$aName = explode('->', $expression->GetName());
if ($aName[0] == 'current_contact_id') {
$aPlaceholders['current_contact_id'] = UserRights::GetContactId();
}
if ($aName[0] == 'current_user') {
} else if ($aName[0] == 'current_user') {
array_push($aCurrentUser, $aName[1]);
}
if ($aName[0] == 'current_contact') {
} else if ($aName[0] == 'current_contact') {
array_push($aCurrentContact, $aName[1]);
}
}
if (count($aCurrentUser) > 0) {
$oUser = UserRights::GetUserObject();
$aPlaceholders['current_user->object()'] = $oUser;
foreach ($aCurrentUser as $sField) {
$aPlaceholders['current_user->'.$sField] = $oUser->Get($sField);
}
static::FillObjectPlaceholders($aPlaceholders, 'current_user', UserRights::GetUserObject(), $aCurrentUser);
}
if (count($aCurrentContact) > 0) {
$oPerson = UserRights::GetContactObject();
$aPlaceholders['current_contact->object()'] = $oPerson;
foreach ($aCurrentContact as $sField) {
$aPlaceholders['current_contact->'.$sField] = $oPerson->Get($sField);
}
static::FillObjectPlaceholders($aPlaceholders, 'current_contact', UserRights::GetContactObject(), $aCurrentContact);
}
}
return $aPlaceholders;
}
/**
* @since 3.1.1 N°6824
* @param array $aPlaceholders
* @param string $sPlaceHolderPrefix
* @param ?\DBObject $oObject
* @param array $aCurrentUser
*
* @return void
*
*/
private static function FillObjectPlaceholders(array &$aPlaceholders, string $sPlaceHolderPrefix, ?\DBObject $oObject, array $aCurrentUser) : void {
$sPlaceHolderKey = $sPlaceHolderPrefix."->object()";
if (is_null($oObject)){
$aContext = [
"current_user_id" => UserRights::GetUserId(),
"null object type" => $sPlaceHolderPrefix,
"fields" => $aCurrentUser,
];
IssueLog::Warning("Unresolved placeholders due to null object in current context", null,
$aContext);
$aPlaceholders[$sPlaceHolderKey] = Dict::Format("Core:Placeholder:CannotBeResolved", $sPlaceHolderKey);
foreach ($aCurrentUser as $sField) {
$sPlaceHolderKey = $sPlaceHolderPrefix . "->$sField";
$aPlaceholders[$sPlaceHolderKey] = Dict::Format("Core:Placeholder:CannotBeResolved", $sPlaceHolderKey);
}
} else {
$aPlaceholders[$sPlaceHolderKey] = $oObject;
foreach ($aCurrentUser as $sField) {
$sPlaceHolderKey = $sPlaceHolderPrefix . "->$sField";
// Mind that the "id" is not viewed as a valid att. code by \MetaModel::IsValidAttCode() so we have to test it manually
if ($sField !== "id" && false === MetaModel::IsValidAttCode(get_class($oObject), $sField)){
$aContext = [
"current_user_id" => UserRights::GetUserId(),
"obj_class" => get_class($oObject),
"placeholder" => $sPlaceHolderKey,
"invalid_field" => $sField,
];
IssueLog::Warning("Unresolved placeholder due to invalid attribute", null,
$aContext);
$aPlaceholders[$sPlaceHolderKey] = Dict::Format("Core:Placeholder:CannotBeResolved", $sPlaceHolderKey);
continue;
}
$aPlaceholders[$sPlaceHolderKey] = $oObject->Get($sField);
}
}
}
/**
* @param \DBSearch $oFilter
*
@@ -5157,7 +5156,7 @@ abstract class MetaModel
*/
protected static function DBCreateTables($aCallback = null)
{
list($aErrors, $aSugFix, $aCondensedQueries) = self::DBCheckFormat();
[$aErrors, $aSugFix, $aCondensedQueries] = self::DBCheckFormat();
//$sSQL = implode('; ', $aCondensedQueries); Does not work - multiple queries not allowed
foreach($aCondensedQueries as $sQuery)
@@ -5179,7 +5178,7 @@ abstract class MetaModel
*/
protected static function DBCreateViews()
{
list($aErrors, $aSugFix) = self::DBCheckViews();
[$aErrors, $aSugFix] = self::DBCheckViews();
foreach($aSugFix as $sClass => $aTarget)
{
@@ -6341,38 +6340,46 @@ abstract class MetaModel
*/
public static function Startup($config, $bModelOnly = false, $bAllowCache = true, $bTraceSourceFiles = false, $sEnvironment = 'production')
{
// Startup on a new environment is not supported
static $bStarted = false;
if ($bStarted) {
return;
}
$bStarted = true;
self::$m_sEnvironment = $sEnvironment;
if (!defined('MODULESROOT'))
{
define('MODULESROOT', APPROOT.'env-'.self::$m_sEnvironment.'/');
try {
if (!defined('MODULESROOT')) {
define('MODULESROOT', APPROOT.'env-'.self::$m_sEnvironment.'/');
self::$m_bTraceSourceFiles = $bTraceSourceFiles;
self::$m_bTraceSourceFiles = $bTraceSourceFiles;
// $config can be either a filename, or a Configuration object (volatile!)
if ($config instanceof Config)
{
self::LoadConfig($config, $bAllowCache);
}
else
{
self::LoadConfig(new Config($config), $bAllowCache);
// $config can be either a filename, or a Configuration object (volatile!)
if ($config instanceof Config) {
self::LoadConfig($config, $bAllowCache);
} else {
self::LoadConfig(new Config($config), $bAllowCache);
}
if ($bModelOnly) {
return;
}
}
if ($bModelOnly)
{
return;
CMDBSource::SelectDB(self::$m_sDBName);
foreach (MetaModel::EnumPlugins('ModuleHandlerApiInterface') as $oPHPClass) {
$oPHPClass::OnMetaModelStarted();
}
ExpressionCache::Warmup();
}
finally {
// Event service must be initialized after the MetaModel startup, otherwise it cannot discover classes implementing the iEventServiceSetup interface
EventService::InitService();
EventService::FireEvent(new EventData(ApplicationEvents::APPLICATION_EVENT_METAMODEL_STARTED));
}
CMDBSource::SelectDB(self::$m_sDBName);
foreach(MetaModel::EnumPlugins('ModuleHandlerApiInterface') as $oPHPClass)
{
$oPHPClass::OnMetaModelStarted();
}
ExpressionCache::Warmup();
}
/**
@@ -6418,7 +6425,9 @@ abstract class MetaModel
ExecutionKPI::EnableDuration(self::$m_oConfig->Get('log_kpi_duration'));
ExecutionKPI::EnableMemory(self::$m_oConfig->Get('log_kpi_memory'));
ExecutionKPI::SetAllowedUser(self::$m_oConfig->Get('log_kpi_user_id'));
ExecutionKPI::SetAllowedUser(self::$m_oConfig->Get('log_kpi_user_id'));
ExecutionKPI::SetGenerateLegacyReport(self::$m_oConfig->Get('log_kpi_generate_legacy_report'));
ExecutionKPI::SetSlowQueries(self::$m_oConfig->Get('log_kpi_slow_queries'));
self::$m_bSkipCheckToWrite = self::$m_oConfig->Get('skip_check_to_write');
self::$m_bSkipCheckExtKeys = self::$m_oConfig->Get('skip_check_ext_keys');
@@ -6512,7 +6521,7 @@ abstract class MetaModel
$aCache['m_aExtensionClassNames'] = self::$m_aExtensionClassNames;
$aCache['m_Category2Class'] = self::$m_Category2Class;
$aCache['m_aRootClasses'] = self::$m_aRootClasses; // array of "classname" => "rootclass"
$aCache['m_aParentClasses'] = self::$m_aParentClasses; // array of ("classname" => array of "parentclass")
$aCache['m_aParentClasses'] = self::$m_aParentClasses; // array of ("classname" => array of "parentclass")
$aCache['m_aChildClasses'] = self::$m_aChildClasses; // array of ("classname" => array of "childclass")
$aCache['m_aClassParams'] = self::$m_aClassParams; // array of ("classname" => array of class information)
$aCache['m_aAttribDefs'] = self::$m_aAttribDefs; // array of ("classname" => array of attributes)
@@ -6537,6 +6546,7 @@ abstract class MetaModel
CMDBSource::InitFromConfig(self::$m_oConfig);
// Later when timezone implementation is correctly done: CMDBSource::SetTimezone($sDBTimezone);
ExecutionKPI::InitStats();
}
/**
@@ -6568,6 +6578,19 @@ abstract class MetaModel
return $value;
}
/**
* @internal Used for resetting the configuration during automated tests
* @param \Config $oConfiguration
*
* @return void
* @since 3.0.4 3.1.1 3.2.0
*/
public static function SetConfig(Config $oConfiguration)
{
self::$m_oConfig = $oConfiguration;
}
/**
* @return Config
*/
@@ -6756,7 +6779,13 @@ abstract class MetaModel
if ($bMustBeFound && empty($aRow))
{
throw new CoreException("No result for the single row query: '$sSQL'");
$sNotFoundErrorMessage = "No result for the single row query";
IssueLog::Info($sNotFoundErrorMessage, LogChannels::CMDB_SOURCE, [
'class' => $sClass,
'key' => $iKey,
'sql_query' => $sSQL,
]);
throw new CoreException($sNotFoundErrorMessage);
}
return $aRow;
@@ -6817,24 +6846,15 @@ abstract class MetaModel
$sClass = $aRow[$sClassAlias."finalclass"];
}
// if an object is already being updated, then this method will return this object instead of recreating a new one.
// At this point the method DBUpdate of a new object with the same class and id won't do anything due to reentrance protection,
// so to ensure that the potential modifications are correctly saved, the object currently being updated is returned.
// DBUpdate() method then will take care that all the modifications will be saved.
if (array_key_exists($sClassAlias.'id', $aRow)) {
$iKey = $aRow[$sClassAlias."id"];
$oObject = self::GetReentranceObject($sClass, $iKey);
if ($oObject !== false) {
return $oObject;
}
}
return new $sClass($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec);
}
/**
* Instantiate an object already persisted to the Database.
*
* Note that LinkedSet attributes are not loaded.
* DBObject::Reload() will be called when getting a LinkedSet attribute
*
* @api
* @see MetaModel::GetObjectWithArchive to get object even if it's archived
* @see utils::PushArchiveMode() to enable search on archived objects
@@ -6849,25 +6869,21 @@ abstract class MetaModel
* $bMustBeFound=false)
* @throws CoreException if no result found and $bMustBeFound=true
* @throws ArchivedObjectException if archive mode disabled and result is archived and $bMustBeFound=true
* @throws \Exception
*
*/
public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null)
{
$oObject = self::GetObjectWithArchive($sClass, $iKey, $bMustBeFound, $bAllowAllData, $aModifierProperties);
if (empty($oObject))
{
if (empty($oObject)) {
return null;
}
if (!utils::IsArchiveMode() && $oObject->IsArchived())
{
if (!utils::IsArchiveMode() && $oObject->IsArchived()) {
if ($bMustBeFound) {
throw new ArchivedObjectException("The object $sClass::$iKey is archived");
} else {
return null;
}
return null;
}
return $oObject;
@@ -6902,6 +6918,22 @@ abstract class MetaModel
return $iCount === 1;
}
public static function GetFinalClassName(string $sClass, int $iKey): string
{
if (MetaModel::IsStandaloneClass($sClass)) {
return $sClass;
}
$sRootClass = MetaModel::GetRootClass($sClass);
$sTable = MetaModel::DBGetTable($sRootClass);
$sKeyCol = MetaModel::DBGetKey($sRootClass);
$sEscapedKey = CMDBSource::Quote($iKey);
$sFinalClassField = Metamodel::DBGetClassField($sRootClass);
$sQuery = "SELECT `{$sFinalClassField}` FROM `{$sTable}` WHERE `{$sKeyCol}` = {$sEscapedKey}";
return CMDBSource::QueryToScalar($sQuery);
}
/**
* Search for the specified class and id. If the object is archived it will be returned anyway (this is for pre-2.4
* module compatibility, see N.1108)
@@ -7639,6 +7671,57 @@ abstract class MetaModel
unset(self::$m_aReentranceProtection[get_class($oObject)][$oObject->GetKey()]);
}
}
/**
* For test purpose
* @throws \ReflectionException
* @since 3.1.0
*/
public static function InitExtensions()
{
// Build the list of available extensions
//
$aInterfaces = [
'iLoginFSMExtension',
'iLogoutExtension',
'iLoginUIExtension',
'iPreferencesExtension',
'iApplicationUIExtension',
'iApplicationObjectExtension',
'iPopupMenuExtension',
'iPageUIExtension',
'iPageUIBlockExtension',
'iBackofficeLinkedScriptsExtension',
'iBackofficeEarlyScriptExtension',
'iBackofficeScriptExtension',
'iBackofficeInitScriptExtension',
'iBackofficeReadyScriptExtension',
'iBackofficeLinkedStylesheetsExtension',
'iBackofficeStyleExtension',
'iBackofficeDictEntriesExtension',
'iBackofficeDictEntriesPrefixesExtension',
'iPortalUIExtension',
'iQueryModifier',
'iOnClassInitialization',
'iModuleExtension',
'iKPILoggerExtension',
'ModuleHandlerApiInterface',
'iNewsroomProvider',
];
foreach ($aInterfaces as $sInterface) {
self::$m_aExtensionClassNames[$sInterface] = array();
}
foreach (get_declared_classes() as $sPHPClass) {
$oRefClass = new ReflectionClass($sPHPClass);
$oExtensionInstance = null;
foreach ($aInterfaces as $sInterface) {
if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable()) {
self::$m_aExtensionClassNames[$sInterface][$sPHPClass] = $sPHPClass;
}
}
}
}
}

View File

@@ -22,7 +22,9 @@
* A class to serialize the execution of some code sections
* Emulates the API of PECL Mutex class
* Relies on MySQL locks because the API sem_get is not always present in the
* installed PHP.
* installed PHP.
*
* @link https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html MySQL locking functions documentation
*
* @copyright Copyright (C) 2013-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
@@ -257,7 +259,7 @@ class iTopMutex
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
if (!$this->hDBLink) {
throw new Exception("Could not connect to the DB server (host=$sServer, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
}
// Make sure that the server variable `wait_timeout` is at least 86400 seconds for this connection,

View File

@@ -3526,6 +3526,7 @@ class CharConcatWSExpression extends CharConcatExpression
$aRes = array();
foreach ($this->m_aExpressions as $oExpr)
{
// TODO: Seems weird, this should rather be $aRes[] = $oExpr->Evaluate($aArgs);
$aRes .= $oExpr->Evaluate($aArgs);
}
return implode($this->m_separator, $aRes);

View File

@@ -169,7 +169,7 @@ class ormCaseLog {
}
// Process the case of an eventual remainder (quick migration of AttributeText fields)
if ($iPos < (strlen($this->m_sLog) - 1))
if ($iPos < (utils::StrLen($this->m_sLog) - 1))
{
$sTextEntry = substr($this->m_sLog, $iPos);
@@ -292,7 +292,7 @@ class ormCaseLog {
}
// Process the case of an eventual remainder (quick migration of AttributeText fields)
if ($iPos < (strlen($this->m_sLog) - 1)) {
if ($iPos < (utils::StrLen($this->m_sLog) - 1)) {
$sTextEntry = substr($this->m_sLog, $iPos);
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
@@ -373,7 +373,7 @@ class ormCaseLog {
}
// Process the case of an eventual remainder (quick migration of AttributeText fields)
if ($iPos < (strlen($this->m_sLog) - 1)) {
if ($iPos < (utils::StrLen($this->m_sLog) - 1)) {
$sTextEntry = substr($this->m_sLog, $iPos);
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
@@ -467,7 +467,7 @@ class ormCaseLog {
$oBlock->AddSubBlock($oCollapsibleBlock);
}
// Process the case of an eventual remainder (quick migration of AttributeText fields)
if ($iPos < (strlen($this->m_sLog) - 1)) {
if ($iPos < (utils::StrLen($this->m_sLog) - 1)) {
// In this case the format is always "text"
$sTextEntry = substr($this->m_sLog, $iPos);
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));

View File

@@ -86,6 +86,33 @@ class ormDocument
{
return ($this->m_data == null);
}
/**
* @param \ormDocument $oCompared
*
* @return bool True if the current ormDocument is equals to $oCompared EXCEPT for its download count. False if any other property is different OR if count is the same.
* @since 3.1.0 N°6502
*/
public function EqualsExceptDownloadsCount(ormDocument $oCompared): bool
{
// First checking equality on others properties
if ($oCompared->GetData() !== $this->GetData()) {
return false;
}
if ($oCompared->GetMimeType() !== $this->GetMimeType()) {
return false;
}
if ($oCompared->GetFileName() !== $this->GetFileName()) {
return false;
}
// Finally check equality of the download count
if ($oCompared->GetDownloadsCount() === $this->GetDownloadsCount()) {
return false;
} else {
return true;
}
}
public function GetMimeType()
{
@@ -316,6 +343,6 @@ class ormDocument
*/
public function GetSignature(): string
{
return md5($this->GetData());
return md5($this->GetData() ?? '');
}
}

View File

@@ -98,4 +98,14 @@ class PluginManager
return $oInstance;
}
/**
* For test purpose
* @return void
* @since 3.1.0
*/
protected static function ResetPlugins()
{
self::$m_aExtensionClasses = null;
}
}

View File

@@ -43,6 +43,12 @@
class SimpleCrypt
{
/**
* @var \SimpleCrypt
* @since 3.1.0 N°5388
*/
protected $oEngine;
public static function GetNewDefaultParams()
{
if(function_exists('sodium_crypto_secretbox_open') && function_exists('random_bytes')){
@@ -62,6 +68,7 @@ class SimpleCrypt
$sEngineName = 'SimpleCrypt' . $sEngineName . 'Engine';
return $sEngineName::GetNewDefaultParams();
}
/**
* Constructor
* @param string $sEngineName Engine for encryption. Values: Simple, Mcrypt, Sodium or OpenSSL

View File

@@ -537,7 +537,7 @@ EOF
}
else
{
throw new Exception('graphviz not found (executable path: '.$sDotExecutable.')');
throw new Exception('graphviz not found');
}
return $sHtml;
}
@@ -592,7 +592,7 @@ EOF
}
else
{
throw new Exception('graphviz not found (executable path: '.$sDotExecutable.')');
throw new Exception('graphviz not found');
}
return $sHtml;
}

View File

@@ -218,8 +218,8 @@ class SQLObjectQueryBuilder
continue;
}
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
// Skip this attribute if not made of SQL columns
if (count($oAttDef->GetSQLExpressions()) == 0)
// Skip this attribute if not made of SQL columns nor in current table
if (count($oAttDef->GetSQLExpressions()) == 0 || $oAttDef->IsExternalField())
{
continue;
}

View File

@@ -50,7 +50,7 @@ abstract class Trigger extends cmdbAbstractObject
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("action_list",
array("linked_class" => "lnkTriggerAction", "ext_key_to_me" => "trigger_id", "ext_key_to_remote" => "action_id", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array())));
$aTags = ContextTag::GetTags();
MetaModel::Init_AddAttribute(new AttributeEnumSet("context", array("allowed_values" => null, "possible_values" => new ValueSetEnumPadded($aTags), "sql" => "context", "depends_on" => array(), "is_null_allowed" => true, "max_items" => 12)));
MetaModel::Init_AddAttribute(new AttributeEnumSet("context", array("allowed_values" => null, "possible_values" => new ValueSetEnumPadded($aTags, true), "sql" => "context", "depends_on" => array(), "is_null_allowed" => true, "max_items" => 12)));
// "complement" is a computed field, fed by Trigger sub-classes, in general in ComputeValues method, for eg. the TriggerOnObject fed it with target_class info
MetaModel::Init_AddAttribute(new AttributeString("complement", array("allowed_values" => null, "sql" => "complement", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
@@ -121,7 +121,9 @@ abstract class Trigger extends cmdbAbstractObject
$oAction = MetaModel::GetObject('Action', $iActionId);
if ($oAction->IsActive())
{
$oKPI = new ExecutionKPI();
$oAction->DoExecute($this, $aContextArgs);
$oKPI->ComputeStatsForExtension($oAction, 'DoExecute');
}
}
}
@@ -254,6 +256,38 @@ abstract class TriggerOnObject extends Trigger
}
}
/**
* Activate trigger based on attribute list given instead of changed attributes
*
* @param array $aContextArgs
* @param array|null $aAttributes if null default to changed attributes
*
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @since 3.1.1 3.2.0 N°6228
*/
public function DoActivateForSpecificAttributes(array $aContextArgs, ?array $aAttributes)
{
if (isset($aContextArgs['this->object()']))
{
/** @var \DBObject $oObject */
$oObject = $aContextArgs['this->object()'];
if (is_null($aAttributes)) {
$aChanges = $oObject->ListPreviousValuesForUpdatedAttributes();
} else {
$aChanges = array_fill_keys($aAttributes, true);
}
if (false === $this->IsTargetObject($oObject->GetKey(), $aChanges)) {
return;
}
}
parent::DoActivate($aContextArgs);
}
/**
* @param $iObjectId
* @param array $aChanges

View File

@@ -161,7 +161,7 @@ abstract class UserRightsAddOnAPI
$oSearchSharers->AllowAllData();
$oSearchSharers->AddCondition_ReferencedBy($oShareSearch, 'sharing_org_id');
$aSharers = array();
foreach($oSearchSharers->ToDataArray(array('id')) as $aRow)
foreach($oSearchSharers->SelectAttributeToArray('id') as $aRow)
{
$aSharers[] = $aRow['id'];
}
@@ -186,7 +186,7 @@ abstract class UserRightsAddOnAPI
$oOrgField = new FieldExpression('org_id', $sShareClass);
$oSearchShares->AddConditionExpression(new BinaryExpression($oOrgField, 'IN', $oListExpr));
$aShared = array();
foreach($oSearchShares->ToDataArray(array($sShareAttCode)) as $aRow)
foreach($oSearchShares->SelectAttributeToArray($sShareAttCode) as $aRow)
{
$aShared[] = $aRow[$sShareAttCode];
}
@@ -235,15 +235,25 @@ abstract class User extends cmdbAbstractObject
MetaModel::Init_AddAttribute(new AttributeString("login", array("allowed_values" => null, "sql" => "login", "default_value" => null, "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeApplicationLanguage("language", array("sql"=>"language", "default_value"=>"EN US", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values" => new ValueSetEnum('enabled,disabled'), "styled_values"=>['enabled' => new ormStyle('ibo-dm-enum--User-status-enabled', 'ibo-dm-enum-alt--User-status-enabled', 'var(--ibo-dm-enum--User-status-enabled--main-color)', 'var(--ibo-dm-enum--User-status-enabled--complementary-color)', null, null),'disabled' => new ormStyle('ibo-dm-enum--User-status-disabled', 'ibo-dm-enum-alt--User-status-disabled', 'var(--ibo-dm-enum--User-status-disabled--main-color)', 'var(--ibo-dm-enum--User-status-disabled--complementary-color)', null, null)], "sql"=>"status", "default_value"=>"enabled", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeApplicationLanguage("language", array("sql" => "language", "default_value" => "EN US", "is_null_allowed" => false, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeEnum("status", array(
"allowed_values" => new ValueSetEnum('enabled,disabled'),
"styled_values" => [
'enabled' => new ormStyle('ibo-dm-enum--User-status-enabled', 'ibo-dm-enum-alt--User-status-enabled', 'var(--ibo-dm-enum--User-status-enabled--main-color)', 'var(--ibo-dm-enum--User-status-enabled--complementary-color)', null, null),
'disabled' => new ormStyle('ibo-dm-enum--User-status-disabled', 'ibo-dm-enum-alt--User-status-disabled', 'var(--ibo-dm-enum--User-status-disabled--main-color)', 'var(--ibo-dm-enum--User-status-disabled--complementary-color)', null, null),
],
"sql" => "status",
"default_value" => "enabled",
"is_null_allowed" => false,
"depends_on" => array(),
)));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("profile_list",
array("linked_class" => "URP_UserProfile", "ext_key_to_me" => "userid", "ext_key_to_remote" => "profileid", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array(), "display_style" => 'property')));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("allowed_org_list", array("linked_class" => "URP_UserOrg", "ext_key_to_me" => "userid", "ext_key_to_remote" => "allowed_org_id", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("profile_list",array("linked_class" => "URP_UserProfile", "ext_key_to_me" => "userid", "ext_key_to_remote" => "profileid", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array(), "display_style" => 'property', "with_php_constraint" => true, "with_php_computation" => true)));
MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("allowed_org_list", array("linked_class" => "URP_UserOrg", "ext_key_to_me" => "userid", "ext_key_to_remote" => "allowed_org_id", "allowed_values" => null, "count_min" => 1, "count_max" => 0, "depends_on" => array(), 'with_php_constraint' => true)));
MetaModel::Init_AddAttribute(new AttributeCaseLog("log", array("sql" => 'log', "is_null_allowed" => true, "default_value" => '', "allowed_values" => null, "depends_on" => array(), "always_load_in_tables" => false)));
// Display lists
MetaModel::Init_SetZListItems('details', array('contactid', 'org_id', 'email', 'login', 'language', 'status', 'profile_list', 'allowed_org_list')); // Unused as it's an abstract class !
MetaModel::Init_SetZListItems('details', array('contactid', 'org_id', 'email', 'login', 'language', 'status', 'profile_list', 'allowed_org_list', 'log')); // Unused as it's an abstract class !
MetaModel::Init_SetZListItems('list', array('finalclass', 'first_name', 'last_name', 'status', 'org_id')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'email', 'language', 'status', 'org_id')); // Criteria of the std search form
@@ -640,23 +650,23 @@ abstract class UserInternal extends User
{
$aParams = array
(
"category" => "core,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",
"reconc_keys" => array('login'),
"db_table" => "priv_internaluser",
"db_key_field" => "id",
"category" => "core,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",
"reconc_keys" => array('login'),
"db_table" => "priv_internaluser",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
// When set, this token allows for password reset
MetaModel::Init_AddAttribute(new AttributeOneWayPassword("reset_pwd_token", array("allowed_values"=>null, "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeOneWayPassword("reset_pwd_token", array("allowed_values" => null, "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('contactid', 'org_id', 'email', 'login', 'status', 'language', 'profile_list', 'allowed_org_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('contactid', 'org_id', 'email', 'login', 'status', 'language', 'profile_list', 'allowed_org_list', 'log')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'first_name', 'last_name', 'status', 'org_id')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('login', 'contactid', 'status', 'org_id')); // Criteria of the std search form
@@ -751,14 +761,25 @@ class UserRights
protected static $m_aCacheContactPictureAbsUrl = [];
/** @var UserRightsAddOnAPI $m_oAddOn */
protected static $m_oAddOn;
protected static $m_oUser;
protected static $m_oRealUser;
protected static $m_oUser = null;
protected static $m_oRealUser = null;
protected static $m_sSelfRegisterAddOn = null;
protected static $m_aAdmins = array();
protected static $m_aPortalUsers = array();
/** @var array array('sName' => $sName, 'bSuccess' => $bSuccess); */
private static $m_sLastLoginStatus = null;
/**
* @return void
* @since 3.0.4 3.1.1 3.2.0
*/
protected static function ResetCurrentUserData()
{
self::$m_oUser = null;
self::$m_oRealUser = null;
self::$m_sLastLoginStatus = null;
}
/**
* @param string $sModuleName
*
@@ -777,8 +798,7 @@ class UserRights
}
self::$m_oAddOn = new $sModuleName;
self::$m_oAddOn->Init();
self::$m_oUser = null;
self::$m_oRealUser = null;
self::ResetCurrentUserData();
}
/**
@@ -836,6 +856,8 @@ class UserRights
}
/**
* Set the current user (as part of the login process)
*
* @param string $sLogin Login of the concerned user
* @param string $sAuthentication
*
@@ -862,6 +884,19 @@ class UserRights
return true;
}
/**
* Reset current user and cleanup associated SESSION data
*
* @return void
* @since 3.0.4 3.1.1 3.2.0
*/
public static function Logoff()
{
self::ResetCurrentUserData();
Dict::SetUserLanguage(null);
self::_ResetSessionCache();
}
/**
* @param string $sLogin Login of the user to check the credentials for
* @param string $sPassword
@@ -1086,9 +1121,7 @@ class UserRights
}
/**
* Return the current user login or an empty string if nobody connected.
*
* @return string
* @return string connected {@see User} login field value, otherwise empty string
*/
public static function GetUser()
{
@@ -1536,9 +1569,9 @@ class UserRights
/**
* @param string $sClass
* @param int $iActionCode
* @param \DBObjectSet $oInstanceSet
* @param \User $oUser
* @param int $iActionCode see UR_ACTION_* constants
* @param DBObjectSet $oInstanceSet
* @param User $oUser
*
* @return int (UR_ALLOWED_YES|UR_ALLOWED_NO|UR_ALLOWED_DEPENDS)
* @throws \CoreException

View File

@@ -53,7 +53,13 @@ abstract class ValueSetDefinition
return $sAllowedValues;
}
/**
* @param array $aArgs
* @param string $sContains
* @param string $sOperation for the values {@see static::LoadValues()}
*
* @return array hash array of keys => values
*/
public function GetValues($aArgs, $sContains = '', $sOperation = 'contains')
{
if (!$this->m_bIsLoaded)
@@ -78,11 +84,22 @@ abstract class ValueSetDefinition
}
}
}
// Sort on the display value
asort($aRet);
$this->SortValues($aRet);
return $aRet;
}
/**
* @param array $aValues Values to sort in the form keys => values
*
* @return void
* @since 3.1.0 N°1646 Create method
*/
public function SortValues(array &$aValues): void
{
// Sort alphabetically on values
natcasesort($aValues);
}
abstract protected function LoadValues($aArgs);
}
@@ -189,11 +206,7 @@ class ValueSetObjects extends ValueSetDefinition
}
/**
* @param $aArgs
* @param string $sContains
* @param string $sOperation for the values @see self::LoadValues()
*
* @return array
* @inheritDoc
* @throws CoreException
* @throws OQLException
*/
@@ -451,10 +464,33 @@ class ValueSetObjects extends ValueSetDefinition
class ValueSetEnum extends ValueSetDefinition
{
protected $m_values;
/**
* @var bool $bSortByValues If true, values will be sorted at runtime (on their values, not their keys), otherwise it is sorted at compile time in a predefined order.
* {@see \MFCompiler::CompileAttributeEnumValues()} for complete reasons.
* @since 3.1.0 N°1646
*/
protected bool $bSortByValues;
public function __construct($Values)
/**
* @param array|string $Values
* @param bool $bLocalizedSort
*
* @since 3.1.0 N°1646 Add $bLocalizedSort parameter
*/
public function __construct($Values, bool $bSortByValues = false)
{
$this->m_values = $Values;
$this->bSortByValues = $bSortByValues;
}
/**
* @see \ValueSetEnum::$bSortByValues
* @return bool
* @since 3.1.0 N°1646
*/
public function IsSortedByValues(): bool
{
return $this->bSortByValues;
}
// Helper to export the data model
@@ -464,6 +500,27 @@ class ValueSetEnum extends ValueSetDefinition
return $this->m_aValues;
}
/**
* @inheritDoc
* @since 3.1.0 N°1646 Overload method
*/
public function SortValues(array &$aValues): void
{
// Force sort by values only if necessary
if ($this->bSortByValues) {
natcasesort($aValues);
return;
}
// Don't sort values as we rely on the order defined during compilation
return;
}
/**
* @param array|string $aArgs
*
* @return true
*/
protected function LoadValues($aArgs)
{
if (is_array($this->m_values))
@@ -491,9 +548,13 @@ class ValueSetEnum extends ValueSetDefinition
class ValueSetEnumPadded extends ValueSetEnum
{
public function __construct($Values)
/**
* @inheritDoc
* @since 3.1.0 N°6448 Add $bSortByValues parameter
*/
public function __construct($Values, bool $bSortByValues = false)
{
parent::__construct($Values);
parent::__construct($Values, $bSortByValues);
if (is_string($Values))
{
$this->LoadValues(null);
@@ -505,6 +566,7 @@ class ValueSetEnumPadded extends ValueSetEnum
$aPaddedValues = array();
foreach ($this->m_aValues as $sKey => $sVal)
{
// Pad keys to the min. length required by the \AttributeSet
$sKey = str_pad($sKey, 3, '_', STR_PAD_LEFT);
$aPaddedValues[$sKey] = $sVal;
}
@@ -553,7 +615,7 @@ class ValueSetEnumClasses extends ValueSetEnum
public function __construct($sCategories = '', $sAdditionalValues = '')
{
$this->m_sCategories = $sCategories;
parent::__construct($sAdditionalValues);
parent::__construct($sAdditionalValues, true /* Classes are always sorted alphabetically */);
}
protected function LoadValues($aArgs)

View File

@@ -80,16 +80,29 @@ $ibo-field--enable-bulk--checkbox--margin-left: $ibo-spacing-300 !default;
}
}
}
/*N°6543 - We need the rule to keep text inside the column when width is defined*/
&[data-attribute-type="AttributeHtml"],
&[data-attribute-type="AttributeText"] {
&[data-attribute-flag-read-only="true"] {
display: grid;
> .ibo-field--value {
max-width: 100%;
overflow: auto;
}
}
}
}
/* Large field = Label on top, value below */
.ibo-field-large {
display: block;
/* Large field = Label on top, value below */
.ibo-field-large {
display: block;
.ibo-field--label {
position: relative; /* Necessary for fullscreen toggler */
display: flex;
align-items: center;
.ibo-field--label {
position: relative; /* Necessary for fullscreen toggler */
display: flex;
align-items: center;
max-width: initial;
width: 100%;
}

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