Compare commits

..

350 Commits

Author SHA1 Message Date
acognet
2363f2fb21 Merge remote-tracking branch 'origin/develop' into feature/4157
# Conflicts:
#	application/ui.linkswidget.class.inc.php
#	core/dbobject.class.php
2022-09-29 17:49:10 +02:00
Eric Espie
f32f36fc74 N°5551 - System information database size is way off 2022-09-26 11:51:51 +02:00
bdalsass
5157f511fc N°5073 - Implements line actions in a datatable (#337)
* datatable row actions

Below is a sample of extra param to enable feature:

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

* datatable row actions (update)

* datatable row actions (update)

* datatable row actions (add template role)

* datatable row actions (align actions)

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

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

* datatable row actions (row actions templates)

* datatable row actions (row actions templates)

* datatable row actions (row actions templates)

* datatable row actions (extends to static and form)

* datatable row actions (extends to static and form)

* datatable row actions (code review S)

* datatable row actions (code review S)

* datatable row actions (code review S)

* Update js/dataTables.main.js

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

* Update js/dataTables.main.js

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

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

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

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

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

* datatable row actions (code review M)

* Update js/dataTables.main.js

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

* Update js/dataTables.main.js

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

* Update js/dataTables.main.js

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

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

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

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

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

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

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

* Update js/dataTables.main.js

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

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

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

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

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

* Update application/utils.inc.php

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

* datatable row actions (code review M2)

* datatable row actions (code review M3)

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2022-09-26 08:20:28 +02:00
Eric Espie
3196e105a1 N°4756 - small adjustments 2022-09-23 15:10:21 +02:00
Eric Espie
b9d865f881 N°4756 - Fix DBInsert/NoReload call order 2022-09-21 18:08:44 +02:00
Molkobain
2e39a650eb Merge remote-tracking branch 'origin/support/3.0' into develop 2022-09-21 17:48:57 +02:00
Pierre Goiffon
e4f6a02de6 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	lib/composer/autoload_files.php
#	lib/composer/autoload_real.php
#	lib/composer/autoload_static.php
2022-09-21 14:28:25 +02:00
Eric Espie
fac455da48 N°4756 - Global event listeners 2022-09-21 10:34:38 +02:00
odain
b01627f39d Merge branch 'saas/3.0' into develop 2022-09-20 16:04:55 +02:00
odain-cbd
766c9f0e7e N°5305 - CSV import ergonomy PR (#332)
Reworked UI feedbacks on following attributes:
- enum
- date
- external key
2022-09-20 16:00:33 +02:00
Eric Espie
d1414a3f34 N°4756 - revert events for datamodel/2.x objects for backward compatibility 2022-09-20 15:35:59 +02:00
Molkobain
6386a302b2 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	composer.json
2022-09-20 13:44:11 +02:00
Molkobain
31454b2946 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	core/config.class.inc.php
#	setup/setuputils.class.inc.php
2022-09-20 13:27:20 +02:00
Eric Espie
4aad555649 N°4756 - Fix unit tests, 2022-09-20 12:18:53 +02:00
Eric Espie
93ee565d29 N°4756 - Fix unit tests 2022-09-19 16:59:51 +02:00
Eric Espie
6c097a128b N°4756 - Fix unit tests 2022-09-19 16:49:40 +02:00
Eric Espie
eea3f78cec N°4756 - Fix unit tests 2022-09-19 16:28:13 +02:00
Eric Espie
6c4caf64c8 N°4756 - Fix unit tests 2022-09-19 16:26:02 +02:00
Eric Espie
88f0013330 N°4756 - Fix unit tests 2022-09-19 16:09:02 +02:00
Eric Espie
aa31da34e5 N°5389 - Email Notification templates with AttributeLinkedSetIndirect failed 2022-09-19 15:30:24 +02:00
Eric Espie
71464f6d0e Merge branch 'develop' into feature/faf_event_service
# Conflicts:
#	core/cmdbobject.class.inc.php
#	core/dbobject.class.php
#	core/designdocument.class.inc.php
#	lib/composer/autoload_files.php
#	lib/composer/autoload_static.php
2022-09-19 09:45:08 +02:00
Pierre Goiffon
e5a9648206 📝 CONTRIBUTING : keep stickers images history in directory 2022-09-14 18:02:16 +02:00
Molkobain
abf2120097 Add 3.0.2-1 to mermaid graph 2022-09-14 17:14:48 +02:00
Pierre Goiffon
2f5eaa2dfd Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	sources/Core/Email/EmailSwiftMailer.php
2022-09-14 15:36:14 +02:00
Pierre Goiffon
b161a863df Merge remote-tracking branch 'origin/support/3.0' into develop 2022-09-14 12:17:57 +02:00
acognet
cea75e07f2 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-09-14 11:01:30 +02:00
Pierre Goiffon
7d1893a2b8 Add @RohacRichard to contributors list 🙌 (#333) 2022-09-14 10:52:20 +02:00
Molkobain
b32d82f11e Add @eduardomozart to contributors list 🙌 2022-09-13 19:35:30 +02:00
Eduardo Mozart de Oliveira
9f963686b5 N°4765 - Update brazilian translations thanks to @eduardomozart (#268)
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2022-09-13 17:20:53 +02:00
Richard Roháč
56f8dd2942 🐛 N°5522 - Fix session storage (breadcrumbs) not cleared on logout (#333)
Co-authored-by: richard.rohac <richard.rohac@elso-systems.com>
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2022-09-13 17:15:54 +02:00
Eric Espie
e4e4365cce Merge remote-tracking branch 'origin/support/3.0' into develop 2022-09-13 12:18:09 +02:00
Anne-Catherine
b061c29a5b N°2250 - DisplayObject with ormLinkSet ignore Removed (#309) 2022-09-13 10:33:12 +02:00
Eric Espie
5d9e58f194 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-09-12 11:48:44 +02:00
Pierre Goiffon
e6821f1d16 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-09-08 18:53:20 +02:00
Eric Espie
320eb83c10 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-09-07 10:48:45 +02:00
Eric Espie
7621a0fd31 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-09-05 12:14:57 +02:00
Benjamin Dalsass
2b84c0384d N°5085 - Move menu is impossible - Error after compilation 2022-08-31 15:31:00 +02:00
Molkobain
146ed0f6a6 N°3390 - Update comment 2022-08-30 20:18:30 +02:00
Molkobain
9826b40ec6 Merge remote-tracking branch 'origin/support/3.0' into develop
# 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
2022-08-30 17:53:13 +02:00
Benjamin Dalsass
b154952869 N°3390 - Upgrade from Symfony 3.4 to Symfony 5.4 2022-08-30 14:36:26 +02:00
Pierre Goiffon
ef07f8366d Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-29 13:50:34 +02:00
Benjamin Dalsass
9a9707e15c N°4517 - PHP 8.1 compatibility: Fix deprecated call str_replace with null value 2022-08-25 11:52:11 +02:00
Molkobain
bbb5b86864 N°5122 - Minor libs update according to new PHP min version 2022-08-23 11:26:47 +02:00
Molkobain
7b60c9c71a Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	composer.json
#	composer.lock
#	lib/composer/installed.json
#	lib/composer/installed.php
#	lib/twig/twig/.travis.yml
#	lib/twig/twig/CHANGELOG
#	lib/twig/twig/composer.json
#	lib/twig/twig/lib/Twig/Loader/String.php
#	lib/twig/twig/src/Environment.php
#	lib/twig/twig/src/ExpressionParser.php
#	lib/twig/twig/src/Extension/AbstractExtension.php
#	lib/twig/twig/src/Extension/CoreExtension.php
#	lib/twig/twig/src/Extension/ProfilerExtension.php
#	lib/twig/twig/src/Loader/ArrayLoader.php
#	lib/twig/twig/src/Loader/ChainLoader.php
#	lib/twig/twig/src/Loader/FilesystemLoader.php
#	lib/twig/twig/src/Node/EmbedNode.php
#	lib/twig/twig/src/Node/Expression/BlockReferenceExpression.php
#	lib/twig/twig/src/Node/Expression/Test/DefinedTest.php
#	lib/twig/twig/src/Node/Expression/TestExpression.php
#	lib/twig/twig/src/Node/ForNode.php
#	lib/twig/twig/src/Node/IfNode.php
#	lib/twig/twig/src/Node/IncludeNode.php
#	lib/twig/twig/src/Node/ModuleNode.php
#	lib/twig/twig/src/Node/Node.php
#	lib/twig/twig/src/Node/WithNode.php
#	lib/twig/twig/src/Template.php
#	lib/twig/twig/src/TokenParser/ApplyTokenParser.php
2022-08-23 10:38:09 +02:00
Pierre Goiffon
2f90b8e6a6 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-22 09:04:39 +02:00
Molkobain
428827ed91 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-18 09:26:01 +02:00
Pierre Goiffon
a31451d5af Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-17 14:29:49 +02:00
Molkobain
99f75111ce N°4517 - PHP 8.1: Fix str_replace() called with null value 2022-08-16 19:03:30 +02:00
Molkobain
a2c6b08cf4 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php
2022-08-16 17:52:03 +02:00
Molkobain
25af155113 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	datamodels/2.x/itop-config-mgmt/module.itop-config-mgmt.php
2022-08-16 17:38:10 +02:00
Pierre Goiffon
d5917299fc N°5410 Fix PHP notice after removing autoloader 2022-08-16 16:58:58 +02:00
Molkobain
25103b9f0b Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-16 14:35:39 +02:00
Molkobain
5442768229 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-16 13:47:00 +02:00
Molkobain
8f0a62c1c9 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-16 12:17:23 +02:00
acognet
02d5f8560f Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-12 18:26:00 +02:00
acognet
c94e24c0ff Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	application/cmdbabstract.class.inc.php
2022-08-12 18:17:00 +02:00
Molkobain
9438d063cd Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-12 14:59:38 +02:00
Molkobain
bb948fdd1b Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-12 11:15:30 +02:00
Eric Espie
6a384b0405 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-12 10:51:46 +02:00
Benjamin Dalsass
df6d459bb0 Symfony 5.4 migration - missing macro import in login template 2022-08-12 10:21:07 +02:00
Eric Espie
37cb882507 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-11 17:18:10 +02:00
acognet
ebab664c1a Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-11 16:54:43 +02:00
Eric Espie
a577799fee Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-11 11:33:59 +02:00
acognet
26b779cd0e Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-10 17:51:35 +02:00
Eric Espie
7a9e16766f Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-10 15:28:13 +02:00
Molkobain
706b81b929 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-10 15:16:09 +02:00
Eric Espie
8fab14f072 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-10 15:01:54 +02:00
Molkobain
08387e2814 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-10 14:54:23 +02:00
Stephen Abello
1f3e91efae Merge branch 'support/3.0' into develop 2022-08-10 14:09:50 +02:00
Molkobain
2aacb58e7f Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-09 17:55:27 +02:00
Eric Espie
cc7dceed1a Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-09 17:42:23 +02:00
Eric Espie
8d8cefe948 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-09 17:11:34 +02:00
Eric Espie
83b4f2bf34 Fix autoloader (directories name) 2022-08-09 14:48:59 +02:00
Eric Espie
11d9435f08 Fix autoloader 2022-08-09 11:53:18 +02:00
Eric Espie
952bceb3c3 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	composer.json
#	core/config.class.inc.php
#	core/legacy/dbobjectsearchlegacy.class.php
#	core/legacy/querybuildercontextlegacy.class.inc.php
#	core/legacy/querybuilderexpressionslegacy.class.inc.php
#	lib/composer/autoload_files.php
#	lib/composer/autoload_static.php
2022-08-09 10:49:40 +02:00
Eric Espie
4b0c94d609 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-09 09:27:49 +02:00
Molkobain
02afa2f19b N°5270 - Move "apereo/phpcas" lib from "authent-cas" module to core composer.json (#320)
* N°5270 - Move "apereo/phpcas" lib from "authent-cas" module to core composer.json

* N°5270 - Rebase on develop branch
2022-08-08 16:44:42 +02:00
Stephen Abello
8cdc251378 Merge branch 'support/3.0' into develop 2022-08-08 16:42:24 +02:00
Molkobain
57c36d0e51 N°5122 - Update libs to new PHP requirements 2022-08-08 14:10:26 +02:00
Molkobain
30021d9236 N°5122 - Change PHP min. version to 7.4.0 2022-08-08 13:59:24 +02:00
Molkobain
95c43adc4e Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-08 12:00:16 +02:00
Molkobain
8357e5b8d0 Fix typo 2022-08-05 19:53:12 +02:00
Molkobain
02af3cf197 N°4838 - Fix comment 2022-08-05 19:45:46 +02:00
Molkobain
1655a377f0 N°4838 - Redirect to login page automatically from logoff screen 2022-08-04 22:56:42 +02:00
acognet
aad4a5e20c Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-04 16:43:20 +02:00
Molkobain
78a29ab3ca N°5108 - Rename variable 2022-08-04 10:11:35 +02:00
Molkobain
79021fba1f Add @rudnerbjoern to contributors list, thanks! 🙌 2022-08-03 13:32:15 +02:00
Molkobain
cf21025cd7 N°3390 - Cleanup "debug" param remains by moving logs to debug level / portal channel 2022-08-03 10:51:32 +02:00
Molkobain
b718d40eb9 N°4517 - PHP 8.1: Replace deprecated FILTER_SANITIZE_STRING constant with FILTER_SANITIZE_SPECIAL_CHARS 2022-08-03 10:37:44 +02:00
Molkobain
ef81bdb1e1 N°4517 - PHP 8.1: Fix return type for classes implementing ArrayAccess
Return type cannot be set to mixed as the code need to be compatible with PHP 7.2+
2022-08-03 10:37:02 +02:00
Molkobain
784f568575 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-08-03 10:35:25 +02:00
Benjamin Dalsass
b30bc64319 update mermaid itop history 2.7.7 2022-08-02 14:44:51 +02:00
Benjamin Dalsass
ca5bbc596e pass twig environment to twig filter filter 2022-08-01 10:33:45 +02:00
Benjamin Dalsass
8188d76f63 remove mixed return type from portal AppVariable 2022-08-01 10:32:04 +02:00
Benjamin Dalsass
57a3da1cca remove outdated PHP min comment 2022-08-01 09:30:18 +02:00
Benjamin Dalsass
eb9b9cfce7 remove kernel depreciation calls 2022-08-01 09:30:18 +02:00
Benjamin Dalsass
8b8e6bab33 remove request param for env dev switch (use log instead for debug) 2022-08-01 09:30:18 +02:00
Benjamin Dalsass
549bfcafd9 Change boostrap default env to prod 2022-08-01 09:30:18 +02:00
Molkobain
435dce91d7 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-07-29 15:28:11 +02:00
Stephen Abello
8c7fa53696 N°5335 Disable datatables hyperlinks when rows are selectable 2022-07-29 09:59:00 +02:00
Molkobain
d60109d3c9 N°4517 - PHP 8.1: Fix typo from htmlentities() replacements 2022-07-28 15:46:40 +02:00
Molkobain
12c9aad445 N°4517 - PHP 8.1: Fix typos 2022-07-28 11:52:28 +02:00
Molkobain
0c90b2f1aa Fix code style 2022-07-28 11:52:28 +02:00
Molkobain
8c27476d2a N°4517 - PHP 8.1: Fix trim() called with null value 2022-07-28 11:52:28 +02:00
Molkobain
dfd0406160 N°4517 - PHP 8.1: Replace usage of strftime() with date() 2022-07-28 11:52:27 +02:00
Molkobain
af36177b03 N°4517 - PHP 8.1: Fix false to array conversion which is longer allowed 2022-07-28 11:22:26 +02:00
Molkobain
7e15c0874d Update autoloaders 2022-07-28 10:34:30 +02:00
Molkobain
86e1f165cd Merge remote-tracking branch 'origin/develop' into feature/4517-from-dev
# Conflicts:
#	CONTRIBUTING.md
#	composer.lock
#	core/designdocument.class.inc.php
#	lib/composer/installed.json
#	lib/composer/installed.php
2022-07-28 10:30:25 +02:00
Molkobain
ee783257d9 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	lib/symfony/polyfill-iconv/Resources/charset/from.big5.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp037.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp1006.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp1026.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp424.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp437.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp500.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp737.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp775.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp850.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp852.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp855.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp856.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp857.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp860.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp861.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp862.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp863.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp864.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp865.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp866.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp869.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp874.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp875.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp932.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp936.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp949.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.cp950.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.iso-8859-1.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.iso-8859-10.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.iso-8859-11.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.iso-8859-13.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.iso-8859-14.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.iso-8859-15.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.iso-8859-16.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.iso-8859-2.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.iso-8859-3.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.iso-8859-4.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.iso-8859-5.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.iso-8859-6.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.iso-8859-7.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.iso-8859-8.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.iso-8859-9.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.koi8-r.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.koi8-u.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.us-ascii.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.windows-1250.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.windows-1251.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.windows-1252.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.windows-1253.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.windows-1254.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.windows-1255.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.windows-1256.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.windows-1257.php
#	lib/symfony/polyfill-iconv/Resources/charset/from.windows-1258.php
2022-07-28 10:11:13 +02:00
Molkobain
d4a0c141b3 #296 Fix typos 2022-07-27 10:35:37 +02:00
Benjamin Dalsass
192dd86469 correct composer PHP max version and update setup utils constant 2022-07-27 08:54:39 +02:00
bdalsass
cd861263fb Update sources/Application/TwigBase/Twig/Extension.php
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2022-07-27 08:54:03 +02:00
Benjamin Dalsass
dbd5c32535 restore important security fix on twig filter
see N°4867 "Twig content not allowed" error when use the extkey widget search icon in the user portal
2022-07-27 08:54:03 +02:00
Molkobain
76062ca8b2 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-07-26 22:32:22 +02:00
Pierre Goiffon
ce7f251064 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-07-26 17:21:59 +02:00
Stephen Abello
5e497aff90 Update OAuth Azure 2022-07-26 14:36:58 +02:00
Stephen Abello
70d1504cd4 Update guzzle library 2022-07-26 14:36:58 +02:00
Benjamin Dalsass
742ef2b23b Update pelago/emogrifier bundle (^6.0.0)
>> files omitted
2022-07-26 14:36:58 +02:00
Benjamin Dalsass
f6c50733fc Update pelago/emogrifier bundle (^6.0.0) 2022-07-26 14:36:58 +02:00
Benjamin Dalsass
25a612da04 Update Symfony bundles 2022-07-26 14:36:58 +02:00
Benjamin Dalsass
31cc294cc3 Upgrade composer php max version to 8.1 2022-07-26 14:36:58 +02:00
bdalsass
d93a0b698b N°5281 - Portal: Add method to declare brick controller since Symfony 5.4 migration (#313)
* Add controllers declaration interface to ItopExtensionsExtraRoutes

* N°3390 - Upgrade from Symfony 3.4 to Symfony 5.4
Container setter issues

* Show Symfony original error page when app debug is on

* Update datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php

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

* Update datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php

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

* Update datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php

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

* Update datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php

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

* Update datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php

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

* Update datamodels/2.x/itop-portal-base/portal/src/Helper/ObjectFormHandlerHelper.php

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

* Update datamodels/2.x/itop-portal-base/portal/src/Helper/ObjectFormHandlerHelper.php

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

* Update datamodels/2.x/itop-portal-base/portal/src/Helper/ObjectFormHandlerHelper.php

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

* Update datamodels/2.x/itop-portal-base/portal/src/Helper/ObjectFormHandlerHelper.php

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

* Update datamodels/2.x/itop-portal-base/portal/src/Routing/ItopExtensionsExtraRoutes.php

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

* Update datamodels/2.x/itop-portal-base/portal/src/Form/ObjectFormManager.php

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

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2022-07-25 14:19:47 +02:00
Molkobain
9cf329c475 Add @Timmy38 to the sample data, welcome! 👋 2022-07-25 11:50:04 +02:00
Molkobain
f978cc4e22 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-07-20 17:51:29 +02:00
Molkobain
494b70950c Merge remote-tracking branch 'origin/support/3.0' into develop 2022-07-18 16:49:05 +02:00
Molkobain
f71fadda17 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-07-18 14:17:16 +02:00
Molkobain
91c73b562a Merge remote-tracking branch 'origin/support/3.0' into develop 2022-07-13 17:15:53 +02:00
Eric Espie
3ae862d690 N°5310 - Mail to ticket: Migration 3.1 - PHPMail 2022-07-12 13:37:02 +02:00
Molkobain
51d5c00bcd N°5313 - Remove "enable_formmanager_content_check" module parameter from itop-portal 2022-07-12 12:06:35 +02:00
Molkobain
99b4fceaf3 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-07-12 11:43:49 +02:00
Eric Espie
e91e53c0ec Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	composer.lock
#	datamodels/2.x/itop-oauth-client/datamodel.itop-oauth-client.xml
#	lib/composer/autoload_files.php
#	lib/composer/autoload_real.php
#	lib/composer/autoload_static.php
#	lib/composer/installed.json
#	lib/composer/installed.php
2022-07-11 17:24:06 +02:00
acognet
c7eea3f51f N°4157 - Relations, modify one and add it again fails, the adding is ignored - Dont-work. It's a idee to solve the problem of display modified link, but actually there is a js bug. 2022-07-08 12:24:53 +02:00
acognet
5a77159ece N°4157 - Relations, modify one and add it again fails, the adding is ignored - check uniqueness rule before saving object 2022-07-08 12:15:46 +02:00
Eric Espie
2c265aab44 N°4756 - Ease extensibility for CRUD operations
- Change event order in CRUD
 - Add LinkHostObject for link update
 - Add events EVENT_SERVICE_DB_ARCHIVE and  EVENT_SERVICE_DB_UNARCHIVE
2022-07-08 10:19:54 +02:00
Eric Espie
fe28319d22 Merge from Develop 2022-07-06 14:28:55 +02:00
Pierre Goiffon
c56cb516ed 🔨 composer/listOutdated : fix default composer path and update doc 2022-07-05 17:59:53 +02:00
Romain Quetiez
db9b239d71 Update CONTRIBUTING.md
Combodo's Copyright does not apply to third-party libraries / icons.
2022-07-05 17:16:00 +02:00
Pierre Goiffon
d793a4e3ab 🚚 Version history : move to /.doc 2022-07-05 15:19:40 +02:00
Pierre Goiffon
dbf4389307 📝 Version history : rotate version labels 2022-07-05 14:34:20 +02:00
Pierre Goiffon
e62c1c629f 📝 Version history : remove duplicated 2.4.0 tag 2022-07-05 14:33:07 +02:00
Pierre Goiffon
797184f452 📝 Version history : change branches order 2022-07-05 14:30:26 +02:00
Pierre Goiffon
9f0ac99abb 📝 New version history file 2022-07-05 12:38:14 +02:00
acognet
6d025479c6 composer dump autoloader 2022-07-01 11:35:42 +02:00
acognet
616381a16c N°5232 - Deprecate \CMDBObject::DBCloneTracked 2022-07-01 11:35:42 +02:00
acognet
e1bf3d9ee0 N°2743 - Upgrade libraries php-parser
php composer.phar update  nikic/php-parser
2022-07-01 11:35:41 +02:00
Romain Quetiez
5ff4db5a4d Update CONTRIBUTING.md
Clarify the copyright ownership
2022-07-01 11:35:41 +02:00
Molkobain
01023801b8 Prepare 3.0.0-beta4
- Update translations
- Update version number
2022-07-01 11:35:40 +02:00
acognet
00bdcf14dd composer dump autoloader 2022-07-01 11:18:41 +02:00
acognet
b98e1889d5 composer dump autoloader 2022-07-01 11:06:58 +02:00
acognet
d1eef0853b N°5232 - Deprecate \CMDBObject::DBCloneTracked 2022-07-01 10:49:07 +02:00
acognet
29f23fa992 N°2743 - Upgrade libraries php-parser
php composer.phar update  nikic/php-parser
2022-07-01 10:45:55 +02:00
acognet
7ec12f1e12 N°4517 - PHP 8.1: Replace htmlentities(***, ENT_QUOTES, 'UTF-8') with utils::EscapeHtml 2022-07-01 09:22:25 +02:00
acognet
139be3a9b7 Fix display of hub connector 2022-07-01 09:05:49 +02:00
Eric Espie
7f6f5c0c3b Refactor event name 2022-06-30 16:24:13 +02:00
Benjamin Dalsass
f5389a2d2d N°4517 - PHP 8.1 compatibility: Replace strlen with Utils::StrLen to avoid null value issue 2022-06-30 16:18:27 +02:00
acognet
31dffcf5e0 N°4517 - PHP 8.1:Fix use of strftime 2022-06-30 15:41:38 +02:00
acognet
4afadc39aa Fix libpng warning: iCCP: known incorrect sRGB profile 2022-06-30 15:41:08 +02:00
Benjamin Dalsass
f13ed9494b N°4517 - PHP 8.1 compatibility: Replace strlen with Utils::StrLen to avoid null value issue 2022-06-30 15:12:59 +02:00
Benjamin Dalsass
acd5547618 N°4517 - PHP 8.1 compatibility: Replace strlen with Utils::StrLen to avoid null value issue 2022-06-30 15:10:28 +02:00
Benjamin Dalsass
8733ac416c N°4517 - PHP 8.1 compatibility: Function with return type must return a value 2022-06-30 15:05:22 +02:00
Benjamin Dalsass
ab40b1b556 N°4517 - PHP 8.1 compatibility: Fix deprecated call str_replace with null value 2022-06-30 15:03:12 +02:00
Eric Espie
682ab44dea Merge branch 'develop' into feature/faf_event_service 2022-06-30 14:22:52 +02:00
Romain Quetiez
0ff25728a4 Update CONTRIBUTING.md
Clarify the copyright ownership
2022-06-30 09:58:17 +02:00
Pierre Goiffon
56fdf8dd63 Merge remote-tracking branch 'origin/support/3.0.0-beta4' into develop
# Conflicts:
#	datamodels/2.x/authent-local/dictionaries/de.dict.authent-local.php
#	datamodels/2.x/authent-local/dictionaries/es_cr.dict.authent-local.php
#	datamodels/2.x/authent-local/dictionaries/zh_cn.dict.authent-local.php
#	datamodels/2.x/itop-backup/dictionaries/de.dict.itop-backup.php
#	datamodels/2.x/itop-backup/dictionaries/fr.dict.itop-backup.php
#	datamodels/2.x/itop-backup/dictionaries/zh_cn.dict.itop-backup.php
#	datamodels/2.x/version.xml
#	dictionaries/cs.dictionary.itop.ui.php
#	dictionaries/de.dictionary.itop.ui.php
#	dictionaries/tr.dictionary.itop.ui.php
#	dictionaries/ui/components/datatable/de.dictionary.itop.datatable.php
#	dictionaries/ui/components/datatable/es_cr.dictionary.itop.datatable.php
#	dictionaries/ui/components/datatable/ru.dictionary.itop.datatable.php
#	dictionaries/ui/components/datatable/zh_cn.dictionary.itop.datatable.php
#	dictionaries/zh_cn.dictionary.itop.ui.php
2022-06-29 10:00:38 +02:00
acognet
0b2510f6cc N°4517 - PHP 8.1: Fix htmlentities 2022-06-29 09:17:25 +02:00
acognet
eb408e2ea4 N°4517 - PHP 8.1: Fix static method in a trait 2022-06-29 09:17:08 +02:00
acognet
958a86bbb5 N°4517 - PHP 8.1: Fix strlen 2022-06-29 09:16:50 +02:00
acognet
b45da1eaa5 N°4517 - PHP 8.1: Fix use of Serializable 2022-06-29 09:16:25 +02:00
Molkobain
6facaec353 N°4517 - PHP 8.1: Replace mixed return types with #[\ReturnTypeWillChange] PHP attributes to restore PHP 7.4 compatibility
Also add comment on previous #[\ReturnTypeWillChange]
2022-06-28 16:34:41 +02:00
Molkobain
7dc4a2776b N°4517 - PHP 8.1: Fix deprecated notice for null value passed to string parameter of native PHP functions 2022-06-28 16:34:35 +02:00
Molkobain
09b0ac38ef N°4517 - PHP 8.1: Hide (for now) deprecate notices of mismatching return types when union types as it would not be supported by PHP 7.2 => 7.4 2022-06-28 16:34:27 +02:00
Molkobain
9d62e87de4 N°4517 - PHP 8.1: Fix MFElement::__construct() being called with $namespaceURI as null when it should be strictly a string
MFDocument::createElement() default value for $namespaceURI is now a string
2022-06-28 16:34:18 +02:00
Molkobain
04dbaef257 N°4517 - PHP 8.1: Fix deprecated usages of MySQLi
* init() method should not be called in OOP
  * real_connect() "flags" param should not be null to match its type (int)
2022-06-28 16:34:12 +02:00
Molkobain
ff12dd99b5 N°4517 - PHP 8.1: Fix return types to match ArrayAccess interface 2022-06-28 16:34:07 +02:00
Molkobain
7ee5184afc N°4517 - PHP 8.1: Fix return types to match Iterator interface 2022-06-28 16:34:01 +02:00
Molkobain
212565b01e N°4517 - PHP 8.1: Fix return types to match parent Countable class
* DBObjectSet::Seek() now returns void instead of the current object, this was mandatory in to order to match parent class. Returned object is never used in iTop core and extensions so we consider it ok and will document it in the migration notes.
2022-06-28 16:32:26 +02:00
Pierre Goiffon
130b4b176f Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	composer.lock
#	lib/composer/autoload_classmap.php
#	lib/composer/autoload_files.php
#	lib/composer/autoload_static.php
#	lib/composer/installed.json
#	lib/composer/installed.php
2022-06-28 16:00:54 +02:00
Purple Grape
0c14423213 improved Chinese translation (#302)
1. words refine
2. remove unwanted blank
3. punctuation related issue

Co-Authored-By: Devin <bdejin@hotmail.com>
2022-06-28 12:34:21 +02:00
acognet
bf01be6f19 N°4988 - History API : change consumers (iTop core) 2022-06-28 11:30:16 +02:00
acognet
b47d3cda12 N°5232 - Deprecate \CMDBObject::DBCloneTracked - fix phpdoc 2022-06-28 11:29:15 +02:00
acognet
01c76f19ed N°4988 - History API : change consumers (iTop core) 2022-06-28 11:26:19 +02:00
Pierre Goiffon
eb8ea0fbd4 Remove Symfony tests dir 2022-06-28 11:14:52 +02:00
Pierre Goiffon
784e553d23 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	composer.json
#	composer.lock
#	lib/composer/autoload_classmap.php
#	lib/composer/autoload_files.php
#	lib/composer/autoload_real.php
#	lib/composer/autoload_static.php
#	lib/composer/installed.php
2022-06-28 11:10:01 +02:00
Benjamin Dalsass
de040a0922 Merge 3.0 into Dev : Upgrade oauth module version to 3.1.0 2022-06-27 09:54:50 +02:00
Benjamin Dalsass
4af54f26b6 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-06-27 09:52:15 +02:00
Benjamin Dalsass
02dab4eb72 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	lib/composer/autoload_files.php
#	lib/composer/autoload_static.php
#	templates/pages/backoffice/oauth/Wizard.html.twig
2022-06-27 08:38:01 +02:00
acognet
306e16147a N°5232 - Deprecate \CMDBObject::DBCloneTracked 2022-06-22 09:19:58 +02:00
acognet
aae1e12b2e N°4988 - History API : change consumers (iTop core) 2022-06-21 18:12:35 +02:00
Benjamin Dalsass
bd3724be8f symfony 5.4 (twig filter initialization correction) 2022-06-20 08:22:23 +02:00
Benjamin Dalsass
683ab25e83 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	lib/composer/autoload_files.php
#	lib/composer/autoload_static.php
2022-06-17 10:14:01 +02:00
Benjamin Dalsass
977a8add67 Adjust php setup requirement to version 7.2.5.
Improve test error message.
2022-06-16 10:04:13 +02:00
bdalsass
79da71ecf8 migration symfony 5 4 (#300)
* symfony 5.4 (diff dev)

* symfony 5.4 (working)

* symfony 5.4 (update autoload)

* symfony 5.4 (remove swiftmailer mailer implementation)

* symfony 5.4 (php doc and split Global accessor class)


### Impacted packages:

composer require php:">=7.2.5 <8.0.0" symfony/console:5.4.* symfony/dotenv:5.4.* symfony/framework-bundle:5.4.* symfony/twig-bundle:5.4.* symfony/yaml:5.4.* --update-with-dependencies

composer require symfony/stopwatch:5.4.* symfony/web-profiler-bundle:5.4.* --dev --update-with-dependencies
2022-06-16 09:13:24 +02:00
Pierre Goiffon
abb13b70b9 Compiler : when generating model file add it to module (#296)
Before this fix, when creating a module for iTop that was creating PHP code (an iTop class for example, a snippet, ...) it was mandatory to add the model.php file in your module.php file ("datamodel" key). If you forgot this, then the compilation was completed OK but the result code wasn't included in iTop.

Now the compiler automatically adds the model.php file to the included files.
2022-06-15 18:07:16 +02:00
Pierre Goiffon
023ead39ec N°4280 Handle modules with non existing model.*.php files imported (#295)
We were crashing iTop when having a module with : 
* in the 'datamodel' key in the module.*.php file a 'model.*.php' declared
* no model.*.php file in the module sources
* no model.*.php generated after compilation

This behavior is improved : 
* if isdevenv an exception will be thrown detailing which module is concerned
* else : 
  - model.*.php won't be added in iTop datamodel autoload
  - a SetupLog::Error will be made
2022-06-15 17:20:13 +02:00
Pierre Goiffon
8890940b06 CONTRIBUTING.md : update stickers text (#292)
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2022-06-14 18:57:44 +02:00
Pierre Goiffon
44a8bfd764 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	composer.lock
#	core/email.class.inc.php
#	datamodels/2.x/itop-portal-base/portal/src/Twig/AppExtension.php
#	lib/bin/generate-deps-for-config-factory
#	lib/bin/generate-deps-for-config-factory.bat
#	lib/bin/generate-factory-for-class
#	lib/bin/generate-factory-for-class.bat
#	lib/composer/autoload_classmap.php
#	lib/composer/autoload_files.php
#	lib/composer/autoload_psr4.php
#	lib/composer/autoload_real.php
#	lib/composer/autoload_static.php
#	lib/composer/installed.json
#	lib/composer/installed.php
#	lib/laminas/laminas-servicemanager/bin/generate-deps-for-config-factory
#	lib/laminas/laminas-servicemanager/bin/generate-factory-for-class
#	lib/laminas/laminas-servicemanager/src/AbstractPluginManager.php
#	lib/laminas/laminas-servicemanager/src/ServiceManager.php
#	lib/laminas/laminas-validator/src/Db/AbstractDb.php
#	lib/laminas/laminas-validator/src/Isbn/Isbn10.php
#	lib/laminas/laminas-validator/src/Isbn/Isbn13.php
#	sources/Core/Email/EmailFactory.php
#	templates/pages/backoffice/oauth/Wizard.html.twig
2022-06-14 09:41:45 +02:00
Benjamin Dalsass
7176218a5f composer autoload mapping correction 2022-06-10 07:37:00 +02:00
Eric Espie
6aef59e42d Merge branch 'develop' into feature/faf_event_service
# Conflicts:
#	core/dbobject.class.php
#	datamodels/2.x/itop-config/config.php
#	lib/composer/autoload_classmap.php
#	lib/composer/autoload_static.php
#	lib/composer/installed.php
2022-06-08 16:51:44 +02:00
bdalsass
2b885beb82 Feature/merge 3 0 develop (#298)
* merging 3.0 into develop

* N°5102 - Allow to send emails (eg. notifications) using GSuite SMTP and OAuth
* migration to iTop 3.1

Co-authored-by: Eric Espie <eric.espie@combodo.com>
2022-06-08 16:27:20 +02:00
Eric Espie
86024107af deprecated includes 2022-06-08 15:55:20 +02:00
Eric Espie
754f87fd0c debug log 2022-06-08 15:54:27 +02:00
Eric Espie
972e894bc5 debug log 2022-06-02 17:32:15 +02:00
Eric Espie
86a2db7e7f Remove AFTER_DISPLAY_PAGE Event 2022-06-02 17:23:07 +02:00
Eric Espie
4c31081de2 Remove AFTER_DISPLAY_PAGE Event 2022-06-02 17:19:43 +02:00
Eric Espie
23c95ebbf3 Block if event is not registered 2022-06-02 13:48:59 +02:00
Eric Espie
e77f21a0b5 refactor Event Listeners 2022-06-01 14:29:43 +02:00
Eric Espie
35e1f080b8 Attachments events 2022-06-01 11:46:00 +02:00
Eric Espie
812c1f6bb4 Migrate datamodel 2022-06-01 11:19:46 +02:00
Eric Espie
9adb7f20ce Event "Compute Values" 2022-06-01 09:11:03 +02:00
Eric Espie
c7e54c66c8 Event "Compute Values" 2022-06-01 09:09:16 +02:00
Eric Espie
f6855b0d2b Display events in the datamodel page 2022-06-01 08:44:12 +02:00
Eric Espie
1ceef602f0 Remove debug 2022-05-31 15:44:56 +02:00
Eric Espie
93cc29f4d9 refactor 2022-05-31 15:42:09 +02:00
Eric Espie
c9317542c8 refactor 2022-05-31 15:37:29 +02:00
Eric Espie
aed8337c51 Protection against reentrance for DBUpdate 2022-05-30 17:03:47 +02:00
Eric Espie
af4a5e1b8d New CRUD behaviour (removed Reload in DBInsert and DBUpdate) and protection against reentrance 2022-05-27 17:46:10 +02:00
acognet
1fd792fed9 N°3950 - Deprecate old unreferenced methods that are @deprecated - remove application/itopwebpage.class.inc.php 2022-05-25 11:35:54 +02:00
Eric Espie
e7c09c83f0 internal doc 2022-05-25 10:59:38 +02:00
Eric Espie
56103d1952 Refactor 2022-05-25 10:38:07 +02:00
Eric Espie
301c308fec Refactor 2022-05-25 10:32:41 +02:00
Eric Espie
b827c68187 Merge branch 'develop' into feature/faf_event_service 2022-05-25 10:04:54 +02:00
Pierre Goiffon
3d541b2a2d Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/src/Twig/AppExtension.php
2022-05-24 11:29:12 +02:00
acognet
44ca8acf37 remove useless comment 2022-05-23 14:27:51 +02:00
acognet
b371072a61 N°3141 - Deprecate legacy SQL build 2022-05-20 11:34:52 +02:00
acognet
d874b749d0 Revert N°3141 - Deprecate legacy SQL build 2022-05-20 11:22:20 +02:00
acognet
cff26563cc N°3950 - Deprecate old unreferenced methods that are @deprecated - Expression:: Render 2022-05-20 11:09:03 +02:00
acognet
fd1064b044 N°3950 - Deprecate old unreferenced methods that are @deprecated - BulkUpdate 2022-05-20 10:40:29 +02:00
Molkobain
6086131d3c Merge remote-tracking branch 'origin/support/3.0' into develop 2022-05-20 09:36:03 +02:00
Molkobain
2b0cc4d0e7 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-05-18 20:01:52 +02:00
Molkobain
4eb43cd02d Merge remote-tracking branch 'origin/support/3.0' into develop 2022-05-18 19:29:57 +02:00
Molkobain
6a332ca60b Merge remote-tracking branch 'origin/support/3.0' into develop 2022-05-18 10:43:22 +02:00
Pierre Goiffon
82f4736ad2 N°4921 Fix cannot find object when attvalue contains special cars
Sanitization filter wasn't set, so we were filtering a bit too much :/
2022-05-17 15:13:24 +02:00
Molkobain
020937d4f6 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-05-16 16:22:53 +02:00
acognet
438b84d4ee N°3141 - Deprecate legacy SQL build 2022-05-16 09:24:42 +02:00
Molkobain
afc8354523 Code cleanup 2022-05-14 20:42:48 +02:00
Molkobain
23fab1bab0 N°4287 - Portal: Factorize TWIG extensions between portal and backoffice 2022-05-14 20:42:48 +02:00
acognet
8b26b34f96 N°4690 - Deprecate "FilterCodes" 2022-05-13 10:28:53 +02:00
acognet
c20cedf266 Deprecate unused and broken method 2022-05-12 17:09:16 +02:00
Molkobain
cfd0b80225 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-05-11 18:14:00 +02:00
Molkobain
865aaf0191 Merge branch 'support/3.0' into develop 2022-05-11 18:12:46 +02:00
Molkobain
bb2c9dedeb N°4628 - Upgrade bulma lib to v0.9.4 to avoid hack from N°4481 2022-05-11 16:55:25 +02:00
Timmy38
891dd31290 N°5072 - Fix default priority to undefined (null) or default value if not nullable (#279)
* Fix default priority to undefined

* Default priority : if null value not allowed, use default value for priority

* Default priority : moving code from ComputeValues() to ComputePriority()

* Default priority : re-indenting code
2022-05-10 17:36:56 +02:00
acognet
6a4217e87b N°3950 - Deprecate old unreferenced methods that are @deprecated - remove ajaxwebpage and iTopWebPage 2022-05-10 16:39:31 +02:00
Eric Espie
34cac2ac1b No autoload for deprecated class 2022-05-10 14:57:04 +02:00
Molkobain
1dd25be6ef N°4034 - Deprecate duplicated TWIG extensions class 2022-05-09 17:58:26 +02:00
Molkobain
7bfa7fa9f4 N°2883 - Improve XML compiler robustness on branding logos 2022-05-09 14:32:03 +02:00
acognet
0a5411d411 N°2363 - API : deprecate old linkedset update pattern 2022-05-09 10:53:53 +02:00
acognet
7598b9e29a N°3357 - Deprecate core/expression.class.inc.php 2022-05-06 17:26:29 +02:00
Pierre Goiffon
edd96d46f9 📝 CONTRIBUTING : fix version name
Rename was forgotten in b026e5ab
Thanks @hipska to have pointed this out !
2022-05-06 15:11:01 +02:00
Pierre Goiffon
00581b82f5 📝 CONTRIBUTING remove duplicate on "allow edits from maintainers" option 2022-05-06 11:11:23 +02:00
Pierre Goiffon
8c8ab9988c 📝 CONTRIBUTING : added the PR "allow edits from maintainers" option 2022-05-03 17:02:48 +02:00
Pierre Goiffon
fe4aa9dcd7 N°3091 Update PHPUnit to 8.5 : fix setUp and teardDown methods signatures
"Return type declaration must be compatible with parent"
See https://phpunit.de/announcements/phpunit-8.html "Return Type of Template Methods"

Was done in support/2.7 (ec143c43) but there is one new test in this branch...
2022-05-03 11:14:59 +02:00
Pierre Goiffon
a71a5f53f7 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	core/email.class.inc.php
2022-05-03 10:52:50 +02:00
Pierre Goiffon
197bad19ab Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	composer.json
#	composer.lock
#	lib/autoload.php
#	lib/composer/autoload_real.php
#	lib/composer/autoload_static.php
#	lib/composer/installed.php
#	lib/true/punycode/LICENSE
#	sources/Composer/iTopComposer.php
2022-05-02 14:33:29 +02:00
Pierre Goiffon
32ed40e733 N°4284 remove unused dependencies 2022-04-29 17:11:10 +02:00
Pierre Goiffon
fa5644064a 📄 N°4284 N°4307 Update licenses 2022-04-29 16:43:56 +02:00
Pierre Goiffon
dd3f77a397 Update lib test dirs 2022-04-29 16:41:38 +02:00
Pierre Goiffon
909fb4c75b Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	composer.json
#	composer.lock
#	core/email.class.inc.php
#	lib/composer/autoload_classmap.php
#	lib/composer/autoload_files.php
#	lib/composer/autoload_psr4.php
#	lib/composer/autoload_static.php
#	lib/composer/installed.json
#	lib/composer/installed.php
#	lib/swiftmailer/swiftmailer/.gitattributes
#	lib/swiftmailer/swiftmailer/.gitignore
#	lib/swiftmailer/swiftmailer/.php_cs.dist
#	lib/swiftmailer/swiftmailer/CHANGES
#	lib/swiftmailer/swiftmailer/LICENSE
#	lib/swiftmailer/swiftmailer/composer.json
#	lib/swiftmailer/swiftmailer/lib/classes/Swift.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Attachment.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/AbstractFilterableInputStream.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/ArrayByteStream.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/FileByteStream.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/ByteStream/TemporaryFileByteStream.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/GenericFixedWidthReader.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/UsAsciiReader.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReader/Utf8Reader.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/CharacterReaderFactory/SimpleCharacterReaderFactory.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/ArrayCharacterStream.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/CharacterStream/NgCharacterStream.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/ConfigurableSpool.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/DependencyContainer.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/EmbeddedFile.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Base64Encoder.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/QpEncoder.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Encoder/Rfc2231Encoder.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandEvent.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Events/CommandListener.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventDispatcher.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Events/EventObject.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseEvent.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Events/ResponseListener.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendEvent.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Events/SendListener.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Events/SimpleEventDispatcher.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportChangeListener.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionEvent.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Events/TransportExceptionListener.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/FailoverTransport.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/FileSpool.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Filterable.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Image.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/InputByteStream.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/IoException.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/ArrayKeyCache.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/DiskKeyCache.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/KeyCacheInputStream.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/NullKeyCache.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/KeyCache/SimpleKeyCacheInputStream.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/LoadBalancedTransport.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mailer.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mailer/ArrayRecipientIterator.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/MemorySpool.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Message.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Attachment.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/Base64ContentEncoder.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/PlainContentEncoder.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EmbeddedFile.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/EncodingObserver.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Header.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/Base64HeaderEncoder.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/AbstractHeader.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/DateHeader.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/IdentificationHeader.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/MailboxHeader.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/OpenDKIMHeader.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/ParameterizedHeader.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/PathHeader.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/Headers/UnstructuredHeader.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/MimePart.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderFactory.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleHeaderSet.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMessage.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Mime/SimpleMimeEntity.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/MimePart.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/NullTransport.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/AntiFloodPlugin.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/BandwidthMonitorPlugin.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/DecoratorPlugin.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ImpersonatePlugin.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/LoggerPlugin.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/ArrayLogger.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Loggers/EchoLogger.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/MessageLogger.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/PopBeforeSmtpPlugin.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/RedirectingPlugin.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporter.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ReporterPlugin.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HitReporter.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/Reporters/HtmlReporter.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Plugins/ThrottlerPlugin.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Preferences.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/SendmailTransport.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Signer.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Signers/BodySigner.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DKIMSigner.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Signers/DomainKeySigner.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Signers/HeaderSigner.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Signers/OpenDKIMSigner.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Signers/SMimeSigner.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/SmtpTransport.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Spool.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/SpoolTransport.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/ByteArrayReplacementFilter.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilter.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/StreamFilters/StringReplacementFilterFactory.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/SwiftException.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/AbstractSmtpTransport.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/AuthHandler.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/Esmtp/Authenticator.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpHandler.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/EsmtpTransport.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/FailoverTransport.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/IoBuffer.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/LoadBalancedTransport.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/NullTransport.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SendmailTransport.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SmtpAgent.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/SpoolTransport.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/Transport/StreamBuffer.php
#	lib/swiftmailer/swiftmailer/lib/classes/Swift/TransportException.php
#	lib/swiftmailer/swiftmailer/lib/dependency_maps/cache_deps.php
#	lib/swiftmailer/swiftmailer/lib/dependency_maps/mime_deps.php
#	lib/swiftmailer/swiftmailer/lib/dependency_maps/transport_deps.php
#	lib/swiftmailer/swiftmailer/lib/mime_types.php
#	lib/swiftmailer/swiftmailer/lib/preferences.php
#	lib/swiftmailer/swiftmailer/lib/swift_required.php
#	lib/swiftmailer/swiftmailer/lib/swiftmailer_generate_mimes_config.php
2022-04-29 16:37:39 +02:00
vdumas
c0f0e354dd N°5124 - ConnectableCI to NetworkDevice update failing 2022-04-26 13:03:43 +02:00
vdumas
d6f58d04f1 N°4702 - DataModel : wrong attribute type for SLA.customercontracts_list 2022-04-25 15:08:46 +02:00
vdumas
cc88ee1de0 Revert "N°4702 - DataModel : wrong attribute type for SLA.customercontracts_list"
This reverts commit 6b7183d500706a2bcf6340eca3478a7ca22ce7e3.
2022-04-25 15:08:46 +02:00
vdumas
8008e4d840 N°4702 - DataModel : wrong attribute type for SLA.customercontracts_list 2022-04-25 15:08:46 +02:00
Molkobain
a4390fbb57 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	test/application/search/CriterionConversionTest.php
#	test/application/search/CriterionParserTest.php
#	test/application/search/SearchFormTest.php
2022-04-25 14:03:02 +02:00
vdumas
f4170ade8a N°4702 - DataModel : wrong attribute type for SLA.customercontracts_list 2022-04-25 10:23:12 +02:00
vdumas
1d9a9bcb28 N°3889 - Missing default search zlist on SLA and SLT 2022-04-22 12:52:01 +02:00
Stephen Abello
178ba60973 N°4307 Replace SwiftMailer with Laminas-mail 2022-04-22 10:58:28 +02:00
Pierre Goiffon
c47f224566 💡 N°4919 add @since 2022-04-19 15:29:35 +02:00
Pierre Goiffon
8fcd454445 N°4919 New 'Launch setup" in Application Upgrade (#244)
Admin will now be able to re-launch the iTop setup directly from the administration console in the Administration / Application Upgrade screen.
Before the only way to launch setup on an existing iTop instance was to change permissions on the configuration file.

This button will be enabled depending on the isDevEnv (if true it will be displayed) and `setup.launch_button.enabled` new configuration parameter (not present by default ; if set to false will always hide the button, if set to true will always display it, if not set will display button depending on isDevEnv only).

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2022-04-19 14:36:15 +02:00
Stephen Abello
ba9d5f0c4b N°3607 Handle error and fix unit test 2022-04-19 09:31:02 +02:00
Stephen Abello
0efc978004 N°3607 Change XML node format 2022-04-15 15:48:21 +02:00
Molkobain
b026e5ab87 Update branches examples on contributing guide 2022-04-13 16:22:02 +02:00
Molkobain
1e75154cc7 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-04-13 16:15:26 +02:00
Thomas Casteleyn
e4a04de9f4 N°5066 - Clean CMDBSource methods (#229)
* Improve function calls

* Deprecate CMDBSource::GetServerInfo

* Improve documentation

* Update core/cmdbsource.class.inc.php

Co-authored-by: Pierre Goiffon <pierre.goiffon@combodo.com>

* Single quotes

* Apply suggestion, so  method still returns empty string instead of null

Co-authored-by: Pierre Goiffon <pierre.goiffon@combodo.com>
2022-04-12 17:10:41 +02:00
Timothee
645e612e8b N°5042 - Fixing inconsistent "Problem" tickets display compared to other types of tickets 2022-04-12 14:49:15 +02:00
Stephen Abello
26a78a10bc N°3607 Allow extensions to register variables/stylesheets to be compiled with every theme 2022-04-12 10:23:50 +02:00
Eric Espie
8ba28adf68 Refactor 2022-04-08 15:10:58 +02:00
Molkobain
1feb5e6ad6 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-04-08 13:32:08 +02:00
Eric Espie
34a26d33a1 Fix reloads 2022-04-07 18:15:18 +02:00
Eric Espie
b0a55e057b Events to cmdbAbstract 2022-04-07 17:02:19 +02:00
Eric Espie
5ac9b05b2d new CRUD 2022-04-06 23:51:21 +02:00
Eric Espie
63e582a07f Unit tests 2022-04-05 10:30:47 +02:00
Eric Espie
470076daa2 Unit tests 2022-04-05 10:29:52 +02:00
Eric Espie
f6d92a189b CRUD reentrance protection 2022-04-05 10:28:12 +02:00
Molkobain
42599eae64 N°3838 - Improve PHPDoc of \DBObjectSearch::AddCondition() as it cannot have multiple condition on the same field 2022-04-04 17:49:41 +02:00
Eric Espie
c788c93542 Merge branch 'develop' into feature/faf_event_service 2022-03-30 08:22:47 +02:00
acognet
b75f29b8d7 N°3067 - Cannot edit in place attributes with HTML editors 2022-03-28 14:18:54 +02:00
Eric Espie
81d285a143 CEX SDK V2:
* Pre and post actions
2022-03-28 09:43:46 +02:00
Molkobain
ad4442ae78 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-03-23 09:49:11 +01:00
acognet
1db4b9a12e Merge remote-tracking branch 'origin/support/3.0' into develop 2022-03-16 09:44:33 +01:00
Eric Espie
a773f0d8a2 EventService: refactoring 2022-03-15 17:53:38 +01:00
Eric Espie
29c6b73d93 EventService: refactoring 2022-03-15 17:50:13 +01:00
Eric Espie
5b52ca4776 EventService: phpdoc 2022-03-15 15:45:03 +01:00
Eric Espie
8ddaf1b731 EventService: call FireEvent with only one parameter (PSR14) 2022-03-15 15:41:55 +01:00
Eric Espie
964ce44577 EventService: code cleanup 2022-03-15 15:11:57 +01:00
Eric Espie
cea6c557ce Merge branch 'develop' into feature/faf_event_service 2022-03-15 10:52:45 +01:00
Molkobain
f4ecdf116a Merge remote-tracking branch 'origin/support/3.0' into develop 2022-03-09 19:55:35 +01:00
Pierre Goiffon
8d4545f008 N°4921 - Add support for attcode & attvalue parameters in URL to access an object (#273)
This is a way to solve problems when an object ref and id isn't equals : for example id=99 and ref = 100.
This could happen since iTop 2.7.0, see https://www.itophub.io/wiki/page?id=2_7_0%3Arelease%3A2_7_whats_new#ticket_ref_generation

Note that id parameter can be set to the object's friendlyname as a workaround, but this might not be enough for some objects where friendlyname contains more that the ref field (for example title, org, ...)

* Admin console : new UI.php URL parameters : attcode and attvalue.
Example URLs : 
/pages/UI.php?operation=details&class=UserRequest&id=99
/pages/UI.php?operation=details&class=UserRequest&attcode=ref&attvalue=R-000100

An exception will be thrown if no object is found or multiple instances are.

* User portal
New route : /object/view/{sObjectClass}/{sObjectAttCode}/{sObjectAttValue}
For example :
/pages/exec.php/object/view/UserRequest/99?exec_module=itop-portal-base&exec_page=index.php&portal_id=itop-portal
/pages/exec.php/object/view/UserRequest/ref/R-000100?exec_module=itop-portal-base&exec_page=index.php&portal_id=itop-portal

On error we will get a 404 error page
2022-03-09 10:51:21 +01:00
Molkobain
5854c199d0 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-03-07 12:54:13 +01:00
Eric Espie
a937d08655 Fix CI (wrong module version) 2022-03-03 10:42:24 +01:00
Eric Espie
4bdf84bf6c Merge remote-tracking branch 'origin/support/3.0' into develop 2022-03-03 10:23:38 +01:00
Pierre Goiffon
9ce88699ca Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/mode_mosaic.html.twig
#	datamodels/2.x/itop-portal-base/portal/templates/bricks/browse/mode_tree.html.twig
2022-02-22 09:40:37 +01:00
acognet
f9e8bf88c8 N°4662 - Portal: Fix broken display of the services catalogue when installing Service Provider + Sample Data 2022-02-15 09:50:44 +01:00
Pierre Goiffon
bddf7a11c8 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-02-14 09:01:26 +01:00
Pierre Goiffon
ee7fb15fc2 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-02-14 08:44:12 +01:00
Pierre Goiffon
8473df0f0c 💡 Add attribute phpdoc for \RestResultWithObjects::$objects 2022-02-11 18:26:16 +01:00
Pierre Goiffon
7eab003f3c N°4714 Fix setup crashing with "Call to undefined method utils::GetItopVersionWikiSyntax()"
Missed this in the merge (d437e2d6) cause there were lots of datamodels/2.x/*/module.*.php changes to discard :(
2022-02-08 15:58:34 +01:00
Pierre Goiffon
d437e2d662 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	application/displayblock.class.inc.php
#	core/config.class.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-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-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
2022-02-08 15:43:20 +01:00
Eric Espie
99819527db Changed the <Hooks> grammar (typo) 2022-02-07 16:28:19 +01:00
Eric Espie
965273009c Changed the <Hooks> grammar to allow module extensibility 2022-02-07 16:10:00 +01:00
Eric Espie
7bee616b1b Update application events definition 2022-02-07 15:25:38 +01:00
Eric Espie
f5302133d9 Add application events definition 2022-02-07 15:06:00 +01:00
Eric Espie
bf2aba1b06 Reformat code 2022-02-07 14:11:00 +01:00
Molkobain
60c372f535 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-02-02 11:20:58 +01:00
Molkobain
22f9d16743 N°4415 - Remove SetupPage:log* 2022-01-27 17:37:14 +01:00
Molkobain
3d3d94e837 N°2522 - API : Deprecate SetupPage:log* 2022-01-27 17:23:12 +01:00
Molkobain
3bec96abe1 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	core/config.class.inc.php
#	setup/itopdesignformat.class.inc.php
2022-01-27 16:09:22 +01:00
Molkobain
44c18395b3 N°4072 - Deprecate ajax.render.php xlsx_* operations 2022-01-27 11:42:30 +01:00
Molkobain
09ff052c1f N°4072 - Add method to deprecate PHP endpoints 2022-01-27 11:42:30 +01:00
Benjamin Dalsass
9ef5fd70c0 Merge remote-tracking branch 'origin/develop' into develop 2022-01-26 08:12:13 +01:00
Benjamin Dalsass
3b599a40f3 N°4454 - Measuring the use of the queryphrase book (code review 2) 2022-01-26 08:11:16 +01:00
Molkobain
1495ab992f N°3824 - Update autoloader 2022-01-23 16:13:55 +01:00
Molkobain
d89dc1aa4d N°3824 - History: Remove deprecated APIs from 2.7 and older 2022-01-21 18:10:12 +01:00
Benjamin Dalsass
1fac2ae787 N°4454 - Measuring the use of the queryphrase book (code review) 2022-01-19 07:48:03 +01:00
Molkobain
ff7d1f2d6a Merge remote-tracking branch 'origin/support/3.0' into develop 2022-01-17 19:21:52 +01:00
Benjamin Dalsass
91d4da85e1 N°4454 - Measuring the use of the queryphrase book 2022-01-14 11:12:30 +01:00
Pierre Goiffon
dcd52d6919 N°3863 - exec.php : allow subdirectories in the page parameter (#221) 2022-01-12 17:24:30 +01:00
Molkobain
9a75ca2c21 Merge remote-tracking branch 'origin/support/3.0' into develop 2022-01-12 16:06:29 +01:00
Molkobain
812606db78 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	core/config.class.inc.php
2022-01-12 15:54:18 +01:00
Molkobain
c17ceb1702 N°4621 - Rename corresponding unit tests folder 2022-01-11 14:35:45 +01:00
Thomas Casteleyn
ce01dad875 utils::GetImageSize can be simplified since minimal PHP version is 7.1.3 2022-01-05 10:15:41 +01:00
Eric Espie
c04beea38c OnInsert and OnUpdate replacement 2021-12-31 17:07:59 +01:00
Pierre Goiffon
9ca106d889 N°4621 Fix criterionconversionabstract.class.inc.php requires 2021-12-31 16:01:29 +01:00
Pierre Goiffon
5f575d524a N°4621 Fix naming inconsistencies in sources/* 2021-12-31 15:44:59 +01:00
Eric Espie
93d88cca37 refactor 2021-12-31 15:26:54 +01:00
Eric Espie
0997750816 cleanup 2021-12-31 15:06:27 +01:00
Eric Espie
427c8b0794 * better comparison 2021-12-31 14:33:23 +01:00
Eric Espie
06008ed8eb * Add KPI object loaded counter 2021-12-31 12:12:32 +01:00
Eric Espie
374b71c017 * refactor
* Add event AFTER_DISPLAY_PAGE
2021-12-31 11:06:03 +01:00
Eric Espie
fba78e7d9b Changed event name to DISPLAY_OBJECT_DETAILS 2021-12-31 08:53:36 +01:00
Eric Espie
551abc861e Merge branch 'develop' into feature/faf_event_service
# Conflicts:
#	application/cmdbabstract.class.inc.php
#	application/loginwebpage.class.inc.php
#	core/dbobject.class.php
#	core/log.class.inc.php
#	lib/composer/autoload_classmap.php
#	lib/composer/autoload_static.php
#	setup/compiler.class.inc.php
#	test/phpunit.xml.dist
2021-12-31 08:42:16 +01:00
acognet
16142bd979 Change version number to 3.1.0 2021-12-23 17:23:24 +01:00
acognet
c1e2ba8abc Change version number to 3.1.0 2021-12-23 17:05:54 +01:00
acognet
cc2efe432e Change version number to 3.1.0 2021-12-23 16:37:32 +01:00
acognet
e879ae2f11 Change version number to 3.1.0 2021-12-23 14:14:51 +01:00
Pierre Goiffon
8bcd2ce571 Merge remote-tracking branch 'origin/support/3.0' into develop
# Conflicts:
#	core/config.class.inc.php
2021-12-23 10:05:53 +01:00
acognet
d301562ffe Change version number to 3.1.0 2021-12-23 09:41:16 +01:00
acognet
caed22ea8d Change version number to 3.1.0 2021-12-23 09:39:24 +01:00
Molkobain
a90a483756 Prepare 3.0.0-beta4
- Update translations
- Update version number
2021-09-08 16:31:11 +02:00
Eric
78f51d40f6 DBObject GetValues() 2020-07-17 15:41:52 +02:00
Eric
8bfc54e6b4 Event Service 2020-07-15 14:42:33 +02:00
4383 changed files with 180517 additions and 162392 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -0,0 +1,61 @@
# iTop version history
```mermaid
%%{init: { 'logLevel': 'debug', 'theme': 'base', 'gitGraph': {'showBranches': true,'mainBranchName': 'develop','rotateCommitLabel': true}} }%%
gitGraph
commit id: "2016-07-06" tag: "2.3.0"
branch support/2.3 order: 900
commit id: "2016-07-08" tag: "2.3.1"
commit id: "2016-12-22" tag: "2.3.3"
commit id: "2017-04-14" tag: "2.3.4"
checkout develop
commit id: "2017-07-12" tag: "2.4.0-beta" type: REVERSE
commit id: "2017-11-16" tag: "2.4.0"
branch support/2.4 order: 890
commit id: "2018-02-14" tag: "2.4.1"
checkout develop
commit id: "2018-04-25" tag: "2.5.0-beta" type: REVERSE
checkout support/2.4
commit id: "2018-06-14" tag: "2.4.2"
checkout develop
commit id: "2018-06-27" tag: "2.5.0"
branch support/2.5 order: 880
checkout develop
commit id: "2019-01-09" tag: "2.6.0"
branch support/2.6 order: 870
commit id: "2019-03-28" tag: "2.6.1"
checkout develop
commit id: "2019-12-18" tag: "2.7.0-beta" type: REVERSE
checkout support/2.5
commit id: "2020-01-22" tag: "2.5.4"
checkout support/2.6
commit id: "2020-01-23" tag: "2.6.3"
checkout develop
commit id: "2020-01-29" tag: "2.7.0-beta2" type: REVERSE
branch support/2.7 order: 860
commit id: "2020-04-01" tag: "2.7.0-1"
checkout support/2.6
commit id: "2020-04-22" tag: "2.6.4"
checkout support/2.7
commit id: "2020-06-26" tag: "2.7.1"
checkout support/2.7
commit id: "2020-12-09" tag: "2.7.3"
commit id: "2021-03-31" tag: "2.7.4"
checkout develop
commit id: "2021-04-06" tag: "3.0.0-beta" type: REVERSE
checkout support/2.7
commit id: "2021-07-05" tag: "2.7.5"
checkout develop
commit id: "2021-07-05." tag: "3.0.0-beta2" type: REVERSE
checkout support/2.7
commit id: "2021-12-17" tag: "2.7.6"
checkout develop
commit id: "2022-01-04" tag: "3.0.0"
branch support/3.0 order: 850
commit id: "2022-04-08" tag: "3.0.1"
checkout support/2.7
commit id: "2022-07-11" tag: "2.7.7"
checkout support/3.0
commit id: "2022-09-12" tag: "3.0.2-1"
checkout develop
```

View File

@@ -1,83 +0,0 @@
<!--
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
- ...
-->
- [ ] ...
- [ ] ...
- [ ] ...

View File

@@ -1,16 +0,0 @@
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 }}

10
.gitignore vendored
View File

@@ -19,7 +19,7 @@
# composer reserver directory, from sources, populate/update using "composer install"
vendor/*
tests/*/vendor/*
test/vendor/*
# all conf but listing prevention
/conf/**
@@ -37,9 +37,7 @@ tests/*/vendor/*
# iTop extensions
/extensions/**
!/extensions/.htaccess
!/extensions/readme.txt
!/extensions/web.config
# all logs but listing prevention
/log/**
@@ -47,10 +45,8 @@ tests/*/vendor/*
!/log/index.php
!/log/web.config
# 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
# PHPUnit cache file
/test/.phpunit.result.cache
# 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 !== 'cli')
if (php_sapi_name() !== '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

@@ -19,16 +19,23 @@
*
*/
$iTopFolder = __DIR__ . "/../../" ;
/**
* Alias for `composer show -loD`
* You can also use `composer outdated -D`
*
* @link https://getcomposer.org/doc/03-cli.md#show
*/
require_once ("$iTopFolder/approot.inc.php");
$iTopFolder = __DIR__."/../../";
require_once("$iTopFolder/approot.inc.php");
$sApproot = APPROOT;
$aTrace = array();
$aParamsConfig = array(
'composer-path' => array(
'default' => 'composer.phar',
)
'default' => 'composer',
),
);
$aParamsConfigNotFound = array_flip(array_keys($aParamsConfig));
$aGivenArgs = $argv;

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 !== 'cli')
if (php_sapi_name() !== '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,24 +19,17 @@
* 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";
$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) {
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

@@ -27,11 +27,14 @@ If you have an idea you're sure would benefit to all of iTop users, you may
[create a corresponding ticket](https://sourceforge.net/p/itop/tickets/new/) to submit it, but be warned that there are lots of good
reasons to refuse such changes.
### 📄 License
iTop is distributed under the AGPL-3.0 license (see the [license.txt] file),
your code must comply with this license.
### 📄 License and copyright
iTop is distributed under the AGPL-3.0 license (see the [license.txt] file).
If you want to use another license, you may [create an extension][wiki new ext].
The iTop repository is divided in three parts: iTop (mainly PHP/JS/XML sources and dictionaries), images, and third-party libraries.
Combodo has the copyright on most of the source files in the iTop part of the repository: please do not modify the existing file copyrights.
Anyhow, you are encouraged to signal your contribution by the mean of `@author` annotations.
If you want to use another license or keep the code ownership (copyright), you may [create an extension][wiki new ext].
[license.txt]: https://github.com/Combodo/iTop/blob/develop/license.txt
[wiki new ext]: https://www.itophub.io/wiki/page?id=latest%3Acustomization%3Astart#by_writing_your_own_extension
@@ -52,26 +55,26 @@ Here are the branches we use and their meaning :
For example, if no version is currently prepared for shipping we could have:
- `develop` containing future 3.0.0 version
- `develop` containing future 3.1.0 version
- `support/3.0`: 3.0.x maintenance version
- `support/2.7`: 2.7.x maintenance version
- `support/2.6`: 2.6.x maintenance version
- `support/2.5`: 2.5.x maintenance version
In this example, when 3.0.0-beta is shipped that will become:
In this example, when 3.1.0-beta is shipped that will become:
- `develop`: future 3.1.0 version
- `release/3.0.0`: 3.0.0-beta
- `develop`: future 3.2.0 version
- `release/3.1.0`: 3.1.0-beta
- `support/3.0`: 3.0.x maintenance version
- `support/2.7`: 2.7.x maintenance version
- `support/2.6`: 2.6.x maintenance version
- `support/2.5`: 2.5.x maintenance version
And when 3.0.0 final will be out:
And when 3.1.0 final will be out:
- `develop`: future 3.1.0 version
- `support/3.0`: 3.0.x maintenance version (will host developments for 3.0.1)
- `develop`: future 3.2.0 version
- `support/3.1`: 3.1.x maintenance version (will host developments for 3.1.1)
- `support/3.0`: 3.0.x maintenance version
- `support/2.7`: 2.7.x maintenance version
- `support/2.6`: 2.6.x maintenance version
- `support/2.5`: 2.5.x maintenance version
Also note that we have a "micro-version" concept : each of those versions have a very small amount of modifications. They are made from
`support/*` branches as well. For example 2.6.2-1 and 2.6.2-2 were made from the `support/2.6.2` branch.
@@ -131,21 +134,19 @@ Our tests are located in the `test/` directory, containing a PHPUnit config file
When your code is working, please:
* squash as much as possible your commits,
* stash as much as possible your commits,
* rebase your branch on our repo last commit,
* create a pull request.
* create a pull request
* mind to check the "[Allow edits from maintainers](https://docs.github.com/en/github-ae@latest/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork)" option !
Detailed procedure to work on fork and create PR is available [in GitHub help pages](https://help.github.com/articles/creating-a-pull-request-from-a-fork/).
You might check the ["Allow edits from maintainers" PR checkbox][allow_edits_checkbox] to ease review.
[allow_edits_checkbox]: https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork#enabling-repository-maintainer-permissions-on-existing-pull-requests
### 🙏 We are thankful
We are thankful for all your contributions to the iTop universe! As a thank you gift, we will send stickers to every iTop (& extensions) contributors!
We are thankful for all your contributions to the iTop universe! As a thank you gift, we will send stickers to every iTop (& extensions) contributors!
Stickers' design might change from one year to another. For the first year we wanted to try a "craft beer label" look, see examples below:
We have one sticker per contribution type. You might get multiple stickers with one contribution though :)
* Bug hunter: Fix a bug
* Translator: Add/update translations
@@ -157,4 +158,6 @@ Stickers' design might change from one year to another. For the first year we wa
* Beta tester: Test and give feedback on beta releases
* Extension developer: Develop and publish an extension
![](.doc/contributing-guide/contributing-stickers-side-by-side.png)
Here is the design of each stickers for year 2022:
![iTop stickers 2022](.doc/contributing-guide/2022.contributing-stickers-side-by-side.png)

8
Jenkinsfile vendored
View File

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

View File

@@ -37,17 +37,19 @@ iTop also offers mass import tools to help you being even more efficient.
- [iTop Forums][1]: community support
- [iTop Tickets][2]: for feature requests and bug reports
- [Releases download][3]
- [iTop requirements][4]
- [Software requirements][4]
- [Documentation][5] covering both iTop and its official extensions
- [iTop Hub][6] : discover and install extensions !
- [iTop versions history][7]
[1]: https://sourceforge.net/p/itop/discussion/
[2]: https://sourceforge.net/p/itop/tickets/
[3]: https://sourceforge.net/projects/itop/files/itop/
[4]: https://www.itophub.io/wiki/page?id=latest:install:requirements
[4]: https://www.itophub.io/wiki/page?id=latest:install:upgrading_itop
[5]: https://www.itophub.io/wiki
[6]: https://store.itophub.io/en_US/
[7]: .doc/itop-version-history.md
[10]: https://www.itophub.io/wiki/page?id=latest%3Adatamodel%3Astart#configuration_management_cmdb
[11]: https://www.itophub.io/wiki/page?id=latest%3Adatamodel%3Astart#ticketing
@@ -98,12 +100,14 @@ We would like to give a special thank you 🤗 to the people from the community
- Lucas, Jonathan
- Malik, Remie
- Mindêllo de Andrade, Lucas (a.k.a [@rokam](https://www.github.com/rokam))
- Mozart de Oliveira, Eduardo (a.k.a [@eduardomozart](https://github.com/eduardomozart))
- Raenker, Martin
- Roháč, Richard (a.k.a [@RohacRichard](https://github.com/RohacRichard))
- Rosenke, Stephan
- 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))
- Tarjányi, Csaba (a.k.a [@tacsaby](https://github.com/tacsaby))
- Stukalov, Ilya (a.k.a [@ilya](https://www.github.com/ilya)-stukalov)
- Tulio, Marco
- Turrubiates, Miguel

View File

@@ -121,7 +121,6 @@ class UserRightsMatrix extends UserRightsAddOnAPI
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
{
// Maybe we should check that no other user with userid == 0 exists
CMDBObject::SetTrackInfo('Initialization');
$oUser = new UserLocal();
$oUser->Set('login', $sAdminUser);
$oUser->Set('password', $sAdminPwd);

View File

@@ -124,7 +124,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
$bGrant = $oUserRights->GetClassStimulusGrant($this->GetKey(), $sClass, $sStimulusCode);
if ($bGrant === true)
{
$aStimuli[] = '<span title="'.$sStimulusCode.': '.htmlentities($oStimulus->GetDescription(), ENT_QUOTES, 'UTF-8').'">'.htmlentities($oStimulus->GetLabel(), ENT_QUOTES, 'UTF-8').'</span>';
$aStimuli[] = '<span title="'.$sStimulusCode.': '.utils::EscapeHtml($oStimulus->GetDescription()).'">'.utils::EscapeHtml($oStimulus->GetLabel()).'</span>';
}
}
$sStimuli = implode(', ', $aStimuli);
@@ -432,29 +432,21 @@ 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')
{
CMDBObject::SetTrackInfo('Initialization');
CMDBObject::SetCurrentChangeFromParams('Initialization create administrator');
$iContactId = 0;
// Support drastic data model changes: no organization class (or not writable)!
if (MetaModel::IsValidClass('Organization') && !MetaModel::IsAbstract('Organization'))
{
if (MetaModel::IsValidClass('Organization') && !MetaModel::IsAbstract('Organization')) {
$oOrg = MetaModel::NewObject('Organization');
$oOrg->Set('name', 'My Company/Department');
$oOrg->Set('code', 'SOMECODE');
$iOrgId = $oOrg->DBInsertNoReload();
// Support drastic data model changes: no Person class (or not writable)!
if (MetaModel::IsValidClass('Person') && !MetaModel::IsAbstract('Person'))
{
if (MetaModel::IsValidClass('Person') && !MetaModel::IsAbstract('Person')) {
$oContact = MetaModel::NewObject('Person');
$oContact->Set('name', 'My last name');
$oContact->Set('first_name', 'My first name');
@@ -496,7 +488,6 @@ 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();
@@ -553,7 +544,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Cache
$this->m_aObjectActionGrants = array();
$this->m_aAdministrators = null;
}
public function LoadCache()
@@ -696,10 +686,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
*/
private function GetAdministrators()
{
if ($this->m_aAdministrators === null)
static $aAdministrators = null;
if ($aAdministrators === null)
{
// Find all administrators
$this->m_aAdministrators = array();
$aAdministrators = array();
$oAdministratorsFilter = new DBObjectSearch('User');
$oLnkFilter = new DBObjectSearch('URP_UserProfile');
$oExpression = new FieldExpression('profileid', 'URP_UserProfile');
@@ -712,10 +704,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
$oSet->OptimizeColumnLoad(array('User' => array('login')));
while($oUser = $oSet->Fetch())
{
$this->m_aAdministrators[] = $oUser->GetKey();
$aAdministrators[] = $oUser->GetKey();
}
}
return $this->m_aAdministrators;
return $aAdministrators;
}
/**
@@ -752,12 +744,8 @@ 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($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
{
$bGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
if (!is_null($bGrant))
@@ -883,16 +871,11 @@ 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($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile)
{
$bGrant = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
if (!is_null($bGrant))
@@ -921,9 +904,8 @@ class UserRightsProfile extends UserRightsAddOnAPI
}
/**
* @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)
* Find out which attribute is corresponding the the dimension 'owner org'
* returns null if no such attribute has been found (no filtering should occur)
*/
public static function GetOwnerOrganizationAttCode($sClass)
{

View File

@@ -278,8 +278,8 @@ class URP_Profiles extends UserRightsBaseClassGUI
{
$oGrant = $oUserRights->GetClassStimulusGrant($this->GetKey(), $sClass, $sStimulusCode);
if (is_object($oGrant) && ($oGrant->Get('permission') == 'yes'))
{
$aStimuli[] = '<span title="'.$sStimulusCode.': '.htmlentities($oStimulus->GetDescription(), ENT_QUOTES, 'UTF-8').'">'.htmlentities($oStimulus->GetLabel(), ENT_QUOTES, 'UTF-8').'</span>';
{
$aStimuli[] = '<span title="'.$sStimulusCode.': '.utils::EscapeHtml($oStimulus->GetDescription()).'">'.utils::EscapeHtml($oStimulus->GetLabel()).'</span>';
}
}
$sStimuli = implode(', ', $aStimuli);
@@ -508,24 +508,18 @@ class UserRightsProfile extends UserRightsAddOnAPI
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
{
// Create a change to record the history of the User object
/** @var \CMDBChange $oChange */
$oChange = MetaModel::NewObject("CMDBChange");
$oChange->Set("date", time());
$oChange->Set("userinfo", "Initialization");
CMDBObject::SetCurrentChangeFromParams('Initialization : create first user admin profile');
$iContactId = 0;
// Support drastic data model changes: no organization class (or not writable)!
if (MetaModel::IsValidClass('Organization') && !MetaModel::IsAbstract('Organization'))
{
if (MetaModel::IsValidClass('Organization') && !MetaModel::IsAbstract('Organization')) {
$oOrg = MetaModel::NewObject('Organization');
$oOrg->Set('name', 'My Company/Department');
$oOrg->Set('code', 'SOMECODE');
$oOrg::SetCurrentChange($oChange);
$iOrgId = $oOrg->DBInsertNoReload();
// Support drastic data model changes: no Person class (or not writable)!
if (MetaModel::IsValidClass('Person') && !MetaModel::IsAbstract('Person'))
{
if (MetaModel::IsValidClass('Person') && !MetaModel::IsAbstract('Person')) {
$oContact = MetaModel::NewObject('Person');
$oContact->Set('name', 'My last name');
$oContact->Set('first_name', 'My first name');
@@ -534,7 +528,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
$oContact->Set('org_id', $iOrgId);
}
$oContact->Set('email', 'my.email@foo.org');
$oContact::SetCurrentChange($oChange);
$iContactId = $oContact->DBInsertNoReload();
}
}
@@ -543,24 +536,22 @@ class UserRightsProfile extends UserRightsAddOnAPI
$oUser = new UserLocal();
$oUser->Set('login', $sAdminUser);
$oUser->Set('password', $sAdminPwd);
if (MetaModel::IsValidAttCode('UserLocal', 'contactid') && ($iContactId != 0))
{
if (MetaModel::IsValidAttCode('UserLocal', 'contactid') && ($iContactId != 0)) {
$oUser->Set('contactid', $iContactId);
}
$oUser->Set('language', $sLanguage); // Language was chosen during the installation
// Add this user to the very specific 'admin' profile
$oAdminProfile = MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", array('name' => ADMIN_PROFILE_NAME), true /*all data*/);
if (is_object($oAdminProfile))
{
if (is_object($oAdminProfile)) {
$oUserProfile = new URP_UserProfile();
$oUserProfile->Set('profileid', $oAdminProfile->GetKey());
$oUserProfile->Set('reason', 'By definition, the administrator must have the administrator profile');
$oSet = DBObjectSet::FromObject($oUserProfile);
$oUser->Set('profile_list', $oSet);
}
$oUser::SetCurrentChange($oChange);
$iUserId = $oUser->DBInsertNoReload();
$oUser->DBInsertNoReload();
return true;
}
@@ -586,10 +577,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
/**
* Read and cache organizations allowed to the given user
*
* @param User $oUser
* @param string $sClass (not used here but can be used in overloads)
* @param $oUser
* @param $sClass (not used here but can be used in overloads)
*
* @return array keys of the User allowed org
* @return array
* @throws \CoreException
* @throws \Exception
*/

View File

@@ -110,8 +110,8 @@ class URP_Profiles extends UserRightsBaseClass
{
$oGrant = $oUserRights->GetClassStimulusGrant($this->GetKey(), $sClass, $sStimulusCode);
if (is_object($oGrant) && ($oGrant->Get('permission') == 'yes'))
{
$aStimuli[] = '<span title="'.$sStimulusCode.': '.htmlentities($oStimulus->GetDescription(), ENT_QUOTES, 'UTF-8').'">'.htmlentities($oStimulus->GetLabel(), ENT_QUOTES, 'UTF-8').'</span>';
{
$aStimuli[] = '<span title="'.$sStimulusCode.': '.utils::EscapeHtml($oStimulus->GetDescription()).'">'.utils::EscapeHtml($oStimulus->GetLabel()).'</span>';
}
}
$sStimuli = implode(', ', $aStimuli);
@@ -568,14 +568,11 @@ class UserRightsProjection extends UserRightsAddOnAPI
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
{
// Create a change to record the history of the User object
$oChange = MetaModel::NewObject("CMDBChange");
$oChange->Set("date", time());
$oChange->Set("userinfo", "Initialization");
CMDBObject::SetCurrentChangeFromParams('Initialization : create first user admin');
$oOrg = new Organization();
$oOrg->Set('name', 'My Company/Department');
$oOrg->Set('code', 'SOMECODE');
$oOrg::SetCurrentChange($oChange);
$iOrgId = $oOrg->DBInsertNoReload();
$oContact = new Person();
@@ -584,7 +581,6 @@ class UserRightsProjection extends UserRightsAddOnAPI
//$oContact->Set('status', 'available');
$oContact->Set('org_id', $iOrgId);
$oContact->Set('email', 'my.email@foo.org');
$oContact::SetCurrentChange($oChange);
$iContactId = $oContact->DBInsertNoReload();
$oUser = new UserLocal();
@@ -592,7 +588,6 @@ class UserRightsProjection extends UserRightsAddOnAPI
$oUser->Set('password', $sAdminPwd);
$oUser->Set('contactid', $iContactId);
$oUser->Set('language', $sLanguage); // Language was chosen during the installation
$oUser::SetCurrentChange($oChange);
$iUserId = $oUser->DBInsertNoReload();
// Add this user to the very specific 'admin' profile
@@ -600,7 +595,6 @@ class UserRightsProjection extends UserRightsAddOnAPI
$oUserProfile->Set('userid', $iUserId);
$oUserProfile->Set('profileid', ADMIN_PROFILE_ID);
$oUserProfile->Set('reason', 'By definition, the administrator must have the administrator profile');
$oUserProfile::SetCurrentChange($oChange);
$oUserProfile->DBInsertNoReload();
return true;
}

View File

@@ -1,20 +1,24 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/application/WebPage/AjaxPage.php, now loadable using autoloader
* @deprecated 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-2021 Combodo SARL
*/
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
//DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/AjaxPage.php, now loadable using autoloader');
// cannot notify depreciation for now as this is still load in autoloader
//DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/AjaxPage.php, now loadable using autoloader');
/**
* Class ajax_page
*
* @deprecated will be removed in 3.1.0 - moved to AjaxPage
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to AjaxPage
*/
class ajax_page extends AjaxPage
{
function __construct($s_title)
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('ajax_page is deprecated. Please use AjaxPage instead');
parent::__construct($s_title);
}
}

View File

@@ -224,7 +224,7 @@ class ApplicationContext
{
$sContext = "";
foreach ($this->aValues as $sName => $sValue) {
$sContext .= "<input type=\"hidden\" name=\"c[$sName]\" value=\"".htmlentities($sValue, ENT_QUOTES, 'UTF-8')."\" />\n";
$sContext .= "<input type=\"hidden\" name=\"c[$sName]\" value=\"".utils::EscapeHtml($sValue)."\" />\n";
}
return $sContext;
}
@@ -238,7 +238,7 @@ class ApplicationContext
{
$aContextInputBlocks = [];
foreach ($this->aValues as $sName => $sValue) {
$aContextInputBlocks[] = InputUIBlockFactory::MakeForHidden("c[$sName]", htmlentities($sValue, ENT_QUOTES, 'UTF-8'));
$aContextInputBlocks[] = InputUIBlockFactory::MakeForHidden("c[$sName]", utils::EscapeHtml($sValue));
}
return $aContextInputBlocks;
}
@@ -376,19 +376,26 @@ class ApplicationContext
{
$oAppContext = new ApplicationContext();
if (is_null($sUrlMakerClass)) {
$sUrlMakerClass = self::GetUrlMakerClass();
}
if (is_null($sUrlMakerClass))
{
$sUrlMakerClass = self::GetUrlMakerClass();
}
$sUrl = call_user_func(array($sUrlMakerClass, 'MakeObjectUrl'), $sObjClass, $sObjKey);
if (utils::StrLen($sUrl) > 0) {
if ($bWithNavigationContext) {
return $sUrl."&".$oAppContext->GetForLink();
} else {
return $sUrl;
}
} else {
return '';
}
if (strlen($sUrl) > 0)
{
if ($bWithNavigationContext)
{
return $sUrl."&".$oAppContext->GetForLink();
}
else
{
return $sUrl;
}
}
else
{
return '';
}
}
/**

View File

@@ -30,9 +30,9 @@ require_once(APPROOT.'application/newsroomprovider.class.inc.php');
* You may implement such interfaces in a module file (e.g. main.mymodule.php)
*
* @api
* @package LoginExtensibilityAPI
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @package Extensibility
* @since 2.7.0
*/
interface iLoginExtension
@@ -40,16 +40,12 @@ interface iLoginExtension
/**
* Return the list of supported login modes for this plugin
*
* @api
*
* @return array of supported login modes
*/
public function ListSupportedLoginModes();
}
/**
* @api
* @package LoginExtensibilityAPI
* @since 2.7.0
*/
interface iLoginFSMExtension extends iLoginExtension
@@ -61,7 +57,6 @@ interface iLoginFSMExtension extends iLoginExtension
* if LoginWebPage::LOGIN_FSM_RETURN_OK is returned then the login is OK and terminated
* if LoginWebPage::LOGIN_FSM_RETURN_IGNORE is returned then the FSM will proceed to next plugin or state
*
* @api
* @param string $sLoginState (see LoginWebPage::LOGIN_STATE_...)
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
@@ -71,8 +66,6 @@ interface iLoginFSMExtension extends iLoginExtension
}
/**
* @api
* @package LoginExtensibilityAPI
* @since 2.7.0
*/
abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
@@ -119,7 +112,6 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
/**
* Initialization
*
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
@@ -133,7 +125,6 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
* Detect login mode explicitly without respecting configured order (legacy mode)
* In most case do nothing here
*
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
@@ -150,7 +141,6 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
* 1 - display login form
* 2 - read the values posted by the user
*
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
@@ -164,7 +154,6 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
* Control the validity of the data provided by the user
* Automatic user provisioning can be done here
*
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
@@ -175,7 +164,6 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
}
/**
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
@@ -186,7 +174,6 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
}
/**
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
@@ -197,7 +184,6 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
}
/**
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
@@ -208,7 +194,6 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
}
/**
* @api
* @param int $iErrorCode (see LoginWebPage::EXIT_CODE_...)
*
* @return int LoginWebPage::LOGIN_FSM_RETURN_ERROR, LoginWebPage::LOGIN_FSM_RETURN_OK or LoginWebPage::LOGIN_FSM_RETURN_IGNORE
@@ -220,28 +205,22 @@ abstract class AbstractLoginFSMExtension implements iLoginFSMExtension
}
/**
* @api
* @package LoginExtensibilityAPI
* @since 2.7.0
*/
interface iLogoutExtension extends iLoginExtension
{
/**
* Execute all actions to log out properly
* @api
*/
public function LogoutAction();
}
/**
* @api
* @package UIExtensibilityAPI
* @since 2.7.0
*/
interface iLoginUIExtension extends iLoginExtension
{
/**
* @api
* @return LoginTwigContext
*/
public function GetTwigContext();
@@ -249,20 +228,18 @@ interface iLoginUIExtension extends iLoginExtension
/**
* @api
* @package PreferencesExtensibilityAPI
* @package Extensibility
* @since 2.7.0
*/
interface iPreferencesExtension
{
/**
* @api
* @param \WebPage $oPage
*
*/
public function DisplayPreferences(WebPage $oPage);
/**
* @api
* @param \WebPage $oPage
* @param string $sOperation
*
@@ -275,7 +252,7 @@ interface iPreferencesExtension
* Extend this class instead of implementing iPreferencesExtension if you don't need to overload all methods
*
* @api
* @package PreferencesExtensibilityAPI
* @package Extensibility
* @since 2.7.0
*/
abstract class AbstractPreferencesExtension implements iPreferencesExtension
@@ -321,7 +298,8 @@ abstract class AbstractPreferencesExtension implements iPreferencesExtension
* A recommended pattern is to cache data by the mean of static members.
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @deprecated
*/
interface iApplicationUIExtension
{
@@ -343,7 +321,6 @@ interface iApplicationUIExtension
* }
* </code>
*
* @api
* @param DBObject $oObject The object being displayed
* @param WebPage $oPage The output context
* @param boolean $bEditMode True if the edition form is being displayed
@@ -357,7 +334,6 @@ interface iApplicationUIExtension
*
* The method is called rigth after all the tabs have been displayed
*
* @api
* @param DBObject $oObject The object being displayed
* @param WebPage $oPage The output context
* @param boolean $bEditMode True if the edition form is being displayed
@@ -372,7 +348,6 @@ interface iApplicationUIExtension
* The method is called after the changes from the standard form have been
* taken into account, and before saving the changes into the database.
*
* @api
* @param DBObject $oObject The object being edited
* @param string $sFormPrefix Prefix given to the HTML form inputs
*
@@ -387,7 +362,6 @@ interface iApplicationUIExtension
* javascript into the edition form, and if that code requires to store temporary data
* (this is the case when a file must be uploaded).
*
* @api
* @param string $sTempId Unique temporary identifier made of session_id and transaction_id. It identifies the object in a unique way.
*
* @return void
@@ -399,7 +373,6 @@ interface iApplicationUIExtension
*
* Sorry, the verb has been reserved. You must implement it, but it is not called as of now.
*
* @api
* @param DBObject $oObject The object being displayed
*
* @return string[] desc
@@ -411,7 +384,6 @@ interface iApplicationUIExtension
*
* Sorry, the verb has been reserved. You must implement it, but it is not called as of now.
*
* @api
* @param DBObject $oObject The object being displayed
*
* @return string Path of the icon, relative to the modules directory.
@@ -431,7 +403,6 @@ interface iApplicationUIExtension
* * HILIGHT_CLASS_OK
* * HILIGHT_CLASS_NONE
*
* @api
* @param DBObject $oObject The object being displayed
*
* @return integer The value representing the mood of the object
@@ -458,7 +429,6 @@ interface iApplicationUIExtension
*
* See also iPopupMenuExtension for greater flexibility
*
* @api
* @param DBObjectSet $oSet A set of persistent objects (DBObject)
*
* @return array
@@ -470,8 +440,9 @@ interface iApplicationUIExtension
* Extend this class instead of implementing iApplicationUIExtension if you don't need to overload
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.7.0
* @deprecated
*/
abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
{
@@ -544,7 +515,7 @@ abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
* or through the GUI.
*
* @api
* @package ORMExtensibilityAPI
* @package Extensibility
*/
interface iApplicationObjectExtension
{
@@ -557,7 +528,6 @@ interface iApplicationObjectExtension
* If the extension returns false, then the framework will perform the usual evaluation.
* Otherwise, the answer is definitively "yes, the object has changed".
*
* @api
* @param \cmdbAbstractObject $oObject The target object
*
* @return boolean True if something has changed for the target object
@@ -570,7 +540,6 @@ interface iApplicationObjectExtension
* The GUI calls this verb and reports any issue.
* Anyhow, this API can be called in other contexts such as the CSV import tool.
*
* @api
* @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.
@@ -584,7 +553,6 @@ interface iApplicationObjectExtension
*
* Please not that it is not possible to cascade deletion by this mean: only stopper issues can be handled.
*
* @api
* @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.
@@ -600,7 +568,6 @@ interface iApplicationObjectExtension
* * {@see DBObject::ListPreviousValuesForUpdatedAttributes()} : list of changed attributes and their values before the change
* * {@see DBObject::Get()} : for a given attribute the new value that was persisted
*
* @api
* @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
@@ -616,7 +583,6 @@ interface iApplicationObjectExtension
*
* The method is called right <b>after</b> the object has been written to the database.
*
* @api
* @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
@@ -630,7 +596,6 @@ interface iApplicationObjectExtension
*
* The method is called right <b>before</b> the object will be deleted from the database.
*
* @api
* @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
@@ -644,7 +609,7 @@ interface iApplicationObjectExtension
* Extend this class instead of iApplicationObjectExtension if you don't need to overload all methods
*
* @api
* @package ORMExtensibilityAPI
* @package Extensibility
* @since 2.7.0
*/
abstract class AbstractApplicationObjectExtension implements iApplicationObjectExtension
@@ -704,7 +669,7 @@ abstract class AbstractApplicationObjectExtension implements iApplicationObjectE
* by the application, as long as the class definition is included somewhere in the code
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.0
*/
interface iPopupMenuExtension
@@ -713,21 +678,18 @@ interface iPopupMenuExtension
* Insert an item into the Actions menu of a list
*
* $param is a DBObjectSet containing the list of objects
* @api
*/
const MENU_OBJLIST_ACTIONS = 1;
/**
* Insert an item into the Toolkit menu of a list
*
* $param is a DBObjectSet containing the list of objects
* @api
*/
const MENU_OBJLIST_TOOLKIT = 2;
/**
* Insert an item into the Actions menu on an object details page
*
* $param is a DBObject instance: the object currently displayed
* @api
*/
const MENU_OBJDETAILS_ACTIONS = 3;
/**
@@ -737,14 +699,12 @@ interface iPopupMenuExtension
* is being displayed.
*
* $param is a Dashboard instance: the dashboard currently displayed
* @api
*/
const MENU_DASHBOARD_ACTIONS = 4;
/**
* Insert an item into the User menu (upper right corner)
*
* $param is null
* @api
*/
const MENU_USER_ACTIONS = 5;
/**
@@ -752,7 +712,6 @@ interface iPopupMenuExtension
*
* $param is an array('portal_id' => $sPortalId, 'object' => $oObject) containing the portal id and a DBObject instance (the object on
* the current line)
* @api
*/
const PORTAL_OBJLISTITEM_ACTIONS = 7;
/**
@@ -760,7 +719,6 @@ interface iPopupMenuExtension
*
* $param is an array('portal_id' => $sPortalId, 'object' => $oObject) containing the portal id and a DBObject instance (the object
* currently displayed)
* @api
*/
const PORTAL_OBJDETAILS_ACTIONS = 8;
@@ -798,7 +756,6 @@ interface iPopupMenuExtension
* This method is called by the framework for each menu.
* The items will be inserted in the menu in the order of the returned array.
*
* @api
* @param int $iMenuId The identifier of the type of menu, as listed by the constants MENU_xxx
* @param mixed $param Depends on $iMenuId, see the constants defined above
*
@@ -811,7 +768,7 @@ interface iPopupMenuExtension
* Base class for the various types of custom menus
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.0
*/
abstract class ApplicationPopupMenuItem
@@ -821,7 +778,7 @@ abstract class ApplicationPopupMenuItem
/** @ignore */
protected $sLabel;
/** @ignore */
protected $sTooltip;
protected $sTooltip;
/** @ignore */
protected $sIconClass;
/** @ignore */
@@ -878,7 +835,6 @@ abstract class ApplicationPopupMenuItem
}
/**
* @api
* @param $aCssClasses
*/
public function SetCssClasses($aCssClasses)
@@ -889,7 +845,6 @@ abstract class ApplicationPopupMenuItem
/**
* Adds a CSS class to the CSS classes that will be put on the menu item
*
* @api
* @param $sCssClass
*/
public function AddCssClass($sCssClass)
@@ -900,7 +855,7 @@ abstract class ApplicationPopupMenuItem
/**
* @param $sTooltip
*
*
* @since 3.0.0
*/
public function SetTooltip($sTooltip)
@@ -910,24 +865,24 @@ abstract class ApplicationPopupMenuItem
/**
* @return string
*
*
* @since 3.0.0
*/
public function GetTooltip()
{
return $this->sTooltip;
}
/**
* @param $sIconClass
*
*
* @since 3.0.0
*/
public function SetIconClass($sIconClass)
{
$this->sIconClass = $sIconClass;
}
}
/**
* @return string
*
@@ -937,7 +892,7 @@ abstract class ApplicationPopupMenuItem
{
return $this->sIconClass;
}
/**
* Returns the components to create a popup menu item in HTML
*
@@ -959,7 +914,7 @@ abstract class ApplicationPopupMenuItem
* Note: This works only in the backoffice, {@see \URLButtonItem} for the end-user portal
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.0
*/
class URLPopupMenuItem extends ApplicationPopupMenuItem
@@ -972,7 +927,6 @@ class URLPopupMenuItem extends ApplicationPopupMenuItem
/**
* Constructor
*
* @api
* @param string $sUID The unique identifier of this menu in iTop... make sure you pass something unique enough
* @param string $sLabel The display label of the menu (must be localized)
* @param string $sUrl If the menu is an hyperlink, provide the absolute hyperlink here
@@ -996,7 +950,7 @@ class URLPopupMenuItem extends ApplicationPopupMenuItem
'tooltip' => $this->sTooltip
);
}
/** @ignore */
public function GetUrl()
{
@@ -1016,7 +970,7 @@ class URLPopupMenuItem extends ApplicationPopupMenuItem
* Note: This works only in the backoffice, {@see \JSButtonItem} for the end-user portal
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.0
*/
class JSPopupMenuItem extends ApplicationPopupMenuItem
@@ -1066,13 +1020,13 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
{
return $this->aIncludeJSFiles;
}
/** @ignore */
public function GetJsCode()
{
return $this->sJsCode;
}
/** @ignore */
public function GetUrl()
{
@@ -1085,7 +1039,7 @@ class JSPopupMenuItem extends ApplicationPopupMenuItem
* will automatically reduce several consecutive separators to just one
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.0
*/
class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
@@ -1094,7 +1048,6 @@ class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
/**
* Constructor
* @api
*/
public function __construct()
{
@@ -1112,7 +1065,7 @@ class SeparatorPopupMenuItem extends ApplicationPopupMenuItem
* Class for adding an item as a button that browses to the given URL
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.0
*/
class URLButtonItem extends URLPopupMenuItem
@@ -1124,7 +1077,7 @@ class URLButtonItem extends URLPopupMenuItem
* Class for adding an item as a button that runs some JS code
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.0
*/
class JSButtonItem extends JSPopupMenuItem
@@ -1148,7 +1101,7 @@ class JSButtonItem extends JSPopupMenuItem
* the specified place and can use the passed iTopWebPage object to add javascript or CSS definitions
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.0
* @deprecated 3.0.0 If you need to include:
* * JS/CSS files/snippets, use {@see \iBackofficeLinkedScriptsExtension}, {@see \iBackofficeLinkedStylesheetsExtension}, etc instead
@@ -1159,7 +1112,6 @@ interface iPageUIExtension
/**
* Add content to the header of the page
*
* @api
* @param iTopWebPage $oPage The page to insert stuff into.
*
* @return string The HTML content to add into the page
@@ -1169,7 +1121,6 @@ interface iPageUIExtension
/**
* Add content to the footer of the page
*
* @api
* @param iTopWebPage $oPage The page to insert stuff into.
*
* @return string The HTML content to add into the page
@@ -1179,7 +1130,6 @@ interface iPageUIExtension
/**
* Add content to the "admin banner"
*
* @api
* @param iTopWebPage $oPage The page to insert stuff into.
*
* @return string The HTML content to add into the page
@@ -1203,7 +1153,7 @@ interface iPageUIExtension
* the specified place and can use the passed iTopWebPage object to add javascript or CSS definitions
*
* @api
* @package UIBlockExtensibilityAPI
* @package Extensibility
* @since 3.0.0
*/
interface iPageUIBlockExtension
@@ -1234,9 +1184,9 @@ interface iPageUIBlockExtension
* Extend this class instead of iPageUIExtension if you don't need to overload all methods
*
* @api
* @package UIExtensibilityAPI
* @package Extensibility
* @since 2.7.0
* @deprecated since 3.0.0 use AbstractPageUIBlockExtension instead
* @deprecated 3.0.0 use AbstractPageUIBlockExtension instead
*/
abstract class AbstractPageUIExtension implements iPageUIExtension
{
@@ -1276,7 +1226,7 @@ abstract class AbstractPageUIExtension implements iPageUIExtension
* Extend this class instead of iPageUIExtension if you don't need to overload all methods
*
* @api
* @package UIBlockExtensibilityAPI
* @package Extensibility
* @since 3.0.0
*/
abstract class AbstractPageUIBlockExtension implements iPageUIBlockExtension
@@ -1311,7 +1261,6 @@ abstract class AbstractPageUIBlockExtension implements iPageUIBlockExtension
*
* @see \iTopWebPage::$a_linked_scripts
* @api
* @package BackofficeUIExtensibilityAPI
* @since 3.0.0
*/
interface iBackofficeLinkedScriptsExtension
@@ -1329,7 +1278,6 @@ interface iBackofficeLinkedScriptsExtension
*
* @see \iTopWebPage::$a_early_scripts
* @api
* @package BackofficeUIExtensibilityAPI
* @since 3.0.0
*/
interface iBackofficeEarlyScriptExtension
@@ -1346,7 +1294,6 @@ interface iBackofficeEarlyScriptExtension
*
* @see \iTopWebPage::$a_scripts
* @api
* @package BackofficeUIExtensibilityAPI
* @since 3.0.0
*/
interface iBackofficeScriptExtension
@@ -1363,7 +1310,6 @@ interface iBackofficeScriptExtension
*
* @see \iTopWebPage::$a_init_scripts
* @api
* @package BackofficeUIExtensibilityAPI
* @since 3.0.0
*/
interface iBackofficeInitScriptExtension
@@ -1380,7 +1326,6 @@ interface iBackofficeInitScriptExtension
*
* @see \iTopWebPage::$a_ready_scripts
* @api
* @package BackofficeUIExtensibilityAPI
* @since 3.0.0
*/
interface iBackofficeReadyScriptExtension
@@ -1397,7 +1342,6 @@ interface iBackofficeReadyScriptExtension
*
* @see \iTopWebPage::$a_linked_stylesheets
* @api
* @package BackofficeUIExtensibilityAPI
* @since 3.0.0
*/
interface iBackofficeLinkedStylesheetsExtension
@@ -1414,7 +1358,6 @@ interface iBackofficeLinkedStylesheetsExtension
*
* @see \iTopWebPage::$a_styles
* @api
* @package BackofficeUIExtensibilityAPI
* @since 3.0.0
*/
interface iBackofficeStyleExtension
@@ -1431,7 +1374,6 @@ interface iBackofficeStyleExtension
*
* @see \iTopWebPage::$a_dict_entries
* @api
* @package BackofficeUIExtensibilityAPI
* @since 3.0.0
*/
interface iBackofficeDictEntriesExtension
@@ -1448,7 +1390,6 @@ interface iBackofficeDictEntriesExtension
*
* @see \iTopWebPage::$a_dict_entries_prefixes
* @api
* @package BackofficeUIExtensibilityAPI
* @since 3.0.0
*/
interface iBackofficeDictEntriesPrefixesExtension
@@ -1464,7 +1405,7 @@ interface iBackofficeDictEntriesPrefixesExtension
* Implement this interface to add content to any enhanced portal page
*
* @api
* @package PortalExtensibilityAPI
* @package Extensibility
*
* @since 2.4.0 interface creation
* @since 2.7.0 change method signatures due to Silex to Symfony migration
@@ -1478,7 +1419,6 @@ interface iPortalUIExtension
/**
* Returns an array of CSS file urls
*
* @api
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return array
@@ -1488,7 +1428,6 @@ interface iPortalUIExtension
/**
* Returns inline (raw) CSS
*
* @api
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return string
@@ -1498,7 +1437,6 @@ interface iPortalUIExtension
/**
* Returns an array of JS file urls
*
* @api
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return array
@@ -1508,7 +1446,6 @@ interface iPortalUIExtension
/**
* Returns raw JS code
*
* @api
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return string
@@ -1518,7 +1455,6 @@ interface iPortalUIExtension
/**
* Returns raw HTML code to put at the end of the <body> tag
*
* @api
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return string
@@ -1528,7 +1464,6 @@ interface iPortalUIExtension
/**
* Returns raw HTML code to put at the end of the #main-wrapper element
*
* @api
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return string
@@ -1538,7 +1473,6 @@ interface iPortalUIExtension
/**
* Returns raw HTML code to put at the end of the #topbar and #sidebar elements
*
* @api
* @param \Symfony\Component\DependencyInjection\Container $oContainer
*
* @return string
@@ -1550,7 +1484,7 @@ interface iPortalUIExtension
* Extend this class instead of iPortalUIExtension if you don't need to overload all methods
*
* @api
* @package PortalExtensibilityAPI
* @package Extensibility
* @since 2.4.0
*/
abstract class AbstractPortalUIExtension implements iPortalUIExtension
@@ -1616,7 +1550,7 @@ abstract class AbstractPortalUIExtension implements iPortalUIExtension
* Implement this interface to add new operations to the REST/JSON web service
*
* @api
* @package RESTExtensibilityAPI
* @package Extensibility
* @since 2.0.1
*/
interface iRestServiceProvider
@@ -1624,7 +1558,6 @@ interface iRestServiceProvider
/**
* Enumerate services delivered by this class
*
* @api
* @param string $sVersion The version (e.g. 1.0) supported by the services
*
* @return array An array of hash 'verb' => verb, 'description' => description
@@ -1634,7 +1567,6 @@ interface iRestServiceProvider
/**
* Enumerate services delivered by this class
*
* @api
* @param string $sVersion The version (e.g. 1.0) supported by the services
* @param string $sVerb
* @param array $aParams
@@ -1648,90 +1580,69 @@ interface iRestServiceProvider
* Minimal REST response structure. Derive this structure to add response data and error codes.
*
* @api
* @package RESTExtensibilityAPI
* @package Extensibility
* @since 2.0.1
*/
class RestResult
{
/**
* Result: no issue has been encountered
* @api
*/
const OK = 0;
/**
* Result: missing/wrong credentials or the user does not have enough rights to perform the requested operation
* @api
*/
const UNAUTHORIZED = 1;
/**
* Result: the parameter 'version' is missing
* @api
*/
const MISSING_VERSION = 2;
/**
* Result: the parameter 'json_data' is missing
* @api
*/
const MISSING_JSON = 3;
/**
* Result: the input structure is not a valid JSON string
* @api
*/
const INVALID_JSON = 4;
/**
* Result: the parameter 'auth_user' is missing, authentication aborted
* @api
*/
const MISSING_AUTH_USER = 5;
/**
* Result: the parameter 'auth_pwd' is missing, authentication aborted
* @api
*/
const MISSING_AUTH_PWD = 6;
/**
* Result: no operation is available for the specified version
* @api
*/
const UNSUPPORTED_VERSION = 10;
/**
* Result: the requested operation is not valid for the specified version
* @api
*/
const UNKNOWN_OPERATION = 11;
/**
* Result: the requested operation cannot be performed because it can cause data (integrity) loss
* @api
*/
const UNSAFE = 12;
/**
* Result: the request page number is not valid. It must be an integer greater than 0
* @api
*/
const INVALID_PAGE = 13;
/**
* Result: the operation could not be performed, see the message for troubleshooting
* @api
*/
const INTERNAL_ERROR = 100;
/**
* Default constructor - ok!
* @api
*/
public function __construct()
{
$this->code = RestResult::OK;
}
/**
* @var int
* @api
*/
public $code;
/**
* @var string
* @api
*/
public $message;
}
@@ -1739,7 +1650,7 @@ class RestResult
* Helpers for implementing REST services
*
* @api
* @package RESTExtensibilityAPI
* @package Extensibility
*/
class RestUtils
{
@@ -1888,7 +1799,6 @@ class RestUtils
/**
* Read and interpret object search criteria from a Rest/Json structure
*
* @api
* @param string $sClass Name of the class
* @param StdClass $oCriteria Hash of attribute code => value (can be a substructure or a scalar, depending on the nature of the
* attriute)
@@ -1949,8 +1859,6 @@ 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)
{
@@ -2000,7 +1908,6 @@ class RestUtils
/**
* Search objects from a polymorph search specification (Rest/Json)
*
* @api
* @param string $sClass Name of the class
* @param mixed $key Either search criteria (substructure), or an object or an OQL string.
* @param int $iLimit The limit of results to return
@@ -2037,16 +1944,8 @@ class RestUtils
elseif (is_string($key))
{
// OQL
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(),
]);
}
}
$oSearch = DBObjectSearch::FromOQL($key);
}
else
{
throw new Exception("Wrong format for key");
@@ -2196,27 +2095,3 @@ 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

@@ -1,8 +1,8 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/application/WebPage/CaptureWebPage.php, now loadable using autoloader
* @deprecated 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-2021 Combodo SARL
*/
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/CaptureWebPage.php, now loadable using autoloader');
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/CaptureWebPage.php, now loadable using autoloader');

View File

@@ -1,8 +1,8 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/application/WebPage/CLIPage.php, now loadable using autoloader
* @deprecated 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-2021 Combodo SARL
*/
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/CLIPage.php, now loadable using autoloader');
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/CLIPage.php, now loadable using autoloader');

View File

@@ -1,6 +1,6 @@
<?php
/*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @copyright Copyright (C) 2010-2022 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
@@ -42,6 +42,7 @@ use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
use Combodo\iTop\Renderer\BlockRenderer;
use Combodo\iTop\Renderer\Console\ConsoleFormRenderer;
define('OBJECT_PROPERTIES_TAB', 'ObjectProperties');
define('HILIGHT_CLASS_CRITICAL', 'red');
@@ -60,11 +61,11 @@ require_once(APPROOT.'application/ui.linksdirectwidget.class.inc.php');
require_once(APPROOT.'application/ui.passwordwidget.class.inc.php');
require_once(APPROOT.'application/ui.extkeywidget.class.inc.php');
require_once(APPROOT.'application/ui.htmleditorwidget.class.inc.php');
require_once(APPROOT.'sources/application/search/searchform.class.inc.php');
require_once(APPROOT.'sources/application/search/criterionparser.class.inc.php');
require_once(APPROOT.'sources/application/search/criterionconversionabstract.class.inc.php');
require_once(APPROOT.'sources/application/search/criterionconversion/criteriontooql.class.inc.php');
require_once(APPROOT.'sources/application/search/criterionconversion/criteriontosearchform.class.inc.php');
require_once(APPROOT.'sources/Application/Search/searchform.class.inc.php');
require_once(APPROOT.'sources/Application/Search/criterionparser.class.inc.php');
require_once(APPROOT.'sources/Application/Search/criterionconversionabstract.class.inc.php');
require_once(APPROOT.'sources/Application/Search/CriterionConversion/criteriontooql.class.inc.php');
require_once(APPROOT.'sources/Application/Search/CriterionConversion/criteriontosearchform.class.inc.php');
/**
* Class cmdbAbstractObject
@@ -516,32 +517,6 @@ HTML
return $aHeaderBlocks;
}
/**
* Display history tab of an object
*
* @deprecated 3.0.0 will be removed in 3.1, see N°3824
*
* @param bool $bEditMode
* @param int $iLimitCount
* @param int $iLimitStart
*
* @param \WebPage $oPage
*
* @throws \CoreException
*
*/
public function DisplayBareHistory(WebPage $oPage, $bEditMode = false, $iLimitCount = 0, $iLimitStart = 0)
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
// history block (with as a tab)
$oHistoryFilter = new DBObjectSearch('CMDBChangeOp');
$oHistoryFilter->AddCondition('objkey', $this->GetKey(), '=');
$oHistoryFilter->AddCondition('objclass', get_class($this), '=');
$oBlock = new HistoryBlock($oHistoryFilter, 'table', false);
$oBlock->SetLimit($iLimitCount, $iLimitStart);
$oBlock->Display($oPage, 'history');
}
/**
* Display properties tab of an object
*
@@ -702,33 +677,26 @@ HTML
$sTargetClass = $oLinkingAttDef->GetTargetClass();
// n:n links => must be allowed to modify the linking class AND read the target class in order to edit the linkedset
if (!UserRights::IsActionAllowed($sLinkedClass,
UR_ACTION_MODIFY) || !UserRights::IsActionAllowed($sTargetClass, UR_ACTION_READ))
{
UR_ACTION_MODIFY) || !UserRights::IsActionAllowed($sTargetClass, UR_ACTION_READ)) {
$iFlags |= OPT_ATT_READONLY;
}
// n:n links => must be allowed to read the linking class AND the target class in order to display the linkedset
if (!UserRights::IsActionAllowed($sLinkedClass,
UR_ACTION_READ) || !UserRights::IsActionAllowed($sTargetClass, UR_ACTION_READ))
{
UR_ACTION_READ) || !UserRights::IsActionAllowed($sTargetClass, UR_ACTION_READ)) {
$iFlags |= OPT_ATT_HIDDEN;
}
}
else
{
} else {
// 1:n links => must be allowed to modify the linked class in order to edit the linkedset
if (!UserRights::IsActionAllowed($sLinkedClass, UR_ACTION_MODIFY))
{
if (!UserRights::IsActionAllowed($sLinkedClass, UR_ACTION_MODIFY)) {
$iFlags |= OPT_ATT_READONLY;
}
// 1:n links => must be allowed to read the linked class in order to display the linkedset
if (!UserRights::IsActionAllowed($sLinkedClass, UR_ACTION_READ))
{
if (!UserRights::IsActionAllowed($sLinkedClass, UR_ACTION_READ)) {
$iFlags |= OPT_ATT_HIDDEN;
}
}
// Non-readable/hidden linkedset... don't display anything
if ($iFlags & OPT_ATT_HIDDEN)
{
if ($iFlags & OPT_ATT_HIDDEN) {
continue;
}
@@ -737,17 +705,13 @@ HTML
$aArgs = array('this' => $this);
$bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE));
if ($bEditMode && (!$bReadOnly))
{
if ($bEditMode && (!$bReadOnly)) {
$sInputId = $this->m_iFormId.'_'.$sAttCode;
if ($oAttDef->IsIndirect())
{
if ($oAttDef->IsIndirect()) {
$oLinkingAttDef = MetaModel::GetAttributeDef($sLinkedClass, $oAttDef->GetExtKeyToRemote());
$sTargetClass = $oLinkingAttDef->GetTargetClass();
}
else
{
} else {
$sTargetClass = $sLinkedClass;
}
@@ -757,7 +721,7 @@ HTML
$sDisplayValue = ''; // not used
$sHTMLValue = "<span id=\"field_{$sInputId}\">".self::GetFormElementForField($oPage, $sClass, $sAttCode,
$oAttDef, $oLinkSet, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'</span>';
$oAttDef, $oOrmLinkSet, $sDisplayValue, $sInputId, '', $iFlags, $aArgs).'</span>';
$this->AddToFieldsMap($sAttCode, $sInputId);
$oPage->add($sHTMLValue);
}
@@ -1013,10 +977,8 @@ HTML
$this->GetSynchroReplicaFlags($sAttCode, $aReasons);
$sTip = '';
foreach ($aReasons as $aRow) {
$sDescription = htmlentities($aRow['description'], ENT_QUOTES,
'UTF-8');
$sDescription = str_replace(array("\r\n", "\n"), "<br/>",
$sDescription);
$sDescription = utils::EscapeHtml($aRow['description']);
$sDescription = str_replace(array("\r\n", "\n"), "<br/>", $sDescription);
$sTip .= "<div class='synchro-source'>";
$sTip .= "<div class='synchro-source-title'>Synchronized with {$aRow['name']}</div>";
$sTip .= "<div class='synchro-source-description'>$sDescription</div>";
@@ -1193,8 +1155,6 @@ HTML
$oPage->SetCurrentTab('UI:PropertiesTab');
$this->DisplayBareProperties($oPage, $bEditMode);
$this->DisplayBareRelations($oPage, $bEditMode);
// TODO 3.0.0: What to do with this?
//$this->DisplayBareHistory($oPage, $bEditMode);
// Note: Adding the JS snippet which enables the image upload should have been done directly by the ActivityPanel which would have kept the independance principle
// of the UIBlock. For now we keep it this way in order to move on and trace this known limitation in N°3736.
@@ -1234,7 +1194,7 @@ HTML
/**
* @param \WebPage $oPage
* @param \CMDBObjectSet $oSet
* @param array $aExtraParams See possible values in {@see DataTableUIBlockFactory::RenderDataTable()}
* @param array $aExtraParams
*
* @throws \ApplicationException
* @throws \CoreException
@@ -1431,7 +1391,7 @@ HTML
} else {
if ($oAttDef instanceof AttributeCaseLog) {
$rawValue = $oObj->Get($sAttCodeEx);
$outputValue = str_replace("\n", "<br/>", htmlentities($rawValue->__toString(), ENT_QUOTES, 'UTF-8'));
$outputValue = str_replace("\n", "<br/>", utils::EscapeHtml($rawValue->__toString()));
// Trick for Excel: treat the content as text even if it begins with an equal sign
$aRow[$oAttDef->GetCode()] = $outputValue;
} else {
@@ -1445,9 +1405,9 @@ HTML
}
}
if ($bLocalize) {
$outputValue = htmlentities($oFinalAttDef->GetEditValue($rawValue), ENT_QUOTES, 'UTF-8');
$outputValue = utils::EscapeHtml($oFinalAttDef->GetEditValue($rawValue));
} else {
$outputValue = htmlentities($rawValue, ENT_QUOTES, 'UTF-8');
$outputValue = utils::EscapeHtml($rawValue);
}
$aRow[$oAttDef->GetCode()] = $outputValue;
}
@@ -1483,7 +1443,7 @@ HTML
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @deprecated since 3.0.0
* @deprecated 3.0.0
*/
public static function GetDisplayExtendedSet(WebPage $oPage, CMDBObjectSet $oSet, $aExtraParams = array())
{
@@ -1923,7 +1883,7 @@ HTML
{
$rawValue = $oObj->Get($sAttCodeEx);
$outputValue = str_replace("\n", "<br/>",
htmlentities($rawValue->__toString(), ENT_QUOTES, 'UTF-8'));
utils::EscapeHtml($rawValue->__toString()));
// Trick for Excel: treat the content as text even if it begins with an equal sign
$aRow[] = '<td x:str>'.$outputValue.'</td>';
}
@@ -1940,14 +1900,11 @@ HTML
$rawValue = '';
}
}
if ($bLocalize)
{
$outputValue = htmlentities($oFinalAttDef->GetEditValue($rawValue), ENT_QUOTES,
'UTF-8');
if ($bLocalize) {
$outputValue = utils::EscapeHtml($oFinalAttDef->GetEditValue($rawValue));
}
else
{
$outputValue = htmlentities($rawValue, ENT_QUOTES, 'UTF-8');
else {
$outputValue = utils::EscapeHtml($rawValue);
}
$aRow[] = '<td>'.$outputValue.'</td>';
}
@@ -2184,7 +2141,7 @@ HTML;
$sHours = "<input class=\"ibo-input ibo-input-duration\" title=\"$sHelpText\" type=\"text\" size=\"2\" name=\"attr_{$sFieldPrefix}{$sAttCode}[h]{$sNameSuffix}\" value=\"{$aVal['hours']}\" id=\"{$iId}_h\"/>";
$sMinutes = "<input class=\"ibo-input ibo-input-duration\" title=\"$sHelpText\" type=\"text\" size=\"2\" name=\"attr_{$sFieldPrefix}{$sAttCode}[m]{$sNameSuffix}\" value=\"{$aVal['minutes']}\" id=\"{$iId}_m\"/>";
$sSeconds = "<input class=\"ibo-input ibo-input-duration\" title=\"$sHelpText\" type=\"text\" size=\"2\" name=\"attr_{$sFieldPrefix}{$sAttCode}[s]{$sNameSuffix}\" value=\"{$aVal['seconds']}\" id=\"{$iId}_s\"/>";
$sHidden = "<input type=\"hidden\" id=\"{$iId}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\"/>";
$sHidden = "<input type=\"hidden\" id=\"{$iId}\" value=\"".utils::EscapeHtml($value)."\"/>";
$sHTMLValue = Dict::Format('UI:DurationForm_Days_Hours_Minutes_Seconds', $sDays, $sHours, $sMinutes, $sSeconds).$sHidden."&nbsp;".$sValidationSpan.$sReloadSpan;
$oPage->add_ready_script("$('#{$iId}').on('update', function(evt, sFormId) { return ToggleDurationField('$iId'); });");
break;
@@ -2194,8 +2151,7 @@ HTML;
$aEventsList[] = 'validate';
$aEventsList[] = 'keyup';
$aEventsList[] = 'change';
$sHTMLValue = "<div class=\"field_input_zone field_input_password ibo-input-wrapper ibo-input-password-wrapper\" data-validation=\"untouched\"><input class=\"ibo-input ibo-input-password\" title=\"$sHelpText\" type=\"password\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" value=\"".htmlentities($value,
ENT_QUOTES, 'UTF-8')."\" id=\"$iId\"/></div>{$sValidationSpan}{$sReloadSpan}";
$sHTMLValue = "<div class=\"field_input_zone field_input_password ibo-input-wrapper ibo-input-password-wrapper\" data-validation=\"untouched\"><input class=\"ibo-input ibo-input-password\" title=\"$sHelpText\" type=\"password\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" value=\"".utils::EscapeHtml($value)."\" id=\"$iId\"/></div>{$sValidationSpan}{$sReloadSpan}";
break;
case 'OQLExpression':
@@ -2347,13 +2303,13 @@ EOF
$sHeader = '<div class="ibo-caselog-entry-form--actions"><div class="""ibo-caselog-entry-form--actions" data-role="ibo-caselog-entry-form--action-buttons--extra-actions"></div></div>'; // will be hidden in CSS (via :empty) if it remains empty
$sEditValue = is_object($value) ? $value->GetModifiedEntry('html') : '';
$sPreviousLog = is_object($value) ? $value->GetAsHTML($oPage, true /* bEditMode */, array('AttributeText', 'RenderWikiHtml')) : '';
$sPreviousLog = is_object($value) ? $value->GetAsHTML($oPage, true /* bEditMode */, array('AttributeText', 'RenderWikiHtml')) : '';
$iEntriesCount = is_object($value) ? count($value->GetIndex()) : 0;
$sHidden = "<input type=\"hidden\" id=\"{$iId}_count\" value=\"$iEntriesCount\"/>"; // To know how many entries the case log already contains
$sHTMLValue = "$sHeader<div class=\"ibo-caselog-entry-form--text-input\" $sStyle data-role=\"ibo-caselog-entry-form--text-input\">";
$sHTMLValue .= "<textarea class=\"htmlEditor ibo-input-richtext-placeholder\" style=\"border:0;width:100%\" title=\"$sHelpText\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" rows=\"8\" cols=\"40\" id=\"$iId\">".htmlentities($sEditValue,ENT_QUOTES,'UTF-8')."</textarea>";
$sHTMLValue .= "$sPreviousLog</div>{$sValidationSpan}{$sReloadSpan}$sHidden";
$sHTMLValue .= "<textarea class=\"htmlEditor ibo-input-richtext-placeholder\" style=\"border:0;width:100%\" title=\"$sHelpText\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" rows=\"8\" cols=\"40\" id=\"$iId\">".utils::EscapeHtml($sEditValue)."</textarea>";
$sHTMLValue .= "$sPreviousLog</div>{$sValidationSpan}{$sReloadSpan}$sHidden";
// Note: This should be refactored for all types of attribute (see at the end of this function) but as we are doing this for a maintenance release, we are scheduling it for the next main release in to order to avoid regressions as much as possible.
$sNullValue = $oAttDef->GetNullValue();
@@ -2410,8 +2366,7 @@ EOF
case 'LinkedSet':
$sInputType = self::ENUM_INPUT_TYPE_LINKEDSET;
if ($oAttDef->IsIndirect()) {
$oWidget = new UILinksWidget($sClass, $sAttCode, $iId, $sNameSuffix,
$oAttDef->DuplicatesAllowed());
$oWidget = new UILinksWidget($sClass, $sAttCode, $iId, $sNameSuffix, $oAttDef->DuplicatesAllowed());
} else {
$oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iId, $sNameSuffix);
}
@@ -2598,16 +2553,16 @@ JS
case 'Set':
case 'TagSet':
$sInputType = self::ENUM_INPUT_TYPE_TAGSET;
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/selectize.min.js');
$oPage->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/selectize.default.css');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.itop-set-widget.js');
$sInputType = self::ENUM_INPUT_TYPE_TAGSET;
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/selectize.min.js');
$oPage->add_linked_stylesheet(utils::GetAbsoluteUrlAppRoot().'css/selectize.default.css');
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.itop-set-widget.js');
$oPage->add_dict_entry('Core:AttributeSet:placeholder');
$oPage->add_dict_entry('Core:AttributeSet:placeholder');
/** @var \ormSet $value */
/** @var \ormSet $value */
$sJson = $oAttDef->GetJsonForWidget($value, $aArgs);
$sEscapedJson = htmlentities($sJson, ENT_QUOTES, 'UTF-8');
$sEscapedJson = utils::EscapeHtml($sJson);
$sSetInputName = "attr_{$sFormPrefix}{$sAttCode}";
// handle form validation
@@ -3054,23 +3009,11 @@ EOF
// Hook the cancel button via jQuery so that it can be unhooked easily as well if needed
$sDefaultUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search_form&class='.$sClass.'&'.$oAppContext->GetForLink();
$sCancelButtonOnClickScript = "let fOnClick{$this->m_iFormId}CancelButton = ";
if(isset($aExtraParams['js_handlers']['cancel_button_on_click'])){
$sCancelButtonOnClickScript .= $aExtraParams['js_handlers']['cancel_button_on_click'];
} else {
$sCancelButtonOnClickScript .= "function() { BackToDetails('$sClass', $iKey, '$sDefaultUrl', $sJSToken)};";
}
$sCancelButtonOnClickScript .= "$('#form_{$this->m_iFormId} button.cancel').on('click', fOnClick{$this->m_iFormId}CancelButton);";
$oPage->add_ready_script($sCancelButtonOnClickScript);
$oPage->add_ready_script("$('#form_{$this->m_iFormId} button.cancel').on('click', function() { BackToDetails('$sClass', $iKey, '$sDefaultUrl', $sJSToken)} );");
$iFieldsCount = count($aFieldsMap);
$sJsonFieldsMap = json_encode($aFieldsMap);
$sLifecycleStateForWizardHelper = '';
if (MetaModel::HasLifecycle($sClass)) {
$sLifecycleStateForWizardHelper = $this->GetState();
}
$sState = $this->GetState();
$sSessionStorageKey = $sClass.'_'.$iKey;
$sTempId = utils::GetUploadTempId($iTransactionId);
$oPage->add_ready_script(InlineImage::EnableCKEditorImageUpload($this, $sTempId));
@@ -3080,7 +3023,7 @@ EOF
sessionStorage.removeItem('$sSessionStorageKey');
// Create the object once at the beginning of the page...
var oWizardHelper$sPrefix = new WizardHelper('$sClass', '$sPrefix', '$sLifecycleStateForWizardHelper');
var oWizardHelper$sPrefix = new WizardHelper('$sClass', '$sPrefix', '$sState');
oWizardHelper$sPrefix.SetFieldsMap($sJsonFieldsMap);
oWizardHelper$sPrefix.SetFieldsCount($iFieldsCount);
EOF
@@ -3386,14 +3329,13 @@ EOF
// Consider only the "expected" fields for the target state
if (array_key_exists($sAttCode, $aExpectedAttributes)) {
$iExpectCode = $aExpectedAttributes[$sAttCode];
// Prompt for an attribute if
// - the attribute must be changed or must be displayed to the user for confirmation
// - or the field is mandatory and currently empty
if (($iExpectCode & (OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) ||
(($iExpectCode & OPT_ATT_MANDATORY) && (false === $this->HasAValue($sAttCode)))) {
$aArgs = array('this' => $this);
(($iExpectCode & OPT_ATT_MANDATORY) && ($this->Get($sAttCode) == ''))) {
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$aArgs = array('this' => $this);
// If the field is mandatory, set it to the only possible value
if ((!$oAttDef->IsNullAllowed()) || ($iExpectCode & OPT_ATT_MANDATORY)) {
if ($oAttDef->IsExternalKey()) {
@@ -3422,35 +3364,32 @@ EOF
}
}
}
$sInputType = '';
$sInputId = 'att_'.$iFieldIndex;
$sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef,
$this->Get($sAttCode), $this->GetEditValue($sAttCode), $sInputId, '', $iExpectCode,
$aArgs, true, $sInputType);
$aAttrib = array(
$this->Get($sAttCode), $this->GetEditValue($sAttCode), 'att_'.$iFieldIndex, '', $iExpectCode,
$aArgs);
$aAttrib = array(
'label' => '<span>'.$oAttDef->GetLabel().'</span>',
'value' => "<span id=\"field_att_$iFieldIndex\">$sHTMLValue</span>",
);
//add attrib for data-attribute
// Prepare metadata attributes
$sAttCode = $oAttDef->GetCode();
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sAttCode = $oAttDef->GetCode();
$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
$sAttDefClass = get_class($oAttDef);
$sAttLabel = MetaModel::GetLabel($sClass, $sAttCode);
$sAttLabel = MetaModel::GetLabel($sClass, $sAttCode);
$aAttrib['attcode'] = $sAttCode;
$aAttrib['atttype'] = $sAttDefClass;
$aAttrib['attcode'] = $sAttCode;
$aAttrib['atttype'] = $sAttDefClass;
$aAttrib['attlabel'] = $sAttLabel;
// - Attribute flags
$aAttrib['attflags'] = $this->GetFormAttributeFlags($sAttCode);
$aAttrib['attflags'] = $this->GetFormAttributeFlags($sAttCode) ;
// - How the field should be rendered
$aAttrib['layout'] = (in_array($oAttDef->GetEditClass(), static::GetAttEditClassesToRenderAsLargeField())) ? 'large' : 'small';
$aAttrib['inputid'] = $sInputId;
$aAttrib['inputtype'] = $sInputType;
$aAttrib['layout'] = (in_array($oAttDef->GetEditClass(), static::GetAttEditClassesToRenderAsLargeField())) ? 'large' : 'small';
// - For simple fields, we get the raw (stored) value as well
$bExcludeRawValue = false;
foreach (static::GetAttDefClassesToExcludeFromMarkupMetadataRawValue() as $sAttDefClassToExclude) {
foreach (static::GetAttDefClassesToExcludeFromMarkupMetadataRawValue() as $sAttDefClassToExclude)
{
if (is_a($sAttDefClass, $sAttDefClassToExclude, true)) {
$bExcludeRawValue = true;
break;
@@ -3458,8 +3397,8 @@ EOF
}
$aAttrib['value_raw'] = ($bExcludeRawValue === false) ? $this->Get($sAttCode) : '';
$aDetails[] = $aAttrib;
$aFieldsMap[$sAttCode] = $sInputId;
$aDetails[] = $aAttrib;
$aFieldsMap[$sAttCode] = 'att_'.$iFieldIndex;
$iFieldIndex++;
$bExistFieldToDisplay = true;
}
@@ -3677,13 +3616,13 @@ HTML;
if ($oAttDef->GetEditClass() == 'Document') {
/** @var \ormDocument $oDocument */
$oDocument = $this->Get($sAttCode);
if (is_object($oDocument) && !$oDocument->IsEmpty()) {
if (!$oDocument->IsEmpty()) {
$sFieldAsHtml = $this->GetAsHTML($sAttCode);
$sDisplayLabel = Dict::S('UI:OpenDocumentInNewWindow_');
$sDisplayUrl = $oDocument->GetDisplayURL(get_class($this), $this->GetKey(), $sAttCode);
$sDownloadLabel = Dict::S('UI:DownloadDocument_');
$sDownloadLabel = Dict::Format('UI:DownloadDocument_');
$sDownloadUrl = $oDocument->GetDownloadURL(get_class($this), $this->GetKey(), $sAttCode);
$sDisplayValue = <<<HTML
@@ -3736,8 +3675,7 @@ HTML;
break;
default:
$oPage->add("<pre>".htmlentities(MyHelpers::beautifulstr($data, 1000, true), ENT_QUOTES,
'UTF-8')."</pre>\n");
$oPage->add("<pre>".utils::EscapeHtml(MyHelpers::beautifulstr($data, 1000, true))."</pre>\n");
}
break;
@@ -4075,18 +4013,14 @@ HTML;
$this->Set($sAttCode, $value);
break;
case 'LinkedSet':
if ($this->IsValueModified($value))
{
if ($this->IsValueModified($value)) {
$oLinkSet = $this->Get($sAttCode);
$sLinkedClass = $oAttDef->GetLinkedClass();
if (array_key_exists('to_be_created', $value) && (count($value['to_be_created']) > 0))
{
if (array_key_exists('to_be_created', $value) && (count($value['to_be_created']) > 0)) {
// Now handle the links to be created
foreach ($value['to_be_created'] as $aData)
{
foreach ($value['to_be_created'] as $aData) {
$sSubClass = $aData['class'];
if (($sLinkedClass == $sSubClass) || (is_subclass_of($sSubClass, $sLinkedClass)))
{
if (($sLinkedClass == $sSubClass) || (is_subclass_of($sSubClass, $sLinkedClass))) {
$aObjData = $aData['data'];
$oLink = MetaModel::NewObject($sSubClass);
$oLink->UpdateObjectFromArray($aObjData);
@@ -4298,28 +4232,20 @@ HTML;
case 'LinkedSet':
/** @var AttributeLinkedSet $oAttDef */
$aRawToBeCreated = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbc", '{}',
'raw_data'), true);
$aRawToBeCreated = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbc", '{}', 'raw_data'), true);
$aToBeCreated = array();
foreach($aRawToBeCreated as $aData)
{
foreach ($aRawToBeCreated as $aData) {
$sSubFormPrefix = $aData['formPrefix'];
$sObjClass = isset($aData['class']) ? $aData['class'] : $oAttDef->GetLinkedClass();
$aObjData = array();
foreach($aData as $sKey => $value)
{
if (preg_match("/^attr_$sSubFormPrefix(.*)$/", $sKey, $aMatches))
{
foreach ($aData as $sKey => $value) {
if (preg_match("/^attr_$sSubFormPrefix(.*)$/", $sKey, $aMatches)) {
$oLinkAttDef = MetaModel::GetAttributeDef($sObjClass, $aMatches[1]);
// Recursing over n:n link datetime attributes
// Note: We might need to do it with other attribute types, like Document or redundancy setting.
if ($oLinkAttDef instanceof AttributeDateTime)
{
$aObjData[$aMatches[1]] = $this->PrepareValueFromPostedForm($sSubFormPrefix,
$aMatches[1], $sObjClass, $aData);
}
else
{
if ($oLinkAttDef instanceof AttributeDateTime) {
$aObjData[$aMatches[1]] = $this->PrepareValueFromPostedForm($sSubFormPrefix, $aMatches[1], $sObjClass, $aData);
} else {
$aObjData[$aMatches[1]] = $value;
}
}
@@ -4327,28 +4253,20 @@ HTML;
$aToBeCreated[] = array('class' => $sObjClass, 'data' => $aObjData);
}
$aRawToBeModified = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbm", '{}',
'raw_data'), true);
$aRawToBeModified = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbm", '{}', 'raw_data'), true);
$aToBeModified = array();
foreach($aRawToBeModified as $iObjKey => $aData)
{
foreach ($aRawToBeModified as $iObjKey => $aData) {
$sSubFormPrefix = $aData['formPrefix'];
$sObjClass = isset($aData['class']) ? $aData['class'] : $oAttDef->GetLinkedClass();
$aObjData = array();
foreach($aData as $sKey => $value)
{
if (preg_match("/^attr_$sSubFormPrefix(.*)$/", $sKey, $aMatches))
{
foreach ($aData as $sKey => $value) {
if (preg_match("/^attr_$sSubFormPrefix(.*)$/", $sKey, $aMatches)) {
$oLinkAttDef = MetaModel::GetAttributeDef($sObjClass, $aMatches[1]);
// Recursing over n:n link datetime attributes
// Note: We might need to do it with other attribute types, like Document or redundancy setting.
if ($oLinkAttDef instanceof AttributeDateTime)
{
$aObjData[$aMatches[1]] = $this->PrepareValueFromPostedForm($sSubFormPrefix,
$aMatches[1], $sObjClass, $aData);
}
else
{
if ($oLinkAttDef instanceof AttributeDateTime) {
$aObjData[$aMatches[1]] = $this->PrepareValueFromPostedForm($sSubFormPrefix, $aMatches[1], $sObjClass, $aData);
} else {
$aObjData[$aMatches[1]] = $value;
}
}
@@ -4357,14 +4275,11 @@ HTML;
}
$value = array(
'to_be_created' => $aToBeCreated,
'to_be_created' => $aToBeCreated,
'to_be_modified' => $aToBeModified,
'to_be_deleted' => json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbd", '[]',
'raw_data'), true),
'to_be_added' => json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tba", '[]',
'raw_data'), true),
'to_be_removed' => json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbr", '[]',
'raw_data'), true),
'to_be_deleted' => json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbd", '[]', 'raw_data'), true),
'to_be_added' => json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tba", '[]', 'raw_data'), true),
'to_be_removed' => json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}_tbr", '[]', 'raw_data'), true),
);
break;
@@ -4493,9 +4408,7 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
}
return $res;
@@ -4512,16 +4425,13 @@ HTML;
protected function DBCloneTracked_Internal($newKey = null)
{
/** @var cmdbAbstractObject $oNewObj */
$oNewObj = MetaModel::GetObject(get_class($this), parent::DBCloneTracked_Internal($newKey));
$oNewObj = 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)
{
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBInsert($oNewObj, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
}
return $oNewObj;
@@ -4535,13 +4445,13 @@ HTML;
// 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()
static $aUpdateReentrance = array();
$sKey = get_class($this).'::'.$this->GetKey();
if (array_key_exists($sKey, $aUpdateReentrance))
{
if (!MetaModel::StartReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this)) {
$sClass = get_class($this);
$sKey = $this->GetKey();
IssueLog::Debug("CRUD: DBUpdate $sClass::$sKey Rejected (reentrance)", LogChannels::DM_CRUD);
return $res;
}
$aUpdateReentrance[$sKey] = true;
try
{
@@ -4549,18 +4459,16 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBUpdate');
}
}
catch (Exception $e)
{
throw $e;
}
finally
{
unset($aUpdateReentrance[$sKey]);
MetaModel::StopReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this);
}
if ($this->IsModified()) {
return $this->DBUpdate();
}
return $res;
@@ -4573,11 +4481,9 @@ HTML;
*/
protected function SetWarningsAsSessionMessages($sMessageIdPrefix)
{
if (!empty($this->m_aCheckWarnings) && is_array($this->m_aCheckWarnings))
{
if (!empty($this->m_aCheckWarnings) && is_array($this->m_aCheckWarnings)) {
$iMsgNb = 0;
foreach ($this->m_aCheckWarnings as $sWarningMessage)
{
foreach ($this->m_aCheckWarnings as $sWarningMessage) {
$iMsgNb++;
$sMessageId = "$sMessageIdPrefix-$iMsgNb"; // each message must have its own messageId !
$this->SetSessionMessageFromInstance($sMessageId, $sWarningMessage, 'warning', 0);
@@ -4585,21 +4491,13 @@ HTML;
}
}
protected static function BulkUpdateTracked_Internal(DBSearch $oFilter, array $aValues)
{
// Todo - invoke the extension
return parent::BulkUpdateTracked_Internal($oFilter, $aValues);
}
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)
{
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete');
}
return parent::DBDeleteTracked_Internal($oDeletionPlan);
@@ -4617,10 +4515,7 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$oKPI = new ExecutionKPI();
$bIsModified = $oExtensionInstance->OnIsModified($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnIsModified');
if ($bIsModified)
if ($oExtensionInstance->OnIsModified($this))
{
return true;
}
@@ -4674,9 +4569,7 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$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);
@@ -4724,9 +4617,7 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{
$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);
@@ -4802,9 +4693,8 @@ HTML;
{
$aReasons = array();
$sTip = '';
foreach($aReasons as $aRow)
{
$sDescription = htmlentities($aRow['description'], ENT_QUOTES, 'UTF-8');
foreach($aReasons as $aRow) {
$sDescription = utils::EscapeHtml($aRow['description']);
$sDescription = str_replace(array("\r\n", "\n"), "<br/>", $sDescription);
$sTip .= "<div class=\"synchro-source\">";
$sTip .= "<div class=\"synchro-source-title\">Synchronized with {$aRow['name']}</div>";
@@ -4816,8 +4706,7 @@ HTML;
// Attribute is read-only
$sHTMLValue = $this->GetAsHTML($sAttCode);
$sHTMLValue .= '<input type="hidden" id="'.$sInputId.'" name="attr_'.$sPrefix.$sAttCode.'" value="'.htmlentities($this->GetEditValue($sAttCode),
ENT_QUOTES, 'UTF-8').'"/>';
$sHTMLValue .= '<input type="hidden" id="'.$sInputId.'" name="attr_'.$sPrefix.$sAttCode.'" value="'.utils::EscapeHtml($this->GetEditValue($sAttCode)).'"/>';
$aFieldsMap[$sAttCode] = $sInputId;
}
else
@@ -5281,11 +5170,6 @@ 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();
}
}
@@ -5790,4 +5674,117 @@ JS
'AttributeOneWayPassword',
);
}
/**
* @return void
* @throws \CoreException
*/
final protected function EventInsertRequested()
{
$this->FireEvent(EVENT_SERVICE_DB_INSERT_REQUESTED);
}
/**
* @return void
* @throws \CoreException
*/
final protected function EventInsertBefore()
{
$this->FireEvent(EVENT_SERVICE_DB_ABOUT_TO_INSERT);
}
/**
* @return void
* @throws \CoreException
*/
final protected function EventInsertAfter()
{
$this->FireEvent(EVENT_SERVICE_DB_INSERT_DONE);
}
final protected function EventComputeValues()
{
$this->FireEvent(EVENT_SERVICE_DB_COMPUTE_VALUES);
}
/**
* @param array $aEventData
*
* @return void
* @throws \CoreException
*/
final protected function EventCheckToWrite(array $aEventData)
{
$this->FireEvent(EVENT_SERVICE_DB_CHECK_TO_WRITE, $aEventData);
}
/**
* @param array $aEventData
*
* @return void
* @throws \CoreException
*/
final protected function EventCheckToDelete(array $aEventData)
{
$this->FireEvent(EVENT_SERVICE_DB_CHECK_TO_DELETE, $aEventData);
}
/**
* @return void
* @throws \CoreException
*/
final protected function EventUpdateRequested()
{
$this->FireEvent(EVENT_SERVICE_DB_UPDATE_REQUESTED);
}
/**
* @return void
* @throws \CoreException
*/
final protected function EventUpdateBefore()
{
$this->FireEvent(EVENT_SERVICE_DB_ABOUT_TO_UPDATE);
}
/**
* @param array $aEventData
*
* @return void
* @throws \CoreException
*/
final protected function EventUpdateAfter(array $aEventData)
{
$this->FireEvent(EVENT_SERVICE_DB_UPDATE_DONE, $aEventData);
}
/**
* @return void
* @throws \CoreException
*/
final protected function EventDeleteBefore()
{
$this->FireEvent(EVENT_SERVICE_DB_ABOUT_TO_DELETE);
}
/**
* @return void
* @throws \CoreException
*/
final protected function EventDeleteAfter()
{
$this->FireEvent(EVENT_SERVICE_DB_DELETE_DONE);
}
final protected function EventArchive()
{
$this->FireEvent(EVENT_SERVICE_DB_ARCHIVE);
}
final protected function EventUnarchive()
{
$this->FireEvent(EVENT_SERVICE_DB_UNARCHIVE);
}
}

View File

@@ -1,8 +1,8 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/application/WebPage/CSVPage.php, now loadable using autoloader
* @deprecated 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-2021 Combodo SARL
*/
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/CSVPage.php, now loadable using autoloader');
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/CSVPage.php, now loadable using autoloader');

View File

@@ -918,11 +918,6 @@ 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(), '=');
@@ -934,7 +929,7 @@ class RuntimeDashboard extends Dashboard
$sDashboardDefinition = $oUserDashboard->Get('contents');
$bCustomized = true;
} else {
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
$sDashboardDefinition = @file_get_contents($sDashboardFile);
}
@@ -942,7 +937,7 @@ class RuntimeDashboard extends Dashboard
$oDashboard = new RuntimeDashboard($sDashBoardId);
$oDashboard->FromXml($sDashboardDefinition);
$oDashboard->SetCustomFlag($bCustomized);
$oDashboard->SetDefinitionFile($sDashboardFileSanitized);
$oDashboard->SetDefinitionFile($sDashboardFile);
} else {
$oDashboard = null;
}
@@ -1141,7 +1136,7 @@ JS
$oToolbar->AddSubBlock($oActionButton);
$aActions = array();
$sFile = addslashes(utils::LocalPath($this->sDefinitionFile));
$sFile = addslashes($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)");
@@ -1264,12 +1259,12 @@ EOF
$sOkButtonLabel = Dict::S('UI:Button:Save');
$sCancelButtonLabel = Dict::S('UI:Button:Cancel');
$sId = utils::HtmlEntities($this->sId);
$sLayoutClass = utils::HtmlEntities($this->sLayoutClass);
$sId = addslashes($this->sId);
$sLayoutClass = addslashes($this->sLayoutClass);
$sAutoReload = $this->bAutoReload ? 'true' : 'false';
$sAutoReloadSec = (string) $this->iAutoReloadSec;
$sTitle = utils::HtmlEntities($this->sTitle);
$sFile = utils::HtmlEntities($this->GetDefinitionFile());
$sTitle = addslashes($this->sTitle);
$sFile = addslashes($this->GetDefinitionFile());
$sUrl = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php';
$sReloadURL = $this->GetReloadURL();
@@ -1554,29 +1549,6 @@ JS
return $this->sDefinitionFile;
}
/**
* @param string $sDashboardFileRelative can also be an absolute path (compatibility with old URL)
*
* @return string full path to the Dashboard file
* @throws \SecurityException if path isn't under approot
* @uses utils::RealPath()
* @since 2.7.8 3.0.3 3.1.0 N°4449 remove FPD
*/
public static function GetDashboardFileFromRelativePath($sDashboardFileRelative)
{
if (utils::RealPath($sDashboardFileRelative, APPROOT)) {
// compatibility with old URL containing absolute path !
return $sDashboardFileRelative;
}
$sDashboardFile = APPROOT.$sDashboardFileRelative;
if (false === utils::RealPath($sDashboardFile, APPROOT)) {
throw new SecurityException('Invalid dashboard file !');
}
return $sDashboardFile;
}
/**
* @param string $sDefinitionFile
*/

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
<classes>
<class id="AbstractResource" _delta="define">
<parent>cmdbAbstractObject</parent>
@@ -185,6 +185,384 @@
</style>
</menu>
</menus>
<events>
<event id="EVENT_SERVICE_DB_INSERT_REQUESTED" _delta="define">
<description>An object insert in the database has been requested. All changes to the object will be persisted automatically.</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="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_DB_ABOUT_TO_INSERT" _delta="define">
<description>An object is about to be inserted in the database (no change possible)</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="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_DB_INSERT_DONE" _delta="define">
<description>An object has been inserted into the database (but not reloaded). All changes to the object will be persisted automatically.</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::AfterInsert</replaces>
<event_data>
<event_datum id="object">
<description>The object inserted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_DB_UPDATE_REQUESTED" _delta="define">
<description>An object update has been requested. All changes to the object will be persisted automatically.</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::OnUpdate, DBObject::DoComputeValues</replaces>
<event_data>
<event_datum id="object">
<description>The object updated</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_DB_ABOUT_TO_UPDATE" _delta="define">
<description>An object is about to be updated in the database (no change possible)</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::OnUpdate</replaces>
<event_data>
<event_datum id="object">
<description>The object updated</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_DB_UPDATE_DONE" _delta="define">
<description>An object has been updated into the database and reloaded. All changes to the object will be persisted automatically.</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::AfterUpdate</replaces>
<event_data>
<event_datum id="object">
<description>The object updated</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_DB_ABOUT_TO_DELETE" _delta="define">
<description>An object is about to be deleted in the database</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::OnDelete</replaces>
<event_data>
<event_datum id="object">
<description>The object deleted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_DB_DELETE_DONE" _delta="define">
<description>An object has been deleted into the database</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::AfterDelete</replaces>
<event_data>
<event_datum id="object">
<description>The object deleted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_DB_BEFORE_APPLY_STIMULUS" _delta="define">
<description>A stimulus is about to be applied to an object</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<event_data>
<event_datum id="object">
<description>The object where the stimulus is targeted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="stimulus">
<description>Current stimulus applied</description>
<type>string</type>
</event_datum>
<event_datum id="previous_state">
<description>Object previous state</description>
<type>string</type>
</event_datum>
<event_datum id="new_state">
<description>Object new state</description>
<type>string</type>
</event_datum>
<event_datum id="save_object">
<description>The object must be saved in the database</description>
<type>boolean</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_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_SERVICE_DB_APPLY_STIMULUS_FAILED" _delta="define">
<description>A stimulus has failed</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<event_data>
<event_datum id="action">
<description>The action that failed to apply the stimulus</description>
<type>string</type>
</event_datum>
<event_datum id="object">
<description>The object where the stimulus is targeted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="stimulus">
<description>Current stimulus applied</description>
<type>string</type>
</event_datum>
<event_datum id="previous_state">
<description>Object previous state</description>
<type>string</type>
</event_datum>
<event_datum id="new_state">
<description>Object new state</description>
<type>string</type>
</event_datum>
<event_datum id="save_object">
<description>The object must be saved in the database</description>
<type>boolean</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_DB_OBJECT_RELOAD" _delta="define">
<description>An object has been re-loaded from the database</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<event_data>
<event_datum id="object">
<description>The object re-loaded</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_DB_COMPUTE_VALUES" _delta="define">
<description>An object needs to be recomputed after changes</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>DBObject::ComputeValues</replaces>
<event_data>
<event_datum id="object">
<description>The object inserted</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_DB_CHECK_TO_WRITE" _delta="define">
<description>Check an object before it is written into the database (no change possible)</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>cmdbAbstractObject::DoCheckToWrite</replaces>
<event_data>
<event_datum id="object">
<description>The object to check</description>
<type>DBObject</type>
</event_datum>
<event_datum id="error_messages">
<description>Array of strings where all the errors found during the object checking are added</description>
<type>array</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_DB_CHECK_TO_DELETE" _delta="define">
<description>Check an object before it is deleted from the database (no change possible)</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<replaces>cmdbAbstractObject::DoCheckToDelete</replaces>
<event_data>
<event_datum id="object">
<description>The object to check</description>
<type>DBObject</type>
</event_datum>
<event_datum id="error_messages">
<description>Array of strings where all the errors found during the object checking are added</description>
<type>array</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_DB_ARCHIVE" _delta="define">
<description>An object has been archived</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<event_data>
<event_datum id="object">
<description>The object archived</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_DB_UNARCHIVE" _delta="define">
<description>An object has been unarchived</description>
<sources>
<source id="cmdbAbstractObject">cmdbAbstractObject</source>
</sources>
<event_data>
<event_datum id="object">
<description>The object unarchived</description>
<type>DBObject</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_DOWNLOAD_DOCUMENT" _delta="define">
<description>A document has been downloaded from the GUI</description>
<sources>
<source id="Document">Document</source>
</sources>
<event_data>
<event_datum id="object">
<description>The object containing the document</description>
<type>DBObject</type>
</event_datum>
<event_datum id="document">
<description>The document downloaded</description>
<type>ormDocument</type>
</event_datum>
<event_datum id="debug_info">
<description>Debug string</description>
<type>string</type>
</event_datum>
</event_data>
</event>
<event id="EVENT_SERVICE_LOGIN" _delta="define">
<description>Inform the listeners about the connection states</description>
<event_data>
<event_datum id="code">
<description>The login step result code (LoginWebPage::EXIT_CODE_...) </description>
<type>integer</type>
</event_datum>
<event_datum id="state">
<description>Current login state (LoginWebPage::LOGIN_STATE_CONNECTED...)</description>
<type>string</type>
</event_datum>
</event_data>
</event>
</events>
<meta>
<classes>
<class id="cmdbAbstractObject" _delta="define">

View File

@@ -19,7 +19,7 @@ use Combodo\iTop\Renderer\Console\ConsoleBlockRenderer;
*
* You should have received a copy of the GNU Affero General Public License
*
* @deprecated since 3.0.0 use Combodo\iTop\Application\UI\Base\Component\DataTable\Datatable
* @deprecated 3.0.0 use Combodo\iTop\Application\UI\Base\Component\DataTable\Datatable
*/
class DataTable
@@ -386,7 +386,7 @@ EOF;
if (!$oPage->IsPrintableVersion())
{
$sMenuTitle = Dict::S('UI:ConfigureThisList');
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li aria-label="'.Dict::S('UI:Menu:Toolkit').'"><i class="fas fa-tools"></i><i class="fas fa-caret-down"></i><ul>';
$sHtml = '<div class="itop_popup toolkit_menu" id="tk_'.$this->iListId.'"><ul><li><i class="fas fa-tools"></i><i class="fas fa-caret-down"></i><ul>';
$oMenuItem1 = new JSPopupMenuItem('iTop::ConfigureList', $sMenuTitle, "$('#datatable_dlg_".$this->iListId."').dialog('open');");
$aActions = array(

View File

@@ -304,7 +304,7 @@ class DisplayBlock
}
}
}
public function GetFilter()
{
return $this->m_oFilter;
@@ -476,7 +476,7 @@ class DisplayBlock
$oExceptionAlert = AlertUIBlockFactory::MakeForFailure('Cannot display results', $sExceptionContent);
$oHtml->AddSubBlock($oExceptionAlert);
}
IssueLog::Error('Exception during GetDisplay: '.$e->getMessage());
ExceptionLog::LogException($e);
}
} else {
// render it as an Ajax (asynchronous) call
@@ -566,7 +566,7 @@ class DisplayBlock
if (($this->m_sStyle != 'links') && ($this->m_sStyle != 'search') && ($this->m_sStyle != 'list_search')) {
$oAppContext = new ApplicationContext();
$sClass = $this->m_oFilter->GetClass();
$aFilterCodes = array_keys(MetaModel::GetClassFilterDefs($sClass));
$aFilterCodes = MetaModel::GetFiltersList($sClass);
$aCallSpec = array($sClass, 'MapContextParam');
if (is_callable($aCallSpec)) {
foreach ($oAppContext->GetNames() as $sContextParam) {
@@ -1045,19 +1045,9 @@ JS
$aCount = $aCounts[$sStateValue];
$sHyperlink = $aCount['link'];
$sCountLabel = $aCount['label'];
$oPill = PillFactory::MakeForState($sClass, $sStateValue);
// N°5849 - Unencode label for ExternalKey attribute because friendlyname is already html encoded thanks to DBObject::GetName() in AttributeExternalKey::GetAllowedValues(). (A fix in this function may have too much impact).
if ($oAttDef instanceof AttributeExternalKey) {
$sPillTooltip = utils::HtmlEntityDecode($sStateLabel);
$sPillLabel = $sStateLabel;
} else {
$sPillTooltip = $sStateLabel;
$sPillLabel = utils::HtmlEntities($sStateLabel);
}
$oPill->SetTooltip($sPillTooltip)
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--count\">$sCountLabel</span><span class=\"ibo-dashlet-header-dynamic--label ibo-text-truncated-with-ellipsis\">".$sPillLabel."</span>");
$oPill = PillFactory::MakeForState($sClass, $sStateValue)
->SetTooltip($sStateLabel)
->AddHtml("<span class=\"ibo-dashlet-header-dynamic--count\">$sCountLabel</span><span class=\"ibo-dashlet-header-dynamic--label ibo-text-truncated-with-ellipsis\">".utils::HtmlEntities($sStateLabel)."</span>");
if ($sHyperlink != '-') {
$oPill->SetUrl($sHyperlink);
}
@@ -1611,7 +1601,6 @@ JS
$iTotalCount = 0;
$aURLs = array();
foreach ($aRes as $iRow => $aRow) {
$sValue = $aRow['grouped_by_1'];
$sHtmlValue = $oGroupByExp->MakeValueLabel($this->m_oFilter, $sValue, $sValue);
@@ -1622,7 +1611,6 @@ JS
'value' => (float)$aRow[$sFctVar],
);
// Build the search for this subset
$oSubsetSearch = $this->m_oFilter->DeepClone();
$oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($sValue));
@@ -1639,13 +1627,9 @@ JS
switch ($sChartType) {
case 'bars':
$iMaxNbCharsInLabel = 0;
$aNames = [];
$aNames = array();
foreach ($aValues as $idx => $aValue) {
$aNames[$idx] = $aValue['label'];
if ($iMaxNbCharsInLabel < mb_strlen($aValue['label'])) {
$iMaxNbCharsInLabel = mb_strlen($aValue['label']);
}
}
$oBlock = new BlockChartAjaxBars();
$oBlock->sJSNames = json_encode($aNames);
@@ -1653,31 +1637,21 @@ JS
$oBlock->sId = $sId;
$oBlock->sJSURLs = $sJSURLs;
$oBlock->sURLForRefresh = str_replace("'", "\'", $sUrl);
$oBlock->iMaxNbCharsInLabel = $iMaxNbCharsInLabel;
break;
case 'pie':
$aColumns = [];
$aNames = [];
$aColumns = array();
$aNames = array();
foreach ($aValues as $idx => $aValue) {
$aColumns[] = array('series_'.$idx, (float)$aValue['value']);
$aNames['series_'.$idx] = $aValue['label'];
}
$iNbLinesToAddForName = 0;
if (count($aNames) > 50) {
// Calculation of the number of legends line add to the height of the graph to have a maximum of 5 legend columns
$iNbLinesIncludedInChartHeight = 10;
$iNbLinesToAddForName = ceil(count($aNames) / 5) - $iNbLinesIncludedInChartHeight;
}
$oBlock = new BlockChartAjaxPie();
$oBlock->sJSColumns = json_encode($aColumns);
$oBlock->sJSNames = json_encode($aNames);
$oBlock->sId = $sId;
$oBlock->sJSURLs = $sJSURLs;
$oBlock->sURLForRefresh = str_replace("'", "\'", $sUrl);
$oBlock->iNbLinesToAddForName = $iNbLinesToAddForName;
break;
}
if (isset($aExtraParams["surround_with_panel"]) && $aExtraParams["surround_with_panel"]) {
@@ -1732,162 +1706,6 @@ JS
}
/**
* Helper class to manage 'blocks' of HTML pieces that are parts of a page and contain some list of cmdb objects
*
* Each block is actually rendered as a <div></div> tag that can be rendered synchronously
* or as a piece of Javascript/JQuery/Ajax that will get its content from another page (ajax.render.php).
* The list of cmdbObjects to be displayed into the block is defined by a filter
* Right now the type of display is either: list, count or details
* - list produces a table listing the objects
* - count produces a paragraphs with a sentence saying 'cont' objects found
* - details display (as table) the details of each object found (best if only one)
*
* @deprecated 3.0.0 will be removed in 3.1, see N°3824
*/
class HistoryBlock extends DisplayBlock
{
protected $iLimitCount;
protected $iLimitStart;
public function __construct(DBSearch $oFilter, $sStyle = 'list', $bAsynchronous = false, $aParams = array(), $oSet = null)
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod();
parent::__construct($oFilter, $sStyle, $bAsynchronous, $aParams, $oSet);
$this->iLimitStart = 0;
$this->iLimitCount = 0;
}
public function SetLimit($iCount, $iStart = 0)
{
$this->iLimitStart = $iStart;
$this->iLimitCount = $iCount;
}
/**
* @param \WebPage $oPage
* @param array $aExtraParams
* @param string $sId
*
* @return string
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DictExceptionMissingString
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*
* @since 2.7.7 3.0.1 3.1.0 N°3129 Remove default value for $aExtraParams and add type hinting for PHP 8.0 compatibility
* (var is unused, and all calls were already made using a default value)
*/
public function GetRenderContent(WebPage $oPage, array $aExtraParams, string $sId)
{
$sHtml = '';
$bTruncated = false;
$oSet = new CMDBObjectSet($this->m_oFilter, array('date' => false));
if (!$oPage->IsPrintableVersion()) {
if (($this->iLimitStart > 0) || ($this->iLimitCount > 0)) {
$oSet->SetLimit($this->iLimitCount, $this->iLimitStart);
if (($this->iLimitCount - $this->iLimitStart) < $oSet->Count()) {
$bTruncated = true;
}
}
}
$sHtml .= "<!-- filter: ".($this->m_oFilter->ToOQL())."-->\n";
switch ($this->m_sStyle) {
case 'toggle':
// First the latest change that the user is allowed to see
do {
$oLatestChangeOp = $oSet->Fetch();
} while (is_object($oLatestChangeOp) && ($oLatestChangeOp->GetDescription() == ''));
if (is_object($oLatestChangeOp)) {
// There is one change in the list... only when the object has been created !
$sDate = $oLatestChangeOp->GetAsHTML('date');
$oChange = MetaModel::GetObject('CMDBChange', $oLatestChangeOp->Get('change'));
$sUserInfo = $oChange->GetAsHTML('userinfo');
$sHtml .= $oPage->GetStartCollapsibleSection(Dict::Format('UI:History:LastModified_On_By', $sDate, $sUserInfo));
$sHtml .= $this->GetHistoryTable($oPage, $oSet);
$sHtml .= $oPage->GetEndCollapsibleSection();
}
break;
case 'table':
default:
if ($bTruncated)
{
$sFilter = htmlentities($this->m_oFilter->serialize(), ENT_QUOTES, 'UTF-8');
$sHtml .= '<div id="history_container"><p>';
$sHtml .= Dict::Format('UI:TruncatedResults', $this->iLimitCount, $oSet->Count());
$sHtml .= ' ';
$sHtml .= '<a href="#" onclick="DisplayHistory(\'#history_container\', \''.$sFilter.'\', 0, 0); return false;">'.Dict::S('UI:DisplayAll').'</a>';
$sHtml .= $this->GetHistoryTable($oPage, $oSet);
$sHtml .= '</p></div>';
$oPage->add_ready_script("$('#{$sId} table.listResults tr:last td').addClass('truncated');");
}
else
{
$sHtml .= $this->GetHistoryTable($oPage, $oSet);
}
$oPage->add_ready_script(InlineImage::FixImagesWidth());
$oPage->add_ready_script("$('.case-log-history-entry-toggle').on('click', function () { $(this).closest('.case-log-history-entry').toggleClass('expanded');});");
$oPage->add_ready_script(
<<<EOF
$('.history_entry').each(function() {
var jMe = $(this);
var oContent = $(this).find('.history_html_content');
if (jMe.height() < oContent.height())
{
jMe.prepend('<span class="history_truncated_toggler"></span>');
jMe.find('.history_truncated_toggler').on('click', function() {
jMe.toggleClass('history_entry_truncated');
});
}
});
EOF
);
}
return new Html($sHtml);
}
protected function GetHistoryTable(WebPage $oPage, DBObjectSet $oSet)
{
$sHtml = '';
// First the latest change that the user is allowed to see
$oSet->Rewind(); // Reset the pointer to the beginning of the set
$aChanges = array();
while($oChangeOp = $oSet->Fetch())
{
$sChangeDescription = $oChangeOp->GetDescription();
if ($sChangeDescription != '')
{
// The change is visible for the current user
$changeId = $oChangeOp->Get('change');
$aChanges[$changeId]['date'] = $oChangeOp->Get('date');
$aChanges[$changeId]['userinfo'] = $oChangeOp->Get('userinfo');
if (!isset($aChanges[$changeId]['log']))
{
$aChanges[$changeId]['log'] = array();
}
$aChanges[$changeId]['log'][] = $sChangeDescription;
}
}
$aAttribs = array('date' => array('label' => Dict::S('UI:History:Date'), 'description' => Dict::S('UI:History:Date+')),
'userinfo' => array('label' => Dict::S('UI:History:User'), 'description' => Dict::S('UI:History:User+')),
'log' => array('label' => Dict::S('UI:History:Changes') , 'description' => Dict::S('UI:History:Changes+')),
);
$aValues = array();
foreach($aChanges as $aChange)
{
$aValues[] = array('date' => AttributeDateTime::GetFormat()->Format($aChange['date']), 'userinfo' => htmlentities($aChange['userinfo'], ENT_QUOTES, 'UTF-8'), 'log' => "<ul><li>".implode('</li><li>', $aChange['log'])."</li></ul>");
}
$sHtml .= $oPage->GetTable($aAttribs, $aValues);
return $sHtml;
}
}
/**
* Displays the 'Actions' menu for a given (list of) object(s)
* The 'style' of the list (see constructor of DisplayBlock) can be either 'list' or 'details'

View File

@@ -1,8 +1,8 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/application/WebPage/ErrorPage.php, now loadable using autoloader
* @deprecated 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-2021 Combodo SARL
*/
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/ErrorPage.php, now loadable using autoloader');
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/ErrorPage.php, now loadable using autoloader');

View File

@@ -60,24 +60,6 @@ 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

@@ -1,36 +0,0 @@
<?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

@@ -838,7 +838,8 @@ class DesignerFormField
{
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
return array('label' => $this->sLabel, 'value' => "<input type=\"text\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">");
return array('label' => $this->sLabel, 'value' => "<input type=\"text\" id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($this->defaultValue)."\">");
}
/**
@@ -1012,9 +1013,8 @@ class DesignerTextField extends DesignerFormField
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
if ($this->IsReadOnly())
{
$sHtmlValue = "<span>".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\"/></span>";
if ($this->IsReadOnly()) {
$sHtmlValue = "<span>".utils::EscapeHtml($this->defaultValue)."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($this->defaultValue)."\"/></span>";
}
else
{
@@ -1038,11 +1038,10 @@ $('#$sId').on('change keyup validate', function() { ValidateWithPattern('$sId',
EOF
);
$sCSSClasses = '';
if (count($this->aCSSClasses) > 0)
{
if (count($this->aCSSClasses) > 0) {
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
}
$sHtmlValue = "<input type=\"text\" $sCSSClasses id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">";
$sHtmlValue = "<input type=\"text\" $sCSSClasses id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($this->defaultValue)."\">";
}
return array('label' => $this->sLabel, 'value' => $sHtmlValue);
}
@@ -1101,10 +1100,9 @@ class DesignerLongTextField extends DesignerTextField
{
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
}
if (!$this->IsReadOnly())
{
if (!$this->IsReadOnly()) {
$oP->add_ready_script(
<<<EOF
<<<EOF
$('#$sId').on('change keyup validate', function() { ValidateWithPattern('$sId', $sMandatory, '$sPattern', $(this).closest('form').attr('id'), $sForbiddenValues); } );
{
var myTimer = null;
@@ -1112,11 +1110,10 @@ $('#$sId').on('change keyup validate', function() { ValidateWithPattern('$sId',
}
EOF
);
$sValue = "<textarea $sCSSClasses id=\"$sId\" name=\"$sName\">".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."</textarea>";
$sValue = "<textarea $sCSSClasses id=\"$sId\" name=\"$sName\">".utils::EscapeHtml($this->defaultValue)."</textarea>";
}
else
{
$sValue = "<div $sCSSClasses id=\"$sId\">".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."</div>";
else {
$sValue = "<div $sCSSClasses id=\"$sId\">".utils::EscapeHtml($this->defaultValue)."</div>";
}
return array('label' => $this->sLabel, 'value' => $sValue);
}
@@ -1145,9 +1142,8 @@ class DesignerIntegerField extends DesignerFormField
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
if ($this->IsReadOnly())
{
$sHtmlValue = "<span>".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\"/></span>";
if ($this->IsReadOnly()) {
$sHtmlValue = "<span>".utils::EscapeHtml($this->defaultValue)."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($this->defaultValue)."\"/></span>";
}
else
{
@@ -1164,11 +1160,10 @@ $('#$sId').on('change keyup validate', function() { ValidateInteger('$sId', $sMa
EOF
);
$sCSSClasses = '';
if (count($this->aCSSClasses) > 0)
{
if (count($this->aCSSClasses) > 0) {
$sCSSClasses = 'class="'.implode(' ', $this->aCSSClasses).'"';
}
$sHtmlValue = "<input type=\"text\" $sCSSClasses id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">";
$sHtmlValue = "<input type=\"text\" $sCSSClasses id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($this->defaultValue)."\">";
}
return array('label' => $this->sLabel, 'value' => $sHtmlValue);
}
@@ -1289,22 +1284,18 @@ class DesignerComboField extends DesignerFormField
{
if ($this->bMultipleSelection)
{
if(in_array($sKey, $this->defaultValue))
{
if(in_array($sKey, $this->defaultValue)) {
$aSelected[] = $sDisplayValue;
$aHiddenValues[] = "<input type=\"hidden\" name=\"{$sName}[]\" value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\"/>";
$aHiddenValues[] = "<input type=\"hidden\" name=\"{$sName}[]\" value=\"".utils::EscapeHtml($sKey)."\"/>";
}
}
else
{
if ($sKey == $this->defaultValue)
{
} else {
if ($sKey == $this->defaultValue) {
$aSelected[] = $sDisplayValue;
$aHiddenValues[] = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\"/>";
$aHiddenValues[] = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($sKey)."\"/>";
}
}
}
$sHtml = "<span $sCSSClasses>".htmlentities(implode(', ', $aSelected), ENT_QUOTES, 'UTF-8').implode($aHiddenValues)."</span>";
$sHtml = "<span $sCSSClasses>".utils::EscapeHtml(implode(', ', $aSelected)).implode($aHiddenValues)."</span>";
}
else
{
@@ -1328,7 +1319,7 @@ class DesignerComboField extends DesignerFormField
}
// Quick and dirty: display the menu parents as a tree
$sHtmlValue = str_replace(' ', '&nbsp;', $sDisplayValue);
$sHtml .= "<option value=\"".htmlentities($sKey, ENT_QUOTES, 'UTF-8')."\" $sSelected>$sHtmlValue</option>";
$sHtml .= "<option value=\"".utils::EscapeHtml($sKey)."\" $sSelected>$sHtmlValue</option>";
}
$sHtml .= "</select></span>";
if ($this->bOtherChoices)
@@ -1379,10 +1370,9 @@ class DesignerBooleanField extends DesignerFormField
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
$sChecked = $this->defaultValue ? 'checked' : '';
if ($this->IsReadOnly())
{
if ($this->IsReadOnly()) {
$sLabel = $this->defaultValue ? Dict::S('UI:UserManagement:ActionAllowed:Yes') : Dict::S('UI:UserManagement:ActionAllowed:No'); //TODO use our own yes/no translations
$sHtmlValue = "<span>".htmlentities($sLabel)."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\"/></span>";
$sHtmlValue = "<span>".utils::EscapeHtml($sLabel)."<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($this->defaultValue)."\"/></span>";
}
else
{
@@ -1450,8 +1440,8 @@ class DesignerHiddenField extends DesignerFormField
{
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
$sChecked = $this->defaultValue ? 'checked' : '';
return array('label' =>'', 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">");
return array('label' => '', 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($this->defaultValue)."\">");
}
}
@@ -1518,7 +1508,7 @@ class DesignerIconSelectionField extends DesignerFormField
EOF
);
} else {
$sValue = '<span style="display:inline-block;line-height:48px;height:48px;"><span><img style="vertical-align:middle" src="'.$this->aAllowedValues[$idx]['icon'].'" />&nbsp;'.htmlentities($this->aAllowedValues[$idx]['label'], ENT_QUOTES, 'UTF-8').'</span></span>';
$sValue = '<span style="display:inline-block;line-height:48px;height:48px;"><span><img style="vertical-align:middle" src="'.$this->aAllowedValues[$idx]['icon'].'" />&nbsp;'.utils::EscapeHtml($this->aAllowedValues[$idx]['label']).'</span></span>';
}
$sReadOnly = $this->IsReadOnly() ? 'disabled' : '';
return array('label' => $this->sLabel, 'value' => $sValue);
@@ -1665,14 +1655,14 @@ class DesignerSortableField extends DesignerFormField
$sId = $this->oForm->GetFieldId($this->sCode);
$sName = $this->oForm->GetFieldName($this->sCode);
$sReadOnly = $this->IsReadOnly() ? 'readonly="readonly"' : '';
$aResult = array('label' => $this->sLabel, 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" $sReadOnly value=\"".htmlentities($this->defaultValue, ENT_QUOTES, 'UTF-8')."\">");
$aResult = array('label' => $this->sLabel, 'value' => "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" $sReadOnly value=\"".utils::EscapeHtml($this->defaultValue)."\">");
$sJSFields = json_encode(array_keys($this->aAllowedValues));
$oP->add_ready_script(
"$('#$sId').sortable_field({aAvailableFields: $sJSFields});"
);
return $aResult;
}
}
@@ -1761,8 +1751,8 @@ class DesignerFormSelectorField extends DesignerFormField
foreach ($this->aSubForms as $iKey => $aFormData) {
if ($iKey == $this->defaultValue) // Default value is actually the index
{
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
$sHiddenValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".htmlentities($iKey, ENT_QUOTES, 'UTF-8')."\"/>";
$sDisplayValue = utils::EscapeHtml($aFormData['label']);
$sHiddenValue = "<input type=\"hidden\" id=\"$sId\" name=\"$sName\" value=\"".utils::EscapeHtml($iKey)."\"/>";
break;
}
}
@@ -1770,8 +1760,8 @@ class DesignerFormSelectorField extends DesignerFormField
} else {
$sHtml = "<span class=\"ibo-input-select-wrapper\"><select $sCSSClasses id=\"$sId\" name=\"$sName\" $sReadOnly>";
foreach ($this->aSubForms as $iKey => $aFormData) {
$sDisplayValue = htmlentities($aFormData['label'], ENT_QUOTES, 'UTF-8');
$sValue = htmlentities($aFormData['value'], ENT_QUOTES, 'UTF-8');
$sDisplayValue = utils::EscapeHtml($aFormData['label']);
$sValue = utils::EscapeHtml($aFormData['value']);
$sSelected = ($iKey == $this->defaultValue) ? 'selected' : '';
$sHtml .= "<option data-value=\"$sValue\" value=\"$iKey\" $sSelected>".$sDisplayValue."</option>";
}

View File

@@ -1,9 +1,9 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/application/WebPage/iTopWebPage.php, now loadable using autoloader
* @deprecated 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-2021 Combodo SARL
*/
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
//DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/iTopWebPage.php, now loadable using autoloader');
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/iTopWebPage.php, now loadable using autoloader');

View File

@@ -1,8 +1,8 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/application/WebPage/iTopWizardWebPage.php, now loadable using autoloader
* @deprecated 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-2021 Combodo SARL
*/
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/iTopWizardWebPage.php, now loadable using autoloader');
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/iTopWizardWebPage.php, now loadable using autoloader');

View File

@@ -59,6 +59,7 @@ class LoginBasic extends AbstractLoginFSMExtension
list($sAuthUser, $sAuthPwd) = $this->GetAuthUserAndPassword();
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal'))
{
$_SESSION['auth_user'] = $sAuthUser;
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
@@ -80,11 +81,6 @@ 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,12 +95,6 @@ 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();
@@ -119,11 +113,6 @@ 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;
}
@@ -139,4 +128,4 @@ class LoginDefaultAfter extends AbstractLoginFSMExtension implements iLogoutExte
}
}
}
}
}

View File

@@ -42,6 +42,7 @@ class LoginExternal extends AbstractLoginFSMExtension
$sAuthUser = $this->GetAuthUser();
if (!UserRights::CheckCredentials($sAuthUser, '', Session::Get('login_mode'), 'external'))
{
$_SESSION['auth_user'] = $sAuthUser;
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
@@ -73,11 +74,6 @@ 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;
@@ -93,4 +89,4 @@ class LoginExternal extends AbstractLoginFSMExtension
/** @var string $sAuthUser */
return $sAuthUser; // Retrieve the value
}
}
}

View File

@@ -44,10 +44,6 @@ 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);
@@ -72,6 +68,7 @@ class LoginForm extends AbstractLoginFSMExtension implements iLoginUIExtension
$sAuthPwd = utils::ReadPostedParam('auth_pwd', null, 'raw_data');
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal'))
{
$_SESSION['auth_user'] = $sAuthUser;
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}

View File

@@ -8,7 +8,10 @@
use Combodo\iTop\Application\Branding;
use Combodo\iTop\TwigExtension;
use Combodo\iTop\Application\TwigBase\Twig\Extension;
use Twig\Environment;
use Twig\Loader\ChainLoader;
use Twig\Loader\FilesystemLoader;
/**
* Twig context for modules extending the login screen
@@ -217,14 +220,14 @@ class LoginTwigRenderer
$sTwigLoaderPath = $oLoginContext->GetTwigLoaderPath();
if ($sTwigLoaderPath != null)
{
$oExtensionLoader = new Twig_Loader_Filesystem();
$oExtensionLoader = new FilesystemLoader();
$oExtensionLoader->setPaths($sTwigLoaderPath);
$aTwigLoaders[] = $oExtensionLoader;
}
$this->aPostedVars = array_merge($this->aPostedVars, $oLoginContext->GetPostedVars());
}
$oCoreLoader = new Twig_Loader_Filesystem(array(), APPROOT.'templates');
$oCoreLoader = new FilesystemLoader(array(), APPROOT.'templates');
$aCoreTemplatesPaths = array('pages/login', 'pages/login/password');
// Having this path declared after the plugins let the plugins replace the core templates
$oCoreLoader->setPaths($aCoreTemplatesPaths);
@@ -232,9 +235,9 @@ class LoginTwigRenderer
$oCoreLoader->setPaths($aCoreTemplatesPaths, 'ItopCore');
$aTwigLoaders[] = $oCoreLoader;
$oLoader = new Twig_Loader_Chain($aTwigLoaders);
$this->oTwig = new Twig_Environment($oLoader);
TwigExtension::RegisterTwigExtensions($this->oTwig);
$oLoader = new ChainLoader($aTwigLoaders);
$this->oTwig = new Environment($oLoader);
Extension::RegisterTwigExtensions($this->oTwig);
}
public function GetDefaultVars()
@@ -306,7 +309,7 @@ class LoginTwigRenderer
}
/**
* @return \Twig_Environment
* @return \Twig\Environment
*/
public function GetTwig()
{

View File

@@ -57,6 +57,7 @@ class LoginURL extends AbstractLoginFSMExtension
$sAuthPwd = utils::ReadParam('auth_pwd', null, false, 'raw_data');
if (!UserRights::CheckCredentials($sAuthUser, $sAuthPwd, Session::Get('login_mode'), 'internal'))
{
$_SESSION['auth_user'] = $sAuthUser;
$iErrorCode = LoginWebPage::EXIT_CODE_WRONGCREDENTIALS;
return LoginWebPage::LOGIN_FSM_ERROR;
}
@@ -92,4 +93,4 @@ class LoginURL extends AbstractLoginFSMExtension
}
return LoginWebPage::LOGIN_FSM_CONTINUE;
}
}
}

View File

@@ -1,5 +1,5 @@
<?php
// Copyright (C) 2010-2023 Combodo SARL
// Copyright (C) 2010-2021 Combodo SARL
//
// This file is part of iTop.
//
@@ -26,6 +26,8 @@
use Combodo\iTop\Application\Branding;
use Combodo\iTop\Application\Helper\Session;
use Combodo\iTop\Service\EventData;
use Combodo\iTop\Service\EventService;
/**
* Web page used for displaying the login form
@@ -35,7 +37,7 @@ class LoginWebPage extends NiceWebPage
{
const EXIT_PROMPT = 0;
const EXIT_HTTP_401 = 1;
const EXIT_RETURN = 2; // Non interactive mode (ajax, rest, ...)
const EXIT_RETURN = 2;
const EXIT_CODE_OK = 0;
const EXIT_CODE_MISSINGLOGIN = 1;
@@ -88,7 +90,7 @@ class LoginWebPage extends NiceWebPage
parent::__construct($sTitle);
$this->SetStyleSheet();
$this->no_cache();
$this->add_http_headers();
$this->add_xframe_options();
}
public function SetStyleSheet()
@@ -105,7 +107,6 @@ class LoginWebPage extends NiceWebPage
/**
* @param $oUser
* @param array $aProfiles
* @param $sOrigin
*
* @return array
* @throws \CoreException
@@ -132,10 +133,6 @@ class LoginWebPage extends NiceWebPage
//add profiles not already linked with user
foreach ($aProfiles as $iProfileId)
{
$oLink = new URP_UserProfile();
$oLink->Set('profileid', $iProfileId);
$oLink->Set('reason', $sOrigin);
$oProfilesSet->AddItem(MetaModel::NewObject('URP_UserProfile', array('profileid' => $iProfileId, 'reason' => $sOrigin)));
}
$oUser->Set('profile_list', $oProfilesSet);
@@ -386,20 +383,14 @@ class LoginWebPage extends NiceWebPage
$this->output();
}
public static function ResetSession($bFullCleanup = false)
public static function ResetSession()
{
if ($bFullCleanup) {
// Unset all of the session variables.
foreach (array_keys($_SESSION) as $sKey) {
Session::Unset($sKey);
}
} else {
Session::Unset('auth_user');
Session::Unset('login_state');
Session::Unset('can_logoff');
Session::Unset('archive_mode');
Session::Unset('impersonate_user');
}
// Unset all of the session variables.
Session::Unset('auth_user');
Session::Unset('login_state');
Session::Unset('can_logoff');
Session::Unset('archive_mode');
Session::Unset('impersonate_user');
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!
@@ -490,11 +481,13 @@ class LoginWebPage extends NiceWebPage
$iResponse = $oLoginFSMExtensionInstance->LoginAction($sLoginState, $iErrorCode);
if ($iResponse == self::LOGIN_FSM_RETURN)
{
EventService::FireEvent(new EventData(EVENT_SERVICE_LOGIN, null, ['code' => $iErrorCode, 'state' => $sLoginState]));
Session::WriteClose();
return $iErrorCode; // Asked to exit FSM, generally login OK
}
if ($iResponse == self::LOGIN_FSM_ERROR)
{
EventService::FireEvent(new EventData(EVENT_SERVICE_LOGIN, null, ['code' => $iErrorCode, 'state' => $sLoginState]));
$sLoginState = self::LOGIN_STATE_SET_ERROR; // Next state will be error
// An error was detected, skip the other plugins turn
break;
@@ -508,6 +501,7 @@ class LoginWebPage extends NiceWebPage
}
catch (Exception $e)
{
EventService::FireEvent(new EventData(EVENT_SERVICE_LOGIN, null, ['state' => $_SESSION['login_state']]));
IssueLog::Error($e->getTraceAsString());
static::ResetSession();
die($e->getMessage());
@@ -964,7 +958,7 @@ class LoginWebPage extends NiceWebPage
}
else
{
if ($iOnExit === self::EXIT_RETURN)
if ($iOnExit == self::EXIT_RETURN)
{
return self::EXIT_CODE_PORTALUSERNOTAUTHORIZED;
}
@@ -1019,7 +1013,7 @@ class LoginWebPage extends NiceWebPage
{
if ($bMustBeAdmin && !UserRights::IsAdministrator())
{
if ($iOnExit === self::EXIT_RETURN)
if ($iOnExit == self::EXIT_RETURN)
{
return self::EXIT_CODE_MUSTBEADMIN;
}
@@ -1035,7 +1029,7 @@ class LoginWebPage extends NiceWebPage
}
$iRet = call_user_func(array(self::$sHandlerClass, 'ChangeLocation'), $sRequestedPortalId, $iOnExit);
}
if ($iOnExit === self::EXIT_RETURN)
if ($iOnExit == self::EXIT_RETURN)
{
return $iRet;
}

View File

@@ -1,8 +1,8 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/application/WebPage/NiceWebPage.php, now loadable using autoloader
* @deprecated 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-2021 Combodo SARL
*/
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/NiceWebPage.php, now loadable using autoloader');
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/NiceWebPage.php, now loadable using autoloader');

View File

@@ -1,8 +1,8 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/application/WebPage/PDFPage.php, now loadable using autoloader
* @deprecated 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-2021 Combodo SARL
*/
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/PDFPage.php, now loadable using autoloader');
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/PDFPage.php, now loadable using autoloader');

View File

@@ -18,8 +18,7 @@
*/
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Field\Field;
use Combodo\iTop\Application\UI\Base\Component\Field\FieldUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\FieldSet\FieldSetUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
use Combodo\iTop\Application\UI\Base\Component\Input\TextArea;
@@ -51,6 +50,7 @@ abstract class Query extends cmdbAbstractObject
"is_null_allowed" => false,
"depends_on" => array(),
)));
MetaModel::Init_AddAttribute(new AttributeText("description", array(
"allowed_values" => null,
"sql" => "description",
@@ -68,6 +68,41 @@ abstract class Query extends cmdbAbstractObject
'display_style' => 'radio_horizontal',
)));
MetaModel::Init_AddAttribute(new AttributeInteger("export_count", array(
"allowed_values" => null,
"sql" => "export_count",
"default_value" => 0,
"is_null_allowed" => false,
"depends_on" => array(),
)));
MetaModel::Init_AddAttribute(new AttributeDateTime("export_last_date", array(
"allowed_values" => null,
"sql" => "export_last_date",
"default_value" => null,
"is_null_allowed" => true,
"depends_on" => array(),
)));
MetaModel::Init_AddAttribute(new AttributeExternalKey("export_last_user_id",
array(
"targetclass"=>'User',
"allowed_values"=>null,
"sql"=>'user_id',
"is_null_allowed"=>true,
"depends_on"=>array(),
"display_style"=>'select',
"always_load_in_tables"=>false,
"on_target_delete"=>DEL_SILENT
)));
MetaModel::Init_AddAttribute(new AttributeExternalField("export_last_user_contact",
array(
"allowed_values"=>null,
"extkey_attcode"=> "export_last_user_id",
"target_attcode"=>"contactid"
)));
// Display lists
MetaModel::Init_SetZListItems('details',
array('name', 'is_template', 'description')); // Attributes to be displayed for the complete details
@@ -78,6 +113,54 @@ abstract class Query extends cmdbAbstractObject
array('name', 'description', 'is_template')); // Criteria of the default search form
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
}
/**
* @inheritdoc
*
* @since 3.1.0
*/
public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '')
{
// read only attribute
if (in_array($sAttCode, ['export_count', 'export_last_date', 'export_last_user_id'])){
return OPT_ATT_READONLY;
}
return parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState);
}
/**
* Return export url.
*
* @param array|null $aValues optional values for the query
*
* @return string|null
* @since 3.1.0
*/
abstract public function GetExportUrl(array $aValues = null) : ?string;
/**
* Update last export information.
*
* @return void
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \MySQLException
* @since 3.1.0
*/
public function UpdateLastExportInformation() : void
{
// last export information
$this->Set('export_last_date', date(AttributeDateTime::GetSQLFormat()));
$this->Set('export_last_user_id', UserRights::GetUserObject());
$this->DBUpdate();
// increment usage counter
$this->DBIncrement('export_count');
}
}
class QueryOQL extends Query
@@ -116,13 +199,51 @@ class QueryOQL extends Query
// Display lists
MetaModel::Init_SetZListItems('details',
array('name', 'is_template', 'description', 'oql', 'fields')); // Attributes to be displayed for the complete details
array(
'col:col1' => array('fieldset:Query:baseinfo' => array('name', 'is_template', 'description', 'oql', 'fields')),
'col:col2' => array('fieldset:Query:exportInfo' => array('export_count', 'export_last_date', 'export_last_user_id', 'export_last_user_contact'))
)
); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('description')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search',
array('name', 'description', 'is_template', 'fields', 'oql')); // Criteria of the std search form
}
/** @inheritdoc */
public function GetExportUrl(array $aValues = null) : ?string
{
try{
// retrieve attributes
$sFields = trim($this->Get('fields'));
$sOql = $this->Get('oql');
// construct base url depending on version
$bExportV1Recommended = ($sFields == '');
if ($bExportV1Recommended) {
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export.php?format=spreadsheet&login_mode=basic&query='.$this->GetKey();
}
else{
$sUrl = utils::GetAbsoluteUrlAppRoot().'webservices/export-v2.php?format=spreadsheet&login_mode=basic&date_format='.urlencode((string)AttributeDateTime::GetFormat()).'&query='.$this->GetKey();
}
// search object from OQL
$oSearch = DBObjectSearch::FromOQL($sOql);
// inject parameters
$aParameters = $oSearch->GetQueryParams();
foreach ($aParameters as $sParam => $val) {
$paramValue = ($aValues === null || $aValues[$sParam] === null) ? $sParam : $aValues[$sParam];
$sUrl .= '&arg_' . $sParam . '=' . $paramValue;
}
return $sUrl;
}
catch(Exception $e){
return null;
}
}
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{
$aFieldsMap = parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
@@ -152,9 +273,11 @@ class QueryOQL extends Query
$sUrl .= '&arg_'.$sParam.'=["'.$sParam.'"]';
}
// add text area inside field set
$oFieldSet = FieldSetUIBlockFactory::MakeStandard(Dict::S('UI:Query:UrlForExcel'));
$oTextArea = new TextArea("", $sUrl, null, 80, 3);
$oFieldUrl = FieldUIBlockFactory::MakeFromObject(Dict::S('UI:Query:UrlForExcel'), $oTextArea, Field::ENUM_FIELD_LAYOUT_LARGE);
$oPage->AddSubBlock($oFieldUrl);
$oFieldSet->AddSubBlock($oTextArea);
$oPage->AddSubBlock($oFieldSet);
if (count($aParameters) == 0) {
$oBlock = new DisplayBlock($oSearch, 'list');
@@ -178,6 +301,7 @@ class QueryOQL extends Query
return $aFieldsMap;
}
// Rolled back until 'fields' can be properly managed by AttributeQueryAttCodeSet
//
// public function ComputeValues()

View File

@@ -98,10 +98,4 @@ else
Session::Set('itop_env', ITOP_DEFAULT_ENV);
}
$sConfigFile = APPCONF.$sEnv.'/'.ITOP_CONFIG_FILE;
try {
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);
}
catch (MySQLException $e) {
IssueLog::Debug($e->getMessage());
throw new MySQLException('Could not connect to the DB server', []);
}
MetaModel::Startup($sConfigFile, false /* $bModelOnly */, $bAllowCache, false /* $bTraceSourceFiles */, $sEnv);

View File

@@ -229,12 +229,10 @@ class DisplayTemplate
static public function UnitTest()
{
require_once(APPROOT.'/application/startup.inc.php');
require_once(APPROOT."/application/itopwebpage.class.inc.php");
$sTemplate = '<div class="page_header">
<div class="actions_details"><a href="#"><span>Actions</span></a></div>
<h1>$class$: <span class="hilite">$name$</span></h1>
<itopblock blockclass="HistoryBlock" type="toggle" encoding="text/oql">SELECT CMDBChangeOp WHERE objkey = $id$ AND objclass = \'$class$\'</itopblock>
</div>
<img src="../../images/connect_to_network.png" style="margin-top:-10px; margin-right:10px; float:right">
<itoptabs>
@@ -353,7 +351,7 @@ class ObjectDetailsTemplate extends DisplayTemplate
$sTip = '';
foreach($aReasons as $aRow)
{
$sDescription = htmlentities($aRow['description'], ENT_QUOTES, 'UTF-8');
$sDescription = utils::EscapeHtml($aRow['description']);
$sDescription = str_replace(array("\r\n", "\n"), "<br/>", $sDescription);
$sTip .= "<div class='synchro-source'>";
$sTip .= "<div class='synchro-source-title'>Synchronized with {$aRow['name']}</div>";
@@ -361,10 +359,10 @@ class ObjectDetailsTemplate extends DisplayTemplate
}
$oPage->add_ready_script("$('#synchro_$iInputId').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
}
// Attribute is read-only
$sHTMLValue = "<span id=\"field_{$iInputId}\">".$this->m_oObj->GetAsHTML($sAttCode);
$sHTMLValue .= '<input type="hidden" id="'.$iInputId.'" name="attr_'.$sAttCode.'" value="'.htmlentities($this->m_oObj->Get($sAttCode), ENT_QUOTES, 'UTF-8').'"/></span>';
$sHTMLValue .= '<input type="hidden" id="'.$iInputId.'" name="attr_'.$sAttCode.'" value="'.utils::EscapeHtml($this->m_oObj->Get($sAttCode)).'"/></span>';
$aFieldsMap[$sAttCode] = $iInputId;
$aParams['this->comments('.$sAttCode.')'] = $sSynchroIcon;
}

View File

@@ -1,7 +1,6 @@
<div class="page_header">
<itopblock blockclass="MenuBlock" type="popup" encoding="text/oql" label="Actions">SELECT $class$ WHERE id = $id$</itopblock>
<h1>$class$: <span class="hilite">$name$</span></h1>
<itopblock blockclass="HistoryBlock" type="toggle" encoding="text/oql">SELECT CMDBChangeOp WHERE objkey = $id$ AND objclass = '$class$'</itopblock>
</div>
<img src="../../images/clean.png" style="margin-top:-20px; margin-right:10px; float:right">
<itopblock blockclass="DisplayBlock" asynchronous="false" type="bare_details" encoding="text/oql">SELECT $class$ WHERE id = $id$</itopblock>

View File

@@ -4,13 +4,17 @@ namespace Combodo\iTop;
use AttributeDate;
use AttributeDateTime;
use DeprecatedCallsLog;
use Dict;
use Exception;
use Twig_Environment;
use Twig_SimpleFilter;
use Twig_SimpleFunction;
use MetaModel;
use Twig\Environment;
use Twig\TwigFilter;
use Twig\TwigFunction;
use utils;
DeprecatedCallsLog::NotifyDeprecatedFile('instead use sources/Application/TwigBase/Twig/Extension.php, which is loaded by the autoloader');
/**
* Class TwigExtension
*
@@ -24,13 +28,13 @@ class TwigExtension
* Registers Twig extensions such as filters or functions.
* It allows us to access some stuff directly in twig.
*
* @param \Twig_Environment $oTwigEnv
* @param Environment $oTwigEnv
*/
public static function RegisterTwigExtensions(Twig_Environment &$oTwigEnv)
public static function RegisterTwigExtensions(Environment &$oTwigEnv)
{
// Filter to translate a string via the Dict::S function
// Usage in twig: {{ 'String:ToTranslate'|dict_s }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('dict_s',
$oTwigEnv->addFilter(new TwigFilter('dict_s',
function ($sStringCode, $sDefault = null, $bUserLanguageOnly = false) {
return Dict::S($sStringCode, $sDefault, $bUserLanguageOnly);
})
@@ -38,7 +42,7 @@ class TwigExtension
// Filter to format a string via the Dict::Format function
// Usage in twig: {{ 'String:ToTranslate'|dict_format() }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('dict_format',
$oTwigEnv->addFilter(new TwigFilter('dict_format',
function ($sStringCode, $sParam01 = null, $sParam02 = null, $sParam03 = null, $sParam04 = null) {
return Dict::Format($sStringCode, $sParam01, $sParam02, $sParam03, $sParam04);
})
@@ -47,16 +51,13 @@ class TwigExtension
// Filter to format output
// example a DateTime is converted to user format
// Usage in twig: {{ 'String:ToFormat'|output_format }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('date_format',
$oTwigEnv->addFilter(new TwigFilter('date_format',
function ($sDate) {
try
{
if (preg_match('@^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$@', trim($sDate)))
{
try {
if (preg_match('@^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$@', trim($sDate))) {
return AttributeDateTime::GetFormat()->Format($sDate);
}
if (preg_match('@^\d\d\d\d-\d\d-\d\d$@', trim($sDate)))
{
if (preg_match('@^\d\d\d\d-\d\d-\d\d$@', trim($sDate))) {
return AttributeDate::GetFormat()->Format($sDate);
}
}
@@ -71,7 +72,7 @@ class TwigExtension
// Filter to format output
// example a DateTime is converted to user format
// Usage in twig: {{ 'String:ToFormat'|output_format }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('size_format',
$oTwigEnv->addFilter(new TwigFilter('size_format',
function ($sSize) {
return utils::BytesToFriendlyFormat($sSize);
})
@@ -79,24 +80,25 @@ class TwigExtension
// Filter to enable base64 encode/decode
// Usage in twig: {{ 'String to encode'|base64_encode }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('base64_encode', 'base64_encode'));
$oTwigEnv->addFilter(new Twig_SimpleFilter('base64_decode', 'base64_decode'));
$oTwigEnv->addFilter(new TwigFilter('base64_encode', 'base64_encode'));
$oTwigEnv->addFilter(new TwigFilter('base64_decode', 'base64_decode'));
// Filter to enable json decode (encode already exists)
// Usage in twig: {{ aSomeArray|json_decode }}
$oTwigEnv->addFilter(new Twig_SimpleFilter('json_decode', function ($sJsonString, $bAssoc = false) {
$oTwigEnv->addFilter(new TwigFilter('json_decode', function ($sJsonString, $bAssoc = false) {
return json_decode($sJsonString, $bAssoc);
})
);
// Filter to add itopversion to an url
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_itop_version', function ($sUrl) {
$oTwigEnv->addFilter(new TwigFilter('add_itop_version', function ($sUrl) {
$sUrl = utils::AddParameterToUrl($sUrl, 'itopversion', ITOP_VERSION);
return $sUrl;
}));
// Filter to add a module's version to an url
$oTwigEnv->addFilter(new Twig_SimpleFilter('add_module_version', function ($sUrl, $sModuleName) {
$oTwigEnv->addFilter(new TwigFilter('add_module_version', function ($sUrl, $sModuleName) {
$sModuleVersion = utils::GetCompiledModuleVersion($sModuleName);
$sUrl = utils::AddParameterToUrl($sUrl, 'moduleversion', $sModuleVersion);
@@ -105,22 +107,36 @@ class TwigExtension
// Function to check our current environment
// Usage in twig: {% if is_development_environment() %}
$oTwigEnv->addFunction(new Twig_SimpleFunction('is_development_environment', function()
{
$oTwigEnv->addFunction(new TwigFunction('is_development_environment', function () {
return utils::IsDevelopmentEnvironment();
}));
// Function to get configuration parameter
// Usage in twig: {{ get_config_parameter('foo') }}
$oTwigEnv->addFunction(new TwigFunction('get_config_parameter', function ($sParamName) {
$oConfig = MetaModel::GetConfig();
return $oConfig->Get($sParamName);
}));
// Function to get a module setting
// Usage in twig: {{ get_module_setting(<MODULE_CODE>, <PROPERTY_CODE> [, <DEFAULT_VALUE>]) }}
// since 3.0.0, but see N°4034 for upcoming evolutions in the 3.1
$oTwigEnv->addFunction(new TwigFunction('get_module_setting', function (string $sModuleCode, string $sPropertyCode, $defaultValue = null) {
$oConfig = MetaModel::GetConfig();
return $oConfig->GetModuleSetting($sModuleCode, $sPropertyCode, $defaultValue);
}));
// Function to get the URL of a static page in a module
// Usage in twig: {{ get_static_page_module_url('itop-my-module', 'path-to-my-page') }}
$oTwigEnv->addFunction(new Twig_SimpleFunction('get_static_page_module_url', function($sModuleName, $sPage)
{
$oTwigEnv->addFunction(new TwigFunction('get_static_page_module_url', function ($sModuleName, $sPage) {
return utils::GetAbsoluteUrlModulesRoot().$sModuleName.'/'.$sPage;
}));
// Function to get the URL of a php page in a module
// Usage in twig: {{ get_page_module_url('itop-my-module', 'path-to-my-my-page.php') }}
$oTwigEnv->addFunction(new Twig_SimpleFunction('get_page_module_url', function($sModuleName, $sPage)
{
$oTwigEnv->addFunction(new TwigFunction('get_page_module_url', function ($sModuleName, $sPage) {
return utils::GetAbsoluteUrlModulePage($sModuleName, $sPage);
}));
}

View File

@@ -5,7 +5,6 @@
*/
use Combodo\iTop\Application\UI\Base\Component\Form\FormUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
use Combodo\iTop\Core\MetaModel\FriendlyNameType;
@@ -308,7 +307,7 @@ EOF
$sHTMLValue .= "<input class=\"field_autocomplete ibo-input ibo-input-select ibo-input-select-autocomplete\" type=\"text\" id=\"label_$this->iId\" value=\"$sDisplayValue\" placeholder='...'/>";
// another hidden input to store & pass the object's Id
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" />\n";
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".utils::HtmlEntities($value)."\" />\n";
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
// Scripts to start the autocomplete and bind some events to it
@@ -324,12 +323,12 @@ EOF
EOF
);
$sHTMLValue .= "<div class=\"ibo-input-select--action-buttons\">";
$sHTMLValue .= " <a href=\"#\" class=\"ibo-input-select--action-button ibo-input-select--action-button--clear ibo-is-hidden\" id=\"mini_clear_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Clear();\" data-tooltip-content='".Dict::S('UI:Button:Clear')."'><i class=\"fas fa-times\"></i></a>";
$sHTMLValue .= " <div class=\"ibo-input-select--action-button ibo-input-select--action-button--clear ibo-is-hidden\" id=\"mini_clear_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Clear();\" data-tooltip-content='".Dict::S('UI:Button:Clear')."'><i class=\"fas fa-times\"></i></div>";
}
if ($bCreate && $bExtensions) {
$sCallbackName = (MetaModel::IsAbstract($this->sTargetClass)) ? 'SelectObjectClass' : 'CreateObject';
$sHTMLValue .= "<a href=\"#\" class=\"ibo-input-select--action-button ibo-input-select--action-button--create\" id=\"mini_add_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\" data-tooltip-content='".Dict::S('UI:Button:Create')."'><i class=\"fas fa-plus\"></i></a>";
$sHTMLValue .= "<div class=\"ibo-input-select--action-button ibo-input-select--action-button--create\" id=\"mini_add_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.{$sCallbackName}();\" data-tooltip-content='".Dict::S('UI:Button:Create')."'><i class=\"fas fa-plus\"></i></div>";
$oPage->add_ready_script(
<<<JS
if ($('#ajax_{$this->iId}').length == 0)
@@ -340,7 +339,7 @@ JS
);
}
if ($bExtensions && MetaModel::IsHierarchicalClass($this->sTargetClass) !== false) {
$sHTMLValue .= "<a href=\"#\" class=\"ibo-input-select--action-button ibo-input-select--action-button--hierarchy\" id=\"mini_tree_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\" data-tooltip-content='".Dict::S('UI:Button:SearchInHierarchy')."'><i class=\"fas fa-sitemap\"></i></a>";
$sHTMLValue .= "<div class=\"ibo-input-select--action-button ibo-input-select--action-button--hierarchy\" id=\"mini_tree_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.HKDisplay();\" data-tooltip-content='".Dict::S('UI:Button:SearchInHierarchy')."'><i class=\"fas fa-sitemap\"></i></div>";
$oPage->add_ready_script(
<<<JS
if ($('#ac_tree_{$this->iId}').length == 0)
@@ -351,7 +350,7 @@ JS
);
}
if ($oAllowedValues->CountExceeds($iMaxComboLength)) {
$sHTMLValue .= " <a href=\"#\" class=\"ibo-input-select--action-button ibo-input-select--action-button--search\" id=\"mini_search_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Search();\" data-tooltip-content='".Dict::S('UI:Button:Search')."'><i class=\"fas fa-search\"></i></a>";
$sHTMLValue .= " <div class=\"ibo-input-select--action-button ibo-input-select--action-button--search\" id=\"mini_search_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Search();\" data-tooltip-content='".Dict::S('UI:Button:Search')."'><i class=\"fas fa-search\"></i></div>";
}
$sHTMLValue .= "</div>";
$sHTMLValue .= "</div>";
@@ -620,7 +619,7 @@ EOF
$sHTMLValue .= "<div class=\"ibo-input-select--action-buttons\"><span class=\"field_input_btn\"><div class=\"mini_button ibo-input-select--action-button\" id=\"mini_search_{$this->iId}\" onClick=\"oACWidget_{$this->iId}.Search();\"><i class=\"fas fa-search\"></i></div></span></div>";
// another hidden input to store & pass the object's Id
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\" />\n";
$sHTMLValue .= "<input type=\"hidden\" id=\"$this->iId\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" value=\"".utils::EscapeHtml($value)."\" />\n";
$JSSearchMode = $this->bSearchMode ? 'true' : 'false';
// Scripts to start the autocomplete and bind some events to it
@@ -905,7 +904,7 @@ JS
{
// For security reasons: check that the "proposed" class is actually a subclass of the linked class
// and that the current user is allowed to create objects of this class
$aSubClasses = MetaModel::EnumChildClasses($this->sTargetClass, ENUM_CHILD_CLASSES_ALL);
$aSubClasses = MetaModel::EnumChildClasses($this->sTargetClass);
$aPossibleClasses = array();
foreach($aSubClasses as $sCandidateClass)
{
@@ -925,7 +924,6 @@ JS
$sDialogTitleEscaped = addslashes($sDialogTitle);
$oPage->add_ready_script("$('#ac_create_$this->iId').dialog({ width: 'auto', height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true, title: '$sDialogTitleEscaped'});\n");
$oPage->add_ready_script("$('#ac_create_{$this->iId} form').removeAttr('onsubmit');");
$oPage->add_ready_script("$('#ac_create_{$this->iId} form').find('select').attr('id', 'ac_create_{$this->iId}_select');");
$oPage->add_ready_script("$('#ac_create_{$this->iId} form').on('submit.uilinksWizard', oACWidget_{$this->iId}.DoSelectObjectClass);");
}
@@ -973,7 +971,7 @@ HTML
foreach (MetaModel::ListAttributeDefs($this->sTargetClass) as $sAttCode => $oAttDef) {
if (($oAttDef instanceof AttributeBlob) || (false)) {
$aFieldsFlags[$sAttCode] = OPT_ATT_READONLY;
$aFieldsComments[$sAttCode] = '&nbsp;<img src="../images/transp-lock.png" style="vertical-align:middle" title="'.htmlentities(Dict::S('UI:UploadNotSupportedInThisMode')).'"/>';
$aFieldsComments[$sAttCode] = '&nbsp;<img src="../images/transp-lock.png" style="vertical-align:middle" title="'.utils::EscapeHtml(Dict::S('UI:UploadNotSupportedInThisMode')).'"/>';
}
}
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), array('formPrefix' => $this->iId, 'noRelations' => true, 'fieldsFlags' => $aFieldsFlags, 'fieldsComments' => $aFieldsComments));
@@ -986,7 +984,7 @@ HTML
$oPage->add_ready_script(<<<JS
$('#ac_create_{$this->iId}').dialog({ width: $(window).width() * 0.6, height: 'auto', maxHeight: $(window).height() - 50, autoOpen: false, modal: true});
$('#dcr_{$this->iId} form').removeAttr('onsubmit');
$('#dcr_{$this->iId} form').on('submit.uilinksWizard', oACWidget_{$this->iId}.DoCreateObject);
$('#dcr_{$this->iId} form').find('button[type="submit"]').on('click', oACWidget_{$this->iId}.DoCreateObject);
JS
);
}

View File

@@ -211,23 +211,7 @@ class UILinksWidgetDirect
$oObj = DBObject::MakeDefaultInstance($sRealClass);
$aPrefillParam = array('source_obj' => $oSourceObj);
$oObj->PrefillForm('creation_from_editinplace', $aPrefillParam);
$aFormExtraParams = array(
'formPrefix' => $this->sInputid,
'noRelations' => true,
'fieldsFlags' => $aFieldFlags,
'js_handlers' => [
'cancel_button_on_click' =>
<<<JS
function() {
// Do nothing, already handled by linksdirectwidget.js
};
JS
,
],
);
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), $aFormExtraParams);
cmdbAbstractObject::DisplayCreationForm($oPage, $sRealClass, $oObj, array(), array('formPrefix' => $this->sInputid, 'noRelations' => true, 'fieldsFlags' => $aFieldFlags));
}
else
{
@@ -271,7 +255,7 @@ JS
$oDiv = UIContentBlockUIBlockFactory::MakeStandard($this->sInputid, ['listContainer']);
$oPage->AddSubBlock($oDiv);
$oDatatable = DataTableUIBlockFactory::MakeForForm($this->sInputid, $aAttribs, $aData);
$oDatatable->SetOptions(['select_mode' => 'custom']);
$oDatatable->SetOptions(['select_mode' => 'custom', 'disable_hyperlinks' => true]);
$oDiv->AddSubBlock($oDatatable);
$sInputName = $sFormPrefix.'attr_'.$this->sAttCode;
$aLabels = array(

View File

@@ -100,26 +100,22 @@ class UILinksWidget
* @param array $aArgs Extra context arguments
* @param DBObject $oCurrentObj The object to which all the elements of the linked set refer to
* @param int $iUniqueId A unique identifier of new links
* @param bool $bReadOnly Display link as editable or read-only. Default is false (editable)
* @param bool $bAllowRemoteExtKeyEdit If true, the ext. key to the remote object can be edited, otherwise it will be read-only
* @param boolean $bReadOnly Display link as editable or read-only. Default is false (editable)
*
* @return array The HTML fragment of the one-row form
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \Exception
*
* @since 3.1.0 3.0.4 3.0.3-1 N°6124 - Workaround performance problem on the modification of an object with an n:n relation having a large volume
*/
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false, $bAllowRemoteExtKeyEdit = true)
protected function GetFormRow(WebPage $oP, DBObject $oLinkedObj, $linkObjOrId, $aArgs, $oCurrentObj, $iUniqueId, $bReadOnly = false, $bModified = false)
{
$sPrefix = "$this->m_sAttCode{$this->m_sNameSuffix}";
$aRow = array();
$aFieldsMap = array();
$iKey = 0;
if (is_object($linkObjOrId) && (!$linkObjOrId->IsNew()))
{
if (is_object($linkObjOrId) && (!$linkObjOrId->IsNew())) {
$iKey = $linkObjOrId->GetKey();
$iRemoteObjKey = $linkObjOrId->Get($this->m_sExtKeyToRemote);
$sPrefix .= "[$iKey][";
@@ -128,52 +124,44 @@ class UILinksWidget
$aArgs['wizHelper'] = "oWizardHelper{$this->m_iInputId}{$iKey}";
$aArgs['this'] = $linkObjOrId;
if ($bReadOnly)
{
if ($bReadOnly) {
$aRow['form::checkbox'] = "";
foreach ($this->m_aEditableFields as $sFieldCode)
{
foreach ($this->m_aEditableFields as $sFieldCode) {
$sDisplayValue = $linkObjOrId->GetEditValue($sFieldCode);
$aRow[$sFieldCode] = $sDisplayValue;
}
}
else
{
} else {
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"$iKey\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$iKey\">";
foreach ($this->m_aEditableFields as $sFieldCode)
{
// N°6124 - Force remote ext. key as read-only if too many items in the linkset
$bReadOnlyField = ($sFieldCode === $this->m_sExtKeyToRemote) && (false === $bAllowRemoteExtKeyEdit);
foreach ($this->m_aEditableFields as $sFieldCode) {
$sSafeFieldId = $this->GetFieldId($linkObjOrId->GetKey(), $sFieldCode);
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $linkObjOrId, $oP, $sNameSuffix, $sSafeFieldId, $bReadOnlyField);
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $linkObjOrId, $oP, $sNameSuffix, $sSafeFieldId);
$aFieldsMap[$sFieldCode] = $sSafeFieldId;
if ($bModified) {
$oP->add_ready_script(
<<<EOF
oWidget{$this->m_iInputId}.AddModified($iUniqueId, {$this->m_iInputId}, $sFieldCode, {$linkObjOrId->Get($sFieldCode)});
EOF
);
}
}
}
$sState = $linkObjOrId->GetState();
$sRemoteKeySafeFieldId = $this->GetFieldId($aArgs['this']->GetKey(), $this->m_sExtKeyToRemote);;
}
else
{
} else {
// form for creating a new record
if (is_object($linkObjOrId))
{
if (is_object($linkObjOrId)) {
// New link existing only in memory
$oNewLinkObj = $linkObjOrId;
$iRemoteObjKey = $oNewLinkObj->Get($this->m_sExtKeyToRemote);
$oNewLinkObj->Set($this->m_sExtKeyToMe,
$oCurrentObj); // Setting the extkey with the object also fills the related external fields
}
else
{
$oNewLinkObj->Set($this->m_sExtKeyToMe, $oCurrentObj); // Setting the extkey with the object also fills the related external fields
} else {
$iRemoteObjKey = $linkObjOrId;
$oNewLinkObj = MetaModel::NewObject($this->m_sLinkedClass);
$oRemoteObj = MetaModel::GetObject($this->m_sRemoteClass, $iRemoteObjKey);
$oNewLinkObj->Set($this->m_sExtKeyToRemote,
$oRemoteObj); // Setting the extkey with the object alsoo fills the related external fields
$oNewLinkObj->Set($this->m_sExtKeyToMe,
$oCurrentObj); // Setting the extkey with the object also fills the related external fields
$oNewLinkObj->Set($this->m_sExtKeyToRemote, $oRemoteObj); // Setting the extkey with the object alsoo fills the related external fields
$oNewLinkObj->Set($this->m_sExtKeyToMe, $oCurrentObj); // Setting the extkey with the object also fills the related external fields
}
$sPrefix .= "[-$iUniqueId][";
$sNameSuffix = "]"; // To make a tabular form
@@ -183,8 +171,7 @@ class UILinksWidget
$sInputValue = $iUniqueId > 0 ? "-$iUniqueId" : "$iUniqueId";
$aRow['form::checkbox'] = "<input class=\"selection\" data-remote-id=\"$iRemoteObjKey\" data-link-id=\"0\" data-unique-id=\"$iUniqueId\" type=\"checkbox\" onClick=\"oWidget".$this->m_iInputId.".OnSelectChange();\" value=\"$sInputValue\">";
if ($iUniqueId > 0)
{
if ($iUniqueId > 0) {
// Rows created with ajax call need OnLinkAdded call.
//
$oP->add_ready_script(
@@ -193,9 +180,7 @@ PrepareWidgets();
oWidget{$this->m_iInputId}.OnLinkAdded($iUniqueId, $iRemoteObjKey);
EOF
);
}
else
{
} else {
// Rows added before loading the form don't have to call OnLinkAdded.
// Listeners are already present and DOM is not recreated
$iPositiveUniqueId = -$iUniqueId;
@@ -207,11 +192,8 @@ EOF
foreach($this->m_aEditableFields as $sFieldCode)
{
// N°6124 - Force remote ext. key as read-only if too many items in the linkset
$bReadOnlyField = ($sFieldCode === $this->m_sExtKeyToRemote) && (false === $bAllowRemoteExtKeyEdit);
$sSafeFieldId = $this->GetFieldId($iUniqueId, $sFieldCode);
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $oNewLinkObj, $oP, $sNameSuffix, $sSafeFieldId, $bReadOnlyField);
$this->AddRowForFieldCode($aRow, $sFieldCode, $aArgs, $oNewLinkObj, $oP, $sNameSuffix, $sSafeFieldId);
$aFieldsMap[$sFieldCode] = $sSafeFieldId;
$sValue = $oNewLinkObj->Get($sFieldCode);
@@ -264,24 +246,11 @@ JS
return $aRow;
}
/**
* @param $aRow
* @param $sFieldCode
* @param $aArgs
* @param $oLnk
* @param $oP
* @param $sNameSuffix
* @param $sSafeFieldId
* @param bool $bReadOnlyField If true, the field will be read-only, otherwise it can be edited
*
* @return void
* @since 3.1.0 3.0.4 3.0.3-1 N°6124 - Workaround performance problem on the modification of an object with an n:n relation having a large volume
*/
private function AddRowForFieldCode(&$aRow, $sFieldCode, &$aArgs, $oLnk, $oP, $sNameSuffix, $sSafeFieldId, $bReadOnlyField = false): void
private function AddRowForFieldCode(&$aRow, $sFieldCode, &$aArgs, $oLnk, $oP, $sNameSuffix, $sSafeFieldId): void
{
if (($sFieldCode === $this->m_sExtKeyToRemote))
{
// Current field is the lnk extkey to the remote class
// current field is the lnk extkey to the remote class
$aArgs['replaceDependenciesByRemoteClassFields'] = true;
$sRowFieldCode = 'static::key';
$aArgs['wizHelperRemote'] = $aArgs['wizHelper'].'_remote';
@@ -303,31 +272,20 @@ JS
$sDisplayValue = $oLnk->GetEditValue($sFieldCode);
$oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode);
if ($bReadOnlyField) {
$sFieldForHtml = $sDisplayValue;
} else {
$sFieldForHtml = cmdbAbstractObject::GetFormElementForField(
$oP,
$this->m_sLinkedClass,
$sFieldCode,
$oAttDef,
$sValue,
$sDisplayValue,
$sSafeFieldId,
$sNameSuffix,
0,
$aArgs
);
}
$aRow[$sRowFieldCode] = <<<HTML
<div class="field_container" style="border:none;">
<div class="field_data">
<div class="field_value">$sFieldForHtml</div>
</div>
</div>
HTML
;
$aRow[$sRowFieldCode] = '<div class="field_container" style="border:none;"><div class="field_data"><div class="field_value">'
.cmdbAbstractObject::GetFormElementForField(
$oP,
$this->m_sLinkedClass,
$sFieldCode,
$oAttDef,
$sValue,
$sDisplayValue,
$sSafeFieldId,
$sNameSuffix,
0,
$aArgs
)
.'</div></div></div>';
}
private function GetFieldId($iLnkId, $sFieldCode, $bSafe = true)
@@ -407,37 +365,45 @@ JS
$oBlock->sRemoteClass = $this->m_sRemoteClass;
$oValue->Rewind();
$bAllowRemoteExtKeyEdit = $oValue->Count() <= utils::GetConfig()->Get('link_set_max_edit_ext_key');
$aForm = array();
$iMaxAddedId = 0;
$iAddedId = -1; // Unique id for new links
while ($oCurrentLink = $oValue->Fetch())
{
$oBlock->aRemoved = json_decode(utils::ReadPostedParam("attr_{$sFormPrefix}{$this->m_sAttCode}_tbd", '[]', 'raw_data'));
$oModified = $oValue->GetModified($this->m_sExtKeyToRemote);
while ($oCurrentLink = $oValue->Fetch()) {
// We try to retrieve the remote object as usual
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote),
false /* Must not be found */);
// If successful, it means that we can edit its link
if ($oLinkedObj !== null) {
$bReadOnly = false;
} // Else we retrieve it without restrictions (silos) and will display its link as readonly
else {
$bReadOnly = true;
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote), false /* Must not be found */, true);
}
if (!in_array($oCurrentLink->GetKey(), $oBlock->aRemoved)) {
$bModified = false;
if (array_key_exists($oCurrentLink->GetKey(), $oModified)) {
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oModified[$oCurrentLink->GetKey()], false /* Must not be found */);
$bModified = true;
} else {
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote), false /* Must not be found */);
}
// If successful, it means that we can edit its link
if ($oLinkedObj !== null) {
$bReadOnly = false;
} // Else we retrieve it without restrictions (silos) and will display its link as readonly
else {
$bReadOnly = true;
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $oCurrentLink->Get($this->m_sExtKeyToRemote), false /* Must not be found */, true);
}
if ($oCurrentLink->IsNew()) {
$key = $iAddedId--;
} else {
$key = $oCurrentLink->GetKey();
}
if ($oCurrentLink->IsNew()) {
$key = $iAddedId--;
} else {
$key = $oCurrentLink->GetKey();
}
$iMaxAddedId = max($iMaxAddedId, $key);
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly, $bAllowRemoteExtKeyEdit);
$iMaxAddedId = max($iMaxAddedId, $key);
$aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly, $bModified);
}
}
$oBlock->iMaxAddedId = (int) $iMaxAddedId;
$oBlock->iMaxAddedId = (int)$iMaxAddedId;
$oDataTable = DataTableUIBlockFactory::MakeForForm("{$this->m_sAttCode}{$this->m_sNameSuffix}", $this->m_aTableConfig, $aForm);
$oDataTable->SetOptions(['select_mode' => 'custom']);
$oDataTable->SetOptions(['select_mode' => 'custom', 'disable_hyperlinks' => true]);
$oBlock->AddSubBlock($oDataTable);
$oBlock->AddControls();
@@ -605,11 +571,10 @@ JS
$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
$iAdditionId = $iMaxAddedId + 1;
$bAllowRemoteExtKeyEdit = count($aLinkedObjectIds) <= utils::GetConfig()->Get('link_set_max_edit_ext_key');
foreach ($aLinkedObjectIds as $iObjectId) {
$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId, false);
if (is_object($oLinkedObj)) {
$aRow = $this->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId, false /* Default value */, $bAllowRemoteExtKeyEdit); // Not yet created link get negative Ids
$aRow = $this->GetFormRow($oP, $oLinkedObj, $iObjectId, array(), $oCurrentObj, $iAdditionId); // Not yet created link get negative Ids
$aData = [];
foreach ($aRow as $item) {
$aData[] = $item;

View File

@@ -60,8 +60,8 @@ class UIPasswordWidget
$sChangedValue = (($sPasswordValue != '*****') || ($sConfirmPasswordValue != '*****')) ? 1 : 0;
$sHtmlValue = '';
$sHtmlValue .= '<div class="field_input_zone field_input_onewaypassword ibo-input-wrapper ibo-input-one-way-password-wrapper">';
$sHtmlValue .= '<input class="ibo-input" type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/>';
$sHtmlValue .= '<div class="ibo-input-wrapper ibo-input-wrapper--with-buttons"><input class="ibo-input" type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'[confirm]"/>';
$sHtmlValue .= '<input class="ibo-input" type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.utils::EscapeHtml($sPasswordValue).'"/>';
$sHtmlValue .= '<div class="ibo-input-wrapper ibo-input-wrapper--with-buttons"><input class="ibo-input" type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.utils::EscapeHtml($sConfirmPasswordValue).'" name="attr_'.$sCode.'[confirm]"/>';
$sHtmlValue .= '<div class="ibo-input-select--action-buttons"><div class="ibo-input-select--action-button ibo-input-select--action-button--create" data-tooltip-content="'.Dict::S('UI:PasswordConfirm').'"><i class="fas fa-question-circle"></i></div></div></div>';
$sHtmlValue .= '<button id="'.$this->iId.'_reset" class="ibo-button ibo-is-regular ibo-is-neutral" onClick="ResetPwd(\''.$this->iId.'\');">';
$sHtmlValue .= '<span class="ibo-button--icon fas fa-undo"></span><span class="ibo-button--label">'.Dict::S('UI:Button:ResetPassword').'</span></button>';

View File

@@ -60,7 +60,7 @@ class UISearchFormForeignKeys
$oPage->add(<<<HTML
<form id="ObjectsAddForm_{$this->m_iInputId}">
<div id="SearchResultsToAdd_{$this->m_iInputId}" style="vertical-align:top;height:100%;overflow:auto;padding:0;border:0;">
<div id="SearchResultsToAdd_{$this->m_iInputId}" style="vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;">
<div style="background: #fff; border:0; text-align:center; vertical-align:middle;"><p>{$sEmptyList}</p></div>
</div>
<input type="hidden" id="count_{$this->m_iInputId}" value="0"/>

View File

@@ -20,7 +20,6 @@
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;
@@ -52,51 +51,42 @@ class utils
{
/**
* @var string
* @since 2.7.10 3.0.0
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_INTEGER = 'integer';
/**
* Datamodel class
* @var string
* @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()
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_CLASS = 'class';
/**
* @var string
* @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
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_STRING = 'string';
/**
* @var string
* @since 2.7.10 3.0.0
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_CONTEXT_PARAM = 'context_param';
/**
* @var string
* @since 2.7.10 3.0.0
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_PARAMETER = 'parameter';
/**
* @var string
* @since 2.7.10 3.0.0
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_FIELD_NAME = 'field_name';
/**
* @var string
* @since 2.7.10 3.0.0
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_TRANSACTION_ID = 'transaction_id';
/**
* @var string For XML / HTML node identifiers
* @since 2.7.10 3.0.0
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER = 'element_identifier';
/**
@@ -106,13 +96,12 @@ class utils
public const ENUM_SANITIZATION_FILTER_VARIABLE_NAME = 'variable_name';
/**
* @var string
* @since 2.7.10 3.0.0
* @since 3.0.0
*/
public const ENUM_SANITIZATION_FILTER_RAW_DATA = 'raw_data';
/**
* @var string
* @since 3.0.2 3.1.0 N°4899
* @since 2.7.10 N°6606
* @since 3.0.2, 3.1.0 N°4899
*/
public const ENUM_SANITIZATION_FILTER_URL = 'url';
@@ -155,8 +144,6 @@ class utils
private static $iNextId = 0;
private static $m_sAppRootUrl = null;
protected static function LoadParamFile($sParamFile)
{
if (!file_exists($sParamFile)) {
@@ -171,7 +158,7 @@ class utils
self::$m_aParamsFromFile = array();
}
$aParamLines = explode("\n", $sParams ?? '');
$aParamLines = explode("\n", $sParams);
foreach ($aParamLines as $sLine)
{
$sLine = trim($sLine);
@@ -228,8 +215,13 @@ class utils
public static function IsModeCLI()
{
$sCleanName = strtolower(trim(PHP_SAPI));
return ($sCleanName === 'cli');
$sSAPIName = php_sapi_name();
$sCleanName = strtolower(trim($sSAPIName));
if ($sCleanName == 'cli') {
return true;
} else {
return false;
}
}
/**
@@ -352,13 +344,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)
@@ -374,7 +366,7 @@ class utils
$retValue = $defaultValue;
}
}
return $retValue;
return $retValue;
}
/**
@@ -393,10 +385,6 @@ 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)
{
@@ -417,13 +405,6 @@ 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_PARAMETER:
case static::ENUM_SANITIZATION_FILTER_FIELD_NAME:
@@ -483,8 +464,7 @@ class utils
// For URL
case static::ENUM_SANITIZATION_FILTER_URL:
// N°6350 - returns only valid URLs
$retValue = filter_var($value, FILTER_VALIDATE_URL);
$retValue = filter_var($value, FILTER_SANITIZE_URL);
break;
default:
@@ -524,11 +504,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')));
@@ -537,7 +517,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;
@@ -550,7 +530,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;
@@ -656,17 +636,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);
@@ -833,27 +813,26 @@ class utils
*/
public static function StringToTime($sDate, $sFormat)
{
// Source: http://php.net/manual/fr/function.strftime.php
// Source: http://php.net/manual/fr/function.strftime.php
// (alternative: http://www.php.net/manual/fr/datetime.formats.date.php)
static $aDateTokens = null;
static $aDateRegexps = null;
if (is_null($aDateTokens))
{
$aSpec = array(
'%d' =>'(?<day>[0-9]{2})',
if (is_null($aDateTokens)) {
$aSpec = array(
'%d' => '(?<day>[0-9]{2})',
'%m' => '(?<month>[0-9]{2})',
'%y' => '(?<year>[0-9]{2})',
'%Y' => '(?<year>[0-9]{4})',
'%H' => '(?<hour>[0-2][0-9])',
'%i' => '(?<minute>[0-5][0-9])',
'%s' => '(?<second>[0-5][0-9])',
);
);
$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;
@@ -870,7 +849,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
@@ -989,7 +968,7 @@ class utils
*/
public static function GetAbsoluteUrlAppRoot($bForceTrustProxy = false)
{
$sUrl = static::$m_sAppRootUrl;
static $sUrl = null;
if ($sUrl === null || $bForceTrustProxy)
{
$sUrl = self::GetConfig()->Get('app_root_url');
@@ -1010,9 +989,8 @@ class utils
}
$sUrl = str_replace(SERVER_NAME_PLACEHOLDER, $sServerName, $sUrl);
}
static::$m_sAppRootUrl = $sUrl;
}
return static::$m_sAppRootUrl;
return $sUrl;
}
/**
@@ -1363,23 +1341,13 @@ 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 static::GetDataPath().'cache-'.MetaModel::GetEnvironment().'/';
return APPROOT.'data/cache-'.MetaModel::GetEnvironment().'/';
}
/**
@@ -1427,7 +1395,7 @@ class utils
public static function GetPopupMenuItemsBlock(iUIBlock &$oContainerBlock, $iMenuId, $param, &$aActions, $sDataTableId = null)
{
// 1st - add standard built-in menu items
//
//
switch($iMenuId)
{
case iPopupMenuExtension::MENU_OBJLIST_TOOLKIT:
@@ -1452,7 +1420,7 @@ class utils
"mailto:?body=".urlencode($sUrl).' ' // Add an extra space to make it work in Outlook
);
}
if (UserRights::IsActionAllowed($param->GetFilter()->GetClass(), UR_ACTION_BULK_READ, $param) != UR_ALLOWED_NO)
{
// Bulk export actions
@@ -1466,7 +1434,7 @@ class utils
}
$aResult[] = new JSPopupMenuItem('UI:Menu:AddToDashboard', Dict::S('UI:Menu:AddToDashboard'), "DashletCreationDlg('$sOQL', '$sContext')");
$aResult[] = new JSPopupMenuItem('UI:Menu:ShortcutList', Dict::S('UI:Menu:ShortcutList'), "ShortcutListDlg('$sOQL', '$sDataTableId', '$sContext')");
break;
case iPopupMenuExtension::MENU_OBJDETAILS_ACTIONS:
@@ -1480,7 +1448,7 @@ class utils
$oContainerBlock->AddJsFileRelPath('js/tabularfieldsselector.js');
$oContainerBlock->AddJsFileRelPath('js/jquery.dragtable.js');
$oContainerBlock->AddCssFileRelPath('css/dragtable.css');
$aResult = array(
new SeparatorPopupMenuItem(),
// Static menus: Email this page & CSV Export
@@ -1498,19 +1466,19 @@ class utils
$oDashboard = $param;
$sDashboardId = $oDashboard->GetId();
$sDashboardFile = $oDashboard->GetDefinitionFile();
$sDashboardFileRelative = utils::LocalPath($sDashboardFile);
$sDlgTitle = addslashes(Dict::S('UI:ImportDashboardTitle'));
$sDlgText = addslashes(Dict::S('UI:ImportDashboardText'));
$sCloseBtn = addslashes(Dict::S('UI:Button:Cancel'));
$sDashboardFileJS = addslashes($sDashboardFileRelative);
$sDashboardFileURL = urlencode($sDashboardFileRelative);
$sDashboardFileJS = addslashes($sDashboardFile);
$sDashboardFileURL = urlencode($sDashboardFile);
$sUploadDashboardTransactId = utils::GetNewTransactionId();
$aResult = array(
new SeparatorPopupMenuItem(),
new URLPopupMenuItem('UI:ExportDashboard', Dict::S('UI:ExportDashBoard'), utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=export_dashboard&id='.$sDashboardId.'&file='.$sDashboardFileURL),
new JSPopupMenuItem('UI:ImportDashboard', Dict::S('UI:ImportDashBoard'), "UploadDashboard({dashboard_id: '$sDashboardId', file: '$sDashboardFileJS', title: '$sDlgTitle', text: '$sDlgText', close_btn: '$sCloseBtn', transaction: '$sUploadDashboardTransactId' })"),
);
if ($oDashboard->GetReloadURL()) {
if ($oDashboard->GetReloadURL())
{
$aResult[] = new SeparatorPopupMenuItem();
$aResult[] = new URLPopupMenuItem('UI:Menu:PrintableVersion', Dict::S('UI:Menu:PrintableVersion'), $oDashboard->GetReloadURL().'&printable=1', '_blank');
}
@@ -1544,7 +1512,7 @@ class utils
if (is_object($oMenuItem))
{
$aActions[$oMenuItem->GetUID()] = $oMenuItem->GetMenuItem();
foreach($oMenuItem->GetLinkedScripts() as $sLinkedScript)
{
$oContainerBlock->AddJsFileRelPath($sLinkedScript);
@@ -1681,7 +1649,7 @@ class utils
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
@@ -1691,13 +1659,13 @@ class utils
{
return str_replace(array(':', '[', ']', '+', '-', ' '), '_', $sId);
}
/**
* Helper to execute an HTTP POST request
* Source: http://netevil.org/blog/2006/nov/http-post-from-php-without-curl
* originaly named after do_post_request
* Does not require cUrl but requires openssl for performing https POSTs.
*
*
* @param string $sUrl The URL to POST the data to
* @param array $aData The data to POST as an array('param_name' => value)
* @param string $sOptionnalHeaders Additional HTTP headers as a string with newlines between headers
@@ -1708,17 +1676,17 @@ class utils
*
* @return string The result of the POST request
* @throws Exception with a specific error message depending on the cause
*/
*/
public static function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null, &$aResponseHeaders = null, $aCurlOptions = array())
{
// $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
if (function_exists('curl_init'))
{
// If cURL is available, let's use it, since it provides a greater control over the various HTTP/SSL options
// For instance fopen does not allow to work around the bug: http://stackoverflow.com/questions/18191672/php-curl-ssl-routinesssl23-get-server-helloreason1112
// by setting the SSLVERSION to 3 as done below.
$aHeaders = explode("\n", $sOptionnalHeaders ?? '');
$aHeaders = explode("\n", $sOptionnalHeaders);
// N°3267 - Webservices: Fix optional headers not being taken into account
// See https://www.php.net/curl_setopt CURLOPT_HTTPHEADER
$aHTTPHeaders = array();
@@ -1745,7 +1713,7 @@ class utils
CURLOPT_POSTFIELDS => http_build_query($aData),
CURLOPT_HTTPHEADER => $aHTTPHeaders,
);
$aAllOptions = $aCurlOptions + $aOptions;
$ch = curl_init($sUrl);
curl_setopt_array($ch, $aAllOptions);
@@ -1771,7 +1739,7 @@ class utils
else
{
// cURL is not available let's try with streams and fopen...
$sData = http_build_query($aData);
$aParams = array('http' => array(
'method' => 'POST',
@@ -1783,7 +1751,7 @@ class utils
$aParams['http']['header'] .= $sOptionnalHeaders;
}
$ctx = stream_context_create($aParams);
$fp = @fopen($sUrl, 'rb', false, $ctx);
if (!$fp)
{
@@ -1824,7 +1792,7 @@ class utils
/**
* Get a standard list of character sets
*
*
* @param array $aAdditionalEncodings Additional values
* @return array of iconv code => english label, sorted by label
*/
@@ -1858,7 +1826,7 @@ class utils
*/
public static function HtmlEntities($sValue)
{
return htmlentities($sValue, ENT_QUOTES, 'UTF-8');
return htmlentities($sValue ?? '', ENT_QUOTES, 'UTF-8');
}
/**
@@ -1874,7 +1842,7 @@ class utils
public static function EscapeHtml($sValue)
{
return htmlspecialchars(
$sValue,
$sValue ?? '',
ENT_QUOTES | ENT_DISALLOWED | ENT_HTML5,
WebPage::PAGES_CHARSET,
false
@@ -1895,41 +1863,24 @@ class utils
return html_entity_decode($sValue, ENT_QUOTES, 'UTF-8');
}
/**
* @param string $sValue value encoded with {@see self::EscapeHtml()}
*
* @return string decoded value
*
* @uses \htmlspecialchars_decode()
* @link https://www.php.net/manual/en/function.htmlspecialchars-decode.php
* @since 3.0.3 3.1.0 N°6020 method creation
*/
public static function EscapedHtmlDecode($sValue)
{
return htmlspecialchars_decode(
$sValue,
ENT_QUOTES | ENT_DISALLOWED | ENT_HTML5
);
}
/**
* Convert a string containing some (valid) HTML markup to plain text
*
* @param string $sHtml
*
* @return string
*/
public static function HtmlToText($sHtml)
{
try {
try
{
//return '<?xml encoding="UTF-8">'.$sHtml;
return \Html2Text\Html2Text::convert('<?xml encoding="UTF-8">'.$sHtml);
}
catch (Exception $e) {
catch(Exception $e)
{
return $e->getMessage();
}
}
/**
* Convert (?) plain text to some HTML markup by replacing newlines by <br/> tags
* and escaping HTML entities
@@ -1940,9 +1891,10 @@ class utils
{
$sText = str_replace("\r\n", "\n", $sText);
$sText = str_replace("\r", "\n", $sText);
return str_replace("\n", '<br/>', htmlentities($sText, ENT_QUOTES, 'UTF-8'));
}
return str_replace("\n", '<br/>', utils::EscapeHtml($sText));
}
/**
* Eventually compiles the SASS (.scss) file into the CSS (.css) file
*
@@ -2004,33 +1956,23 @@ class utils
$iCurrentMaxExecTime = (int) ini_get('max_execution_time');
set_time_limit(0);
// Compiling SASS
$sCss = $oSass->compileString($sSassContent);
$oCompilationRes = $oSass->compileString($sSassContent);
set_time_limit(intval($iCurrentMaxExecTime));
return $sCss->getCss();
return $oCompilationRes->getCss();
}
/**
* Get the size of an image from a string.
*
* @see \getimagesizefromstring()
* @param $sImageData string The image data, as a string.
*
* @return array|false
*/
public static function GetImageSize($sImageData)
{
if (function_exists('getimagesizefromstring')) // PHP 5.4.0 or higher
{
$aRet = @getimagesizefromstring($sImageData);
}
else if(ini_get('allow_url_fopen'))
{
// work around to avoid creating a tmp file
$sUri = 'data://application/octet-stream;base64,'.base64_encode($sImageData);
$aRet = @getimagesize($sUri);
}
else
{
// Damned, need to create a tmp file
$sTempFile = tempnam(SetupUtils::GetTmpDir(), 'img-');
@file_put_contents($sTempFile, $sImageData);
$aRet = @getimagesize($sTempFile);
@unlink($sTempFile);
}
return $aRet;
return @getimagesizefromstring($sImageData);
}
/**
@@ -2066,7 +2008,7 @@ class utils
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.");
@@ -2080,14 +2022,14 @@ class utils
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"))
{
@@ -2095,38 +2037,38 @@ class utils
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.
@@ -2162,9 +2104,26 @@ class utils
*/
public static function GetCurrentModuleName($iCallDepth = 0)
{
return ModuleService::GetInstance()->GetCurrentModuleName($iCallDepth + 1);
$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;
}
/**
* **Warning** : returned result can be invalid as we're using backtrace to find the module dir name
*
@@ -2184,7 +2143,24 @@ class utils
*/
public static function GetCurrentModuleDir($iCallDepth)
{
return ModuleService::GetInstance()->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;
}
/**
@@ -2199,9 +2175,14 @@ class utils
*/
public static function GetCurrentModuleUrl()
{
return ModuleService::GetInstance()->GetCurrentModuleUrl(1);
$sDir = static::GetCurrentModuleDir(1);
if ( $sDir !== '')
{
return static::GetAbsoluteUrlModulesRoot().'/'.$sDir;
}
return '';
}
/**
* @param string $sProperty The name of the property to retrieve
* @param mixed $defaultvalue
@@ -2209,18 +2190,24 @@ class utils
*/
public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
{
return ModuleService::GetInstance()->GetCurrentModuleSetting($sProperty, $defaultvalue);
$sModuleName = static::GetCurrentModuleName(1);
return MetaModel::GetModuleSetting($sModuleName, $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)
{
return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName);
$aModulesInfo = GetModulesInfo();
if (array_key_exists($sModuleName, $aModulesInfo))
{
return $aModulesInfo[$sModuleName]['version'];
}
return null;
}
/**
* Check if the given path/url is an http(s) URL
* @param string $sPath
@@ -2235,7 +2222,7 @@ class utils
}
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
@@ -2257,7 +2244,7 @@ class utils
$aParams = array();
foreach(explode('&', $sQuery) as $sChunk)
{
$aParts = explode('=', $sChunk ?? '');
$aParts = explode('=', $sChunk);
if (count($aParts) != 2) continue;
$aParams[$aParts[0]] = urldecode($aParts[1]);
}
@@ -2284,7 +2271,7 @@ class utils
}
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
@@ -2328,7 +2315,7 @@ class utils
'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
@@ -2384,7 +2371,7 @@ class utils
}
$sExtension = strtolower(pathinfo($sPath, PATHINFO_EXTENSION));
$sFileName = basename($sPath);
if (array_key_exists($sExtension, $aKnownExtensions))
{
$sMimeType = $aKnownExtensions[$sExtension];
@@ -2398,13 +2385,13 @@ class utils
}
return $oUploadedDoc;
}
protected static function ParseHeaders($aHeaders)
{
$aCleanHeaders = array();
foreach( $aHeaders as $sKey => $sValue )
{
$aTokens = explode(':', $sValue ?? '', 2);
$aTokens = explode(':', $sValue, 2);
if(isset($aTokens[1]))
{
$aCleanHeaders[trim($aTokens[0])] = trim($aTokens[1]);
@@ -2423,7 +2410,7 @@ class utils
}
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
@@ -2731,10 +2718,24 @@ HTML;
$aAutoloadClassMaps = array_merge($aAutoloadClassMaps, glob(APPROOT.'env-'.utils::GetCurrentEnvironment().'/*/vendor/composer/autoload_classmap.php'));
$aClassMap = [];
$aAutoloaderErrors = [];
foreach ($aAutoloadClassMaps as $sAutoloadFile) {
if (false === static::RealPath($sAutoloadFile, APPROOT)) {
// can happen when we still have the autoloader symlink in env-*, but it points to a file that no longer exists
$aAutoloaderErrors[] = $sAutoloadFile;
continue;
}
$aTmpClassMap = include $sAutoloadFile;
/** @noinspection SlowArrayOperationsInLoopInspection we are getting an associative array so the documented workarounds cannot be used */
$aClassMap = array_merge($aClassMap, $aTmpClassMap);
}
if (count($aAutoloaderErrors) > 0) {
IssueLog::Debug(
"\utils::GetClassesForInterface cannot load some of the autoloader files",
LogChannels::CORE,
['autoloader_errors' => $aAutoloaderErrors]
);
}
// Add already loaded classes
$aCurrentClasses = array_fill_keys(get_declared_classes(), '');
@@ -2748,21 +2749,15 @@ HTML;
$bSkipped = true;
}
else {
$sPHPFile = self::LocalPath($sPHPFile);
if ($sPHPFile !== false) {
$sPHPFile = '/'.$sPHPFile; // for regex
foreach ($aExcludedPath as $sExcludedPath) {
// Note: We use '#' as delimiters as usual '/' is often used in paths.
if ($sExcludedPath !== '' && preg_match('#'.$sExcludedPath.'#', $sPHPFile) === 1) {
$bSkipped = true;
break;
}
foreach ($aExcludedPath as $sExcludedPath) {
// Note: We use '#' as delimiters as usual '/' is often used in paths.
if ($sExcludedPath !== '' && preg_match('#'.$sExcludedPath.'#', $sPHPFile) === 1) {
$bSkipped = true;
break;
}
} else {
$bSkipped = true; // file not found
}
}
if(!$bSkipped){
try {
$oRefClass = new ReflectionClass($sPHPClass);
@@ -2797,7 +2792,7 @@ HTML;
$aResultPref = [];
$aShortcutPrefs = appUserPreferences::GetPref('keyboard_shortcuts', []);
// Note: Mind the 4 blackslashes, see utils::GetClassesForInterface()
$aShortcutClasses = utils::GetClassesForInterface('iKeyboardShortcut', '', array('[\\\\/]lib[\\\\/]', '[\\\\/]node_modules[\\\\/]', '[\\\\/]test[\\\\/]', '[\\\\/]tests[\\\\/]'));
$aShortcutClasses = utils::GetClassesForInterface('iKeyboardShortcut', '', array('[\\\\/]lib[\\\\/]', '[\\\\/]node_modules[\\\\/]', '[\\\\/]test[\\\\/]'));
foreach ($aShortcutClasses as $cShortcutPlugin) {
$sTriggeredElement = $cShortcutPlugin::GetShortcutTriggeredElementSelector();
@@ -2805,7 +2800,7 @@ HTML;
$sKey = isset($aShortcutPrefs[$aShortcutKey['id']]) ? $aShortcutPrefs[$aShortcutKey['id']] : $aShortcutKey['key'];
// Format key for display
$aKeyParts = explode('+', $sKey ?? '');
$aKeyParts = explode('+', $sKey);
$aFormattedKeyParts = [];
foreach ($aKeyParts as $sKeyPart) {
$aFormattedKeyParts[] = ucfirst(trim($sKeyPart));
@@ -2872,7 +2867,6 @@ 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
{
@@ -2888,7 +2882,6 @@ 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
{
@@ -3150,15 +3143,55 @@ HTML;
*/
public static function AddParameterToUrl(string $sUrl, string $sParamName, string $sParamValue): string
{
if (strpos($sUrl, '?') === false)
{
if (strpos($sUrl, '?') === false) {
$sUrl = $sUrl.'?'.urlencode($sParamName).'='.urlencode($sParamValue);
}
else
{
} else {
$sUrl = $sUrl.'&'.urlencode($sParamName).'='.urlencode($sParamValue);
}
return $sUrl;
}
/**
* Return traits array used by a class and by parent classes hierarchy.
*
* @see https://www.php.net/manual/en/function.class-uses.php#110752
*
* @param string $sClass Class to scan
* @param bool $bAutoload Autoload flag
*
* @return array traits used
* @since 3.1.0
*/
public static function TraitsUsedByClass(string $sClass, bool $bAutoload = true): array
{
$aTraits = [];
do {
$aTraits = array_merge(class_uses($sClass, $bAutoload), $aTraits);
} while ($sClass = get_parent_class($sClass));
foreach ($aTraits as $sTrait => $same) {
$aTraits = array_merge(class_uses($sTrait, $bAutoload), $aTraits);
}
return array_unique($aTraits);
}
/**
* Test trait usage by a class or by parent classes hierarchy.
*
* @param string $sTrait Trait to search for
* @param string $sClass Class to check
*
* @return bool
* @since 3.1.0
*/
public static function IsTraitUsedByClass(string $sTrait, string $sClass): bool
{
return in_array($sTrait, self::TraitsUsedByClass($sClass, true));
}
public static function GetUniqId()
{
return hash('sha256', uniqid(sprintf('%x', rand()), true).sprintf('%x', rand()));
}
}

View File

@@ -1,8 +1,8 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/application/WebPage/WebPage.php, now loadable using autoloader
* @deprecated 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-2021 Combodo SARL
*/
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/WebPage.php, now loadable using autoloader');
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/WebPage.php, now loadable using autoloader');

View File

@@ -347,7 +347,6 @@ 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()
{
@@ -360,39 +359,19 @@ 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
*/
static function ParseJsonSet($oMe, $sLinkClass, $sExtKeyToMe, $sJsonSet)
{
$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,8 +1,8 @@
<?php
/**
* @deprecated 3.0.0 will be removed in 3.1.0 - moved to sources/application/WebPage/XMLPage.php, now loadable using autoloader
* @deprecated 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-2021 Combodo SARL
*/
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/application/WebPage/XMLPage.php, now loadable using autoloader');
DeprecatedCallsLog::NotifyDeprecatedFile('moved to sources/Application/WebPage/XMLPage.php, now loadable using autoloader');

View File

@@ -11,7 +11,7 @@ define('APPCONF', APPROOT.'conf/');
*
* @see ITOP_CORE_VERSION to get full iTop core version
*/
define('ITOP_DESIGN_LATEST_VERSION', '3.0');
define('ITOP_DESIGN_LATEST_VERSION', '3.1');
/**
* Constant containing the iTop core version, whatever application was built
@@ -23,13 +23,6 @@ define('ITOP_DESIGN_LATEST_VERSION', '3.0');
* @used-by utils::GetItopVersionWikiSyntax()
* @used-by iTopModulesPhpVersionIntegrationTest
*/
define('ITOP_CORE_VERSION', '3.0.4');
/**
* @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)
*/
const ITOP_PHPUNIT_RUNNING_CONSTANT_NAME = 'ITOP_PHPUNIT_RUNNING';
define('ITOP_CORE_VERSION', '3.1.0');
require_once APPROOT.'bootstrap.inc.php';

View File

@@ -45,7 +45,6 @@ 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';
@@ -68,7 +67,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));
$sSAPIName = strtoupper(trim(php_sapi_name()));
switch (true)
{

View File

@@ -4,7 +4,7 @@
"type": "project",
"license": "AGPL-3.0-only",
"require": {
"php": ">=7.1.3 <8.1.0",
"php": ">=7.4.0 <8.2.0",
"ext-ctype": "*",
"ext-dom": "*",
"ext-gd": "*",
@@ -12,29 +12,26 @@
"ext-json": "*",
"ext-mysqli": "*",
"ext-soap": "*",
"apereo/phpcas" : "~1.3",
"combodo/tcpdf": "~6.4.4",
"firebase/php-jwt": "~6.4.0",
"guzzlehttp/guzzle": "^6.5.8",
"guzzlehttp/psr7": "~1.9.1",
"guzzlehttp/guzzle": "^7.4.5",
"laminas/laminas-mail": "^2.11",
"laminas/laminas-servicemanager": "^3.5",
"league/oauth2-google": "^3.0",
"nikic/php-parser": "~4.13.2",
"nikic/php-parser": "~4.14.0",
"pear/archive_tar": "~1.4.14",
"pelago/emogrifier": "~3.1.0",
"pelago/emogrifier": "^6.0.0",
"scssphp/scssphp": "^1.10.3",
"swiftmailer/swiftmailer": "~6.3.0",
"symfony/console": "~3.4.47",
"symfony/dotenv": "~3.4.47",
"symfony/framework-bundle": "~3.4.47",
"symfony/twig-bundle": "~3.4.47",
"symfony/yaml": "~3.4.47",
"thenetworg/oauth2-azure": "^2.0",
"twig/twig": "~1.43.1"
"symfony/console": "5.4.*",
"symfony/dotenv": "5.4.*",
"symfony/framework-bundle": "5.4.*",
"symfony/twig-bundle": "5.4.*",
"symfony/yaml": "5.4.*",
"thenetworg/oauth2-azure": "^2.0"
},
"require-dev": {
"symfony/stopwatch": "~3.4.47",
"symfony/web-profiler-bundle": "~3.4.47"
"symfony/stopwatch": "5.4.*",
"symfony/web-profiler-bundle": "5.4.*"
},
"suggest": {
"ext-libsodium": "Required to use the AttributeEncryptedString.",
@@ -46,7 +43,7 @@
},
"config": {
"platform": {
"php": "7.1.3"
"php": "7.4.0"
},
"vendor-dir": "lib",
"preferred-install": {
@@ -63,6 +60,7 @@
"sources"
],
"exclude-from-classmap": [
"application/twigextension.class.inc.php",
"core/oql/build/PHP/",
"core/apc-emulation.php",
"application/startup.inc.php",

3473
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -59,16 +59,9 @@ 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
*/
public static function SetDbConnectionMockForQuery(?mysqli $oMysqli = null): void
public static function SetDbConnectionMockForQuery(?mysqli $oMysqli): void
{
if (is_null($oMysqli)) {
// Reset to standard connection
static::$oDbCnxMockableForQuery = static::$oDbCnxStandard;
}
else {
static::$oDbCnxMockableForQuery = $oMysqli;
}
static::$oDbCnxMockableForQuery = $oMysqli;
}
}

View File

@@ -419,7 +419,6 @@ class MyHelpers
//}
return $sOutput;
}
}
/**
@@ -471,9 +470,9 @@ class Str
public static function pure2html($pure, $maxLength = false)
{
// Check for HTML entities, but be careful the DB is in UTF-8
return $maxLength
? htmlentities(substr($pure, 0, $maxLength), ENT_QUOTES, 'UTF-8')
: htmlentities($pure, ENT_QUOTES, 'UTF-8');
return $maxLength
? utils::EscapeHtml(substr($pure, 0, $maxLength))
: utils::EscapeHtml($pure);
}
public static function pure2sql($pure, $maxLength = false)
{
@@ -524,3 +523,5 @@ class Str
return (strtolower($sString) == $sString);
}
}
?>

View File

@@ -110,7 +110,7 @@ class apcFile
*/
static public function GetCacheFileName($sKey = '')
{
$sPath = str_replace(array(' ', '/', '\\', '.'), '-', $sKey ?? '');
$sPath = str_replace(array(' ', '/', '\\', '.'), '-', $sKey);
return utils::GetCachePath().'apc-emul/'.$sPath;
}

View File

@@ -181,7 +181,7 @@ abstract class AsyncTask extends DBObject
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
$bExponential = (bool) ($aConfig['exponential_delay'] ?? $bExponential);
$bExponential = (bool)$aConfig['exponential_delay'] ?? $bExponential;
}
return $bExponential;
}
@@ -409,8 +409,6 @@ class AsyncSendEmail extends AsyncTask
$oNew->Set('to', $oEMail->GetRecipientTO(true /* string */));
$oNew->Set('subject', $oEMail->GetSubject());
// $oNew->Set('version', 1);
// $sMessage = serialize($oEMail);
$oNew->Set('version', 2);
$sMessage = $oEMail->SerializeV2();
$oNew->Set('message', $sMessage);

View File

@@ -705,18 +705,6 @@ abstract class AttributeDefinition
return is_null($proposedValue);
}
/**
* @param mixed $proposedValue
*
* @return bool True if $proposedValue is an actual value set in the attribute, false is the attribute remains "empty"
* @since 3.0.3, 3.1.0 N°5784
*/
public function HasAValue($proposedValue): bool
{
// Default implementation, we don't really know what type $proposedValue will be
return is_null($proposedValue);
}
/**
* force an allowed value (type conversion and possibly forces a value as mySQL would do upon writing!
*
@@ -866,6 +854,11 @@ abstract class AttributeDefinition
//abstract protected GetBasicFilterHTMLInput();
abstract public function GetBasicFilterSQLExpr($sOpCode, $value);
/**
* since 3.1.0 return has changed (N°4690 - Deprecate "FilterCodes")
*
* @return array filtercode => attributecode
*/
public function GetFilterDefinitions()
{
return array();
@@ -1396,15 +1389,6 @@ class AttributeDashboard extends AttributeDefinition
{
return '';
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
// Always return false for now, we don't consider a custom version of a dashboard
return false;
}
}
/**
@@ -2254,22 +2238,6 @@ class AttributeLinkedSet extends AttributeDefinition
{
return false;
}
/**
* @inheritDoc
* @param \ormLinkSet $proposedValue
*/
public function HasAValue($proposedValue): bool
{
// Protection against wrong value type
if (false === ($proposedValue instanceof ormLinkSet))
{
return parent::HasAValue($proposedValue);
}
// We test if there is at least 1 item in the linkset (new or existing), not if an item is being added to it.
return $proposedValue->Count() > 0;
}
}
/**
@@ -2489,7 +2457,7 @@ class AttributeDBFieldVoid extends AttributeDefinition
public function GetFilterDefinitions()
{
return array($this->GetCode() => new FilterFromAttribute($this));
return array($this->GetCode() => $this->GetCode());
}
public function GetBasicFilterOperators()
@@ -2651,14 +2619,6 @@ class AttributeInteger extends AttributeDBField
return is_null($proposedValue);
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
return utils::IsNotNullOrEmptyString($proposedValue);
}
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue))
@@ -2758,19 +2718,6 @@ class AttributeObjectKey extends AttributeDBFieldVoid
return ($proposedValue == 0);
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
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))
@@ -2783,6 +2730,7 @@ class AttributeObjectKey extends AttributeDBFieldVoid
}
if (MetaModel::IsValidObject($proposedValue))
{
/** @var \DBObject $proposedValue */
return $proposedValue->GetKey();
}
@@ -2969,14 +2917,6 @@ class AttributeDecimal extends AttributeDBField
return is_null($proposedValue);
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
return utils::IsNotNullOrEmptyString($proposedValue);
}
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue))
@@ -3388,14 +3328,6 @@ class AttributeString extends AttributeDBField
return ($proposedValue == '');
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
return utils::IsNotNullOrEmptyString($proposedValue);
}
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue))
@@ -4247,20 +4179,21 @@ class AttributeText extends AttributeString
public function GetEditValue($sValue, $oHostObj = null)
{
if ($this->GetFormat() == 'text')
{
if (preg_match_all(WIKI_OBJECT_REGEXP, $sValue, $aAllMatches, PREG_SET_ORDER))
{
foreach($aAllMatches as $iPos => $aMatches)
{
// N°4517 - PHP 8.1 compatibility: str_replace call with null cause deprecated message
if ($sValue == null) {
return '';
}
if ($this->GetFormat() == 'text') {
if (preg_match_all(WIKI_OBJECT_REGEXP, $sValue, $aAllMatches, PREG_SET_ORDER)) {
foreach ($aAllMatches as $iPos => $aMatches) {
$sClass = trim($aMatches[1]);
$sName = trim($aMatches[2]);
$sLabel = (!empty($aMatches[4])) ? trim($aMatches[4]) : null;
if (MetaModel::IsValidClass($sClass))
{
if (MetaModel::IsValidClass($sClass)) {
$sClassLabel = MetaModel::GetName($sClass);
$sReplacement = "[[$sClassLabel:$sName" . (!empty($sLabel) ? " | $sLabel" : "") . "]]";
$sReplacement = "[[$sClassLabel:$sName".(!empty($sLabel) ? " | $sLabel" : "")."]]";
$sValue = str_replace($aMatches[0], $sReplacement, $sValue);
}
}
@@ -4297,31 +4230,31 @@ class AttributeText extends AttributeString
public function MakeRealValue($proposedValue, $oHostObj)
{
$sValue = $proposedValue;
switch ($this->GetFormat())
{
// N°4517 - PHP 8.1 compatibility: str_replace call with null cause deprecated message
if ($sValue == null) {
return '';
}
switch ($this->GetFormat()) {
case 'html':
if (($sValue !== null) && ($sValue !== ''))
{
if (($sValue !== null) && ($sValue !== '')) {
$sValue = HTMLSanitizer::Sanitize($sValue);
}
break;
case 'text':
default:
if (preg_match_all(WIKI_OBJECT_REGEXP, $sValue, $aAllMatches, PREG_SET_ORDER))
{
foreach($aAllMatches as $iPos => $aMatches)
{
if (preg_match_all(WIKI_OBJECT_REGEXP, $sValue, $aAllMatches, PREG_SET_ORDER)) {
foreach ($aAllMatches as $iPos => $aMatches) {
$sClassLabel = trim($aMatches[1]);
$sName = trim($aMatches[2]);
$sLabel = (!empty($aMatches[4])) ? trim($aMatches[4]) : null;
$sLabel = (!empty($aMatches[4])) ? trim($aMatches[4]) : null;
if (!MetaModel::IsValidClass($sClassLabel))
{
if (!MetaModel::IsValidClass($sClassLabel)) {
$sClass = MetaModel::GetClassFromLabel($sClassLabel);
if ($sClass)
{
$sReplacement = "[[$sClassLabel:$sName" . (!empty($sLabel) ? " | $sLabel" : "") . "]]";
if ($sClass) {
$sReplacement = "[[$sClassLabel:$sName".(!empty($sLabel) ? " | $sLabel" : "")."]]";
$sValue = str_replace($aMatches[0], $sReplacement, $sValue);
}
}
@@ -4551,22 +4484,6 @@ class AttributeCaseLog extends AttributeLongText
return ($proposedValue->GetText() == '');
}
/**
* @inheritDoc
* @param \ormCaseLog $proposedValue
*/
public function HasAValue($proposedValue): bool
{
// Protection against wrong value type
if (false === ($proposedValue instanceof ormCaseLog)) {
return parent::HasAValue($proposedValue);
}
// We test if there is at least 1 entry in the log, not if the user is adding one
return $proposedValue->GetEntryCount() > 0;
}
public function ScalarToSQL($value)
{
if (!is_string($value) && !is_null($value))
@@ -6105,7 +6022,9 @@ class AttributeDateTime extends AttributeDBField
public function GetDefaultValue(DBObject $oHostObject = null)
{
// null value will be replaced by the current date, if not already set, in DoComputeValues
if (!$this->IsNullAllowed()) {
return date($this->GetInternalFormat());
}
return $this->GetNullValue();
}
@@ -6171,11 +6090,6 @@ 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))
@@ -6892,14 +6806,6 @@ class AttributeExternalKey extends AttributeDBFieldVoid
return ($proposedValue == 0);
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
return ((int) $proposedValue) !== 0;
}
public function MakeRealValue($proposedValue, $oHostObj)
{
if (is_null($proposedValue))
@@ -7594,7 +7500,7 @@ class AttributeExternalField extends AttributeDefinition
public function GetFilterDefinitions()
{
return array($this->GetCode() => new FilterFromAttribute($this));
return array($this->GetCode() => $this->GetCode());
}
public function GetBasicFilterOperators()
@@ -7632,16 +7538,6 @@ class AttributeExternalField extends AttributeDefinition
return $oExtAttDef->IsNull($proposedValue);
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
$oExtAttDef = $this->GetExtAttDef();
return $oExtAttDef->HasAValue($proposedValue);
}
public function MakeRealValue($proposedValue, $oHostObj)
{
$oExtAttDef = $this->GetExtAttDef();
@@ -7776,17 +7672,6 @@ class AttributeExternalField extends AttributeDefinition
*/
class AttributeURL extends AttributeString
{
/**
* @var string
* SCHEME....... USER....................... PASSWORD.......................... HOST/IP........... PORT.......... PATH......................... GET............................................ ANCHOR..........................
* Example: http://User:passWord@127.0.0.1:8888/patH/Page.php?arrayArgument[2]=something:blah20#myAnchor
* @link http://www.php.net/manual/fr/function.preg-match.php#93824 regexp source
* @since 3.0.1 N°4515 handle Alfresco and Sharepoint URLs
* @since 3.0.3 moved from Config to AttributeURL constant
*/
public const DEFAULT_VALIDATION_PATTERN = /** @lang RegExp */
'(https?|ftp)\://([a-zA-Z0-9+!*(),;?&=\$_.-]+(\:[a-zA-Z0-9+!*(),;?&=\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\:[0-9]{2,5})?(/([a-zA-Z0-9:%+\$_-]\.?)+)*/?(\?[a-zA-Z+&\$_.-][a-zA-Z0-9;:[\]@&%=+/\$_.,-]*)?(#[a-zA-Z0-9_.-][a-zA-Z0-9+\$_.-]*)?';
/**
* Useless constructor, but if not present PHP 7.4.0/7.4.1 is crashing :( (N°2329)
*
@@ -7929,7 +7814,7 @@ class AttributeBlob extends AttributeDefinition
public function GetDefaultValue(DBObject $oHostObject = null)
{
return "";
return new ormDocument('', '', '');
}
public function IsNullAllowed(DBObject $oHostObject = null)
@@ -7943,9 +7828,9 @@ class AttributeBlob extends AttributeDefinition
}
/**
* {@inheritDoc}
*
* @param string $proposedValue Can be an URL (including an URL to iTop itself), or a local path (CSV import)
* 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}
*
* @see AttributeDefinition::MakeRealValue()
*/
@@ -8228,20 +8113,6 @@ class AttributeBlob extends AttributeDefinition
return $oFormField;
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
if (false === ($proposedValue instanceof ormDocument)) {
return parent::HasAValue($proposedValue);
}
// Empty file (no content, just a filename) are supported since PR {@link https://github.com/Combodo/combodo-email-synchro/pull/17}, so we check for both empty content and empty filename to determine that a document has no value
return utils::IsNotNullOrEmptyString($proposedValue->GetData()) && utils::IsNotNullOrEmptyString($proposedValue->GetFileName());
}
}
/**
@@ -8292,6 +8163,11 @@ class AttributeImage extends AttributeBlob
return $oDoc;
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
return new ormDocument('', '', '');
}
/**
* Check that the supplied ormDocument actually contains an image
* {@inheritDoc}
@@ -8666,18 +8542,17 @@ class AttributeStopWatch extends AttributeDefinition
public function GetFilterDefinitions()
{
$aRes = array(
$this->GetCode() => new FilterFromAttribute($this),
$this->GetCode().'_started' => new FilterFromAttribute($this, '_started'),
$this->GetCode().'_laststart' => new FilterFromAttribute($this, '_laststart'),
$this->GetCode().'_stopped' => new FilterFromAttribute($this, '_stopped')
$this->GetCode() => $this->GetCode(),
$this->GetCode().'_started' => $this->GetCode(),
$this->GetCode().'_laststart' => $this->GetCode(),
$this->GetCode().'_stopped' => $this->GetCode(),
);
foreach($this->ListThresholds() as $iThreshold => $aFoo)
{
foreach ($this->ListThresholds() as $iThreshold => $aFoo) {
$sPrefix = $this->GetCode().'_'.$iThreshold;
$aRes[$sPrefix.'_deadline'] = new FilterFromAttribute($this, '_deadline');
$aRes[$sPrefix.'_passed'] = new FilterFromAttribute($this, '_passed');
$aRes[$sPrefix.'_triggered'] = new FilterFromAttribute($this, '_triggered');
$aRes[$sPrefix.'_overrun'] = new FilterFromAttribute($this, '_overrun');
$aRes[$sPrefix.'_deadline'] = $this->GetCode();
$aRes[$sPrefix.'_passed'] = $this->GetCode();
$aRes[$sPrefix.'_triggered'] = $this->GetCode();
$aRes[$sPrefix.'_overrun'] = $this->GetCode();
}
return $aRes;
@@ -9268,17 +9143,6 @@ class AttributeStopWatch extends AttributeDefinition
return $sRet;
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
// A stopwatch always has a value
return true;
}
}
/**
@@ -9430,7 +9294,7 @@ class AttributeSubItem extends AttributeDefinition
public function GetFilterDefinitions()
{
return array($this->GetCode() => new FilterFromAttribute($this));
return array($this->GetCode() => $this->GetCode());
}
public function GetBasicFilterOperators()
@@ -9764,23 +9628,6 @@ class AttributeOneWayPassword extends AttributeDefinition implements iAttributeN
return '*****';
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
// Protection against wrong value type
if (false === ($proposedValue instanceof ormPassword)) {
// On object creation, the attribute value is "" instead of an ormPassword...
if (is_string($proposedValue)) {
return utils::IsNotNullOrEmptyString($proposedValue);
}
return parent::HasAValue($proposedValue);
}
return $proposedValue->IsEmpty() === false;
}
}
// Indexed array having two dimensions
@@ -9830,15 +9677,6 @@ class AttributeTable extends AttributeDBField
return (count($proposedValue) == 0);
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
return count($proposedValue) > 0;
}
public function GetEditValue($sValue, $oHostObj = null)
{
return '';
@@ -10360,18 +10198,6 @@ abstract class AttributeSet extends AttributeDBFieldVoid
return $proposedValue->Count() == 0;
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
if (false === ($proposedValue instanceof ormSet)) {
return parent::HasAValue($proposedValue);
}
return $proposedValue->Count() > 0;
}
/**
* To be overloaded for localized enums
*
@@ -11533,6 +11359,13 @@ class AttributeTagSet extends AttributeSet
return new ormTagSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()), $this->GetCode(), $this->GetMaxItems());
}
public function GetDefaultValue(DBObject $oHostObject = null)
{
$oTagSet = new ormTagSet(MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()), $this->GetCode(), $this->GetMaxItems());
$oTagSet->SetValues([]);
return $oTagSet;
}
public function IsNull($proposedValue)
{
if (is_null($proposedValue))
@@ -12127,7 +11960,7 @@ class AttributeFriendlyName extends AttributeDefinition
public function GetFilterDefinitions()
{
return array($this->GetCode() => new FilterFromAttribute($this));
return array($this->GetCode() => $this->GetCode());
}
public function GetBasicFilterOperators()
@@ -12964,7 +12797,7 @@ class AttributeCustomFields extends AttributeDefinition
$sRet = $value->GetAsHTML($bLocalize);
} catch (Exception $e)
{
$sRet = 'Custom field error: '.htmlentities($e->getMessage(), ENT_QUOTES, 'UTF-8');
$sRet = 'Custom field error: '.utils::EscapeHtml($e->getMessage());
}
return $sRet;
@@ -13079,21 +12912,6 @@ class AttributeCustomFields extends AttributeDefinition
return $bEquals;
}
/**
* @inheritDoc
*/
public function HasAValue($proposedValue): bool
{
// Protection against wrong value type
if (false === ($proposedValue instanceof ormCustomFieldsValue)) {
return parent::HasAValue($proposedValue);
}
return count($proposedValue->GetValues()) > 0;
}
}
class AttributeArchiveFlag extends AttributeBoolean
@@ -13279,7 +13097,7 @@ class AttributeObsolescenceFlag extends AttributeBoolean
public function GetDefaultValue(DBObject $oHostObject = null)
{
return $this->MakeRealValue("", $oHostObject);
return $this->MakeRealValue(false, $oHostObject);
}
public function IsNullAllowed()

View File

@@ -42,6 +42,17 @@ abstract class CellChangeSpec
return $this->m_sOql;
}
/**
* @since 3.1.0 N°5305
*/
public function GetDisplayableValueAndDescription(): string
{
return sprintf("%s%s",
$this->GetDisplayableValue(),
$this->GetDescription()
);
}
abstract public function GetDescription();
}
@@ -86,26 +97,90 @@ class CellStatus_Issue extends CellStatus_Modify
parent::__construct($proposedValue, $previousValue);
}
public function GetDescription()
public function GetDisplayableValue()
{
if (is_null($this->m_proposedValue))
{
return Dict::Format('UI:CSVReport-Value-SetIssue', $this->m_sReason);
return Dict::Format('UI:CSVReport-Value-SetIssue');
}
return Dict::Format('UI:CSVReport-Value-ChangeIssue', $this->m_proposedValue, $this->m_sReason);
return Dict::Format('UI:CSVReport-Value-ChangeIssue', \utils::EscapeHtml($this->m_proposedValue));
}
public function GetDescription()
{
return $this->m_sReason;
}
/*
* @since 3.1.0 N°5305
*/
public function GetDisplayableValueAndDescription(): string
{
return sprintf("%s. %s",
$this->GetDisplayableValue(),
$this->GetDescription()
);
}
}
class CellStatus_SearchIssue extends CellStatus_Issue
{
public function __construct()
/** @var string|null $m_sAllowedValues */
private $m_sAllowedValues;
/**
* @since 3.1.0 N°5305
* @var string $sSerializedSearch
*/
private $sSerializedSearch;
/** @var string|null $m_sTargetClass */
private $m_sTargetClass;
/**
* CellStatus_SearchIssue constructor.
* @since 3.1.0 N°5305
*
* @param string $sOql : main message
* @param string $sReason : main message
* @param null $sClass : used for additional message that provides allowed values for current class $sClass
* @param null $sAllowedValues : used for additional message that provides allowed values $sAllowedValues for current class
*/
public function __construct($sSerializedSearch, $sReason, $sClass=null, $sAllowedValues=null)
{
parent::__construct(null, null, null);
parent::__construct(null, null, $sReason);
$this->sSerializedSearch = $sSerializedSearch;
$this->m_sAllowedValues = $sAllowedValues;
$this->m_sTargetClass = $sClass;
}
public function GetDisplayableValue()
{
if (null === $this->m_sReason) {
return Dict::Format('UI:CSVReport-Value-NoMatch', '');
}
return $this->m_sReason;
}
public function GetDescription()
{
return Dict::S('UI:CSVReport-Value-NoMatch');
if (\utils::IsNullOrEmptyString($this->m_sAllowedValues) ||
\utils::IsNullOrEmptyString($this->m_sTargetClass)) {
return '';
}
return Dict::Format('UI:CSVReport-Value-NoMatch-PossibleValues', $this->m_sTargetClass, $this->m_sAllowedValues);
}
/**
* @since 3.1.0 N°5305
* @return string
*/
public function GetSearchLinkUrl()
{
return sprintf("UI.php?operation=search&filter=%s",
rawurlencode($this->sSerializedSearch)
);
}
}
@@ -126,11 +201,24 @@ class CellStatus_NullIssue extends CellStatus_Issue
class CellStatus_Ambiguous extends CellStatus_Issue
{
protected $m_iCount;
/**
* @since 3.1.0 N°5305
* @var string
*/
protected $sSerializedSearch;
public function __construct($previousValue, $iCount, $sOql)
/**
* @since 3.1.0 N°5305
*
* @param $previousValue
* @param int $iCount
* @param string $sSerializedSearch
*
*/
public function __construct($previousValue, $iCount, $sSerializedSearch)
{
$this->m_iCount = $iCount;
$this->m_sQuery = $sOql;
$this->sSerializedSearch = $sSerializedSearch;
parent::__construct(null, $previousValue, '');
}
@@ -139,6 +227,17 @@ class CellStatus_Ambiguous extends CellStatus_Issue
$sCount = $this->m_iCount;
return Dict::Format('UI:CSVReport-Value-Ambiguous', $sCount);
}
/**
* @since 3.1.0 N°5305
* @return string
*/
public function GetSearchLinkUrl()
{
return sprintf("UI.php?operation=search&filter=%s",
rawurlencode($this->sSerializedSearch)
);
}
}
@@ -211,6 +310,26 @@ class RowStatus_Issue extends RowStatus
}
}
/**
* class dedicated to testability
* not used/ignored in csv imports UI/CLI
* @since 3.1.0 N°5305
*/
class RowStatus_Error extends RowStatus
{
/** @var string */
protected $m_sError;
public function __construct($sError)
{
$this->m_sError = $sError;
}
public function GetDescription()
{
return $this->m_sError;
}
}
/**
* BulkChange
@@ -220,17 +339,35 @@ class RowStatus_Issue extends RowStatus
*/
class BulkChange
{
/** @var string */
protected $m_sClass;
protected $m_aData; // Note: hereafter, iCol maybe actually be any acceptable key (string)
// #@# todo: rename the variables to sColIndex
protected $m_aAttList; // attcode => iCol
protected $m_aExtKeys; // aExtKeys[sExtKeyAttCode][sExtReconcKeyAttCode] = iCol;
protected $m_aReconcilKeys; // attcode (attcode = 'id' for the pkey)
protected $m_sSynchroScope; // OQL - if specified, then the missing items will be reported
protected $m_aOnDisappear; // array of attcode => value, values to be set when an object gets out of scope (ignored if no scope has been defined)
protected $m_sDateFormat; // Date format specification, see DateTime::createFromFormat
protected $m_bLocalizedValues; // Values in the data set are localized (see AttributeEnum)
protected $m_aExtKeysMappingCache; // Cache for resolving external keys based on the given search criterias
/** @var array<string, string> attcode as key, iCol as value */
protected $m_aAttList;
/** @var array<string, array<string, string>> sExtKeyAttCode as key, array of sExtReconcKeyAttCode/iCol as value */
protected $m_aExtKeys;
/** @var string[] list of attcode (attcode = 'id' for the pkey) */
protected $m_aReconcilKeys;
/** @var string OQL - if specified, then the missing items will be reported */
protected $m_sSynchroScope;
/**
* @var array<string, mixed> attcode as key, attvalue as value. Values to be set when an object gets out of scope
* (ignored if no scope has been defined)
*/
protected $m_aOnDisappear;
/**
* @see DateTime::createFromFormat
* @var string Date format specification
*/
protected $m_sDateFormat;
/**
* @see AttributeEnum
* @var boolean true if Values in the data set are localized
*/
protected $m_bLocalizedValues;
/** @var array Cache for resolving external keys based on the given search criterias */
protected $m_aExtKeysMappingCache;
public function __construct($sClass, $aData, $aAttList, $aExtKeys, $aReconcilKeys, $sSynchroScope = null, $aOnDisappear = null, $sDateFormat = null, $bLocalize = false)
{
@@ -266,25 +403,25 @@ class BulkChange
{
$oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode);
$oReconFilter = new DBObjectSearch($oExtKey->GetTargetClass());
foreach ($this->m_aExtKeys[$sAttCode] as $sForeignAttCode => $iCol)
foreach ($this->m_aExtKeys[$sAttCode] as $sReconKeyAttCode => $iCol)
{
if ($sForeignAttCode == 'id')
if ($sReconKeyAttCode == 'id')
{
$value = (int) $aRowData[$iCol];
}
else
{
// The foreign attribute is one of our reconciliation key
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sForeignAttCode);
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sReconKeyAttCode);
$value = $oForeignAtt->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
}
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
$oReconFilter->AddCondition($sReconKeyAttCode, $value, '=');
$aResults[$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
}
$oExtObjects = new CMDBObjectSet($oReconFilter);
$aKeys = $oExtObjects->ToArray();
return array($oReconFilter->ToOql(), $aKeys);
return array($oReconFilter, $aKeys);
}
// Returns true if the CSV data specifies that the external key must be left undefined
@@ -321,7 +458,7 @@ class BulkChange
// External keys reconciliation
//
foreach($this->m_aExtKeys as $sAttCode => $aKeyConfig)
foreach($this->m_aExtKeys as $sAttCode => $aReconKeys)
{
// Skip external keys used for the reconciliation process
// if (!array_key_exists($sAttCode, $this->m_aAttList)) continue;
@@ -330,7 +467,7 @@ class BulkChange
if ($this->IsNullExternalKeySpec($aRowData, $sAttCode))
{
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
foreach ($aReconKeys as $sReconKeyAttCode => $iCol)
{
// Default reporting
// $aRowData[$iCol] is always null
@@ -352,25 +489,24 @@ class BulkChange
$oReconFilter = new DBObjectSearch($oExtKey->GetTargetClass());
$aCacheKeys = array();
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
foreach ($aReconKeys as $sReconKeyAttCode => $iCol)
{
// The foreign attribute is one of our reconciliation key
if ($sForeignAttCode == 'id')
if ($sReconKeyAttCode == 'id')
{
$value = $aRowData[$iCol];
}
else
{
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sForeignAttCode);
$oForeignAtt = MetaModel::GetAttributeDef($oExtKey->GetTargetClass(), $sReconKeyAttCode);
$value = $oForeignAtt->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
}
$aCacheKeys[] = $value;
$oReconFilter->AddCondition($sForeignAttCode, $value, '=');
$oReconFilter->AddCondition($sReconKeyAttCode, $value, '=');
$aResults[$iCol] = new CellStatus_Void(utils::HtmlEntities($aRowData[$iCol]));
}
$sCacheKey = implode('_|_', $aCacheKeys); // Unique key for this query...
$iForeignKey = null;
$sOQL = '';
// TODO: check if *too long* keys can lead to collisions... and skip the cache in such a case...
if (!array_key_exists($sAttCode, $this->m_aExtKeysMappingCache))
{
@@ -379,9 +515,8 @@ class BulkChange
if (array_key_exists($sCacheKey, $this->m_aExtKeysMappingCache[$sAttCode]))
{
// Cache hit
$iCount = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['c'];
$iObjectFoundCount = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['c'];
$iForeignKey = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['k'];
$sOQL = $this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['oql'];
// Record the hit
$this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey]['h']++;
}
@@ -389,34 +524,35 @@ class BulkChange
{
// Cache miss, let's initialize it
$oExtObjects = new CMDBObjectSet($oReconFilter);
$iCount = $oExtObjects->Count();
if ($iCount == 1)
$iObjectFoundCount = $oExtObjects->Count();
if ($iObjectFoundCount == 1)
{
$oForeignObj = $oExtObjects->Fetch();
$iForeignKey = $oForeignObj->GetKey();
}
$this->m_aExtKeysMappingCache[$sAttCode][$sCacheKey] = array(
'c' => $iCount,
'c' => $iObjectFoundCount,
'k' => $iForeignKey,
'oql' => $oReconFilter->ToOql(),
'h' => 0, // number of hits on this cache entry
);
}
switch($iCount)
switch($iObjectFoundCount)
{
case 0:
$aErrors[$sAttCode] = Dict::S('UI:CSVReport-Value-Issue-NotFound');
$aResults[$sAttCode]= new CellStatus_SearchIssue();
break;
$oCellStatus_SearchIssue = $this->GetCellSearchIssue($oReconFilter);
$aResults[$sAttCode] = $oCellStatus_SearchIssue;
$aErrors[$sAttCode] = Dict::S('UI:CSVReport-Value-Issue-NotFound');
break;
case 1:
// Do change the external key attribute
$oTargetObj->Set($sAttCode, $iForeignKey);
break;
// Do change the external key attribute
$oTargetObj->Set($sAttCode, $iForeignKey);
break;
default:
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-FoundMany', $iCount);
$aResults[$sAttCode]= new CellStatus_Ambiguous($oTargetObj->Get($sAttCode), $iCount, $sOQL);
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-FoundMany', $iObjectFoundCount);
$aResults[$sAttCode]= new CellStatus_Ambiguous($oTargetObj->Get($sAttCode), $iObjectFoundCount, $oReconFilter->serialize());
}
}
@@ -433,7 +569,7 @@ class BulkChange
else
{
$aResults[$sAttCode]= new CellStatus_Modify($iForeignObj, $oTargetObj->GetOriginal($sAttCode));
foreach ($aKeyConfig as $sForeignAttCode => $iCol)
foreach ($aReconKeys as $sReconKeyAttCode => $iCol)
{
// Report the change on reconciliation values as well
$aResults[$iCol] = new CellStatus_Modify(utils::HtmlEntities($aRowData[$iCol]));
@@ -467,21 +603,36 @@ class BulkChange
$iFlags = ($oTargetObj->IsNew())
? $oTargetObj->GetInitialStateAttributeFlags($sAttCode, $aReasons)
: $oTargetObj->GetAttributeFlags($sAttCode, $aReasons);
if ((($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY) && ($oTargetObj->Get($sAttCode) != $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues))) {
if ( (($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY) && ( $oTargetObj->Get($sAttCode) != $aRowData[$iCol]) ) {
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Readonly', $sAttCode, $oTargetObj->Get($sAttCode), $aRowData[$iCol]);
} else if ($oAttDef->IsLinkSet() && $oAttDef->IsIndirect()) {
try {
}
else if ($oAttDef->IsLinkSet() && $oAttDef->IsIndirect())
{
try
{
$oSet = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
$oTargetObj->Set($sAttCode, $oSet);
}
catch (CoreException $e) {
catch(CoreException $e)
{
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-Format', $e->getMessage());
}
} else {
}
else
{
$value = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues);
if (is_null($value) && (strlen($aRowData[$iCol]) > 0)) {
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-NoMatch', $sAttCode);
} else {
if (is_null($value) && (strlen($aRowData[$iCol]) > 0))
{
if ($oAttDef instanceof AttributeEnum || $oAttDef instanceof AttributeTagSet){
/** @var AttributeDefinition $oAttributeDefinition */
$oAttributeDefinition = $oAttDef;
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-AllowedValues', $sAttCode, implode(',', $oAttributeDefinition->GetAllowedValues()));
} else {
$aErrors[$sAttCode] = Dict::Format('UI:CSVReport-Value-Issue-NoMatch', $sAttCode);
}
}
else
{
$res = $oTargetObj->CheckValue($sAttCode, $value);
if ($res === true)
{
@@ -559,6 +710,95 @@ class BulkChange
return $aResults;
}
/**
* search with current permissions did not match
* let's search why and give some more feedbacks to the user through proper labels
*
* @param DBObjectSearch $oDbSearchWithConditions search used to find external key
*
* @return \CellStatus_SearchIssue
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*
* @since 3.1.0 N°5305
*/
protected function GetCellSearchIssue($oDbSearchWithConditions) : CellStatus_SearchIssue {
//current search with current permissions did not match
//let's search why and give some more feedback to the user
$sSerializedSearch = $oDbSearchWithConditions->serialize();
// Count all objects with all permissions without any condition
$oDbSearchWithoutAnyCondition = new DBObjectSearch($oDbSearchWithConditions->GetClass());
$oDbSearchWithoutAnyCondition->AllowAllData(true);
$oExtObjectSet = new CMDBObjectSet($oDbSearchWithoutAnyCondition);
$iAllowAllDataObjectCount = $oExtObjectSet->Count();
if ($iAllowAllDataObjectCount === 0) {
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch-NoObject', $oDbSearchWithConditions->GetClass());
return new CellStatus_SearchIssue($sSerializedSearch, $sReason);
}
// Count all objects with current user permissions
$oDbSearchWithoutAnyCondition->AllowAllData(false);
$oExtObjectSetWithCurrentUserPermissions = new CMDBObjectSet($oDbSearchWithoutAnyCondition);
$iCurrentUserRightsObjectCount = $oExtObjectSetWithCurrentUserPermissions->Count();
if ($iCurrentUserRightsObjectCount === 0){
// No objects visible by current user
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch-NoObject-ForCurrentUser', $oDbSearchWithConditions->GetClass());
return new CellStatus_SearchIssue($sSerializedSearch, $sReason);
}
try{
$aDisplayedAllowedValues = [];
// Possibles values are displayed to UI user. we have to limit the amount of displayed values
$oExtObjectSetWithCurrentUserPermissions->SetLimit(4);
for($i = 0; $i < 3; $i++){
/** @var \DBObject $oVisibleObject */
$oVisibleObject = $oExtObjectSetWithCurrentUserPermissions->Fetch();
if (is_null($oVisibleObject)){
break;
}
$aCurrentAllowedValueFields = [];
foreach ($oDbSearchWithConditions->GetInternalParams() as $sForeignAttCode => $sValue){
$aCurrentAllowedValueFields[] = $oVisibleObject->Get($sForeignAttCode);
}
$aDisplayedAllowedValues[] = implode(" ", $aCurrentAllowedValueFields);
}
$allowedValues = implode(", ", $aDisplayedAllowedValues);
if ($oExtObjectSetWithCurrentUserPermissions->Count() > 3){
$allowedValues .= "...";
}
} catch(Exception $e) {
IssueLog::Error("failure during CSV import when fetching few visible objects: ", null,
[ 'target_class' => $oDbSearchWithConditions->GetClass(), 'criteria' => $oDbSearchWithConditions->GetCriteria(), 'message' => $e->getMessage()]
);
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch-NoObject-ForCurrentUser', $oDbSearchWithConditions->GetClass());
return new CellStatus_SearchIssue($sSerializedSearch, $sReason);
}
if ($iAllowAllDataObjectCount != $iCurrentUserRightsObjectCount) {
// No match and some objects NOT visible by current user. including current search maybe...
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch-SomeObjectNotVisibleForCurrentUser', $oDbSearchWithConditions->GetClass());
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues);
}
// No match. This is not linked to any right issue
// Possible values: DD,DD
$aCurrentValueFields = [];
foreach ($oDbSearchWithConditions->GetInternalParams() as $sValue){
$aCurrentValueFields[] = $sValue;
}
$value =implode(" ", $aCurrentValueFields);
$sReason = Dict::Format('UI:CSVReport-Value-NoMatch', $value);
return new CellStatus_SearchIssue($sSerializedSearch, $sReason, $oDbSearchWithConditions->GetClass(), $allowedValues);
}
protected function PrepareMissingObject(&$oTargetObj, &$aErrors)
{
$aResults = array();
@@ -670,6 +910,8 @@ class BulkChange
{
$sErrors = implode(', ', $aErrors);
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
//__ERRORS__ used by tests only
$aResult[$iRow]["__ERRORS__"] = new RowStatus_Error($sErrors);
return $oTargetObj;
}
@@ -736,6 +978,8 @@ class BulkChange
{
$sErrors = implode(', ', $aErrors);
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
//__ERRORS__ used by tests only
$aResult[$iRow]["__ERRORS__"] = new RowStatus_Error($sErrors);
return;
}
@@ -785,6 +1029,8 @@ class BulkChange
{
$sErrors = implode(', ', $aErrors);
$aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Attribute'));
//__ERRORS__ used by tests only
$aResult[$iRow]["__ERRORS__"] = new RowStatus_Error($sErrors);
return;
}
@@ -872,14 +1118,18 @@ class BulkChange
$sFormat = $sDateFormat;
}
$oFormat = new DateTimeFormat($sFormat);
$sDateExample = $oFormat->Format(new DateTime('2022-10-23 16:25:33'));
$sRegExp = $oFormat->ToRegExpr('/');
if (!preg_match($sRegExp, $this->m_aData[$iRow][$iCol]))
$sErrorMsg = Dict::Format('UI:CSVReport-Row-Issue-ExpectedDateFormat', $sDateExample);
if (!preg_match($sRegExp, $sValue))
{
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
$aResult[$iRow][$iCol] = new CellStatus_Issue(utils::HtmlEntities($sValue), null, $sErrorMsg);
}
else
{
$oDate = DateTime::createFromFormat($sFormat, $this->m_aData[$iRow][$iCol]);
$oDate = DateTime::createFromFormat($sFormat, $sValue);
if ($oDate !== false)
{
$sNewDate = $oDate->format($oAttDef->GetInternalFormat());
@@ -889,7 +1139,7 @@ class BulkChange
{
// Leave the cell unchanged
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
$aResult[$iRow][$sAttCode] = new CellStatus_Issue(null, utils::HtmlEntities($this->m_aData[$iRow][$iCol]), Dict::S('UI:CSVReport-Row-Issue-DateFormat'));
$aResult[$iRow][$iCol] = new CellStatus_Issue($sValue, null, $sErrorMsg);
}
}
}
@@ -943,7 +1193,9 @@ class BulkChange
else
{
// The value has to be found or verified
list($sQuery, $aMatches) = $this->ResolveExternalKey($aRowData, $sAttCode, $aResult[$iRow]);
/** var DBObjectSearch $oReconFilter */
list($oReconFilter, $aMatches) = $this->ResolveExternalKey($aRowData, $sAttCode, $aResult[$iRow]);
if (count($aMatches) == 1)
{
@@ -953,11 +1205,12 @@ class BulkChange
}
elseif (count($aMatches) == 0)
{
$aResult[$iRow][$sAttCode] = new CellStatus_SearchIssue();
$oCellStatus_SearchIssue = $this->GetCellSearchIssue($oReconFilter);
$aResult[$iRow][$sAttCode] = $oCellStatus_SearchIssue;
}
else
{
$aResult[$iRow][$sAttCode] = new CellStatus_Ambiguous(null, count($aMatches), $sQuery);
$aResult[$iRow][$sAttCode] = new CellStatus_Ambiguous(null, count($aMatches), $oReconFilter->serialize());
}
}
}
@@ -981,7 +1234,7 @@ class BulkChange
}
else
{
$oReconciliationFilter->AddCondition($sAttCode, $valuecondition, '=', true);
$oReconciliationFilter->AddCondition($sAttCode, $valuecondition, '=');
}
}
if ($bSkipQuery)
@@ -1010,7 +1263,7 @@ class BulkChange
default:
// Found several matches, ambiguous
$aResult[$iRow]["__STATUS__"]= new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Ambiguous'));
$aResult[$iRow]["id"]= new CellStatus_Ambiguous(0, $oReconciliationSet->Count(), $oReconciliationFilter->ToOql());
$aResult[$iRow]["id"]= new CellStatus_Ambiguous(0, $oReconciliationSet->Count(), $oReconciliationFilter->serialize());
$aResult[$iRow]["finalclass"]= 'n/a';
}
}

View File

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

View File

@@ -472,7 +472,7 @@ class CMDBChangeOpSetAttributeBlob extends CMDBChangeOpSetAttribute
$sDisplayLabel = Dict::S('UI:OpenDocumentInNewWindow_');
$sDisplayUrl = $oPrevDoc->GetDisplayURL(get_class($this), $this->GetKey(), 'prevdata');
$sDownloadLabel = Dict::S('UI:DownloadDocument_');
$sDownloadLabel = Dict::Format('UI:DownloadDocument_');
$sDownloadUrl = $oPrevDoc->GetDownloadURL(get_class($this), $this->GetKey(), 'prevdata');
$sDocView = <<<HTML
@@ -885,7 +885,7 @@ class CMDBChangeOpSetAttributeCaseLog extends CMDBChangeOpSetAttribute
*/
protected function ToHtml($sRawText)
{
return str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sRawText, ENT_QUOTES, 'UTF-8'));
return str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sRawText));
}
}
@@ -1177,9 +1177,8 @@ class CMDBChangeOpSetAttributeCustomFields extends CMDBChangeOpSetAttribute
$oHandler = $oAttDef->GetHandler($aValues);
$sValueDesc = $oHandler->GetAsHTML($aValues);
}
catch (Exception $e)
{
$sValueDesc = 'Custom field error: '.htmlentities($e->getMessage(), ENT_QUOTES, 'UTF-8');
catch (Exception $e) {
$sValueDesc = 'Custom field error: '.utils::EscapeHtml($e->getMessage());
}
$sTextView = '<div>'.$sValueDesc.'</div>';

View File

@@ -499,7 +499,7 @@ abstract class CMDBObject extends DBObject
$oMyChangeOp->Set("objkey", $this->GetKey());
$oMyChangeOp->Set("attcode", $sAttCode);
$oMyChangeOp->Set("oldvalue", $original);
$oMyChangeOp->Set("newvalue", $value[$sAttCode]);
$oMyChangeOp->Set("newvalue", $value);
$iId = $oMyChangeOp->DBInsertNoReload();
}
elseif ($oAttDef instanceOf AttributeCustomFields)
@@ -623,6 +623,9 @@ abstract class CMDBObject extends DBObject
return $this->DBCloneTracked_Internal();
}
/**
* @deprecated 3.1.0 N°5232 not used
*/
public function DBCloneTracked(CMDBChange $oChange, $newKey = null)
{
self::SetCurrentChange($oChange);
@@ -637,20 +640,6 @@ abstract class CMDBObject extends DBObject
return $newKey;
}
public function DBUpdate()
{
// Copy the changes list before the update (the list should be reset afterwards)
$aChanges = $this->ListChanges();
if (count($aChanges) == 0)
{
return;
}
$ret = parent::DBUpdate();
return $ret;
}
/**
* @param null $oDeletionPlan
*
@@ -685,46 +674,7 @@ abstract class CMDBObject extends DBObject
protected function DBDeleteTracked_Internal(&$oDeletionPlan = null)
{
$ret = parent::DBDelete($oDeletionPlan);
return $ret;
}
public static function BulkUpdate(DBSearch $oFilter, array $aValues)
{
return static::BulkUpdateTracked_Internal($oFilter, $aValues);
}
public static function BulkUpdateTracked(CMDBChange $oChange, DBSearch $oFilter, array $aValues)
{
self::SetCurrentChange($oChange);
static::BulkUpdateTracked_Internal($oFilter, $aValues);
}
protected static function BulkUpdateTracked_Internal(DBSearch $oFilter, array $aValues)
{
// $aValues is an array of $sAttCode => $value
// Get the list of objects to update (and load it before doing the change)
$oObjSet = new CMDBObjectSet($oFilter);
$oObjSet->Load();
// Keep track of the previous values (will be overwritten when the objects are synchronized with the DB)
$aOriginalValues = array();
$oObjSet->Rewind();
while ($oItem = $oObjSet->Fetch())
{
$aOriginalValues[$oItem->GetKey()] = $oItem->m_aOrigValues;
}
// Update in one single efficient query
$ret = parent::BulkUpdate($oFilter, $aValues);
// Record... in many queries !!!
$oObjSet->Rewind();
while ($oItem = $oObjSet->Fetch())
{
$aChangedValues = $oItem->ListChangedValues($aValues);
$oItem->RecordAttChanges($aChangedValues, $aOriginalValues[$oItem->GetKey()]);
}
return $ret;
}

View File

@@ -42,12 +42,6 @@ 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
@@ -163,7 +157,7 @@ class CMDBSource
$iPort = null;
self::InitServerAndPort($sDbHost, $sServer, $iPort);
$iFlags = null;
$iFlags = 0;
// *some* errors (like connection errors) will throw mysqli_sql_exception instead of generating warnings printed to the output
// but some other errors will still cause the query() method to return false !!!
@@ -172,7 +166,6 @@ class CMDBSource
try
{
$oMysqli = new mysqli();
$oMysqli->init();
if ($bTlsEnabled)
{
@@ -219,19 +212,16 @@ class CMDBSource
/**
* @param string $sDbHost initial value ("p:domain:port" syntax)
* @param string $sServer server 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)
* @param int $iPort port variable to update
*/
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];
}
@@ -249,6 +239,10 @@ class CMDBSource
{
$iPort = (int)($aConnectInfo[1]);
}
else
{
$iPort = 3306;
}
}
/**
@@ -356,19 +350,21 @@ class CMDBSource
}
/**
* Get the version of the database server.
*
* @return string
* @throws \MySQLException
*
* @uses \CMDBSource::QueryToCol() so needs a connection opened !
* @uses \CMDBSource::QueryToScalar() so needs a connection opened !
*/
public static function GetDBVersion()
{
$aVersions = self::QueryToCol('SELECT Version() as version', 'version');
return $aVersions[0];
return static::QueryToScalar('SELECT VERSION()', 0);
}
/**
* @return string
* @deprecated Use `CMDBSource::GetDBVersion` instead.
* @uses mysqli_get_server_info
*/
public static function GetServerInfo()
{
@@ -435,7 +431,6 @@ class CMDBSource
{
self::$m_sDBName = '';
}
self::_TablesInfoCacheReset(); // reset the table info cache!
}
public static function CreateTable($sQuery)
@@ -548,9 +543,10 @@ class CMDBSource
/**
* @param string $sSQLQuery
*
* @return mysqli_result|null
* @throws MySQLException
* @throws MySQLHasGoneAwayException
* @return \mysqli_result|null
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \CoreException
*
* @since 2.7.0 N°679 handles nested transactions
*/
@@ -611,9 +607,8 @@ class CMDBSource
{
self::LogDeadLock($e, true);
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
} finally {
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
}
}
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
if ($oResult === false) {
$aContext = array('query' => $sSql);
@@ -631,24 +626,18 @@ 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, $bCheckMysqliErrno = true)
private static function LogDeadLock(Exception $e, $bForQuery = false)
{
// checks MySQL error code
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";
$iMySqlErrorNo = DbConnectionWrapper::GetDbConnection($bForQuery)->errno;
if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK))) {
return;
}
// Get error info
@@ -675,10 +664,7 @@ class CMDBSource
);
DeadLockLog::Info($sMessage, $iMySqlErrorNo, $aLogContext);
IssueLog::Error($sMessage, LogChannels::DEADLOCK, [
'exception.class' => get_class($e),
'exception.message' => $e->getMessage(),
]);
IssueLog::Error($sMessage, LogChannels::DEADLOCK, $e->getMessage());
}
/**
@@ -1168,8 +1154,8 @@ class CMDBSource
*/
public static function IsSameFieldTypes($sItopGeneratedFieldType, $sDbFieldType)
{
[$sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
[$sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions] = static::GetFieldDataTypeAndOptions($sDbFieldType);
list($sItopFieldDataType, $sItopFieldTypeOptions, $sItopFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sItopGeneratedFieldType);
list($sDbFieldDataType, $sDbFieldTypeOptions, $sDbFieldOtherOptions) = static::GetFieldDataTypeAndOptions($sDbFieldType);
if (strcasecmp($sItopFieldDataType, $sDbFieldDataType) !== 0)
{
@@ -1522,20 +1508,14 @@ class CMDBSource
* Returns the value of the specified server variable
* @param string $sVarName Name of the server variable
* @return mixed Current value of the variable
*/
* @throws \MySQLQueryHasNoResultException|\MySQLException
*/
public static function GetServerVariable($sVarName)
{
$result = '';
$sSql = "SELECT @@$sVarName as theVar";
$aRows = self::QueryToArray($sSql);
if (count($aRows) > 0)
{
$result = $aRows[0]['theVar'];
}
return $result;
$sSql = 'SELECT @@'.$sVarName;
return static::QueryToScalar($sSql, 0) ?: '';
}
/**
* Returns the privileges of the current user
* @return string privileges in a raw format
@@ -1608,19 +1588,7 @@ 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.0.3-dev');
define('ITOP_VERSION', '3.1.0-dev');
define('ITOP_VERSION_NAME', 'Fullmoon');
define('ITOP_REVISION', 'svn');
@@ -129,6 +129,22 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'event_service.debug.filter_events' => [
'type' => 'array',
'description' => 'Filter Event Service debug by events',
'default' => '',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'event_service.debug.filter_sources' => [
'type' => 'array',
'description' => 'Filter Event Service debug by event sources',
'default' => '',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'app_env_label' => [
'type' => 'string',
'description' => 'Label displayed to describe the current application environment, defaults to the environment name (e.g. "production")',
@@ -480,14 +496,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => true,
],
'link_set_max_edit_ext_key' => [
'type' => 'integer',
'description' => 'Maximum number of items in the link that allow editing the remote external key. Above that limit, remote external key cannot be edited. Mind that setting this limit too high can have a negative impact on performances.',
'default' => 50,
'value' => 50,
'source_of_value' => '',
'show_in_conf_sample' => true,
],
'tag_set_item_separator' => [
'type' => 'string',
'description' => 'Tag set from string: tag label separator',
@@ -584,30 +592,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'email_transport_smtp.allow_self_signed' => [
'type' => 'bool',
'description' => 'Allow self signed peer certificates',
'default' => false,
'value' => false,
'email_css' => [
'type' => 'string',
'description' => 'CSS that will override the standard stylesheet used for the notifications',
'default' => "",
'value' => "",
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'email_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',
'default' => "",
'value' => "",
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'email_default_sender_address' => [
'type' => 'string',
'description' => 'Default address provided in the email from header field.',
@@ -907,11 +899,16 @@ class Config
'show_in_conf_sample' => false,
],
'url_validation_pattern' => [
'type' => 'string',
'description' => 'Regular expression to validate/detect the format of an URL (URL attributes and Wiki formatting for Text attributes)',
'default' => AttributeURL::DEFAULT_VALIDATION_PATTERN,
'value' => '',
'source_of_value' => '',
'type' => 'string',
'description' => 'Regular expression to validate/detect the format of an URL (URL attributes and Wiki formatting for Text attributes)',
'default' => /** @lang RegExp */
'(https?|ftp)\://([a-zA-Z0-9+!*(),;?&=\$_.-]+(\:[a-zA-Z0-9+!*(),;?&=\$_.-]+)?@)?([a-zA-Z0-9-.]{3,})(\:[0-9]{2,5})?(/([a-zA-Z0-9:%+\$_-]\.?)+)*/?(\?[a-zA-Z+&\$_.-][a-zA-Z0-9;:[\]@&%=+/\$_.-]*)?(#[a-zA-Z0-9_.-][a-zA-Z0-9+\$_.-]*)?',
// SCHEME....... USER....................... PASSWORD.......................... HOST/IP........... PORT.......... PATH......................... GET............................................ ANCHOR..........................
// Example: http://User:passWord@127.0.0.1:8888/patH/Page.php?arrayArgument[2]=something:blah20#myAnchor
// RegExp source: http://www.php.net/manual/fr/function.preg-match.php#93824
// Update with N°4515
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => true,
],
'email_validation_pattern' => [
@@ -989,14 +986,6 @@ 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.',
@@ -1443,14 +1432,6 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'allow_rest_services_via_tokens' => [
'type' => 'bool',
'description' => 'When set to true, REST endpoint token authorization works even with secure_rest_services set.',
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'search_manual_submit' => [
'type' => 'array',
'description' => 'Force manual submit of search all requests',
@@ -1531,14 +1512,6 @@ 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!',
@@ -1587,6 +1560,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'setup.launch_button.enabled' => [
'type' => 'bool',
'description' => 'If true displays in the Application Upgrade screen a button allowing to launch the setup in a single click (no more manual config file permission change needed)',
'default' => null,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
];
public function IsProperty($sPropCode)
@@ -1904,9 +1885,9 @@ class Config
}
if (strlen($sNoise) > 0)
{
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
// Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack)
throw new ConfigException('Syntax error in configuration file',
array('file' => $sConfigFile, 'error' => '<tt>'.htmlentities($sNoise, ENT_QUOTES, 'UTF-8').'</tt>'));
array('file' => $sConfigFile, 'error' => '<tt>'.utils::EscapeHtml($sNoise, ENT_QUOTES).'</tt>'));
}
if (!isset($MySettings) || !is_array($MySettings))
@@ -2193,24 +2174,6 @@ 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;
@@ -2732,7 +2695,7 @@ class ConfigPlaceholdersResolver
}
$sPattern = '/\%(env|server)\((\w+)\)(?:\?:(\w*))?\%/'; //3 capturing groups, ie `%env(HTTP_PORT)?:8080%` produce: `env` `HTTP_PORT` and `8080`.
if (! preg_match_all($sPattern, $rawValue, $aMatchesCollection, PREG_SET_ORDER))
{
return $rawValue;

View File

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

View File

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

View File

@@ -12,7 +12,6 @@ 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
@@ -115,7 +114,6 @@ 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);
@@ -127,8 +125,8 @@ class CSVBulkExport extends TabularBulkExport
$sRawSeparator = utils::ReadParam('separator', ',', true, 'raw_data');
$sCustomDateTimeFormat = utils::ReadParam('', ',', true, 'raw_data');
$aSep = array(
';' => Dict::S('UI:CSVImport:SeparatorSemicolon+'),
',' => Dict::S('UI:CSVImport:SeparatorComma+'),
';' => Dict::S('UI:CSVImport:SeparatorSemicolon+'),
',' => Dict::S('UI:CSVImport:SeparatorComma+'),
'tab' => Dict::S('UI:CSVImport:SeparatorTab+'),
);
$sOtherSeparator = '';
@@ -136,10 +134,10 @@ class CSVBulkExport extends TabularBulkExport
$sOtherSeparator = $sRawSeparator;
$sRawSeparator = 'other';
}
$aSep['other'] = Dict::S('UI:CSVImport:SeparatorOther').' <input type="text" size="3" name="other-separator" value="'.htmlentities($sOtherSeparator, ENT_QUOTES, 'UTF-8').'"/>';
$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", htmlentities($sVal, ENT_QUOTES, 'UTF-8'), $sLabel, "radio");
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "separator", utils::EscapeHtml($sVal), $sLabel, "radio");
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawSeparator));
$oRadio->SetBeforeInput(false);
$oRadio->GetInput()->AddCSSClass('ibo-input--label-right');
@@ -154,7 +152,7 @@ class CSVBulkExport extends TabularBulkExport
$sRawQualifier = utils::ReadParam('text-qualifier', '"', true, 'raw_data');
$aQualifiers = array(
'"' => Dict::S('UI:CSVImport:QualifierDoubleQuote+'),
'"' => Dict::S('UI:CSVImport:QualifierDoubleQuote+'),
'\'' => Dict::S('UI:CSVImport:QualifierSimpleQuote+'),
);
$sOtherQualifier = '';
@@ -162,11 +160,11 @@ class CSVBulkExport extends TabularBulkExport
$sOtherQualifier = $sRawQualifier;
$sRawQualifier = 'other';
}
$aQualifiers['other'] = Dict::S('UI:CSVImport:QualifierOther').' <input type="text" size="3" name="other-text-qualifier" value="'.htmlentities($sOtherQualifier, ENT_QUOTES, 'UTF-8').'"/>';
$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", htmlentities($sVal, ENT_QUOTES, 'UTF-8'), $sLabel, "radio");
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawQualifier));
$oRadio = InputUIBlockFactory::MakeForInputWithLabel($sLabel, "text-qualifier", utils::EscapeHtml($sVal), $sLabel, "radio");
$oRadio->GetInput()->SetIsChecked(($sVal == $sRawSeparator));
$oRadio->SetBeforeInput(false);
$oRadio->GetInput()->AddCSSClass('ibo-input--label-right');
$oRadio->GetInput()->AddCSSClass('ibo-input-checkbox');
@@ -211,8 +209,8 @@ class CSVBulkExport extends TabularBulkExport
$sDateTimeFormat = utils::ReadParam('date_format', (string)AttributeDateTime::GetFormat(), true, 'raw_data');
$sDefaultFormat = htmlentities((string)AttributeDateTime::GetFormat(), ENT_QUOTES, 'UTF-8');
$sExample = htmlentities(date((string)AttributeDateTime::GetFormat()), ENT_QUOTES, 'UTF-8');
$sDefaultFormat = utils::EscapeHtml((string)AttributeDateTime::GetFormat());
$sExample = utils::EscapeHtml(date((string)AttributeDateTime::GetFormat()));
$oRadioDefault = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatDefault_Example', $sDefaultFormat, $sExample), "csv_date_format_radio", "default", "csv_date_time_format_default", "radio");
$oRadioDefault->GetInput()->SetIsChecked(($sDateTimeFormat == (string)AttributeDateTime::GetFormat()));
$oRadioDefault->SetBeforeInput(false);
@@ -220,7 +218,7 @@ class CSVBulkExport extends TabularBulkExport
$oFieldSetDate->AddSubBlock($oRadioDefault);
$oFieldSetDate->AddSubBlock(new Html('</br>'));
$sFormatInput = '<input type="text" size="15" name="date_format" id="csv_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
$sFormatInput = '<input type="text" size="15" name="date_format" id="csv_custom_date_time_format" title="" value="'.utils::EscapeHtml($sDateTimeFormat).'"/>';
$oRadioCustom = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput), "csv_date_format_radio", "custom", "csv_date_time_format_custom", "radio");
$oRadioCustom->SetDescription(Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip'));
$oRadioCustom->GetInput()->SetIsChecked($sDateTimeFormat !== (string)AttributeDateTime::GetFormat());
@@ -248,17 +246,18 @@ EOF
}
protected function GetSampleData($oObj, $sAttCode)
{
if ($sAttCode != 'id')
{
{
if ($sAttCode != 'id') {
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
{
$sClass = (get_class($oAttDef) == 'AttributeDateTime') ? 'user-formatted-date-time' : 'user-formatted-date';
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.htmlentities($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj), ENT_QUOTES, 'UTF-8').'</div>';
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.utils::EscapeHtml($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj)).'</div>';
}
}
return '<div class="text-preview">'.htmlentities($this->GetValue($oObj, $sAttCode), ENT_QUOTES, 'UTF-8').'</div>';
return '<div class="text-preview">'.utils::EscapeHtml($this->GetValue($oObj, $sAttCode)).'</div>';
}
protected function GetValue($oObj, $sAttCode)

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0">
<itop_design xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.1">
<user_rights>
<profiles>
<profile id="1024" _delta="define">
@@ -270,4 +270,4 @@
</class>
</classes>
</meta>
</itop_design>
</itop_design>

File diff suppressed because it is too large Load Diff

View File

@@ -38,7 +38,7 @@ interface iDBObjectSetIterator extends Countable
*
* @return int
*/
public function Count();
public function Count(): int;
/**
* Reset the cursor to the first item in the collection. Equivalent to Seek(0)
@@ -52,7 +52,7 @@ interface iDBObjectSetIterator extends Countable
*
* @param int $iRow
*/
public function Seek($iPosition);
public function Seek($iPosition): void;
/**
* Fetch the object at the current position in the collection and move the cursor to the next position.

View File

@@ -413,6 +413,10 @@ class DBObjectSearch extends DBSearch
}
/**
* Important: If you need to add a condition on the same $sFilterCode several times with different $value values; do not use this method as the previous $value occurences will be replaced by the last. Instead use:
* * {@see \DBObjectSearch::AddConditionExpression()} in loops to add conditions one by one
* * {@see \DBObjectSearch::AddConditionForInOperatorUsingParam()} for IN/NOT IN queries with lots of params at once
*
* @param string $sFilterCode
* @param mixed $value
* @param string $sOpCode operator to use : 'IN', 'NOT IN', 'Contains',' Begins with', 'Finishes with', ...
@@ -423,22 +427,16 @@ class DBObjectSearch extends DBSearch
* @param bool $bParseSearchString
*
* @throws \CoreException
*
* @see AddConditionForInOperatorUsingParam for IN/NOT IN queries with lots of params
*/
public function AddCondition($sFilterCode, $value, $sOpCode = null, $bParseSearchString = false)
{
MyHelpers::CheckKeyInArray('filter code in class: '.$this->GetClass(), $sFilterCode, MetaModel::GetClassFilterDefs($this->GetClass()));
MyHelpers::CheckKeyInArray('filter code in class: '.$this->GetClass(), $sFilterCode, MetaModel::GetFilterAttribList($this->GetClass()));
$oField = new FieldExpression($sFilterCode, $this->GetClassAlias());
if (empty($sOpCode))
{
if ($sFilterCode == 'id')
{
if (empty($sOpCode)) {
if ($sFilterCode == 'id') {
$sOpCode = '=';
}
else
{
} else {
$oAttDef = MetaModel::GetAttributeDef($this->GetClass(), $sFilterCode);
$oNewCondition = $oAttDef->GetSmartConditionExpression($value, $oField, $this->m_aParams);
$this->AddConditionExpression($oNewCondition);
@@ -1103,39 +1101,22 @@ class DBObjectSearch extends DBSearch
public function Filter($sClassAlias, DBSearch $oFilter)
{
// If the conditions are the correct ones for Intersect
if (MetaModel::IsParentClass($oFilter->GetFirstJoinedClass(), $this->GetFirstJoinedClass())) {
if (MetaModel::IsParentClass($oFilter->GetFirstJoinedClass(),$this->GetFirstJoinedClass()))
{
return $this->Intersect($oFilter);
}
if ($oFilter instanceof DBUnionSearch) {
$aFilters = $oFilter->GetSearches();
} else {
$aFilters = [$oFilter];
/** @var \DBObjectSearch $oFilteredSearch */
$oFilteredSearch = $this->DeepClone();
$oFilterExpression = self::FilterSubClass($oFilteredSearch, $sClassAlias, $oFilter, $this->m_aClasses);
if ($oFilterExpression === false)
{
throw new CoreException("Limitation: cannot filter search");
}
$aSearches = [];
foreach ($aFilters as $oRightFilter) {
/** @var \DBObjectSearch $oFilteredSearch */
$oFilteredSearch = $this->DeepClone();
$oFilterExpression = self::FilterSubClass($oFilteredSearch, $sClassAlias, $oRightFilter, $this->m_aClasses);
if ($oFilterExpression === false) {
throw new CoreException("Limitation: cannot filter search");
}
$oFilteredSearch->AddConditionExpression($oFilterExpression);
$oFilteredSearch->AddConditionExpression($oFilterExpression);
$aSearches[] = $oFilteredSearch;
}
if (count($aSearches) == 0) {
throw new CoreException('Filtering '.$this->ToOQL().' by '.$oFilter->ToOQL().' failed');
}
if (count($aSearches) == 1) {
// return a DBObjectSearch
return $aSearches[0];
}
return new DBUnionSearch($aSearches);
return $oFilteredSearch;
}
/**
@@ -1201,10 +1182,22 @@ class DBObjectSearch extends DBSearch
* @throws \CoreException
*/
public function Intersect(DBSearch $oFilter)
{
return $this->IntersectSubClass($oFilter, $this->m_aClasses);
}
/**
* @param \DBSearch $oFilter
* @param array $aRootClasses classes of the root search (for aliases)
*
* @return \DBUnionSearch|mixed
* @throws \CoreException
*/
protected function IntersectSubClass(DBSearch $oFilter, $aRootClasses)
{
if ($oFilter instanceof DBUnionSearch)
{
// Develop!
// Develop!
$aFilters = $oFilter->GetSearches();
}
else
@@ -1215,61 +1208,56 @@ class DBObjectSearch extends DBSearch
$aSearches = array();
foreach ($aFilters as $oRightFilter)
{
$aSearches[] = $this->IntersectSubClass($oRightFilter, $this->m_aClasses);
}
// Limitation: the queried class must be the first declared class
if ($oRightFilter->GetFirstJoinedClassAlias() != $oRightFilter->GetClassAlias())
{
throw new CoreException("Limitation: cannot merge two queries if the queried class ({$oRightFilter->GetClass()} AS {$oRightFilter->GetClassAlias()}) is not the first joined class ({$oRightFilter->GetFirstJoinedClass()} AS {$oRightFilter->GetFirstJoinedClassAlias()})");
}
/** @var \DBObjectSearch $oLeftFilter */
$oLeftFilter = $this->DeepClone();
$oRightFilter = $oRightFilter->DeepClone();
$bAllowAllData = ($oLeftFilter->IsAllDataAllowed() && $oRightFilter->IsAllDataAllowed());
if ($bAllowAllData)
{
$oLeftFilter->AllowAllData();
}
if ($oLeftFilter->GetFirstJoinedClass() != $oRightFilter->GetClass())
{
if (MetaModel::IsParentClass($oLeftFilter->GetFirstJoinedClass(), $oRightFilter->GetClass()))
{
// Specialize $oLeftFilter
$oLeftFilter->ChangeClass($oRightFilter->GetClass(), $oLeftFilter->GetFirstJoinedClassAlias());
}
elseif (MetaModel::IsParentClass($oRightFilter->GetFirstJoinedClass(), $oLeftFilter->GetClass()))
{
// Specialize $oRightFilter
$oRightFilter->ChangeClass($oLeftFilter->GetFirstJoinedClass());
}
else
{
throw new CoreException("Attempting to merge a filter of class '{$oLeftFilter->GetClass()}' with a filter of class '{$oRightFilter->GetClass()}'");
}
}
$aAliasTranslation = array();
$oLeftFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation);
$oLeftFilter->MergeWith_InNamespace($oRightFilter, $aRootClasses, $aAliasTranslation);
$oRightFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation);
$oLeftFilter->TransferConditionExpression($oRightFilter, $aAliasTranslation);
$aSearches[] = $oLeftFilter;
}
if (count($aSearches) == 1)
{
// return a DBObjectSearch
return $aSearches[0];
}
return new DBUnionSearch($aSearches);
}
/**
* @param \DBObjectSearch $oRightFilter
* @param array $aRootClasses classes of the root search (for aliases)
*
* @return \DBObjectSearch
* @throws \CoreException
*/
protected function IntersectSubClass(DBObjectSearch $oRightFilter, array $aRootClasses): DBObjectSearch
{
// Limitation: the queried class must be the first declared class
if ($oRightFilter->GetFirstJoinedClassAlias() != $oRightFilter->GetClassAlias()) {
throw new CoreException("Limitation: cannot merge two queries if the queried class ({$oRightFilter->GetClass()} AS {$oRightFilter->GetClassAlias()}) is not the first joined class ({$oRightFilter->GetFirstJoinedClass()} AS {$oRightFilter->GetFirstJoinedClassAlias()})");
else
{
return new DBUnionSearch($aSearches);
}
/** @var \DBObjectSearch $oLeftFilter */
$oLeftFilter = $this->DeepClone();
/** @var DBObjectSearch $oRightFilter */
$oRightFilter = $oRightFilter->DeepClone();
$bAllowAllData = ($oLeftFilter->IsAllDataAllowed() && $oRightFilter->IsAllDataAllowed());
if ($bAllowAllData) {
$oLeftFilter->AllowAllData();
}
if ($oLeftFilter->GetFirstJoinedClass() != $oRightFilter->GetClass()) {
if (MetaModel::IsParentClass($oLeftFilter->GetFirstJoinedClass(), $oRightFilter->GetClass())) {
// Specialize $oLeftFilter
$oLeftFilter->ChangeClass($oRightFilter->GetClass(), $oLeftFilter->GetFirstJoinedClassAlias());
} elseif (MetaModel::IsParentClass($oRightFilter->GetFirstJoinedClass(), $oLeftFilter->GetClass())) {
// Specialize $oRightFilter
$oRightFilter->ChangeClass($oLeftFilter->GetFirstJoinedClass());
} else {
throw new CoreException("Attempting to merge a filter of class '{$oLeftFilter->GetClass()}' with a filter of class '{$oRightFilter->GetClass()}'");
}
}
$aAliasTranslation = array();
$oLeftFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation);
$oLeftFilter->MergeWith_InNamespace($oRightFilter, $aRootClasses, $aAliasTranslation);
$oRightFilter->RenameNestedQueriesAliasesInNameSpace($aRootClasses, $aAliasTranslation);
$oLeftFilter->TransferConditionExpression($oRightFilter, $aAliasTranslation);
return $oLeftFilter;
}
/**
@@ -1910,16 +1898,13 @@ class DBObjectSearch extends DBSearch
// Hide objects that are not visible to the current user
//
$oSearch = $this;
if (!$this->IsAllDataAllowed() && !$this->IsDataFiltered())
{
if (!$this->IsAllDataAllowed() && !$this->IsDataFiltered()) {
$oVisibleObjects = UserRights::GetSelectFilter($this->GetClass(), $this->GetModifierProperties('UserRightsGetSelectFilter'));
if ($oVisibleObjects === false)
{
if ($oVisibleObjects === false) {
// Make sure this is a valid search object, saying NO for all
$oVisibleObjects = DBObjectSearch::FromEmptySet($this->GetClass());
}
if (is_object($oVisibleObjects))
{
if (is_object($oVisibleObjects)) {
$oVisibleObjects->AllowAllData();
$oSearch = $this->Intersect($oVisibleObjects);
$oSearch->SetDataFiltered();
@@ -1939,63 +1924,49 @@ class DBObjectSearch extends DBSearch
if (isset($_SERVER['REQUEST_URI']))
{
$aContextData['sRequestUri'] = $_SERVER['REQUEST_URI'];
}
else if (isset($_SERVER['SCRIPT_NAME']))
{
} else if (isset($_SERVER['SCRIPT_NAME'])) {
$aContextData['sRequestUri'] = $_SERVER['SCRIPT_NAME'];
}
else
{
} else {
$aContextData['sRequestUri'] = '';
}
// Need to identify the query
$sOqlQuery = $oSearch->ToOql(false, null, true);
if ((strpos($sOqlQuery, '`id` IN (') !== false) || (strpos($sOqlQuery, '`id` NOT IN (') !== false))
{
if ((strpos($sOqlQuery, '`id` IN (') !== false) || (strpos($sOqlQuery, '`id` NOT IN (') !== false)) {
// Requests containing "id IN" are not worth caching
$bCanCache = false;
}
$aContextData['sOqlQuery'] = $sOqlQuery;
if (count($aModifierProperties))
{
if (count($aModifierProperties)) {
array_multisort($aModifierProperties);
$sModifierProperties = json_encode($aModifierProperties);
}
else
{
} else {
$sModifierProperties = '';
}
$aContextData['sModifierProperties'] = $sModifierProperties;
$sRawId = Dict::GetUserLanguage().'-'.$sOqlQuery.$sModifierProperties;
if (!is_null($aAttToLoad))
{
if (!is_null($aAttToLoad)) {
$sRawId .= json_encode($aAttToLoad);
}
$aContextData['aAttToLoad'] = $aAttToLoad;
if (!is_null($aGroupByExpr))
{
foreach($aGroupByExpr as $sAlias => $oExpr)
{
$sRawId .= 'g:'.$sAlias.'!'.$oExpr->Render();
if (!is_null($aGroupByExpr)) {
foreach ($aGroupByExpr as $sAlias => $oExpr) {
$sRawId .= 'g:'.$sAlias.'!'.$oExpr->RenderExpression();
}
}
if (!is_null($aSelectExpr))
{
foreach($aSelectExpr as $sAlias => $oExpr)
{
$sRawId .= 'se:'.$sAlias.'!'.$oExpr->Render();
if (!is_null($aSelectExpr)) {
foreach ($aSelectExpr as $sAlias => $oExpr) {
$sRawId .= 'se:'.$sAlias.'!'.$oExpr->RenderExpression();
}
}
$aContextData['aGroupByExpr'] = $aGroupByExpr;
$aContextData['aSelectExpr'] = $aSelectExpr;
$sRawId .= $bGetCount;
$aContextData['bGetCount'] = $bGetCount;
if (is_array($aSelectedClasses))
{
if (is_array($aSelectedClasses)) {
$sRawId .= implode(',', $aSelectedClasses); // Unions may alter the list of selected columns
}
$aContextData['aSelectedClasses'] = $aSelectedClasses;
@@ -2106,17 +2077,13 @@ class DBObjectSearch extends DBSearch
// 3rd step - position the attributes in the hierarchy of classes
//
$oSubClassExp->Browse(function($oNode) use ($sSubClass) {
if ($oNode instanceof FieldExpression)
{
if ($oNode instanceof FieldExpression) {
$sAttCode = $oNode->GetName();
$oAttDef = MetaModel::GetAttributeDef($sSubClass, $sAttCode);
if ($oAttDef->IsExternalField())
{
if ($oAttDef->IsExternalField()) {
$sKeyAttCode = $oAttDef->GetKeyAttCode();
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sKeyAttCode);
}
else
{
} else {
$sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sAttCode);
}
$sParent = MetaModel::GetAttributeOrigin($sClassOfAttribute, $oNode->GetName());
@@ -2124,12 +2091,11 @@ class DBObjectSearch extends DBSearch
}
});
$sSignature = $oSubClassExp->Render();
if (!array_key_exists($sSignature, $aExpressions))
{
$sSignature = $oSubClassExp->RenderExpression();
if (!array_key_exists($sSignature, $aExpressions)) {
$aExpressions[$sSignature] = array(
'expression' => $oSubClassExp,
'classes' => array(),
'classes' => array(),
);
}
$aExpressions[$sSignature]['classes'][] = $sSubClass;

View File

@@ -141,7 +141,7 @@ class DBObjectSet implements iDBObjectSetIterator
{
$sRet = '';
$this->Rewind();
$sRet .= "Set (".$this->m_oFilter->ToOQL(true).")<br/>\n";
$sRet .= "Set (".$this->m_oFilter->ToOQL().")<br/>\n";
$sRet .= "Query: <pre style=\"font-size: smaller; display:inline;\">".$this->m_oFilter->MakeSelectQuery().")</pre>\n";
$sRet .= $this->Count()." records<br/>\n";
@@ -154,7 +154,6 @@ class DBObjectSet implements iDBObjectSetIterator
}
$sRet .= "</ul>\n";
}
$this->Rewind();
return $sRet;
}
@@ -767,10 +766,7 @@ 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
@@ -846,15 +842,12 @@ class DBObjectSet implements iDBObjectSetIterator
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
public function Count()
public function Count(): int
{
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);
@@ -865,42 +858,6 @@ 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
*
@@ -917,11 +874,8 @@ 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);
@@ -932,7 +886,7 @@ class DBObjectSet implements iDBObjectSetIterator
{
$iCount = 0;
}
}
}
else
{
$iCount = $this->m_iNumTotalDBRows;
@@ -957,11 +911,8 @@ 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);
@@ -972,7 +923,7 @@ class DBObjectSet implements iDBObjectSetIterator
{
$iCount = 0;
}
}
}
else
{
$iCount = $this->m_iNumTotalDBRows;
@@ -1126,14 +1077,13 @@ class DBObjectSet implements iDBObjectSetIterator
*
* @param int $iRow
*
* @return int|mixed
*
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @since 3.1.0 N°4517 Now returns void for return type to match parent class and be compatible with PHP 8.1
*/
public function Seek($iRow)
public function Seek($iRow): void
{
if (!$this->m_bLoaded) $this->Load();
@@ -1142,7 +1092,6 @@ class DBObjectSet implements iDBObjectSetIterator
{
$this->m_oSQLResult->data_seek($this->m_iCurrRow);
}
return $this->m_iCurrRow;
}
/**

View File

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

View File

@@ -83,6 +83,8 @@ class DesignDocument extends DOMDocument
*
* @return int
*/
// Return type union is not supported by PHP 7.4, we can remove the following PHP attribute and add the return type once iTop min PHP version is PHP 8.0+
#[\ReturnTypeWillChange]
public function save($filename, $options = null)
{
$this->documentElement->setAttribute('xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance");
@@ -97,13 +99,12 @@ class DesignDocument extends DOMDocument
public function Dump($bReturnRes = false)
{
$sXml = $this->saveXML();
if ($bReturnRes)
{
if ($bReturnRes) {
return $sXml;
}
echo "<pre>\n";
echo htmlentities($sXml);
echo utils::EscapeHtml($sXml);
echo "</pre>\n";
return '';
@@ -181,6 +182,26 @@ class DesignElement extends \DOMElement
return $this->ownerDocument->GetNodes($sXPath, $this);
}
public static function ToArray(DesignElement $oNode)
{
$aRes = [];
if ($oNode->GetNodes('./*')->length == 0) {
return $oNode->GetText('');
}
foreach ($oNode->GetNodes('./*') as $oSubNode) {
/** @var \Combodo\iTop\DesignElement $oSubNode */
$aSubArray = DesignElement::ToArray($oSubNode);
if ($oSubNode->hasAttribute('id')) {
$aRes[$oSubNode->getAttribute('id')] = $aSubArray;
} else {
$aRes[$oSubNode->tagName] = $aSubArray;
}
}
return $aRes;
}
/**
* Create an HTML representation of the DOM, for debugging purposes
*
@@ -196,13 +217,13 @@ class DesignElement extends \DOMElement
$oDoc->appendChild($oClone);
$sXml = $oDoc->saveXML($oClone);
if ($bReturnRes)
{
if ($bReturnRes) {
return $sXml;
}
echo "<pre>\n";
echo htmlentities($sXml);
echo utils::EscapeHtml($sXml);
echo "</pre>\n";
return '';
}
/**

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,11 +56,10 @@ 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 = null)
public static function SetUserLanguage($sLanguageCode)
{
if (!is_null($sLanguageCode) && !array_key_exists($sLanguageCode, self::$m_aLanguages))
if (!array_key_exists($sLanguageCode, self::$m_aLanguages))
{
throw new DictExceptionUnknownLanguage($sLanguageCode);
}
@@ -116,50 +115,33 @@ class Dict
* @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 all registered dictionaries.");
IssueLog::Warning("Cannot find $sLangCode in dictionnaries. default labels displayed");
// It may happen, when something happens before the dictionaries get loaded
return [ 'label' => $sStringCode, 'lang' => $sLangCode ];
return $sStringCode;
}
$aCurrentDictionary = self::$m_aData[$sLangCode];
if (is_array($aCurrentDictionary) && array_key_exists($sStringCode, $aCurrentDictionary))
{
return [ 'label' => $aCurrentDictionary[$sStringCode], 'lang' => $sLangCode ];
return $aCurrentDictionary[$sStringCode];
}
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 [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => self::$m_sDefaultLanguage ];
return $aDefaultDictionary[$sStringCode];
}
// Attempt to find the string in english
//
@@ -168,17 +150,17 @@ class Dict
$aDefaultDictionary = self::$m_aData['EN US'];
if (is_array($aDefaultDictionary) && array_key_exists($sStringCode, $aDefaultDictionary))
{
return [ 'label' => $aDefaultDictionary[$sStringCode], 'lang' => 'EN US' ];
return $aDefaultDictionary[$sStringCode];
}
}
// Could not find the string...
//
if (is_null($sDefault))
{
return [ 'label' => $sStringCode, 'lang' => null ];
return $sStringCode;
}
return [ 'label' => $sDefault, 'lang' => null ];
return $sDefault;
}
@@ -194,25 +176,19 @@ class Dict
*/
public static function Format($sFormatCode /*, ... arguments ....*/)
{
['label' => $sLocalizedFormat, 'lang' => $sLangCode] = self::GetLabelAndLangCode($sFormatCode);
$sLocalizedFormat = self::S($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);
}
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);
}
return vsprintf($sLocalizedFormat, $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'
@@ -222,7 +198,7 @@ class Dict
{
self::$m_aData[$sLanguageCode] = $aEntries;
}
/**
* Set the list of available languages
* @param hash $aLanguagesList
@@ -283,7 +259,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))
{
@@ -293,7 +269,7 @@ class Dict
}
return $bResult;
}
/**
* Enable caching (cached using APC)
* @param string $sApplicationPrefix The prefix for uniquely identiying this iTop instance
@@ -336,14 +312,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]))
@@ -351,7 +327,7 @@ class Dict
$aMissing[$sStringCode] = $sValue;
}
}
foreach (self::$m_aData[$sLanguageCode] as $sStringCode => $sValue)
{
if (!array_key_exists($sStringCode, self::$m_aData[$sLanguageRef]))
@@ -374,7 +350,7 @@ class Dict
}
return array($aMissing, $aUnexpected, $aNotTranslated, $aOK);
}
public static function Dump()
{
MyHelpers::var_dump_html(self::$m_aData);
@@ -397,7 +373,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
@@ -410,7 +386,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)
{
@@ -419,7 +395,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

@@ -459,11 +459,10 @@ class DisplayableNode extends GraphNode
{
$aContext = $aContextDefs[$key];
$aRootCauses = array();
foreach($aObjects as $oRootCause)
{
foreach ($aObjects as $oRootCause) {
$aRootCauses[] = $oRootCause->GetHyperlink();
}
$sHtml .= '<p><img style="max-height: 24px; vertical-align:bottom;" class="ibo-class-icon ibo-is-small" src="'.utils::GetAbsoluteUrlModulesRoot().$aContext['icon'].'" title="'.htmlentities(Dict::S($aContext['dict'])).'">&nbsp;'.implode(', ', $aRootCauses).'</p>';
$sHtml .= '<p><img style="max-height: 24px; vertical-align:bottom;" class="ibo-class-icon ibo-is-small" src="'.utils::GetAbsoluteUrlModulesRoot().$aContext['icon'].'" title="'.utils::EscapeHtml(Dict::S($aContext['dict'])).'">&nbsp;'.implode(', ', $aRootCauses).'</p>';
}
$sHtml .= '<hr/>';
}
@@ -1335,18 +1334,17 @@ class DisplayableGraph extends SimpleGraph
}
$oPdf->Rect($xMin, $yMin, $fMaxWidth + $fIconSize + 3*$fPadding, $yMax - $yMin, 'D');
if ($sComments != '')
{
if ($sComments != '') {
// Draw the comment text (surrounded by a rectangle)
$xPos = $xMin + $fMaxWidth + $fIconSize + 4*$fPadding;
$w = $xMax - $xPos - 2*$fPadding;
$xPos = $xMin + $fMaxWidth + $fIconSize + 4 * $fPadding;
$w = $xMax - $xPos - 2 * $fPadding;
$iNbLines = 1;
$sText = '<p>'.str_replace("\n", '<br/>', htmlentities($sComments, ENT_QUOTES, 'UTF-8'), $iNbLines).'</p>';
$sText = '<p>'.str_replace("\n", '<br/>', utils::EscapeHtml($sComments), $iNbLines).'</p>';
$fLineHeight = $oPdf->getStringHeight($w, $sText);
$h = (1+$iNbLines) * $fLineHeight;
$yPos = $yMax - 2*$fPadding - $h;
$h = (1 + $iNbLines) * $fLineHeight;
$yPos = $yMax - 2 * $fPadding - $h;
$oPdf->writeHTMLCell($w, $h, $xPos + $fPadding, $yPos + $fPadding, $sText, 0 /* border */, 1 /* ln */);
$oPdf->Rect($xPos, $yPos, $w + 2*$fPadding, $h + 2*$fPadding, 'D');
$oPdf->Rect($xPos, $yPos, $w + 2 * $fPadding, $h + 2 * $fPadding, 'D');
$yMax = $yPos - $fPadding;
}

View File

@@ -27,9 +27,6 @@
use Combodo\iTop\Core\Email\EmailFactory;
use Combodo\iTop\Core\Email\iEMail;
Swift_Preferences::getInstance()->setCharset('UTF-8');
define ('EMAIL_SEND_OK', 0);
define ('EMAIL_SEND_PENDING', 1);
define ('EMAIL_SEND_ERROR', 2);
@@ -112,7 +109,7 @@ class EMail implements iEMail
* @throws \CoreException
* @throws \Symfony\Component\CssSelector\Exception\SyntaxErrorException
*/
static public function UnSerializeV2($sSerializedMessage)
public static function UnSerializeV2($sSerializedMessage)
{
return EmailFactory::GetMailer()::UnSerializeV2($sSerializedMessage);
}
@@ -154,7 +151,7 @@ class EMail implements iEMail
public function SetBody($sBody, $sMimeType = 'text/html', $sCustomStyles = null)
{
$this->oMailer->SetBody($sBody, $sMimeType, $sCustomStyles);
}
}
public function AddPart($sText, $sMimeType = 'text/html')
{

View File

@@ -10,7 +10,6 @@ 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');
@@ -83,7 +82,6 @@ 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);
@@ -102,8 +100,8 @@ class ExcelBulkExport extends TabularBulkExport
$sDateTimeFormat = utils::ReadParam('date_format', (string)AttributeDateTime::GetFormat(), true, 'raw_data');
$sDefaultFormat = htmlentities((string)AttributeDateTime::GetFormat(), ENT_QUOTES, 'UTF-8');
$sExample = htmlentities(date((string)AttributeDateTime::GetFormat()), ENT_QUOTES, 'UTF-8');
$sDefaultFormat = utils::EscapeHtml((string)AttributeDateTime::GetFormat());
$sExample = utils::EscapeHtml(date((string)AttributeDateTime::GetFormat()));
$oRadioDefault = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatDefault_Example', $sDefaultFormat, $sExample), "excel_date_format_radio", "default", "excel_date_time_format_default", "radio");
$oRadioDefault->GetInput()->SetIsChecked(($sDateTimeFormat == (string)AttributeDateTime::GetFormat()));
$oRadioDefault->SetBeforeInput(false);
@@ -111,7 +109,7 @@ class ExcelBulkExport extends TabularBulkExport
$oFieldSetDate->AddSubBlock($oRadioDefault);
$oFieldSetDate->AddSubBlock(new Html('</br>'));
$sFormatInput = '<input type="text" size="15" name="date_format" id="excel_custom_date_time_format" title="" value="'.htmlentities($sDateTimeFormat, ENT_QUOTES, 'UTF-8').'"/>';
$sFormatInput = '<input type="text" size="15" name="date_format" id="excel_custom_date_time_format" title="" value="'.utils::EscapeHtml($sDateTimeFormat).'"/>';
$oRadioCustom = InputUIBlockFactory::MakeForInputWithLabel(Dict::Format('Core:BulkExport:DateTimeFormatCustom_Format', $sFormatInput), "excel_date_format_radio", "custom", "excel_date_time_format_custom", "radio");
$oRadioCustom->SetDescription(Dict::S('UI:CSVImport:CustomDateTimeFormatTooltip'));
$oRadioCustom->GetInput()->SetIsChecked($sDateTimeFormat !== (string)AttributeDateTime::GetFormat());
@@ -158,16 +156,17 @@ EOF
protected function GetSampleData($oObj, $sAttCode)
{
if ($sAttCode != 'id')
{
if ($sAttCode != 'id') {
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
{
$sClass = (get_class($oAttDef) == 'AttributeDateTime') ? 'user-formatted-date-time' : 'user-formatted-date';
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.htmlentities($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj), ENT_QUOTES, 'UTF-8').'</div>';
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.utils::EscapeHtml($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj)).'</div>';
}
}
return '<div class="text-preview">'.htmlentities($this->GetValue($oObj, $sAttCode), ENT_QUOTES, 'UTF-8').'</div>';
return '<div class="text-preview">'.utils::EscapeHtml($this->GetValue($oObj, $sAttCode)).'</div>';
}
protected function GetValue($oObj, $sAttCode)

View File

@@ -1,4 +1,8 @@
<?php
// The file has been moved in iTop 2.2.0+ (revision 3803)
// Preserve backward compatibility with some external tools (Cf. toolkit)
// @deprecated 3.1.0
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use core/oql/expression.class.inc.php instead');
require_once(APPROOT.'core/oql/expression.class.inc.php');

View File

@@ -31,8 +31,9 @@ require_once('MyHelpers.class.inc.php');
/**
* Definition of a filter (could be made out of an existing attribute, or from an expression)
* Definition of a filter (could be made out of an existing attribute, or from an expression)
*
* @deprecated 3.1.0 not used N°4690 - Deprecate "FilterCodes"
* @package iTopORM
*/
abstract class FilterDefinition
@@ -46,6 +47,7 @@ abstract class FilterDefinition
public function __construct($sCode, $aParams = array())
{
DeprecatedCallsLog::NotifyDeprecatedPhpMethod("Deprecated class ".$this->GetClass().". Do not use. Will be removed in next version.");
$this->m_sCode = $sCode;
$this->m_aParams = $aParams;
$this->ConsistencyCheck();
@@ -98,8 +100,9 @@ abstract class FilterDefinition
}
/**
* Match against the object unique identifier
* Match against the object unique identifier
*
* @deprecated 3.1.0 N°4690 - Deprecate "FilterCodes"
* @package iTopORM
*/
class FilterPrivateKey extends FilterDefinition
@@ -145,8 +148,9 @@ class FilterPrivateKey extends FilterDefinition
}
/**
* Match against an existing attribute (the attribute type will determine the available operators)
* Match against an existing attribute (the attribute type will determine the available operators)
*
* @deprecated 3.1.0 N°4690 - Deprecate "FilterCodes"
* @package iTopORM
*/
class FilterFromAttribute extends FilterDefinition

View File

@@ -62,7 +62,8 @@ class HTMLBulkExport extends TabularBulkExport
if ($oAttDef instanceof AttributeDateTime) // AttributeDate is derived from AttributeDateTime
{
$sClass = (get_class($oAttDef) == 'AttributeDateTime') ? 'user-formatted-date-time' : 'user-formatted-date';
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.htmlentities($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj), ENT_QUOTES, 'UTF-8').'</div>';
return '<div class="'.$sClass.'" data-date="'.$oObj->Get($sAttCode).'">'.utils::EscapeHtml($oAttDef->GetEditValue($oObj->Get($sAttCode), $oObj)).'</div>';
}
}
return $this->GetValue($oObj, $sAttCode);

View File

@@ -34,35 +34,32 @@ abstract class HTMLSanitizer
/**
* Sanitize an HTML string with the configured sanitizer, falling back to HTMLDOMSanitizer in case of Exception or invalid configuration
*
* @param string $sHTML
* @param string $sConfigKey eg. 'html_sanitizer', 'svg_sanitizer'
*
* @return string
*/
public static function Sanitize($sHTML, $sConfigKey = 'html_sanitizer')
public static function Sanitize($sHTML)
{
$sSanitizerClass = utils::GetConfig()->Get($sConfigKey);
if (!class_exists($sSanitizerClass)) {
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a valid class. Will use HTMLDOMSanitizer as the default sanitizer.');
$sSanitizerClass = MetaModel::GetConfig()->Get('html_sanitizer');
if(!class_exists($sSanitizerClass))
{
IssueLog::Warning('The configured "html_sanitizer" class "'.$sSanitizerClass.'" is not a valid class. Will use HTMLDOMSanitizer as the default sanitizer.');
$sSanitizerClass = 'HTMLDOMSanitizer';
}
else if(!is_subclass_of($sSanitizerClass, 'HTMLSanitizer'))
{
IssueLog::Warning('The configured "html_sanitizer" class "'.$sSanitizerClass.'" is not a subclass of HTMLSanitizer. Will use HTMLDOMSanitizer as the default sanitizer.');
$sSanitizerClass = 'HTMLDOMSanitizer';
} else if (!is_subclass_of($sSanitizerClass, 'HTMLSanitizer')) {
if ($sConfigKey === 'html_sanitizer') {
IssueLog::Warning('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of '.HTMLSanitizer::class.'. Will use HTMLDOMSanitizer as the default sanitizer.');
$sSanitizerClass = 'HTMLDOMSanitizer';
} else {
IssueLog::Error('The configured "'.$sConfigKey.'" class "'.$sSanitizerClass.'" is not a subclass of '.HTMLSanitizer::class.' ! Won\'t sanitize string.');
return $sHTML;
}
}
try {
try
{
$oSanitizer = new $sSanitizerClass();
$sCleanHTML = $oSanitizer->DoSanitize($sHTML);
}
catch (Exception $e) {
if ($sSanitizerClass != 'HTMLDOMSanitizer') {
catch(Exception $e)
{
if($sSanitizerClass != 'HTMLDOMSanitizer')
{
IssueLog::Warning('Failed to sanitize an HTML string with "'.$sSanitizerClass.'". The following exception occured: '.$e->getMessage());
IssueLog::Warning('Will try to sanitize with HTMLDOMSanitizer.');
// try again with the HTMLDOMSanitizer

View File

@@ -242,7 +242,7 @@ class InlineImage extends DBObject
public static function OnFormCancel($sTempId): bool
{
// Protection against unfortunate massive delete of inline images when a null temp ID is passed
if (utils::IsNullOrEmptyString($sTempId)) {
if (strlen($sTempId) === 0) {
IssueLog::Trace('OnFormCancel "error" $sTempId is null or empty', LogChannels::INLINE_IMAGE, array(
'$sTempId' => $sTempId,
'$sUser' => UserRights::GetUser(),
@@ -295,13 +295,12 @@ class InlineImage extends DBObject
{
$sImgTag = $aImgInfo[0][0];
$sSecret = '';
if (preg_match('/data-img-secret="([0-9a-f]+)"/', $sImgTag, $aSecretMatches))
{
if (preg_match('/data-img-secret="([0-9a-f]+)"/', $sImgTag, $aSecretMatches)) {
$sSecret = '&s='.$aSecretMatches[1];
}
$sAttId = $aImgInfo[2][0];
$sNewImgTag = preg_replace('/src="[^"]+"/', 'src="'.htmlentities($sUrl.$sAttId.$sSecret, ENT_QUOTES, 'UTF-8').'"', $sImgTag); // preserve other attributes, must convert & to &amp; to be idempotent with CKEditor
$sNewImgTag = preg_replace('/src="[^"]+"/', 'src="'.utils::EscapeHtml($sUrl.$sAttId.$sSecret).'"', $sImgTag); // preserve other attributes, must convert & to &amp; to be idempotent with CKEditor
$aNeedles[] = $sImgTag;
$aReplacements[] = $sNewImgTag;
}
@@ -536,8 +535,8 @@ JS
$iObjKey = $oObject->GetKey();
$sAbsoluteUrlAppRoot = utils::GetAbsoluteUrlAppRoot();
$sToggleFullScreen = htmlentities(Dict::S('UI:ToggleFullScreen'), ENT_QUOTES, 'UTF-8');
$sToggleFullScreen = utils::EscapeHtml(Dict::S('UI:ToggleFullScreen'));
return
<<<JS
// Hook the file upload of all CKEditor instances

View File

@@ -1,14 +1,27 @@
<?php
/**
* @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;
// Copyright (C) 2010-2021 Combodo SARL
//
// This file is part of iTop.
//
// iTop is free software; you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// iTop is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Measures operations duration, memory usage, etc. (and some other KPIs)
*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class ExecutionKPI
@@ -17,8 +30,6 @@ 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
@@ -75,39 +86,14 @@ 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 = 'Measures: '.implode(', ', $aFeatures);
$sFeatures = implode(', ', $aFeatures);
$sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'";
$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";
return "KPI logging is active for $sFor. Measures: $sFeatures";
}
static public function ReportStats()
@@ -115,28 +101,7 @@ 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)
@@ -149,9 +114,9 @@ class ExecutionKPI
$sHtml = "<hr/>";
$sHtml .= "<div style=\"background-color: grey; padding: 10px;\">";
$sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - $sRequest</h3>";
$sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - ".$_SERVER['REQUEST_URI']." (".$_SERVER['REQUEST_METHOD'].")</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\">";
@@ -292,7 +257,7 @@ class ExecutionKPI
$sTotalInter = round($fTotalInter, 3);
$sMinInter = round($fMinInter, 3);
$sMaxInter = round($fMaxInter, 3);
if (($fTotalInter >= self::$m_fSlowQueries))
if (($fTotalInter >= $fSlowQueries))
{
if ($bDisplayHeader)
{
@@ -320,19 +285,37 @@ 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
//
@@ -340,15 +323,9 @@ class ExecutionKPI
{
global $fItopStarted;
if (!self::IsEnabled()) {
return;
}
$aNewEntry = null;
$fStarted = $this->m_fStarted;
$fStopped = $this->m_fStarted;
if (self::$m_bEnabled_Duration) {
if (self::$m_bEnabled_Duration) {
$fStopped = MyHelpers::getmicrotime();
$aNewEntry = array(
'op' => $sOperationDesc,
@@ -359,9 +336,6 @@ 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();
@@ -371,103 +345,40 @@ class ExecutionKPI
}
$aNewEntry['mem_begin'] = $this->m_iInitialMemory;
$aNewEntry['mem_end'] = $iCurrentMemory;
$iPeakMemory = self::memory_get_peak_usage();
$aNewEntry['mem_peak'] = $iPeakMemory;
if (function_exists('memory_get_peak_usage'))
{
$aNewEntry['mem_peak'] = memory_get_peak_usage();
}
// Reset for the next operation (if the object is recycled)
$this->m_iInitialMemory = $iCurrentMemory;
}
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)
if (!is_null($aNewEntry))
{
self::$m_aExecData[] = $aNewEntry;
}
$this->ResetCounters();
}
public function ComputeStatsForExtension($object, $sMethod)
{
if (!self::IsEnabled()) {
return;
}
$sSignature = ModuleService::GetInstance()->GetModuleMethodSignature($object, $sMethod);
if (utils::StartsWith($sSignature, '[')) {
$this->ComputeStats('Extension', $sSignature);
}
}
public function ComputeStats($sOperation, $sArguments)
{
if (!self::IsEnabled()) {
return;
}
$fDuration = 0;
if (self::$m_bEnabled_Duration) {
$fStopped = MyHelpers::getmicrotime();
$fDuration = $fStopped - $this->m_fStarted;
$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);
}
}
$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);
}
protected function ResetCounters()
@@ -497,7 +408,35 @@ class ExecutionKPI
static protected function memory_get_usage()
{
return memory_get_usage(true);
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;
}
}
static public function memory_get_peak_usage($bRealUsage = false)
@@ -510,4 +449,3 @@ class ExecutionKPI
return 0;
}
}

View File

@@ -1,9 +1,9 @@
<?php
// Copyright (C) 2010-2023 Combodo SARL
// Copyright (C) 2010-2021 Combodo SARL
//
// 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.
@@ -448,7 +448,7 @@ class LogFileNameBuilderFactory
/**
* File logging
*
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @since 2.7.0 N°2518 N°2793 file log rotation
*/
@@ -545,8 +545,12 @@ class LogChannels
public const APC = 'apc';
/**
* @since 3.0.0
* @var string
* @since 3.0.1 N°4849
* @since 2.7.7 N°4635
*/
public const NOTIFICATIONS = 'notifications';
public const CLI = 'CLI';
/**
@@ -556,31 +560,23 @@ class LogChannels
*/
public const CMDB_SOURCE = 'cmdbsource';
/**
* @since 3.0.0
*/
public const CONSOLE = 'console';
public const CONSOLE = 'console';
public const CORE = 'core';
public const CORE = 'core';
public const DEADLOCK = 'DeadLock';
/**
* @var string
* @since 2.7.9 3.0.3 3.1.0 N°5588
*/
public const EXPORT = 'export';
public const DEADLOCK = 'DeadLock';
public const INLINE_IMAGE = 'InlineImage';
public const PORTAL = 'portal';
/**
* @var string
* @since 3.0.1 N°4849
* @since 2.7.7 N°4635
* @since 3.1.0 specific channel for event service
*/
public const NOTIFICATIONS = 'notifications';
public const EVENT_SERVICE = 'EventService';
public const PORTAL = 'portal';
public const DM_CRUD = 'DMCRUD';
}
@@ -961,9 +957,7 @@ class ToolsLog extends LogAPI
/**
* @see \CMDBSource::LogDeadLock()
* @since 2.7.1 PR #139
*
* @link https://dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks.html
* @since 2.7.1
*/
class DeadLockLog extends LogAPI
{
@@ -988,10 +982,10 @@ class DeadLockLog extends LogAPI
{
switch ($iMysqlErrorNo)
{
case CMDBSource::MYSQL_ERRNO_WAIT_TIMEOUT:
case 1205:
return self::CHANNEL_WAIT_TIMEOUT;
break;
case CMDBSource::MYSQL_ERRNO_DEADLOCK:
case 1213:
return self::CHANNEL_DEADLOCK_FOUND;
break;
default:
@@ -1021,23 +1015,20 @@ class DeadLockLog extends LogAPI
/**
* Starting with the WARNING level we will log in a dedicated file (/log/deprecated-calls.log) :
* - iTop deprecated files or code
* - protected trigger_error calls with E_DEPRECATED or E_USER_DEPRECATED
*
* For the last category, if {@see utils::IsDevelopmentEnvironment()} is true we will do a trigger_error()
*
* @since 3.0.0 N°3731 first implementation
* @link https://www.itophub.io/wiki/page?id=latest:admin:log:channels#deprecated_calls channel used
* @since 3.0.0 N°3731
*/
class DeprecatedCallsLog extends LogAPI
{
public const ENUM_CHANNEL_PHP_METHOD = 'deprecated-php-method';
/**
* @var string
* @since 3.1.0
*/
public const ENUM_CHANNEL_PHP_ENDPOINT = 'deprecated-php-endpoint';
public const ENUM_CHANNEL_PHP_LIBMETHOD = 'deprecated-php-libmethod';
public const ENUM_CHANNEL_FILE = 'deprecated-file';
public const CHANNEL_DEFAULT = self::ENUM_CHANNEL_PHP_METHOD;
/** @var string Warning this constant won't be used directly ! To see the real default level check {@see GetLevelDefault()} */
public const LEVEL_DEFAULT = self::LEVEL_ERROR;
/** @var \FileLog we want our own instance ! */
@@ -1071,23 +1062,16 @@ class DeprecatedCallsLog extends LogAPI
* @uses \set_error_handler() to catch deprecated notices
*
* @since 3.0.0 N°3002 logs deprecated notices in called code
* @since 3.0.4 N°6274 do not set handler when in PHPUnit context (otherwise PHP notices won't be caught)
*/
public static function Enable($sTargetFile = null): void {
public static function Enable($sTargetFile = null): void
{
if (empty($sTargetFile)) {
$sTargetFile = APPROOT.'log/deprecated-calls.log';
}
parent::Enable($sTargetFile);
if (
(
(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);
if (static::IsLogLevelEnabledSafe(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD)) {
set_error_handler([static::class, 'DeprecatedNoticesErrorHandler']);
}
}
@@ -1119,12 +1103,7 @@ class DeprecatedCallsLog extends LogAPI
}
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 4);
$iStackDeprecatedMethodLevel = 2; // level 0 = current method, level 1 = @trigger_error, level 2 = method containing the `trigger_error` call (can be either 'trigger_deprecation' or the faulty method), level 3 = In some cases, method containing the 'trigger_deprecation' call
// In case current level is actually a 'trigger_deprecation' call, try to go one level further to get the real deprecated method
if (array_key_exists($iStackDeprecatedMethodLevel, $aStack) && ($aStack[$iStackDeprecatedMethodLevel]['function'] === 'trigger_deprecation') && array_key_exists($iStackDeprecatedMethodLevel + 1, $aStack)) {
$iStackDeprecatedMethodLevel++;
}
$iStackDeprecatedMethodLevel = 2; // level 0 = current method, level 1 = @trigger_error, level 2 = method containing the `trigger_error` call
$sDeprecatedObject = $aStack[$iStackDeprecatedMethodLevel]['class'];
$sDeprecatedMethod = $aStack[$iStackDeprecatedMethodLevel]['function'];
if (($sDeprecatedObject === __CLASS__) && ($sDeprecatedMethod === 'Log')) {
@@ -1169,6 +1148,7 @@ class DeprecatedCallsLog extends LogAPI
* - else call parent method
*
* In other words, when in dev mode all deprecated calls will be logged to file
*
*/
protected static function GetLevelDefault(string $sConfigKey)
{
@@ -1184,9 +1164,7 @@ class DeprecatedCallsLog extends LogAPI
}
/**
* @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
*
* @throws \ConfigException
* @link https://www.php.net/debug_backtrace
* @uses \debug_backtrace()
*/
@@ -1238,7 +1216,19 @@ class DeprecatedCallsLog extends LogAPI
}
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
$sMessage = self::GetMessageFromStack($aStack);
$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})";
}
if (!is_null($sAdditionalMessage)) {
$sMessage .= ' : '.$sAdditionalMessage;
@@ -1248,41 +1238,32 @@ class DeprecatedCallsLog extends LogAPI
}
/**
* @param array $aDebugBacktrace data from {@see debug_backtrace()}
* @return string message to print to the log
* @param string|null $sAdditionalMessage
* @since 3.1.0
*/
private static function GetMessageFromStack(array $aDebugBacktrace): string
public static function NotifyDeprecatedPhpEndpoint(?string $sAdditionalMessage = null): void
{
// 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 .= ')';
try {
if (!static::IsLogLevelEnabled(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_ENDPOINT)) {
return;
}
}
catch (ConfigException $e) {
return;
}
return $sMessage;
}
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
$iStackDeprecatedMethodLevel = 0; // level 0 = current method, level 1 = method containing the `NotifyDeprecatedPhpMethod` call
$sDeprecatedUrl = $_SERVER['REQUEST_URI'];
$sCallerFile = $aStack[$iStackDeprecatedMethodLevel]['file'];
$sCallerLine = $aStack[$iStackDeprecatedMethodLevel]['line'];
$sMessage = "Call to endpoint {$sDeprecatedUrl} in {$sCallerFile}#L{$sCallerLine}";
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 (!is_null($sAdditionalMessage)) {
$sMessage .= ' : '.$sAdditionalMessage;
}
if (array_key_exists('file', $aCurrentLevelDebugTrace)) {
$sCallerFile = $aCurrentLevelDebugTrace['file'];
$sCallerLine = $aCurrentLevelDebugTrace['line'] ?? "";
$sMessage .= "{$sCallerFile}#L{$sCallerLine}";
}
return $sMessage;
static::Warning($sMessage, self::ENUM_CHANNEL_PHP_ENDPOINT);
}
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array()): void
@@ -1541,8 +1522,6 @@ class ExceptionLog extends LogAPI
*/
private static function GetLastEventIssue()
{
$oRet = self::$oLastEventIssue;
self::$oLastEventIssue = null;
return $oRet;
return self::$oLastEventIssue;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -22,9 +22,7 @@
* 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.
*
* @link https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html MySQL locking functions documentation
* installed PHP.
*
* @copyright Copyright (C) 2013-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
@@ -259,7 +257,7 @@ class iTopMutex
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
if (!$this->hDBLink) {
throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
throw new Exception("Could not connect to the DB server (host=$sServer, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')');
}
// Make sure that the server variable `wait_timeout` is at least 86400 seconds for this connection,

View File

@@ -107,7 +107,7 @@ abstract class Expression {
/**
* recursive rendering
*
* @deprecated use RenderExpression
* @deprecated 3.0.0 use RenderExpression
*
* @param array $aArgs used as input by default, or used as output if bRetrofitParams set to True
* @param bool $bRetrofitParams
@@ -118,7 +118,7 @@ abstract class Expression {
public function Render(&$aArgs = null, $bRetrofitParams = false)
{
// cannot notify depreciation for now as this is still MASSIVELY used in iTop core !
//DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use RenderExpression');
DeprecatedCallsLog::NotifyDeprecatedPhpMethod('use RenderExpression');
return $this->RenderExpression(false, $aArgs, $bRetrofitParams);
}
@@ -1765,6 +1765,7 @@ class FieldExpression extends UnaryExpression
*/
public function MakeValueLabel($oFilter, $sValue, $sDefault)
{
$sAttCode = $this->GetName();
$sParentAlias = $this->GetParent();
@@ -3049,8 +3050,7 @@ class FunctionExpression extends Expression
public function MakeValueLabel($oFilter, $sValue, $sDefault)
{
static $aWeekDayToString = null;
if (is_null($aWeekDayToString))
{
if (is_null($aWeekDayToString)) {
// Init the correspondance table
$aWeekDayToString = array(
0 => Dict::S('DayOfWeek-Sunday'),
@@ -3059,7 +3059,7 @@ class FunctionExpression extends Expression
3 => Dict::S('DayOfWeek-Wednesday'),
4 => Dict::S('DayOfWeek-Thursday'),
5 => Dict::S('DayOfWeek-Friday'),
6 => Dict::S('DayOfWeek-Saturday')
6 => Dict::S('DayOfWeek-Saturday'),
);
}
static $aMonthToString = null;
@@ -3067,15 +3067,15 @@ class FunctionExpression extends Expression
{
// Init the correspondance table
$aMonthToString = array(
1 => Dict::S('Month-01'),
2 => Dict::S('Month-02'),
3 => Dict::S('Month-03'),
4 => Dict::S('Month-04'),
5 => Dict::S('Month-05'),
6 => Dict::S('Month-06'),
7 => Dict::S('Month-07'),
8 => Dict::S('Month-08'),
9 => Dict::S('Month-09'),
1 => Dict::S('Month-01'),
2 => Dict::S('Month-02'),
3 => Dict::S('Month-03'),
4 => Dict::S('Month-04'),
5 => Dict::S('Month-05'),
6 => Dict::S('Month-06'),
7 => Dict::S('Month-07'),
8 => Dict::S('Month-08'),
9 => Dict::S('Month-09'),
10 => Dict::S('Month-10'),
11 => Dict::S('Month-11'),
12 => Dict::S('Month-12'),
@@ -3083,30 +3083,22 @@ class FunctionExpression extends Expression
}
$sRes = $sDefault;
if (strtolower($this->m_sVerb) == 'date_format')
{
if (strtolower($this->m_sVerb) == 'date_format') {
$oFormatExpr = $this->m_aArgs[1];
if ($oFormatExpr->Render() == "'%w'")
{
if (isset($aWeekDayToString[(int)$sValue]))
{
if ($oFormatExpr->RenderExpression() == "'%w'") {
if (isset($aWeekDayToString[(int)$sValue])) {
$sRes = $aWeekDayToString[(int)$sValue];
}
}
elseif ($oFormatExpr->Render() == "'%Y-%m'")
{
} elseif ($oFormatExpr->RenderExpression() == "'%Y-%m'") {
// yyyy-mm => "yyyy month"
$iMonth = (int) substr($sValue, -2); // the two last chars
$iMonth = (int)substr($sValue, -2); // the two last chars
$sRes = substr($sValue, 0, 4).' '.$aMonthToString[$iMonth];
}
elseif ($oFormatExpr->Render() == "'%Y-%m-%d'")
{
} elseif ($oFormatExpr->RenderExpression() == "'%Y-%m-%d'") {
// yyyy-mm-dd => "month d"
$iMonth = (int) substr($sValue, 5, 2);
$iMonth = (int)substr($sValue, 5, 2);
$sRes = $aMonthToString[$iMonth].' '.(int)substr($sValue, -2);
}
elseif ($oFormatExpr->Render() == "'%H'")
{
} elseif ($oFormatExpr->RenderExpression() == "'%H'") {
// H => "H Hour(s)"
$sRes = $sValue.':00';
}

View File

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

View File

@@ -58,22 +58,19 @@ class OQLException extends CoreException
public function getHtmlDesc($sHighlightHtmlBegin = '<span style="font-weight: bolder">', $sHighlightHtmlEnd = '</span>')
{
$sRet = htmlentities($this->m_MyIssue.", found '".$this->m_sUnexpected."' in: ", ENT_QUOTES, 'UTF-8');
$sRet .= htmlentities(substr($this->m_sInput, 0, $this->m_iCol), ENT_QUOTES, 'UTF-8');
$sRet .= $sHighlightHtmlBegin.htmlentities(substr($this->m_sInput, $this->m_iCol, strlen($this->m_sUnexpected)), ENT_QUOTES, 'UTF-8').$sHighlightHtmlEnd;
$sRet .= htmlentities(substr($this->m_sInput, $this->m_iCol + strlen($this->m_sUnexpected)), ENT_QUOTES, 'UTF-8');
$sRet = utils::EscapeHtml($this->m_MyIssue.", found '".$this->m_sUnexpected."' in: ");
$sRet .= utils::EscapeHtml(substr($this->m_sInput, 0, $this->m_iCol));
$sRet .= $sHighlightHtmlBegin.utils::EscapeHtml(substr($this->m_sInput, $this->m_iCol, strlen($this->m_sUnexpected))).$sHighlightHtmlEnd;
$sRet .= utils::EscapeHtml(substr($this->m_sInput, $this->m_iCol + strlen($this->m_sUnexpected)));
if (!is_null($this->m_aExpecting) && (count($this->m_aExpecting) > 0))
{
if (count($this->m_aExpecting) < 30)
{
if (!is_null($this->m_aExpecting) && (count($this->m_aExpecting) > 0)) {
if (count($this->m_aExpecting) < 30) {
$sExpectations = '{'.implode(', ', $this->m_aExpecting).'}';
$sRet .= ", expecting ".htmlentities($sExpectations, ENT_QUOTES, 'UTF-8');
}
$sRet .= ", expecting ".utils::EscapeHtml($sExpectations);
}
$sSuggest = self::FindClosestString($this->m_sUnexpected, $this->m_aExpecting);
if (strlen($sSuggest) > 0)
{
$sRet .= ", I would suggest to use '$sHighlightHtmlBegin".htmlentities($sSuggest, ENT_QUOTES, 'UTF-8')."$sHighlightHtmlEnd'";
if (strlen($sSuggest) > 0) {
$sRet .= ", I would suggest to use '$sHighlightHtmlBegin".utils::EscapeHtml($sSuggest)."$sHighlightHtmlEnd'";
}
}

View File

@@ -252,7 +252,7 @@ class ormCaseLog {
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == static::ENUM_FORMAT_TEXT))
{
$sCSSClass = 'caselog_entry';
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
}
else
{
@@ -292,19 +292,15 @@ class ormCaseLog {
}
// Process the case of an eventual remainder (quick migration of AttributeText fields)
if ($iPos < (strlen($this->m_sLog) - 1))
{
if ($iPos < (strlen($this->m_sLog) - 1)) {
$sTextEntry = substr($this->m_sLog, $iPos);
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
if (count($this->m_aIndex) == 0)
{
if (count($this->m_aIndex) == 0) {
$sHtml .= '<div class="caselog_entry" style="'.$sStyleCaseLogEntry.'"">';
$sHtml .= $sTextEntry;
$sHtml .= '</div>';
}
else
{
} else {
$sHtml .= '<div class="caselog_header" style="'.$sStyleCaseLogHeader.'">';
$sHtml .= Dict::S('UI:CaseLog:InitialValue');
$sHtml .= '</div>';
@@ -327,24 +323,18 @@ class ormCaseLog {
$sHtml = '<ul class="case_log_simple_html">';
$iPos = 0;
$aIndex = $this->m_aIndex;
for($index=count($aIndex)-1 ; $index >= 0 ; $index--)
{
for($index=count($aIndex)-1 ; $index >= 0 ; $index--) {
$iPos += $aIndex[$index]['separator_length'];
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
$sCSSClass = 'case_log_simple_html_entry_html';
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == static::ENUM_FORMAT_TEXT))
{
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == static::ENUM_FORMAT_TEXT)) {
$sCSSClass = 'case_log_simple_html_entry';
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
if (!is_null($aTransfoHandler))
{
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
if (!is_null($aTransfoHandler)) {
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
}
}
else
{
if (!is_null($aTransfoHandler))
{
} else {
if (!is_null($aTransfoHandler)) {
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry, true /* wiki "links" only */);
}
$sTextEntry = InlineImage::FixUrls($sTextEntry);
@@ -383,19 +373,15 @@ class ormCaseLog {
}
// Process the case of an eventual remainder (quick migration of AttributeText fields)
if ($iPos < (strlen($this->m_sLog) - 1))
{
if ($iPos < (strlen($this->m_sLog) - 1)) {
$sTextEntry = substr($this->m_sLog, $iPos);
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
if (count($this->m_aIndex) == 0)
{
if (count($this->m_aIndex) == 0) {
$sHtml .= '<li>';
$sHtml .= $sTextEntry;
$sHtml .= '</li>';
}
else
{
} else {
$sHtml .= '<li>';
$sHtml .= Dict::S('UI:CaseLog:InitialValue');
$sHtml .= '<div class="case_log_simple_html_entry" style="'.$sStyleCaseLogEntry.'">';
@@ -437,11 +423,9 @@ class ormCaseLog {
}
$iPos += $aIndex[$index]['separator_length'];
$sTextEntry = substr($this->m_sLog, $iPos, $aIndex[$index]['text_length']);
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == static::ENUM_FORMAT_TEXT))
{
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
if (!is_null($aTransfoHandler))
{
if (!array_key_exists('format', $aIndex[$index]) || ($aIndex[$index]['format'] == static::ENUM_FORMAT_TEXT)) {
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
if (!is_null($aTransfoHandler)) {
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
}
}
@@ -483,19 +467,16 @@ 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 < (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/>", htmlentities($sTextEntry, ENT_QUOTES, 'UTF-8'));
if (!is_null($aTransfoHandler))
{
$sTextEntry = str_replace(array("\r\n", "\n", "\r"), "<br/>", utils::EscapeHtml($sTextEntry));
if (!is_null($aTransfoHandler)) {
$sTextEntry = call_user_func($aTransfoHandler, $sTextEntry);
}
if (count($this->m_aIndex) == 0)
{
$oCollapsibleBlock = CollapsibleSectionUIBlockFactory::MakeStandard( '');
if (count($this->m_aIndex) == 0) {
$oCollapsibleBlock = CollapsibleSectionUIBlockFactory::MakeStandard('');
$oCollapsibleBlock->AddSubBlock(new Html($sTextEntry));
$oCollapsibleBlock->SetOpenedByDefault(true);
$oBlock->AddSubBlock($oCollapsibleBlock);

View File

@@ -25,6 +25,9 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
use Combodo\iTop\Service\EventData;
use Combodo\iTop\Service\EventService;
/**
* ormDocument
@@ -109,17 +112,14 @@ class ormDocument
public function GetAsHTML()
{
$sResult = '';
if ($this->IsEmpty())
{
if ($this->IsEmpty()) {
// If the filename is not empty, display it, this is used
// by the creation wizard while the file has not yet been uploaded
$sResult = htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8');
}
else
{
$sResult = utils::EscapeHtml($this->GetFileName());
} else {
$data = $this->GetData();
$sSize = utils::BytesToFriendlyFormat(strlen($data));
$sResult = htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8').' ('.$sSize.')<br/>';
$sResult = utils::EscapeHtml($this->GetFileName()).' ('.$sSize.')<br/>';
}
return $sResult;
}
@@ -131,7 +131,8 @@ class ormDocument
public function GetDisplayLink($sClass, $Id, $sAttCode)
{
$sUrl = $this->GetDisplayURL($sClass, $Id, $sAttCode);
return "<a href=\"$sUrl\" target=\"_blank\" >".htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8')."</a>\n";
return "<a href=\"$sUrl\" target=\"_blank\" >".utils::EscapeHtml($this->GetFileName())."</a>\n";
}
/**
@@ -141,7 +142,8 @@ class ormDocument
public function GetDownloadLink($sClass, $Id, $sAttCode)
{
$sUrl = $this->GetDownloadURL($sClass, $Id, $sAttCode);
return "<a href=\"$sUrl\">".htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8')."</a>\n";
return "<a href=\"$sUrl\">".utils::EscapeHtml($this->GetFileName())."</a>\n";
}
/**
@@ -194,7 +196,6 @@ class ormDocument
* @param string $sContentDisposition Either 'inline' or 'attachment'
* @param string $sSecretField The attcode of the field containing a "secret" to be provided in order to retrieve the file
* @param string $sSecretValue The value of the secret to be compared with the value of the attribute $sSecretField
* @return none
*/
public static function DownloadDocument(WebPage $oPage, $sClass, $id, $sAttCode, $sContentDisposition = 'attachment', $sSecretField = null, $sSecretValue = null)
{
@@ -213,6 +214,12 @@ class ormDocument
$oDocument = $oObj->Get($sAttCode);
if (is_object($oDocument))
{
$aEventData = array(
'debug_info' => $oDocument->GetFileName(),
'object' => $oObj,
'document' => $oDocument,
);
EventService::FireEvent(new EventData(EVENT_SERVICE_DOWNLOAD_DOCUMENT, $sClass, $aEventData));
$oPage->TrashUnexpectedOutput();
$oPage->SetContentType($oDocument->GetMimeType());
$oPage->SetContentDisposition($sContentDisposition,$oDocument->GetFileName());

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