Compare commits

...

281 Commits

Author SHA1 Message Date
Benjamin Dalsass
74e87ed8e3 N°6218 - 1:n & n:n - Read mode: No refresh of tab count on Add/Remove in pop-up 2023-11-14 08:59:46 +01:00
Pierre Goiffon
f5a0b4bf8e Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-13 16:27:34 +01:00
Pierre Goiffon
a2b9583379 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-11-13 16:25:53 +01:00
Pierre Goiffon
77409eed99 🎨 DBObject small phpdoc fixes 2023-11-13 16:25:37 +01:00
Molkobain
eb7c971091 N°6948 - Upgrade "symfony/twig-bridge" to v5.4.31 2023-11-13 15:17:52 +01:00
Eric Espie
4d5a704d9a N°6062 - Fix unit tests 2023-11-13 15:14:29 +01:00
Eric Espie
b758113752 N°6436 - Integrate Performance Audit pre requisite (log events for extensions) 2023-11-13 15:02:54 +01:00
Stephen Abello
5465287089 Merge branch 'support/3.0' into support/3.1 2023-11-13 11:21:21 +01:00
Stephen Abello
09be84f69d N°6908 - Security hardening 2023-11-13 11:19:02 +01:00
Romain Quetiez
3b987f97eb Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-10 16:15:08 +01:00
Romain Quetiez
d9bdcfeae3 N°6658 - Fix regression: do not reset current user's profile cache 2023-11-10 15:57:08 +01:00
Pierre Goiffon
8df7c22464 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-10 15:21:10 +01:00
Pierre Goiffon
d725ba3d84 N°6765 Avoid behat scenario loading issues on portal modal (#569)
- New CombodoJsActivity API
- Replace existing calls in NiceWebPage (ready scripts)
- Add calls in ready block in portal object create template (used in both create and edit)
2023-11-10 15:10:37 +01:00
Molkobain
2d8ecd465b N°6903 - Fix crash when emptying file attribute (eg. picture of a contact) 2023-11-09 18:17:35 +01:00
Molkobain
34ba4fa0ce N°6917 - Security hardening 2023-11-09 16:43:37 +01:00
Molkobain
08d22219f4 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-11-08 14:59:56 +01:00
Molkobain
8a3d81c430 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-11-08 14:59:06 +01:00
Anne-Catherine
83a70daf68 N°6887 - Fix excessive OQL requests to display user's grant matrix (#564)
* N°6887 - Fix excessive OQL requests to display user's grant matrix

* N°6887 - Rename variable and add PHPDoc

---------

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

* N°6849 - PR advice to avoid modifying aDeps
2023-10-31 11:07:29 +01:00
Benjamin Dalsass
1385a3dc03 N°6774 - Risque d'erreur de chargement des relations dans le portail en visualisation 2023-10-31 10:18:21 +01:00
Benjamin Dalsass
789f0c826b N°6557 - PHP 8.1 : Deprecated Constant when adding a contact to ticket on ticket creation 2023-10-31 09:34:00 +01:00
Molkobain
ac070b0cbe Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-10-30 16:58:43 +01:00
Molkobain
e21dc4d21c Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-10-30 16:55:45 +01:00
Molkobain
a49a4e6c2b N°6886 - Add OAuth tests folder to removable directories list 2023-10-30 16:54:28 +01:00
Stephen Abello
95aa4afe75 N°6385 - Allow to disable LinkedSet (1:n & n:n) edition by XML in host edition 2023-10-30 16:29:25 +01:00
bdalsass
74004fa375 N°6651 - 3.1 bulk modify n:n like Tagset: checkbox ignored (#552)
- handle selectize enable/disable to update operation hidden input state
- don't execute elements click callback when input disabled
- merge current and initial values for the initial value
2023-10-30 13:57:41 +01:00
vdumas
d2fc87c6f9 N°6884 - Error on creating User from a Person edit screen 2023-10-30 12:22:07 +01:00
Romain Quetiez
5264a1f10d New test related to the build of SQL queries 2023-10-27 16:01:57 +02:00
Romain Quetiez
f0199a4cf2 Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-27 15:50:17 +02:00
Romain Quetiez
f8877ef3e7 Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-27 15:49:58 +02:00
Romain Quetiez
00b1156526 Optimize tests execution time (force cache usage and set the modification date in the past instead of waiting 1s) 2023-10-27 15:45:28 +02:00
Romain Quetiez
39d2ba8d1b Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-27 15:43:11 +02:00
Romain Quetiez
322adcb180 Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-27 14:27:00 +02:00
Romain Quetiez
77e7685c90 Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-27 14:24:31 +02:00
Romain Quetiez
3c14e5e032 Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-27 14:24:16 +02:00
Romain Quetiez
f58ec7e38e Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-27 14:22:20 +02:00
Romain Quetiez
73fd0b06b2 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/integration-tests/DictionariesConsistencyTest.php
#	tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php
#	tests/php-unit-tests/unitary-tests/core/CMDBSource/TransactionsTest.php
#	tests/php-unit-tests/unitary-tests/sources/Application/TwigBase/Twig/TwigTest.php
2023-10-27 14:12:06 +02:00
Romain Quetiez
8fa9336568 Fix regression introduced with the optimization done in 15148f7, and seen only in the context of the CI 2023-10-27 11:21:56 +02:00
Romain Quetiez
15148f7d1d Fix regression introduced with the optimization done in 798cd10, and seen only if APC is enabled 2023-10-27 10:43:13 +02:00
Romain Quetiez
7e8589ba95 Fix regression introduced with the optimization done in d641504. Cope with the fact that sometimes the admin account already exists, sometimes not. 2023-10-27 09:21:36 +02:00
Romain Quetiez
fba668207f Optimize tests execution time (test rework and defensive cleanup) 2023-10-26 21:35:52 +02:00
Romain Quetiez
798cd10d6b Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-26 21:23:47 +02:00
Romain Quetiez
442721bcb5 Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-26 21:22:54 +02:00
Romain Quetiez
1a9049d277 Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-26 21:16:24 +02:00
Romain Quetiez
c0931af91a Optimize tests execution time (no need for process isolation as long as we leave the premises clean, set file modification date instead of waiting for 1 second) 2023-10-26 21:15:57 +02:00
Romain Quetiez
29e9a06dc1 Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-26 21:10:47 +02:00
Romain Quetiez
d6415042ae Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-26 21:10:07 +02:00
Romain Quetiez
90006667fe Optimize tests execution time (copy fixture files only when necessary) 2023-10-26 20:58:26 +02:00
Romain Quetiez
fd351df08b Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-10-26 20:53:24 +02:00
Romain Quetiez
7419749ba6 Prerequisites for boosting tests 2023-10-26 20:51:28 +02:00
Romain Quetiez
73bed04555 Twig tests not executed 2023-10-26 10:47:11 +02:00
Romain Quetiez
8893cdac1d Optimize tests execution time (no need for process isolation as long as we leave the premises clean) 2023-10-26 10:44:37 +02:00
Romain Quetiez
7f245a15be Optimize tests execution time (suppress meaningless test and merge two test in one, while preserving test coverage) 2023-10-25 23:01:05 +02:00
Romain Quetiez
b5c46ccd4a Optimize tests execution time (x10 / no need for a systematic check of date formats, which was ok as a first approach) 2023-10-25 22:59:03 +02:00
Romain Quetiez
7fbc211c43 Optimize tests execution time (x50 / eval is way faster than exec) 2023-10-25 22:57:03 +02:00
Romain Quetiez
cf774cdb90 Explain why process isolation is a must 2023-10-25 22:18:05 +02:00
Romain Quetiez
722a58491c Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php
2023-10-25 22:08:08 +02:00
Romain Quetiez
037dfe1df6 Optimize tests execution time 2023-10-25 17:51:12 +02:00
Romain Quetiez
0b26d45014 Prerequisites for boosting tests 2023-10-25 17:50:41 +02:00
Eric Espie
44e826543d N°6870 - Community re-integration support 2023-10-25 14:30:16 +02:00
Eric Espie
50f5ab6be4 Fix ModelFactory tests 2023-10-25 14:03:26 +02:00
Eric Espie
941412a365 Community integration: changed default behaviour for datamodel XML nodes without _delta from 'must_exist' to 'merge' 2023-10-25 10:05:46 +02:00
Stephen Abello
a9bd62dc43 N°6385 - Allow to disable LinkedSet (1:n & n:n) edition by XML
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-10-24 11:09:12 +02:00
Stephen Abello
4f336abeb8 N°6861 - Display warning message when creating/editing a mandatory blob in modal 2023-10-23 15:51:38 +02:00
Molkobain
40d1ae0b1c Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-10-23 15:17:43 +02:00
Molkobain
b9c566238a Merge remote-tracking branch 'origin/support/2.7' into support/3.0
Remove PHPUnit annotations as from support/3.0 and newer they are no longer necessary
2023-10-23 15:09:49 +02:00
Molkobain
4fd8177165 N°3715 - Fix unit tests 2023-10-23 14:55:06 +02:00
Anne-Cath
c597c34e5c Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-10-20 17:06:11 +02:00
Anne-Cath
0cc0f39d9e Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-10-20 17:04:26 +02:00
Anne-Catherine
a2cdf214f0 N°3715 - Export above 1000 entries ignore obsolete data from user preference (#468)
* N°3715 - Export above 10000 entries ignore obsolete data from user preference
2023-10-20 17:02:16 +02:00
Anne-Cath
189fd1d9e3 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-10-20 16:55:41 +02:00
Anne-Cath
4f75d012e5 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-10-20 16:54:37 +02:00
Anne-Catherine
013173019f N°2909 - Search on Enum, Date, TagSet,... with index fails (#496) 2023-10-20 16:45:35 +02:00
Anne-Catherine
ed8d4df5ef N°5786 - Problem with text color in public log and in AttributeHTML (#527)
* N°5786 - Problem with text color in public log and in AttributeHTML
2023-10-17 12:09:52 +02:00
Pierre Goiffon
31a1370028 Merge branch 'support/3.1.0' into support/3.1
# Conflicts:
#	core/DbConnectionWrapper.php
#	tests/php-unit-tests/unitary-tests/core/CMDBSource/TransactionsTest.php
2023-10-17 11:13:29 +02:00
Stephen Abello
aa618468d1 Merge branch 'support/3.0' into support/3.1 2023-10-17 09:23:06 +02:00
Stephen Abello
fadfd94bac Merge branch 'support/2.7' into support/3.0 2023-10-17 09:19:16 +02:00
Stephen Abello
9469681a0c N°6777 - Security hardening 2023-10-17 09:12:40 +02:00
Pierre Goiffon
d4ab55dd9a Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	pages/ajax.render.php
2023-10-13 17:33:26 +02:00
Pierre Goiffon
da27ddba82 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	application/utils.inc.php
#	pages/ajax.render.php
2023-10-13 17:27:03 +02:00
Pierre Goiffon
c72cb7e70e N°6606 security hardening 2023-10-13 17:15:37 +02:00
Pierre Goiffon
9df92665e0 N°6606 Backport of utils::ENUM_SANITIZATION_FILTER_* constants
Were introduced in 3.0.0, but not added to the support/2.7 branch
2023-10-13 17:10:35 +02:00
Molkobain
819baa3951 Merge remote-tracking branch 'origin/support/3.1.0' into support/3.1 2023-10-06 17:47:52 +02:00
Eric Espie
c78024394e Better log of twig syntax errors 2023-10-06 15:25:27 +02:00
Eric Espie
4267f2b855 N°6747 - Customizing UserLDAP, generates presentation error messages in Designer during MTP 2023-10-06 09:53:26 +02:00
vdumas
ba8f18e1d4 N°6815 - DataModel : wrong attribute type for SLA.customercontracts_list 2023-10-05 18:08:46 +02:00
vdumas
ba13d24206 N°6814 - Datamodel remove lnkConnectableCIToNetworkDevice uniqueness rule 2023-10-05 18:08:46 +02:00
Stephen Abello
ab3a4a2468 Merge branch 'support/3.0' into support/3.1
# Conflicts:
#	pages/ajax.render.php
2023-10-02 15:16:18 +02:00
Stephen Abello
3647291475 N°6778 - Security hardening 2023-10-02 15:06:17 +02:00
Molkobain
a472d83e3d Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	application/wizardhelper.class.inc.php
#	pages/ajax.render.php
2023-09-26 22:21:48 +02:00
Molkobain
6dc6392fab Merge remote-tracking branch 'origin/support/3.0.3' into support/3.0 2023-09-26 22:20:02 +02:00
Anne-Catherine
e793b02f8b N°6766 - Fix dependent fields not updated due to WizardHelper.UpdateFields() being triggered too early (#548)
* N°6766 - Javascript : function WizardHelper.UpdateFields triggered to early does not update fields

* N°6766 - Code review

---------

Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
2023-09-26 12:25:56 +02:00
Pierre Goiffon
c5cb84f976 N°6733 Fix some attributes impl not prompted in transitions when mandatory 2023-09-26 11:32:06 +02:00
Molkobain
12c0edc530 N°6547 - Fix flags init being an hard-coded int instead of the corresponding constant 2023-09-21 18:25:44 +02:00
Stephen Abello
61565b25a3 N°6767 - Error in ajax request when there's dict to load and no onready scripts 2023-09-21 10:40:22 +02:00
Pierre Goiffon
7619d055dd N°6695 Handle multiline dict entries in portal tooltips (#542)
Report Tippy fix from admin console (/css/backoffice/vendors/_tippy.scss) to user portal.
Thanks DaveHeart  for the bug report !
2023-09-20 18:10:56 +02:00
Molkobain
f01997f2ad Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-09-20 16:16:53 +02:00
Molkobain
fc6e98b534 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-09-20 16:12:51 +02:00
Molkobain
8ecebee511 PHP unit tests: Fix typo for "final private" methods as they can't be both 2023-09-20 16:11:39 +02:00
Eric Espie
b5e26061e1 Merge branch 'issue/6667_trigger_on_state_enter' into support/3.1 2023-09-20 10:11:58 +02:00
Pierre Goiffon
14fa20b428 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	tests/ci_description.ini
2023-09-19 12:30:16 +02:00
Pierre Goiffon
2690fa3315 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-09-19 12:27:19 +02:00
Pierre Goiffon
35cd965360 N°6629 Update ci_description php_version 2023-09-19 12:25:40 +02:00
Pierre Goiffon
133fc29ad5 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-09-19 09:58:53 +02:00
Pierre Goiffon
83a5b98f82 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-09-19 09:58:40 +02:00
Pierre Goiffon
e5dd51f637 N°6600 Portal download attachment : don't display anymore SQL query on attachment not found error (#525) 2023-09-19 09:54:43 +02:00
Molkobain
2f6bcc3534 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-09-19 08:41:46 +02:00
Molkobain
4923418f58 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-09-19 08:39:41 +02:00
Molkobain
0a6c82dfe1 N°6752 - PHP unit tests: Fix typo in postbuild_integration.xml.dist 2023-09-19 08:37:46 +02:00
Molkobain
f89d843ab3 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-09-18 16:06:53 +02:00
Molkobain
2dd7f5cada Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-09-18 15:37:32 +02:00
Molkobain
24c0f4950f Add missing .htaccess / web.config files in .gitignore for /extensions folder 2023-09-18 15:26:06 +02:00
Molkobain
d4dbbc59d4 N°6754 - PHP unit tests: Add local PHPUnit XML files to .gitignore 2023-09-18 15:23:52 +02:00
Molkobain
dc0cd44c79 N°6752 - PHP unit tests: Migrate usages of unitestautoload.php to composer autoloader 2023-09-18 15:14:44 +02:00
Pierre Goiffon
d7df249586 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	application/ajaxwebpage.class.inc.php
#	application/capturewebpage.class.inc.php
#	application/clipage.class.inc.php
#	application/csvpage.class.inc.php
#	application/errorpage.class.inc.php
#	application/itopwebpage.class.inc.php
#	application/itopwizardwebpage.class.inc.php
#	application/nicewebpage.class.inc.php
#	application/pdfpage.class.inc.php
#	application/webpage.class.inc.php
#	application/xmlpage.class.inc.php
2023-09-18 15:10:19 +02:00
Pierre Goiffon
f3c4fcb0f5 💡 Pages files : add depreciation version 2023-09-18 15:07:32 +02:00
Pierre Goiffon
9a8e9a0b01 N°5491 Fix HU iTopUpdate:Error:MissingFile dict key 2023-09-18 08:45:45 +02:00
vdumas
f6a7e6b4e1 N°3506 - allow_target_creation based on UR_ACTION_MODIFY 2023-09-15 16:40:20 +02:00
Stephen Abello
2829fb291f Update contribution stickers with 2023 design (better late than never 🙃) 2023-09-15 15:43:50 +02:00
odain
6110abfc7f Merge branch 'support/3.0' into support/3.1 2023-09-15 10:08:37 +02:00
odain
6046f44f56 Merge branch 'support/2.7' into support/3.0 2023-09-15 10:08:08 +02:00
odain
6c6131ce03 N°5491 - test enhancement to reduce false positive 2023-09-15 10:07:42 +02:00
Stephen Abello
178c922407 Merge branch 'support/3.0' into support/3.1 2023-09-15 09:57:59 +02:00
Stephen Abello
343e87a8d4 N°6581 - Security hardening 2023-09-15 09:55:51 +02:00
Stephen Abello
d7e5d6fb7f N°6734 - Fix not being able to add dashlets to a dashboard when iBackofficeDictEntriesExtension is used and its JS files are loaded through ajax calls 2023-09-14 17:25:00 +02:00
Pierre Goiffon
993606f102 Merge branch 'support/3.1.0' into support/3.1
# Conflicts:
#	setup/itopdesignformat.class.inc.php
2023-09-14 16:54:28 +02:00
Eric Espie
44c189223e Merge branch 'support/3.1.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/unitary-tests/core/DBObjectTest.php
2023-09-14 14:31:29 +02:00
odain-cbd
e76728b2bf N°5491 - Inconsistent dictionnary entries regarding arguments to pass to Dict::Format-test first (#545) 2023-09-13 12:02:49 +02:00
odain
3e258f32cc N°5491-fix redundant GetNonPublicStaticProperty 2023-09-13 10:30:56 +02:00
odain
3c51d6fb98 N°5491- fix dictionary test 2023-09-13 10:27:19 +02:00
odain
7cfe1389aa Merge branch 'support/3.0' into support/3.1 2023-09-13 10:20:38 +02:00
Stephen Abello
7292a8540b N°6547 - Disallow linkset edition when lnk attribute is readonly 2023-09-13 10:53:32 +02:00
odain
f65c690462 N°5491-fix test 2023-09-13 10:03:05 +02:00
odain
ecf8bc42fa Merge branch 'support/2.7' into support/3.0 2023-09-13 10:01:15 +02:00
vdumas
faba812fc1 N°6646 - Wrong dictionary entry for FR - Lnk Contact / Contrat 2023-09-12 12:48:33 +02:00
vdumas
add433d702 N°6706 - Missing dictionary entry for DE - Lnk Provider Contract / Service 2023-09-12 12:24:56 +02:00
vdumas
9c99cb35e5 N°6706 - Wrong dictionary entry for FR - Lnk Provider Contract / Service 2023-09-12 12:21:25 +02:00
Pierre Goiffon
9d392ad167 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-09-07 14:53:02 +02:00
Pierre Goiffon
ea8509db1f N°6709 Use ItopTestCase::RequireOnceCurrentModuleFile in GetAppRoot 2023-09-07 14:47:36 +02:00
Pierre Goiffon
df25ce76b6 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-09-07 14:43:29 +02:00
Pierre Goiffon
e946fc65fc N°6709 New ItopTestCase::RequireOnceCurrentModuleFile 2023-09-07 14:38:19 +02:00
Pierre Goiffon
d203e075a8 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-09-06 09:09:37 +02:00
Pierre Goiffon
dbe2f66539 Merge remote-tracking branch 'origin/support/2.7' into support/3.0 2023-09-06 09:07:45 +02:00
Pierre Goiffon
0d8ff7bbac N°6629 Set commit tests back to Mysql
For now we have perf issues on Jenkins with MariaDB (see N°6694)
2023-09-04 10:25:41 +02:00
Eric Espie
48eb022824 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-09-01 14:15:06 +02:00
Stephen Abello
03c9ffc033 N°6560 - Fix unescaped backtick in friendlyname breaking details page scripts 2023-09-01 09:41:23 +02:00
Eric Espie
61a9a4ac65 Fix unit tests for MariaDB 2023-09-01 09:29:21 +02:00
acognet
38962e68ee Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	pages/ajax.render.php
2023-08-31 16:22:01 +02:00
Pierre Goiffon
483dbb4a5d N°6658 Remove useless annotations
See comment for ItopTestCase::$preserveGlobalState
2023-08-31 16:06:34 +02:00
acognet
1f4dcc4f9e N°5136 - Relations: Fix "Select All objects" adding obsolete objects even if "show obsolete data" param. not activated - Merge from support/2.7 2023-08-31 16:04:03 +02:00
acognet
e86309669e Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	pages/ajax.render.php
2023-08-31 15:56:16 +02:00
Pierre Goiffon
6d6f55acf7 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/src/BaseTestCase/ItopDataTestCase.php
2023-08-31 15:40:56 +02:00
Pierre Goiffon
6ebcd44bb1 💡 N°6658 Add more comments and since tags 2023-08-31 15:34:44 +02:00
Anne-Catherine
f8fb51fea0 N°5145 - Fix attachments missing in new ticket when clone from an old ticket with object copier (#530) 2023-08-31 15:27:23 +02:00
Anne-Catherine
bf768311c2 N°5136 - "Select All objects" add obsolete objects even if the parameter show obsolete data is not activated (#467) 2023-08-31 15:13:20 +02:00
Anne-Catherine
d797436786 N°6555 - Add class description in tooltip of Dashlet badge (#504)
cheery pick from branch develop due to target branch error
2023-08-31 14:55:06 +02:00
Anne-Catherine
b508c0d983 N°6152 - Search: Criteria & object list loaded twice (#495) 2023-08-31 12:03:31 +02:00
Anne-Catherine
351893bbdd N°4494 - Fix auto-locking on log save and transition (#358) 2023-08-31 11:23:58 +02:00
vdumas
59e4bb028f N°6682 - Pbs with Audit classes and AccessRight 2023-08-29 16:41:27 +02:00
vdumas
6d895371ec N°6682 - AuditDomain XML meta declaration missing 2023-08-29 16:41:27 +02:00
Stephen Abello
ab91631e68 N°6677 - Ensure emails in test are never sent to cc'd and bcc'd addresses 2023-08-29 15:56:52 +02:00
Eric Espie
cc4af0a027 N°6667 - Ignore trigger on state entering with auto-dispatch 2023-08-22 14:30:18 +02:00
Eric Espie
3366bae0ab N°6061 - Add tests on Expression evaluation 2023-08-18 15:34:06 +02:00
Romain Quetiez
03b484c349 Tests: fix test not working on MariaDB (unexpected coma tolerated by MySQL) 2023-08-18 12:13:11 +02:00
Molkobain
70081ecf33 N°6436 - Add unit test for API introduced in 3.1 (\iFieldRendererMappingsExtension) 2023-08-18 10:31:05 +02:00
Molkobain
575ba1cd7b Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	core/metamodel.class.php
2023-08-18 10:24:50 +02:00
Molkobain
d130959692 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	core/metamodel.class.php
2023-08-18 10:14:51 +02:00
Molkobain
a8c689c6c0 N°6436 - Add unit test to ensure that we don't lose an API during merge between branches 2023-08-18 09:55:45 +02:00
Molkobain
1990ccb5d8 N°6436 - Move interfaces enumeration from 1 line to 1 line / interface (and re-ordered them) for easier merges in newer branches 2023-08-18 09:52:55 +02:00
Molkobain
e107be56e4 N°6097 - Tests: Fix missing hook entry in PHPUnit XML file that led to compiled environment being re-build for each test case 2023-08-18 09:51:15 +02:00
Romain Quetiez
0f8e87e001 Tests: allow execution of RouterTest alone, fix tool to execute each test class separately 2023-08-18 08:44:54 +02:00
Molkobain
d92d2b5e9e Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	core/metamodel.class.php
2023-08-17 21:36:19 +02:00
Romain Quetiez
ebd0136773 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php
2023-08-17 18:36:34 +02:00
Molkobain
f6653e1594 N°6436 - Restore 3.0 APIs lost during 6433678d merge 2023-08-17 17:47:46 +02:00
Romain Quetiez
65bb76b9e3 N°6658 - Boost PHPUnit tests execution 2023-08-17 17:27:55 +02:00
Molkobain
f238593966 Merge remote-tracking branch 'origin/support/3.0' into support/3.1
# Conflicts:
#	tests/php-unit-tests/src/BaseTestCase/ItopDataTestCase.php
#	tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php
2023-08-11 09:19:49 +02:00
Molkobain
d951d3b872 💚 Fix typo in extended class name 2023-08-11 09:05:30 +02:00
Molkobain
ccceb870e3 Merge remote-tracking branch 'origin/support/2.7' into support/3.0
# Conflicts:
#	tests/php-unit-tests/src/BaseTestCase/ItopDataTestCase.php
#	tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php
2023-08-10 15:53:05 +02:00
Molkobain
ed6df77cbb N°6097 - Tests: Optimize performances by creating custom env. only once and re-using it across test classes 2023-08-10 15:45:39 +02:00
Molkobain
1ad28312ec N°6097 - Tests: Introduce autoloader for "utility" classes and move them to a sub-folder for better organization as folder was still messy
Note that unittestautoload.php is now useless. We just keep for now until everything is migrated (projects / branches / modules)
2023-08-10 15:45:39 +02:00
Molkobain
f002aa04cd N°6097 - Tests: Enable PHP unit tests on a custom DataModel 2023-08-10 15:45:39 +02:00
Molkobain
b86d70623e N°6097 - Tests: Temporarily add test case for the new ItopCustomDatamodelTestCase class 2023-08-10 15:45:39 +02:00
Molkobain
fe3467309d N°6097 - Tests: Refactor base test classes for better extensibility 2023-08-10 15:45:39 +02:00
Molkobain
851ab9c356 N°6097 - Add \utils::GetDataPath() method to avoid duplicating manual path build 2023-08-10 15:45:39 +02:00
Molkobain
aef3c2e609 N°6097 - Fix \CMDBSource::DropDB() not resetting cache like \CMDBSource::DropTable() which can lead to errors when trying to re-create it afterwards 2023-08-10 15:45:39 +02:00
Pierre Goiffon
5212e15cc4 Merge remote-tracking branch 'origin/support/3.0' into support/3.1 2023-08-10 14:41:05 +02:00
Pierre Goiffon
f04fc546b5 N°6643 Fix TypeError in \CMDBSource::LogDeadLock 2023-08-10 14:34:09 +02:00
Lars Kaltefleiter
caf3076b12 N°3441 - Portal: Fix failure to open an object containing a link to an archived object (#523)
* N°3441 - Portal : cannot open an object containing a link to an archived object

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

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

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

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

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

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

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

---------

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

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

(cherry picked from commit 4c9ea0c9d4)

# Conflicts:
#	tests/php-unit-tests/integration-tests/DictionariesConsistencyTest.php
2023-07-06 15:45:09 +02:00
Stephen Abello
aa1834170b N°6427 - Fix SwiftMailer not retrieving sendmail path 2023-07-06 14:31:54 +02:00
Stephen Abello
f94d67ab35 N°6340 - Fix permission refused when sending an email and renewing OAuth token in synchronous mode 2023-07-06 10:28:10 +02:00
Stephen Abello
3048c8c41f N°5560 - Display an error when trying to regenerate an expired OAuth token 2023-07-06 09:52:00 +02:00
Stephen Abello
246e4a9f50 N°6123 - Fix warnings when launching a backup on MariaDB > v10.6.1 with localhost dbhost 2023-07-06 09:28:01 +02:00
odain
0001e8ffc4 💚 use new ci validation 2020-10-09 10:13:51 +02:00
289 changed files with 6592 additions and 2802 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

View File

@@ -62,6 +62,12 @@ gitGraph
commit id: "2022-12-28" tag: "2.7.8" commit id: "2022-12-28" tag: "2.7.8"
checkout support/3.0 checkout support/3.0
commit id: "2023-04-12" tag: "3.0.3" commit id: "2023-04-12" tag: "3.0.3"
checkout develop
commit id: "2023-06-19" tag: "3.1.0-beta" type: REVERSE
commit id: "2023-07-26" tag: "3.1.0-1" type: HIGHLIGHT
branch support/3.1 order: 840
checkout support/3.1
commit id: "2023-08-09" tag: "3.1.0-2"
``` ```
To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start). To learn more, check the [iTop community versions history on the official wiki](https://www.itophub.io/wiki/page?id=latest:release:start).

6
.gitignore vendored
View File

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

View File

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

View File

@@ -446,6 +446,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
UR_ACTION_BULK_DELETE => 'bd', 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 // Installation: create the very first user
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US') public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US')
{ {
@@ -502,6 +508,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
} }
protected $m_aUserOrgs = array(); // userid -> array of orgid 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) // Built on demand, could be optimized if necessary (doing a query for each attribute that needs to be read)
protected $m_aObjectActionGrants = array(); protected $m_aObjectActionGrants = array();
@@ -558,6 +565,7 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Cache // Cache
$this->m_aObjectActionGrants = array(); $this->m_aObjectActionGrants = array();
$this->m_aAdministrators = null;
} }
public function LoadCache() public function LoadCache()
@@ -700,12 +708,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
*/ */
private function GetAdministrators() private function GetAdministrators()
{ {
static $aAdministrators = null; if ($this->m_aAdministrators === null)
if ($aAdministrators === null)
{ {
// Find all administrators // Find all administrators
$aAdministrators = array(); $this->m_aAdministrators = array();
$oAdministratorsFilter = new DBObjectSearch('User'); $oAdministratorsFilter = new DBObjectSearch('User');
$oLnkFilter = new DBObjectSearch('URP_UserProfile'); $oLnkFilter = new DBObjectSearch('URP_UserProfile');
$oExpression = new FieldExpression('profileid', 'URP_UserProfile'); $oExpression = new FieldExpression('profileid', 'URP_UserProfile');
@@ -718,10 +724,10 @@ class UserRightsProfile extends UserRightsAddOnAPI
$oSet->OptimizeColumnLoad(array('User' => array('login'))); $oSet->OptimizeColumnLoad(array('User' => array('login')));
while($oUser = $oSet->Fetch()) while($oUser = $oSet->Fetch())
{ {
$aAdministrators[] = $oUser->GetKey(); $this->m_aAdministrators[] = $oUser->GetKey();
} }
} }
return $aAdministrators; return $this->m_aAdministrators;
} }
/** /**
@@ -758,8 +764,12 @@ class UserRightsProfile extends UserRightsAddOnAPI
$sAction = self::$m_aActionCodes[$iActionCode]; $sAction = self::$m_aActionCodes[$iActionCode];
$bStatus = null; $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 // Call the API of UserRights because it caches the list for us
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile) foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
{ {
$bGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction); $bGrant = $this->GetProfileActionGrant($iProfile, $sClass, $sAction);
if (!is_null($bGrant)) if (!is_null($bGrant))
@@ -885,11 +895,16 @@ class UserRightsProfile extends UserRightsAddOnAPI
// Note: this code is VERY close to the code of IsActionAllowed() // Note: this code is VERY close to the code of IsActionAllowed()
$iUser = $oUser->GetKey(); $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 // 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 // and acceptable to consider only the root class of the object set
$bStatus = null; $bStatus = null;
// Call the API of UserRights because it caches the list for us // Call the API of UserRights because it caches the list for us
foreach(UserRights::ListProfiles($oUser) as $iProfile => $oProfile) foreach($this->aUsersProfilesList[$iUser] as $iProfile => $oProfile)
{ {
$bGrant = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode); $bGrant = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode);
if (!is_null($bGrant)) if (!is_null($bGrant))

View File

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

View File

@@ -335,7 +335,6 @@ abstract class AbstractPreferencesExtension implements iPreferencesExtension
* A recommended pattern is to cache data by the mean of static members. * A recommended pattern is to cache data by the mean of static members.
* *
* @api * @api
* @deprecated 3.1.0 N°4756 use the new event service instead, see {@see DBObject::FireEvent()} method
* @package UIExtensibilityAPI * @package UIExtensibilityAPI
*/ */
interface iApplicationUIExtension interface iApplicationUIExtension
@@ -487,7 +486,6 @@ interface iApplicationUIExtension
* @api * @api
* @package UIExtensibilityAPI * @package UIExtensibilityAPI
* @since 2.7.0 * @since 2.7.0
* @deprecated
*/ */
abstract class AbstractApplicationUIExtension implements iApplicationUIExtension abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
{ {
@@ -560,6 +558,7 @@ abstract class AbstractApplicationUIExtension implements iApplicationUIExtension
* or through the GUI. * or through the GUI.
* *
* @api * @api
* @deprecated 3.1.0 N°4756 use the new event service instead, see {@see DBObject::FireEvent()} method. More details on each method PHPDoc.
* @package ORMExtensibilityAPI * @package ORMExtensibilityAPI
*/ */
interface iApplicationObjectExtension interface iApplicationObjectExtension
@@ -574,6 +573,7 @@ interface iApplicationObjectExtension
* Otherwise, the answer is definitively "yes, the object has changed". * Otherwise, the answer is definitively "yes, the object has changed".
* *
* @api * @api
* @deprecated 3.1.0 N°4756 No alternative available, this API was unstable and is abandoned
* @param \cmdbAbstractObject $oObject The target object * @param \cmdbAbstractObject $oObject The target object
* *
* @return boolean True if something has changed for the target object * @return boolean True if something has changed for the target object
@@ -587,6 +587,7 @@ interface iApplicationObjectExtension
* Anyhow, this API can be called in other contexts such as the CSV import tool. * Anyhow, this API can be called in other contexts such as the CSV import tool.
* *
* @api * @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_CHECK_TO_WRITE event instead
* @param \cmdbAbstractObject $oObject The target object * @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. * @return string[] A list of errors message. An error message is made of one line and it can be displayed to the end-user.
@@ -601,6 +602,7 @@ interface iApplicationObjectExtension
* Please not that it is not possible to cascade deletion by this mean: only stopper issues can be handled. * Please not that it is not possible to cascade deletion by this mean: only stopper issues can be handled.
* *
* @api * @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_CHECK_TO_DELETE event instead
* @param \cmdbAbstractObject $oObject The target object * @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. * @return string[] A list of errors message. An error message is made of one line and it can be displayed to the end-user.
@@ -617,6 +619,7 @@ interface iApplicationObjectExtension
* * {@see DBObject::Get()} : for a given attribute the new value that was persisted * * {@see DBObject::Get()} : for a given attribute the new value that was persisted
* *
* @api * @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_AFTER_WRITE event instead
* @param \cmdbAbstractObject $oObject The target object * @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 * @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 * once for all the changes made within the current page
@@ -633,6 +636,7 @@ interface iApplicationObjectExtension
* The method is called right <b>after</b> the object has been written to the database. * The method is called right <b>after</b> the object has been written to the database.
* *
* @api * @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_AFTER_WRITE event instead
* @param \cmdbAbstractObject $oObject The target object * @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 * @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 * once for all the changes made within the current page
@@ -647,6 +651,7 @@ interface iApplicationObjectExtension
* The method is called right <b>before</b> the object will be deleted from the database. * The method is called right <b>before</b> the object will be deleted from the database.
* *
* @api * @api
* @deprecated 3.1.0 N°4756 Use EVENT_DB_AFTER_DELETE event instead
* @param \cmdbAbstractObject $oObject The target object * @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 * @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 * once for all the changes made within the current page
@@ -660,6 +665,7 @@ interface iApplicationObjectExtension
* Extend this class instead of iApplicationObjectExtension if you don't need to overload all methods * Extend this class instead of iApplicationObjectExtension if you don't need to overload all methods
* *
* @api * @api
* @deprecated 3.1.0 N°4756 use the new event service instead, see {@see DBObject::FireEvent()} method
* @package ORMExtensibilityAPI * @package ORMExtensibilityAPI
* @since 2.7.0 * @since 2.7.0
*/ */
@@ -2246,3 +2252,27 @@ interface iModuleExtension
*/ */
public function __construct(); public function __construct();
} }
/**
* KPI logging extensibility point
*
* KPI Logger extension
*/
interface iKPILoggerExtension
{
/**
* Init the statistics collected
*
* @return void
*/
public function InitStats();
/**
* Add a new KPI to the stats
*
* @param \Combodo\iTop\Core\Kpi\KpiLogData $oKpiLogData
*
* @return mixed
*/
public function LogOperation($oKpiLogData);
}

View File

@@ -34,15 +34,15 @@ class AuditCategory extends cmdbAbstractObject
{ {
$aParams = array $aParams = array
( (
"category" => "application, grant_by_profile", "category" => "application,grant_by_profile",
"key_type" => "autoincrement", "key_type" => "autoincrement",
"name_attcode" => "name", "name_attcode" => "name",
"state_attcode" => "", "state_attcode" => "",
"reconc_keys" => array('name'), "reconc_keys" => array('name'),
"db_table" => "priv_auditcategory", "db_table" => "priv_auditcategory",
"db_key_field" => "id", "db_key_field" => "id",
"db_finalclass_field" => "", "db_finalclass_field" => "",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-folder.svg'), 'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-audit-folder.svg'),
); );
MetaModel::Init_Params($aParams); MetaModel::Init_Params($aParams);
MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("name", array("description"=>"Short name for this category", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));

View File

@@ -35,7 +35,7 @@ class AuditDomain extends cmdbAbstractObject
{ {
$aParams = array $aParams = array
( (
"category" => "application, grant_by_profile", "category" => "application,grant_by_profile",
"key_type" => "autoincrement", "key_type" => "autoincrement",
"name_attcode" => "name", "name_attcode" => "name",
"complementary_name_attcode" => array('description'), "complementary_name_attcode" => array('description'),

View File

@@ -35,7 +35,7 @@ class AuditRule extends cmdbAbstractObject
{ {
$aParams = array $aParams = array
( (
"category" => "application, grant_by_profile", "category" => "application,grant_by_profile",
"key_type" => "autoincrement", "key_type" => "autoincrement",
"name_attcode" => "name", "name_attcode" => "name",
"state_attcode" => "", "state_attcode" => "",

View File

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

View File

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

View File

@@ -744,7 +744,13 @@ HTML
$oPage->SetCurrentTab($sTabCode, $oAttDef->GetLabel().$sCount, $sTabDescription); $oPage->SetCurrentTab($sTabCode, $oAttDef->GetLabel().$sCount, $sTabDescription);
$aArgs = array('this' => $this); $aArgs = array('this' => $this);
$bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE));
$sEditWhen = $oAttDef->GetEditWhen();
// Calculate if edit_when allows to edit based on current $bEditMode
$bIsEditableBasedOnEditWhen = ($sEditWhen === LINKSET_EDITWHEN_ALWAYS) ||
($bEditMode ? $sEditWhen === LINKSET_EDITWHEN_ON_HOST_EDITION : $sEditWhen === LINKSET_EDITWHEN_ON_HOST_DISPLAY);
$bReadOnly = ($iFlags & (OPT_ATT_READONLY | OPT_ATT_SLAVE)) || !$bIsEditableBasedOnEditWhen;
if ($bEditMode && (!$bReadOnly)) { if ($bEditMode && (!$bReadOnly)) {
$sInputId = $this->m_iFormId.'_'.$sAttCode; $sInputId = $this->m_iFormId.'_'.$sAttCode;
$sDisplayValue = ''; // not used $sDisplayValue = ''; // not used
@@ -754,9 +760,9 @@ HTML
$oPage->add($sHTMLValue); $oPage->add($sHTMLValue);
} else { } else {
if ($oAttDef->IsIndirect()) { if ($oAttDef->IsIndirect()) {
$oBlockLinkSetViewTable = new BlockIndirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef); $oBlockLinkSetViewTable = new BlockIndirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef, $bReadOnly);
} else { } else {
$oBlockLinkSetViewTable = new BlockDirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef); $oBlockLinkSetViewTable = new BlockDirectLinkSetViewTable($oPage, $this, $sClass, $sAttCode, $oAttDef, $bReadOnly);
} }
$oPage->AddUiBlock($oBlockLinkSetViewTable); $oPage->AddUiBlock($oBlockLinkSetViewTable);
} }
@@ -1166,7 +1172,7 @@ HTML
/** /**
* @param \WebPage $oPage * @param \WebPage $oPage
* @param \CMDBObjectSet $oSet * @param \CMDBObjectSet $oSet
* @param array $aExtraParams * @param array $aExtraParams See possible values in {@see DataTableUIBlockFactory::RenderDataTable()}
* *
* @throws \ApplicationException * @throws \ApplicationException
* @throws \CoreException * @throws \CoreException
@@ -4547,7 +4553,9 @@ HTML;
foreach (MetaModel::EnumPlugins(iApplicationObjectExtension::class) as $oExtensionInstance) { foreach (MetaModel::EnumPlugins(iApplicationObjectExtension::class) as $oExtensionInstance) {
$sExtensionClass = get_class($oExtensionInstance); $sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBInsert()"); $this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBInsert()");
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBInsert($this, self::GetCurrentChange()); $oExtensionInstance->OnDBInsert($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
} }
} }
@@ -4562,13 +4570,16 @@ HTML;
protected function DBCloneTracked_Internal($newKey = null) protected function DBCloneTracked_Internal($newKey = null)
{ {
$oNewObj = parent::DBCloneTracked_Internal($newKey); /** @var cmdbAbstractObject $oNewObj */
$oNewObj = MetaModel::GetObject(get_class($this), parent::DBCloneTracked_Internal($newKey));
// Invoke extensions after insertion (the object must exist, have an id, etc.) // Invoke extensions after insertion (the object must exist, have an id, etc.)
/** @var \iApplicationObjectExtension $oExtensionInstance */ /** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{ {
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBInsert($oNewObj, self::GetCurrentChange()); $oExtensionInstance->OnDBInsert($oNewObj, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBInsert');
} }
return $oNewObj; return $oNewObj;
@@ -4605,7 +4616,9 @@ HTML;
foreach (MetaModel::EnumPlugins(iApplicationObjectExtension::class) as $oExtensionInstance) { foreach (MetaModel::EnumPlugins(iApplicationObjectExtension::class) as $oExtensionInstance) {
$sExtensionClass = get_class($oExtensionInstance); $sExtensionClass = get_class($oExtensionInstance);
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBUpdate()"); $this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnDBUpdate()");
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange()); $oExtensionInstance->OnDBUpdate($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBUpdate');
} }
} }
@@ -4649,7 +4662,9 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */ /** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{ {
$oKPI = new ExecutionKPI();
$oExtensionInstance->OnDBDelete($this, self::GetCurrentChange()); $oExtensionInstance->OnDBDelete($this, self::GetCurrentChange());
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnDBDelete');
} }
return parent::DBDeleteTracked_Internal($oDeletionPlan); return parent::DBDeleteTracked_Internal($oDeletionPlan);
@@ -4668,7 +4683,10 @@ HTML;
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{ {
$sExtensionClass = get_class($oExtensionInstance); $sExtensionClass = get_class($oExtensionInstance);
if ($oExtensionInstance->OnIsModified($this)) { $oKPI = new ExecutionKPI();
$bIsModified = $oExtensionInstance->OnIsModified($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnIsModified');
if ($bIsModified) {
$this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified() -> true"); $this->LogCRUDDebug(__METHOD__, "Calling $sExtensionClass::OnIsModified() -> true");
return true; return true;
} else { } else {
@@ -4724,7 +4742,9 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */ /** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{ {
$oKPI = new ExecutionKPI();
$aNewIssues = $oExtensionInstance->OnCheckToWrite($this); $aNewIssues = $oExtensionInstance->OnCheckToWrite($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToWrite');
if (is_array($aNewIssues) && (count($aNewIssues) > 0)) // Some extensions return null instead of an empty array 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); $this->m_aCheckIssues = array_merge($this->m_aCheckIssues, $aNewIssues);
@@ -4772,7 +4792,9 @@ HTML;
/** @var \iApplicationObjectExtension $oExtensionInstance */ /** @var \iApplicationObjectExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance) foreach(MetaModel::EnumPlugins('iApplicationObjectExtension') as $oExtensionInstance)
{ {
$oKPI = new ExecutionKPI();
$aNewIssues = $oExtensionInstance->OnCheckToDelete($this); $aNewIssues = $oExtensionInstance->OnCheckToDelete($this);
$oKPI->ComputeStatsForExtension($oExtensionInstance, 'OnCheckToDelete');
if (is_array($aNewIssues) && count($aNewIssues) > 0) if (is_array($aNewIssues) && count($aNewIssues) > 0)
{ {
$this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues); $this->m_aDeleteIssues = array_merge($this->m_aDeleteIssues, $aNewIssues);

View File

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

View File

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

View File

@@ -1246,6 +1246,10 @@ JS
} else { } else {
$oBlock = DashletFactory::MakeForDashletBadge($sClassIconUrl, $sHyperlink, $iCount, $sClassLabel, null, null, $aRefreshParams); $oBlock = DashletFactory::MakeForDashletBadge($sClassIconUrl, $sHyperlink, $iCount, $sClassLabel, null, null, $aRefreshParams);
} }
$sClassDescription = MetaModel::GetClassDescription($sClass);
if (utils::IsNotNullOrEmptyString($sClassDescription)) {
$oBlock->SetClassDescription($sClassDescription);
}
return $oBlock; return $oBlock;
} }
@@ -2038,7 +2042,7 @@ class MenuBlock extends DisplayBlock
} }
//---------------------------------------------------- //----------------------------------------------------
// Any style but NOT "listInObject" (linksets) actions // Any style but NOT \DisplayBlock::ENUM_STYLE_LIST_IN_OBJECT (linksets) actions
//---------------------------------------------------- //----------------------------------------------------
if ($this->m_sStyle !== static::ENUM_STYLE_LIST_IN_OBJECT) { if ($this->m_sStyle !== static::ENUM_STYLE_LIST_IN_OBJECT) {
switch ($iSetCount) { switch ($iSetCount) {

View File

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

View File

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

View File

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

View File

@@ -248,6 +248,7 @@ class LoginWebPage extends NiceWebPage
$oEmail = new Email(); $oEmail = new Email();
$oEmail->SetRecipientTO($sTo); $oEmail->SetRecipientTO($sTo);
$sFrom = MetaModel::GetConfig()->Get('forgot_password_from'); $sFrom = MetaModel::GetConfig()->Get('forgot_password_from');
$sFrom = utils::IsNullOrEmptyString($sFrom) ? MetaModel::GetConfig()->Get('email_default_sender_address') : $sFrom;
$oEmail->SetRecipientFrom($sFrom); $oEmail->SetRecipientFrom($sFrom);
$oEmail->SetSubject(Dict::S('UI:ResetPwd-EmailSubject', $oUser->Get('login'))); $oEmail->SetSubject(Dict::S('UI:ResetPwd-EmailSubject', $oUser->Get('login')));
$sResetUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?loginop=reset_pwd&auth_user='.urlencode($oUser->Get('login')).'&token='.urlencode($sToken); $sResetUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?loginop=reset_pwd&auth_user='.urlencode($oUser->Get('login')).'&token='.urlencode($sToken);

View File

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

View File

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

View File

@@ -296,7 +296,7 @@ class QueryOQL extends Query
} }
catch catch
(OQLException $e) { (OQLException $e) {
$oAlert = AlertUIBlockFactory::MakeForFailure(Dict::Format('UI:RunQuery:Error'), $e->getHtmlDesc()) $oAlert = AlertUIBlockFactory::MakeForFailure(Dict::S('UI:RunQuery:Error'), $e->getHtmlDesc())
->SetIsClosable(false) ->SetIsClosable(false)
->SetIsCollapsible(false); ->SetIsCollapsible(false);
$oAlert->AddCSSClass('mb-5'); $oAlert->AddCSSClass('mb-5');

View File

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

View File

@@ -163,7 +163,7 @@ class UIExtKeyWidget
$oPage->add_linked_script('../js/extkeywidget.js'); $oPage->add_linked_script('../js/extkeywidget.js');
$oPage->add_linked_script('../js/forms-json-utils.js'); $oPage->add_linked_script('../js/forms-json-utils.js');
$bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_BULK_MODIFY) && $bAllowTargetCreation); $bCreate = (!$this->bSearchMode) && (UserRights::IsActionAllowed($this->sTargetClass, UR_ACTION_MODIFY) && $bAllowTargetCreation);
$bExtensions = true; $bExtensions = true;
$sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm'); $sMessage = Dict::S('UI:Message:EmptyList:UseSearchForm');
$sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_'; $sAttrFieldPrefix = ($this->bSearchMode) ? '' : 'attr_';
@@ -975,6 +975,10 @@ HTML
// Remove blob edition from creation form @see N°5863 to allow blob edition in modal context // Remove blob edition from creation form @see N°5863 to allow blob edition in modal context
FormHelper::DisableAttributeBlobInputs($this->sTargetClass, $aFormExtraParams); FormHelper::DisableAttributeBlobInputs($this->sTargetClass, $aFormExtraParams);
if(FormHelper::HasMandatoryAttributeBlobInputs($oNewObj)){
$oPage->AddUiBlock(FormHelper::GetAlertForMandatoryAttributeBlobInputsInModal(FormHelper::ENUM_MANDATORY_BLOB_MODE_CREATE));
}
cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), $aFormExtraParams); cmdbAbstractObject::DisplayCreationForm($oPage, $this->sTargetClass, $oNewObj, array(), $aFormExtraParams);
$oPage->add(<<<HTML $oPage->add(<<<HTML
</div> </div>

View File

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

View File

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

View File

@@ -20,6 +20,7 @@
use Combodo\iTop\Application\Helper\Session; use Combodo\iTop\Application\Helper\Session;
use Combodo\iTop\Application\UI\Base\iUIBlock; use Combodo\iTop\Application\UI\Base\iUIBlock;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use Combodo\iTop\Service\Module\ModuleService;
use ScssPhp\ScssPhp\Compiler; use ScssPhp\ScssPhp\Compiler;
use ScssPhp\ScssPhp\OutputStyle; use ScssPhp\ScssPhp\OutputStyle;
use ScssPhp\ScssPhp\ValueConverter; use ScssPhp\ScssPhp\ValueConverter;
@@ -51,22 +52,31 @@ class utils
{ {
/** /**
* @var string * @var string
* @since 3.0.0 * @since 2.7.10 3.0.0
*/ */
public const ENUM_SANITIZATION_FILTER_INTEGER = 'integer'; public const ENUM_SANITIZATION_FILTER_INTEGER = 'integer';
/** /**
* Datamodel class
* @var string * @var string
* @since 3.0.0 * @since 2.7.10 3.0.0
* @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6606 update PHPDoc
* @uses MetaModel::IsValidClass()
*/ */
public const ENUM_SANITIZATION_FILTER_CLASS = 'class'; public const ENUM_SANITIZATION_FILTER_CLASS = 'class';
/** /**
* @var string * @var string
* @since 3.0.0 * @since 2.7.10 3.0.4 3.1.1 3.2.0 N°6606
* @uses class_exists()
*/
public const ENUM_SANITIZATION_FILTER_PHP_CLASS = 'php_class';
/**
* @var string
* @since 2.7.10 3.0.0
*/ */
public const ENUM_SANITIZATION_FILTER_STRING = 'string'; public const ENUM_SANITIZATION_FILTER_STRING = 'string';
/** /**
* @var string * @var string
* @since 3.0.0 * @since 2.7.10 3.0.0
*/ */
public const ENUM_SANITIZATION_FILTER_CONTEXT_PARAM = 'context_param'; public const ENUM_SANITIZATION_FILTER_CONTEXT_PARAM = 'context_param';
/** /**
@@ -81,22 +91,22 @@ class utils
public const ENUM_SANITIZATION_FILTER_OPERATION = 'operation'; public const ENUM_SANITIZATION_FILTER_OPERATION = 'operation';
/** /**
* @var string * @var string
* @since 3.0.0 * @since 2.7.10 3.0.0
*/ */
public const ENUM_SANITIZATION_FILTER_PARAMETER = 'parameter'; public const ENUM_SANITIZATION_FILTER_PARAMETER = 'parameter';
/** /**
* @var string * @var string
* @since 3.0.0 * @since 2.7.10 3.0.0
*/ */
public const ENUM_SANITIZATION_FILTER_FIELD_NAME = 'field_name'; public const ENUM_SANITIZATION_FILTER_FIELD_NAME = 'field_name';
/** /**
* @var string * @var string
* @since 3.0.0 * @since 2.7.10 3.0.0
*/ */
public const ENUM_SANITIZATION_FILTER_TRANSACTION_ID = 'transaction_id'; public const ENUM_SANITIZATION_FILTER_TRANSACTION_ID = 'transaction_id';
/** /**
* @var string For XML / HTML node identifiers * @var string For XML / HTML node identifiers
* @since 3.0.0 * @since 2.7.10 3.0.0
*/ */
public const ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER = 'element_identifier'; public const ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER = 'element_identifier';
/** /**
@@ -106,12 +116,13 @@ class utils
public const ENUM_SANITIZATION_FILTER_VARIABLE_NAME = 'variable_name'; public const ENUM_SANITIZATION_FILTER_VARIABLE_NAME = 'variable_name';
/** /**
* @var string * @var string
* @since 3.0.0 * @since 2.7.10 3.0.0
*/ */
public const ENUM_SANITIZATION_FILTER_RAW_DATA = 'raw_data'; public const ENUM_SANITIZATION_FILTER_RAW_DATA = 'raw_data';
/** /**
* @var string * @var string
* @since 3.0.2, 3.1.0 N°4899 * @since 3.0.2 3.1.0 N°4899
* @since 2.7.10 N°6606
*/ */
public const ENUM_SANITIZATION_FILTER_URL = 'url'; public const ENUM_SANITIZATION_FILTER_URL = 'url';
@@ -154,6 +165,8 @@ class utils
private static $iNextId = 0; private static $iNextId = 0;
private static $m_sAppRootUrl = null;
protected static function LoadParamFile($sParamFile) protected static function LoadParamFile($sParamFile)
{ {
if (!file_exists($sParamFile)) { if (!file_exists($sParamFile)) {
@@ -395,6 +408,10 @@ class utils
* @since 2.7.0 new 'element_identifier' filter * @since 2.7.0 new 'element_identifier' filter
* @since 3.0.0 new utils::ENUM_SANITIZATION_* const * @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.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) protected static function Sanitize_Internal($value, $sSanitizationFilter)
{ {
@@ -415,6 +432,13 @@ class utils
$retValue = filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS); $retValue = filter_var($value, FILTER_SANITIZE_SPECIAL_CHARS);
break; 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_CONTEXT_PARAM:
case static::ENUM_SANITIZATION_FILTER_ROUTE: case static::ENUM_SANITIZATION_FILTER_ROUTE:
case static::ENUM_SANITIZATION_FILTER_OPERATION: case static::ENUM_SANITIZATION_FILTER_OPERATION:
@@ -480,6 +504,7 @@ class utils
// For URL // For URL
case static::ENUM_SANITIZATION_FILTER_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_VALIDATE_URL);
break; break;
@@ -1023,7 +1048,7 @@ class utils
*/ */
public static function GetAbsoluteUrlAppRoot($bForceTrustProxy = false) public static function GetAbsoluteUrlAppRoot($bForceTrustProxy = false)
{ {
static $sUrl = null; $sUrl = static::$m_sAppRootUrl;
if ($sUrl === null || $bForceTrustProxy) if ($sUrl === null || $bForceTrustProxy)
{ {
$sUrl = self::GetConfig()->Get('app_root_url'); $sUrl = self::GetConfig()->Get('app_root_url');
@@ -1044,8 +1069,9 @@ class utils
} }
$sUrl = str_replace(SERVER_NAME_PLACEHOLDER, $sServerName, $sUrl); $sUrl = str_replace(SERVER_NAME_PLACEHOLDER, $sServerName, $sUrl);
} }
static::$m_sAppRootUrl = $sUrl;
} }
return $sUrl; return static::$m_sAppRootUrl;
} }
/** /**
@@ -1396,13 +1422,23 @@ class utils
return APPROOT . 'env-' . MetaModel::GetEnvironment() . '/'; 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 * @return string A path to a folder into which any module can store cache data
* The corresponding folder is created or cleaned upon code compilation * The corresponding folder is created or cleaned upon code compilation
*/ */
public static function GetCachePath() public static function GetCachePath()
{ {
return APPROOT.'data/cache-'.MetaModel::GetEnvironment().'/'; return static::GetDataPath().'cache-'.MetaModel::GetEnvironment().'/';
} }
/** /**
@@ -2265,24 +2301,7 @@ SQL;
*/ */
public static function GetCurrentModuleName($iCallDepth = 0) public static function GetCurrentModuleName($iCallDepth = 0)
{ {
$sCurrentModuleName = ''; return ModuleService::GetInstance()->GetCurrentModuleName($iCallDepth + 1);
$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;
} }
/** /**
@@ -2304,24 +2323,7 @@ SQL;
*/ */
public static function GetCurrentModuleDir($iCallDepth) public static function GetCurrentModuleDir($iCallDepth)
{ {
$sCurrentModuleDir = ''; return ModuleService::GetInstance()->GetCurrentModuleDir($iCallDepth);
$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;
} }
/** /**
@@ -2336,12 +2338,7 @@ SQL;
*/ */
public static function GetCurrentModuleUrl() public static function GetCurrentModuleUrl()
{ {
$sDir = static::GetCurrentModuleDir(1); return ModuleService::GetInstance()->GetCurrentModuleUrl(1);
if ( $sDir !== '')
{
return static::GetAbsoluteUrlModulesRoot().'/'.$sDir;
}
return '';
} }
/** /**
@@ -2351,8 +2348,7 @@ SQL;
*/ */
public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null) public static function GetCurrentModuleSetting($sProperty, $defaultvalue = null)
{ {
$sModuleName = static::GetCurrentModuleName(1); return ModuleService::GetInstance()->GetCurrentModuleSetting($sProperty, $defaultvalue);
return MetaModel::GetModuleSetting($sModuleName, $sProperty, $defaultvalue);
} }
/** /**
@@ -2361,12 +2357,7 @@ SQL;
*/ */
public static function GetCompiledModuleVersion($sModuleName) public static function GetCompiledModuleVersion($sModuleName)
{ {
$aModulesInfo = GetModulesInfo(); return ModuleService::GetInstance()->GetCompiledModuleVersion($sModuleName);
if (array_key_exists($sModuleName, $aModulesInfo))
{
return $aModulesInfo[$sModuleName]['version'];
}
return null;
} }
/** /**
@@ -2691,24 +2682,26 @@ SQL;
} }
/** /**
* Returns the local path relative to the iTop installation of an existing file * Returns the local path relative to the iTop installation (APPROOT or the given base path)
* Dir separator is changed to '/' for consistency among the different OS * Dir separator is changed to '/' for consistency among the different OS
* *
* @param string $sAbsolutePath absolute path * @param string $sAbsolutePath absolute path
* @param string $sBasePath Base path for the resulting local path (default APPROOT)
* *
* @return false|string * @return false|string The generated local path or false if absolute path is not under the base path
* @since 3.1.1 Added base path defaulted to previous version APPROOT
*/ */
final public static function LocalPath($sAbsolutePath) final public static function LocalPath($sAbsolutePath, string $sBasePath = APPROOT)
{ {
$sRootPath = realpath(APPROOT); $sRootPath = realpath($sBasePath);
$sFullPath = realpath($sAbsolutePath); $sFullPath = realpath($sAbsolutePath);
if (($sFullPath === false) || !self::StartsWith($sFullPath, $sRootPath)) if (($sFullPath === false) || !self::StartsWith($sFullPath, $sRootPath))
{ {
return false; return false;
} }
$sLocalPath = substr($sFullPath, strlen($sRootPath.DIRECTORY_SEPARATOR)); $sLocalPath = substr($sFullPath, strlen($sRootPath.DIRECTORY_SEPARATOR));
$sLocalPath = str_replace(DIRECTORY_SEPARATOR, '/', $sLocalPath);
return $sLocalPath; return str_replace(DIRECTORY_SEPARATOR, '/', $sLocalPath);
} }
/** /**
@@ -2900,7 +2893,7 @@ HTML;
// Add already loaded classes // Add already loaded classes
$aCurrentClasses = array_fill_keys(get_declared_classes(), ''); $aCurrentClasses = array_fill_keys(get_declared_classes(), '');
$aClassMap = array_merge($aClassMap, $aCurrentClasses); $aClassMap = array_merge($aCurrentClasses, $aClassMap);
foreach ($aClassMap as $sPHPClass => $sPHPFile) { foreach ($aClassMap as $sPHPClass => $sPHPFile) {
$bSkipped = false; $bSkipped = false;
@@ -2925,11 +2918,12 @@ HTML;
$bSkipped = true; // file not found $bSkipped = true; // file not found
} }
} }
if(!$bSkipped){ if(!$bSkipped){
try { try {
$oRefClass = new ReflectionClass($sPHPClass); $oRefClass = new ReflectionClass($sPHPClass);
if ($oRefClass->implementsInterface($sInterface) && $oRefClass->isInstantiable()) { if ($oRefClass->implementsInterface($sInterface) &&
!$oRefClass->isInterface() && !$oRefClass->isAbstract() && !$oRefClass->isTrait()) {
$aMatchingClasses[] = $sPHPClass; $aMatchingClasses[] = $sPHPClass;
} }
} catch (Exception $e) { } catch (Exception $e) {

View File

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

View File

@@ -351,6 +351,7 @@ class WizardHelper
/** /**
* @return string JS code to be executed for fields update * @return string JS code to be executed for fields update
* @since 3.0.0 N°3198 * @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() public function GetJsForUpdateFields()
{ {
@@ -363,6 +364,32 @@ class WizardHelper
JS; 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 * Function with an old pattern of code
* @deprecated 3.1.0 * @deprecated 3.1.0
@@ -371,11 +398,9 @@ JS;
{ {
$aSet = json_decode($sJsonSet, true); // true means hash array instead of object $aSet = json_decode($sJsonSet, true); // true means hash array instead of object
$oSet = CMDBObjectSet::FromScratch($sLinkClass); $oSet = CMDBObjectSet::FromScratch($sLinkClass);
foreach ($aSet as $aLinkObj) foreach ($aSet as $aLinkObj) {
{
$oLink = MetaModel::NewObject($sLinkClass); $oLink = MetaModel::NewObject($sLinkClass);
foreach ($aLinkObj as $sAttCode => $value) foreach ($aLinkObj as $sAttCode => $value) {
{
$oAttDef = MetaModel::GetAttributeDef($sLinkClass, $sAttCode); $oAttDef = MetaModel::GetAttributeDef($sLinkClass, $sAttCode);
if (($oAttDef->IsExternalKey()) && ($value != '') && ($value > 0)) if (($oAttDef->IsExternalKey()) && ($value != '') && ($value > 0))
{ {

View File

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

View File

@@ -45,6 +45,7 @@ define('MAINTENANCE_MODE_FILE', APPROOT.'data/.maintenance');
define('READONLY_MODE_FILE', APPROOT.'data/.readonly'); define('READONLY_MODE_FILE', APPROOT.'data/.readonly');
$fItopStarted = microtime(true); $fItopStarted = microtime(true);
$iItopInitialMemory = memory_get_usage(true);
if (!isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false) { if (!isset($GLOBALS['bBypassAutoload']) || $GLOBALS['bBypassAutoload'] == false) {
require_once APPROOT.'/lib/autoload.php'; require_once APPROOT.'/lib/autoload.php';

22
composer.lock generated
View File

@@ -4476,16 +4476,16 @@
}, },
{ {
"name": "symfony/twig-bridge", "name": "symfony/twig-bridge",
"version": "v5.4.11", "version": "v5.4.31",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/symfony/twig-bridge.git", "url": "https://github.com/symfony/twig-bridge.git",
"reference": "63b8a50d48c9fe3d04e77307d4f1771dd848baa8" "reference": "fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/63b8a50d48c9fe3d04e77307d4f1771dd848baa8", "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942",
"reference": "63b8a50d48c9fe3d04e77307d4f1771dd848baa8", "reference": "fc6ee0a3b672ea12ca1f26592d257bfc7f4ee942",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -4498,22 +4498,22 @@
"phpdocumentor/reflection-docblock": "<3.2.2", "phpdocumentor/reflection-docblock": "<3.2.2",
"phpdocumentor/type-resolver": "<1.4.0", "phpdocumentor/type-resolver": "<1.4.0",
"symfony/console": "<5.3", "symfony/console": "<5.3",
"symfony/form": "<5.3", "symfony/form": "<5.4.21|>=6,<6.2.7",
"symfony/http-foundation": "<5.3", "symfony/http-foundation": "<5.3",
"symfony/http-kernel": "<4.4", "symfony/http-kernel": "<4.4",
"symfony/translation": "<5.2", "symfony/translation": "<5.2",
"symfony/workflow": "<5.2" "symfony/workflow": "<5.2"
}, },
"require-dev": { "require-dev": {
"doctrine/annotations": "^1.12", "doctrine/annotations": "^1.12|^2",
"egulias/email-validator": "^2.1.10|^3", "egulias/email-validator": "^2.1.10|^3|^4",
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
"symfony/asset": "^4.4|^5.0|^6.0", "symfony/asset": "^4.4|^5.0|^6.0",
"symfony/console": "^5.3|^6.0", "symfony/console": "^5.3|^6.0",
"symfony/dependency-injection": "^4.4|^5.0|^6.0", "symfony/dependency-injection": "^4.4|^5.0|^6.0",
"symfony/expression-language": "^4.4|^5.0|^6.0", "symfony/expression-language": "^4.4|^5.0|^6.0",
"symfony/finder": "^4.4|^5.0|^6.0", "symfony/finder": "^4.4|^5.0|^6.0",
"symfony/form": "^5.3|^6.0", "symfony/form": "^5.4.21|^6.2.7",
"symfony/http-foundation": "^5.3|^6.0", "symfony/http-foundation": "^5.3|^6.0",
"symfony/http-kernel": "^4.4|^5.0|^6.0", "symfony/http-kernel": "^4.4|^5.0|^6.0",
"symfony/intl": "^4.4|^5.0|^6.0", "symfony/intl": "^4.4|^5.0|^6.0",
@@ -4577,7 +4577,7 @@
"description": "Provides integration for Twig with various Symfony components", "description": "Provides integration for Twig with various Symfony components",
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"support": { "support": {
"source": "https://github.com/symfony/twig-bridge/tree/v5.4.11" "source": "https://github.com/symfony/twig-bridge/tree/v5.4.31"
}, },
"funding": [ "funding": [
{ {
@@ -4593,7 +4593,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2022-07-20T13:00:38+00:00" "time": "2023-11-09T21:19:08+00:00"
}, },
{ {
"name": "symfony/twig-bundle", "name": "symfony/twig-bundle",
@@ -5276,5 +5276,5 @@
"platform-overrides": { "platform-overrides": {
"php": "7.4.0" "php": "7.4.0"
}, },
"plugin-api-version": "2.3.0" "plugin-api-version": "2.6.0"
} }

View File

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

View File

@@ -650,6 +650,9 @@ class ActionEmail extends ActionNotification
$aMessageContent['subject'] = 'TEST['.$aMessageContent['subject'].']'; $aMessageContent['subject'] = 'TEST['.$aMessageContent['subject'].']';
$aMessageContent['body'] = $sTestBody; $aMessageContent['body'] = $sTestBody;
$aMessageContent['to'] = $this->Get('test_recipient'); $aMessageContent['to'] = $this->Get('test_recipient');
// N°6677 Ensure emails in test are never sent to cc'd and bcc'd addresses
$aMessageContent['cc'] = '';
$aMessageContent['bcc'] = '';
} }
// Note: N°4849 We pass the "References" identifier instead of the "Message-ID" on purpose as we want notifications emails to group around the triggering iTop object, not just the users' replies to the notification // Note: N°4849 We pass the "References" identifier instead of the "Message-ID" on purpose as we want notifications emails to group around the triggering iTop object, not just the users' replies to the notification
$aMessageContent['in_reply_to'] = $aMessageContent['references']; $aMessageContent['in_reply_to'] = $aMessageContent['references'];

View File

@@ -91,6 +91,12 @@ define('LINKSET_EDITMODE_ACTIONS', 2); // Show the usual 'Actions' popup menu
define('LINKSET_EDITMODE_INPLACE', 3); // The "linked" objects can be created/modified/deleted in place define('LINKSET_EDITMODE_INPLACE', 3); // The "linked" objects can be created/modified/deleted in place
define('LINKSET_EDITMODE_ADDREMOVE', 4); // The "linked" objects can be added/removed in place define('LINKSET_EDITMODE_ADDREMOVE', 4); // The "linked" objects can be added/removed in place
define('LINKSET_EDITWHEN_NEVER', 0); // The linkset cannot be edited at all from inside this object
define('LINKSET_EDITWHEN_ON_HOST_EDITION', 1); // The only possible action is to open a new window to create a new object
define('LINKSET_EDITWHEN_ON_HOST_DISPLAY', 2); // Show the usual 'Actions' popup menu
define('LINKSET_EDITWHEN_ALWAYS', 3); // Show the usual 'Actions' popup menu
define('LINKSET_DISPLAY_STYLE_PROPERTY', 'property'); define('LINKSET_DISPLAY_STYLE_PROPERTY', 'property');
define('LINKSET_DISPLAY_STYLE_TAB', 'tab'); define('LINKSET_DISPLAY_STYLE_TAB', 'tab');
@@ -791,7 +797,7 @@ abstract class AttributeDefinition
public function HasAValue($proposedValue): bool public function HasAValue($proposedValue): bool
{ {
// Default implementation, we don't really know what type $proposedValue will be // Default implementation, we don't really know what type $proposedValue will be
return is_null($proposedValue); return !(is_null($proposedValue));
} }
/** /**
@@ -1703,6 +1709,15 @@ class AttributeLinkedSet extends AttributeDefinition
public function GetEditMode() public function GetEditMode()
{ {
return $this->GetOptional('edit_mode', LINKSET_EDITMODE_ACTIONS); return $this->GetOptional('edit_mode', LINKSET_EDITMODE_ACTIONS);
}
/**
* @return int see LINKSET_EDITWHEN_* constants
* @since 3.1.1 3.2.0 N°6385
*/
public function GetEditWhen(): int
{
return $this->GetOptional('edit_when', LINKSET_EDITWHEN_ALWAYS);
} }
/** /**
@@ -8584,7 +8599,7 @@ class AttributeBlob extends AttributeDefinition
public function RecordAttChange(DBObject $oObject, $original, $value): void public function RecordAttChange(DBObject $oObject, $original, $value): void
{ {
// N°6502 Don't record history if only the download count has changed // N°6502 Don't record history if only the download count has changed
if ($original->EqualsExceptDownloadsCount($value)) { if ((null !== $original) && (null !== $value) && $original->EqualsExceptDownloadsCount($value)) {
return; return;
} }

View File

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

View File

@@ -431,6 +431,7 @@ class CMDBSource
{ {
self::$m_sDBName = ''; self::$m_sDBName = '';
} }
self::_TablesInfoCacheReset(); // reset the table info cache!
} }
public static function CreateTable($sQuery) public static function CreateTable($sQuery)
@@ -607,8 +608,9 @@ class CMDBSource
{ {
self::LogDeadLock($e, true); self::LogDeadLock($e, true);
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e)); 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) { if ($oResult === false) {
$aContext = array('query' => $sSql); $aContext = array('query' => $sSql);
@@ -626,18 +628,24 @@ class CMDBSource
} }
/** /**
* @param \Exception $e * @param Exception $e
* @param bool $bForQuery to get the proper DB connection * @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 2.7.1
* @since 3.0.0 N°4325 add new optional parameter to use the correct DB connection * @since 3.0.0 N°4325 add new optional parameter to use the correct DB connection
* @since 3.0.4 3.1.1 3.2.0 N°6643 new bCheckMysqliErrno parameter as a workaround for mysqli::errno cannot be mocked
*/ */
private static function LogDeadLock(Exception $e, $bForQuery = false) private static function LogDeadLock(Exception $e, $bForQuery = false, $bCheckMysqliErrno = true)
{ {
// checks MySQL error code // checks MySQL error code
$iMySqlErrorNo = DbConnectionWrapper::GetDbConnection($bForQuery)->errno; if ($bCheckMysqliErrno) {
if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK))) { $iMySqlErrorNo = DbConnectionWrapper::GetDbConnection($bForQuery)->errno;
return; if (!in_array($iMySqlErrorNo, array(self::MYSQL_ERRNO_WAIT_TIMEOUT, self::MYSQL_ERRNO_DEADLOCK))) {
return;
}
} else {
$iMySqlErrorNo = "N/A";
} }
// Get error info // Get error info
@@ -664,7 +672,10 @@ class CMDBSource
); );
DeadLockLog::Info($sMessage, $iMySqlErrorNo, $aLogContext); DeadLockLog::Info($sMessage, $iMySqlErrorNo, $aLogContext);
IssueLog::Error($sMessage, LogChannels::DEADLOCK, $e->getMessage()); IssueLog::Error($sMessage, LogChannels::DEADLOCK, [
'exception.class' => get_class($e),
'exception.message' => $e->getMessage(),
]);
} }
/** /**

View File

@@ -29,7 +29,7 @@ define('ITOP_APPLICATION_SHORT', 'iTop');
* *
* @see ITOP_CORE_VERSION to get iTop core version * @see ITOP_CORE_VERSION to get iTop core version
*/ */
define('ITOP_VERSION', '3.1.0-dev'); define('ITOP_VERSION', '3.1.1-dev');
define('ITOP_VERSION_NAME', 'Fullmoon'); define('ITOP_VERSION_NAME', 'Fullmoon');
define('ITOP_REVISION', 'svn'); define('ITOP_REVISION', 'svn');
@@ -656,22 +656,22 @@ class Config
'source_of_value' => '', 'source_of_value' => '',
'show_in_conf_sample' => false, 'show_in_conf_sample' => false,
], ],
'email_transport_smtp.allow_self_signed' => array( 'email_transport_smtp.allow_self_signed' => [
'type' => 'bool', 'type' => 'bool',
'description' => 'Allow self signed peer certificates', 'description' => 'Allow self signed peer certificates',
'default' => false, 'default' => false,
'value' => false, 'value' => false,
'source_of_value' => '', 'source_of_value' => '',
'show_in_conf_sample' => false, 'show_in_conf_sample' => false,
), ],
'email_transport_smtp.verify_peer' => array( 'email_transport_smtp.verify_peer' => [
'type' => 'bool', 'type' => 'bool',
'description' => 'Verify peer certificate', 'description' => 'Verify peer certificate',
'default' => true, 'default' => true,
'value' => true, 'value' => true,
'source_of_value' => '', 'source_of_value' => '',
'show_in_conf_sample' => false, 'show_in_conf_sample' => false,
), ],
'email_css' => [ 'email_css' => [
'type' => 'string', 'type' => 'string',
'description' => 'CSS that will override the standard stylesheet used for the notifications', 'description' => 'CSS that will override the standard stylesheet used for the notifications',
@@ -1069,6 +1069,14 @@ class Config
'source_of_value' => '', 'source_of_value' => '',
'show_in_conf_sample' => false, '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' => [ 'max_linkset_output' => [
'type' => 'integer', '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.', '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.',

View File

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

View File

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

View File

@@ -219,6 +219,19 @@
<field id="friendlyname" xsi:type="AttributeFriendlyName"/> <field id="friendlyname" xsi:type="AttributeFriendlyName"/>
</fields> </fields>
</class> </class>
<class id="AuditDomain" _delta="define">
<parent>cmdbAbstractObject</parent>
<properties>
<category>application, grant_by_profile</category>
</properties>
<fields>
<field id="name" xsi:type="AttributeString"/>
<field id="description" xsi:type="AttributeString"/>
<field id="icon" xsi:type="AttributeImage"/>
<field id="categories_list" xsi:type="AttributeLinkedSet"/>
<field id="friendlyname" xsi:type="AttributeFriendlyName"/>
</fields>
</class>
<class id="Query" _delta="define"> <class id="Query" _delta="define">
<!-- Generated by toolkit/export-class-to-meta.php --> <!-- Generated by toolkit/export-class-to-meta.php -->
<parent>cmdbAbstractObject</parent> <parent>cmdbAbstractObject</parent>

View File

@@ -9,6 +9,7 @@ use Combodo\iTop\Service\Events\EventData;
use Combodo\iTop\Service\Events\EventException; use Combodo\iTop\Service\Events\EventException;
use Combodo\iTop\Service\Events\EventService; use Combodo\iTop\Service\Events\EventService;
use Combodo\iTop\Service\Events\EventServiceLog; use Combodo\iTop\Service\Events\EventServiceLog;
use Combodo\iTop\Service\Module\ModuleService;
use Combodo\iTop\Service\TemporaryObjects\TemporaryObjectManager; use Combodo\iTop\Service\TemporaryObjects\TemporaryObjectManager;
/** /**
@@ -1152,7 +1153,9 @@ abstract class DBObject implements iDisplay
return; //skip! return; //skip!
} }
$this->FireEventComputeValues(); $this->FireEventComputeValues();
$oKPI = new ExecutionKPI();
$this->ComputeValues(); $this->ComputeValues();
$oKPI->ComputeStatsForExtension($this, 'ComputeValues');
} }
/** /**
@@ -2408,7 +2411,6 @@ abstract class DBObject implements iDisplay
* @throws \ArchivedObjectException * @throws \ArchivedObjectException
* @throws \CoreException * @throws \CoreException
* @throws \OQLException * @throws \OQLException
*
*/ */
public function DoCheckToWrite() public function DoCheckToWrite()
{ {
@@ -2460,7 +2462,6 @@ abstract class DBObject implements iDisplay
} }
/** /**
*
* @api * @api
* @api-advanced * @api-advanced
* *
@@ -2476,9 +2477,8 @@ abstract class DBObject implements iDisplay
* @throws \ArchivedObjectException * @throws \ArchivedObjectException
* @throws \CoreException * @throws \CoreException
* @throws \OQLException * @throws \OQLException
*
*/ */
public final function CheckToWrite($bDoComputeValues = true) final public function CheckToWrite($bDoComputeValues = true)
{ {
if (MetaModel::SkipCheckToWrite()) if (MetaModel::SkipCheckToWrite())
{ {
@@ -2488,7 +2488,6 @@ abstract class DBObject implements iDisplay
{ {
$this->m_aCheckIssues = array(); $this->m_aCheckIssues = array();
$oKPI = new ExecutionKPI();
if ($bDoComputeValues) { if ($bDoComputeValues) {
$this->DoComputeValues(); $this->DoComputeValues();
} }
@@ -2498,8 +2497,9 @@ abstract class DBObject implements iDisplay
$this->FireEventCheckToWrite(); $this->FireEventCheckToWrite();
$this->SetReadWrite(); $this->SetReadWrite();
$oKPI = new ExecutionKPI();
$this->DoCheckToWrite(); $this->DoCheckToWrite();
$oKPI->ComputeStats('CheckToWrite', get_class($this)); $oKPI->ComputeStatsForExtension($this, 'DoCheckToWrite');
if (count($this->m_aCheckIssues) == 0) if (count($this->m_aCheckIssues) == 0)
{ {
$this->m_bCheckStatus = true; $this->m_bCheckStatus = true;
@@ -3122,7 +3122,9 @@ abstract class DBObject implements iDisplay
// Ensure the update of the values (we are accessing the data directly) // Ensure the update of the values (we are accessing the data directly)
$this->DoComputeValues(); $this->DoComputeValues();
$oKPI = new ExecutionKPI();
$this->OnInsert(); $this->OnInsert();
$oKPI->ComputeStatsForExtension($this, 'OnInsert');
$this->FireEventBeforeWrite(); $this->FireEventBeforeWrite();
@@ -3178,7 +3180,9 @@ abstract class DBObject implements iDisplay
$this->DBInsertSingleTable($sParentClass); $this->DBInsertSingleTable($sParentClass);
} }
$oKPI = new ExecutionKPI();
$this->OnObjectKeyReady(); $this->OnObjectKeyReady();
$oKPI->ComputeStatsForExtension($this, 'OnObjectKeyReady');
$this->UpdateCurrentObjectInCrudStack(); $this->UpdateCurrentObjectInCrudStack();
$this->DBWriteLinks(); $this->DBWriteLinks();
@@ -3255,7 +3259,9 @@ abstract class DBObject implements iDisplay
public function PostInsertActions(): void public function PostInsertActions(): void
{ {
$this->FireEventAfterWrite([], true); $this->FireEventAfterWrite([], true);
$oKPI = new ExecutionKPI();
$this->AfterInsert(); $this->AfterInsert();
$oKPI->ComputeStatsForExtension($this, 'AfterInsert');
// Activate any existing trigger // Activate any existing trigger
$sClass = get_class($this); $sClass = get_class($this);
@@ -3353,7 +3359,9 @@ abstract class DBObject implements iDisplay
try { try {
$this->DoComputeValues(); $this->DoComputeValues();
$this->ComputeStopWatchesDeadline(false); $this->ComputeStopWatchesDeadline(false);
$oKPI = new ExecutionKPI();
$this->OnUpdate(); $this->OnUpdate();
$oKPI->ComputeStatsForExtension($this, 'OnUpdate');
$this->FireEventBeforeWrite(); $this->FireEventBeforeWrite();
@@ -3567,10 +3575,13 @@ abstract class DBObject implements iDisplay
public function PostUpdateActions(array $aChanges): void public function PostUpdateActions(array $aChanges): void
{ {
$this->FireEventAfterWrite($aChanges, false); $this->FireEventAfterWrite($aChanges, false);
$oKPI = new ExecutionKPI();
$this->AfterUpdate(); $this->AfterUpdate();
$oKPI->ComputeStatsForExtension($this, 'AfterUpdate');
// - TriggerOnObjectUpdate // - TriggerOnObjectUpdate
$aParams = array('class_list' => MetaModel::EnumParentClasses(get_class($this), ENUM_PARENT_CLASSES_ALL)); $aClassList = MetaModel::EnumParentClasses(get_class($this), ENUM_PARENT_CLASSES_ALL);
$aParams = array('class_list' => $aClassList);
$oSet = new DBObjectSet(DBObjectSearch::FromOQL('SELECT TriggerOnObjectUpdate AS t WHERE t.target_class IN (:class_list)'), $oSet = new DBObjectSet(DBObjectSearch::FromOQL('SELECT TriggerOnObjectUpdate AS t WHERE t.target_class IN (:class_list)'),
array(), $aParams); array(), $aParams);
while ($oTrigger = $oSet->Fetch()) { while ($oTrigger = $oSet->Fetch()) {
@@ -3584,6 +3595,44 @@ abstract class DBObject implements iDisplay
} }
} }
$sClass = get_class($this);
if (MetaModel::HasLifecycle($sClass))
{
$sStateAttCode = MetaModel::GetStateAttributeCode($sClass);
if (isset($this->m_aPreviousValuesForUpdatedAttributes[$sStateAttCode])) {
$sPreviousState = $this->m_aPreviousValuesForUpdatedAttributes[$sStateAttCode];
// Change state triggers...
$aParams = array(
'class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL),
'previous_state' => $sPreviousState,
'new_state' => $this->Get($sStateAttCode),
);
$oSet = new DBObjectSet(DBObjectSearch::FromOQL('SELECT TriggerOnStateLeave AS t WHERE t.target_class IN (:class_list) AND t.state=:previous_state'), array(), $aParams);
while ($oTrigger = $oSet->Fetch()) {
/** @var \TriggerOnStateLeave $oTrigger */
try {
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch (Exception $e) {
$oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e);
}
}
$oSet = new DBObjectSet(DBObjectSearch::FromOQL('SELECT TriggerOnStateEnter AS t WHERE t.target_class IN (:class_list) AND t.state=:new_state'), array(), $aParams);
while ($oTrigger = $oSet->Fetch()) {
/** @var \TriggerOnStateEnter $oTrigger */
try {
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch (Exception $e) {
$oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e);
}
}
}
}
// Activate any existing trigger // Activate any existing trigger
// - TriggerOnObjectMention // - TriggerOnObjectMention
// Forgotten by the fix of N°3245 // Forgotten by the fix of N°3245
@@ -3794,7 +3843,9 @@ abstract class DBObject implements iDisplay
return; return;
} }
$oKPI = new ExecutionKPI();
$this->OnDelete(); $this->OnDelete();
$oKPI->ComputeStatsForExtension($this, 'OnDelete');
// Activate any existing trigger // Activate any existing trigger
$sClass = get_class($this); $sClass = get_class($this);
@@ -3902,7 +3953,9 @@ abstract class DBObject implements iDisplay
} }
$this->FireEventAfterDelete(); $this->FireEventAfterDelete();
$oKPI = new ExecutionKPI();
$this->AfterDelete(); $this->AfterDelete();
$oKPI->ComputeStatsForExtension($this, 'AfterDelete');
$this->m_bIsInDB = false; $this->m_bIsInDB = false;
@@ -4275,36 +4328,6 @@ abstract class DBObject implements iDisplay
$this->DBWrite(); $this->DBWrite();
} }
// Change state triggers...
$aParams = array(
'class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL),
'previous_state' => $sPreviousState,
'new_state' => $sNewState,
);
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateLeave AS t WHERE t.target_class IN (:class_list) AND t.state=:previous_state"), array(), $aParams);
while ($oTrigger = $oSet->Fetch()) {
/** @var \TriggerOnStateLeave $oTrigger */
try {
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch (Exception $e) {
$oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e);
}
}
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateEnter AS t WHERE t.target_class IN (:class_list) AND t.state=:new_state"), array(), $aParams);
while ($oTrigger = $oSet->Fetch()) {
/** @var \TriggerOnStateEnter $oTrigger */
try {
$oTrigger->DoActivate($this->ToArgs('this'));
}
catch (Exception $e) {
$oTrigger->LogException($e, $this);
utils::EnrichRaisedException($oTrigger, $e);
}
}
$this->FireEvent(EVENT_DB_AFTER_APPLY_STIMULUS, $aEventData); $this->FireEvent(EVENT_DB_AFTER_APPLY_STIMULUS, $aEventData);
} }
else else
@@ -6260,7 +6283,10 @@ abstract class DBObject implements iDisplay
} }
} }
finally { finally {
$oKPI->ComputeStats('FireEvent', $sEvent); if (!$oKPI->ComputeStatsForExtension($this, $sCallback, "Event: $sEvent")) {
$sSignature = ModuleService::GetInstance()->GetModuleMethodSignature($this, $sCallback);
$oKPI->ComputeStats('FireEvent', "$sEvent callback: $sSignature");
}
} }
} }
if (!is_null($oFirstException)) { if (!is_null($oFirstException)) {

View File

@@ -767,7 +767,10 @@ class DBObjectSet implements iDBObjectSetIterator
try try
{ {
$oKPI = new ExecutionKPI();
$this->m_oSQLResult = CMDBSource::Query($sSQL); $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) } catch (MySQLException $e)
{ {
// 1116 = ER_TOO_MANY_TABLES // 1116 = ER_TOO_MANY_TABLES
@@ -847,8 +850,11 @@ class DBObjectSet implements iDBObjectSetIterator
{ {
if (is_null($this->m_iNumTotalDBRows)) if (is_null($this->m_iNumTotalDBRows))
{ {
$oKPI = new ExecutionKPI();
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true); $sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, 0, 0, true);
$resQuery = CMDBSource::Query($sSQL); $resQuery = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), 0, 0, true);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if (!$resQuery) return 0; if (!$resQuery) return 0;
$aRow = CMDBSource::FetchArray($resQuery); $aRow = CMDBSource::FetchArray($resQuery);
@@ -859,6 +865,42 @@ class DBObjectSet implements iDBObjectSetIterator
return $this->m_iNumTotalDBRows + count($this->m_aAddedObjects); // Does it fix Trac #887 ?? 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 * Check if the count exceeds a given limit
* *
@@ -875,8 +917,11 @@ class DBObjectSet implements iDBObjectSetIterator
{ {
if (is_null($this->m_iNumTotalDBRows)) if (is_null($this->m_iNumTotalDBRows))
{ {
$oKPI = new ExecutionKPI();
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true); $sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
$resQuery = CMDBSource::Query($sSQL); $resQuery = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if ($resQuery) if ($resQuery)
{ {
$aRow = CMDBSource::FetchArray($resQuery); $aRow = CMDBSource::FetchArray($resQuery);
@@ -887,7 +932,7 @@ class DBObjectSet implements iDBObjectSetIterator
{ {
$iCount = 0; $iCount = 0;
} }
} }
else else
{ {
$iCount = $this->m_iNumTotalDBRows; $iCount = $this->m_iNumTotalDBRows;
@@ -912,8 +957,11 @@ class DBObjectSet implements iDBObjectSetIterator
{ {
if (is_null($this->m_iNumTotalDBRows)) if (is_null($this->m_iNumTotalDBRows))
{ {
$oKPI = new ExecutionKPI();
$sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true); $sSQL = $this->m_oFilter->MakeSelectQuery(array(), $this->m_aArgs, null, null, $iLimit + 2, 0, true);
$resQuery = CMDBSource::Query($sSQL); $resQuery = CMDBSource::Query($sSQL);
$sOQL = $this->GetPseudoOQL($this->m_oFilter, array(), $iLimit + 2, 0, true);
$oKPI->ComputeStats('OQL Query Exec', $sOQL);
if ($resQuery) if ($resQuery)
{ {
$aRow = CMDBSource::FetchArray($resQuery); $aRow = CMDBSource::FetchArray($resQuery);
@@ -924,7 +972,7 @@ class DBObjectSet implements iDBObjectSetIterator
{ {
$iCount = 0; $iCount = 0;
} }
} }
else else
{ {
$iCount = $this->m_iNumTotalDBRows; $iCount = $this->m_iNumTotalDBRows;

View File

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

View File

@@ -1,27 +1,15 @@
<?php <?php
// Copyright (C) 2010-2023 Combodo SARL /**
// * @copyright Copyright (C) 2010-2023 Combodo SARL
// This file is part of iTop. * @license http://opensource.org/licenses/AGPL-3.0
// */
// 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 use Combodo\iTop\Core\Kpi\KpiLogData;
// the Free Software Foundation, either version 3 of the License, or use Combodo\iTop\Service\Module\ModuleService;
// (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) * Measures operations duration, memory usage, etc. (and some other KPIs)
*
* @copyright Copyright (C) 2010-2023 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/ */
class ExecutionKPI class ExecutionKPI
@@ -30,6 +18,8 @@ class ExecutionKPI
static protected $m_bEnabled_Memory = false; static protected $m_bEnabled_Memory = false;
static protected $m_bBlameCaller = false; static protected $m_bBlameCaller = false;
static protected $m_sAllowedUser = '*'; static protected $m_sAllowedUser = '*';
static protected $m_bGenerateLegacyReport = true;
static protected $m_fSlowQueries = 0;
static protected $m_aStats = []; // Recurrent operations static protected $m_aStats = []; // Recurrent operations
static protected $m_aExecData = []; // One shot operations static protected $m_aExecData = []; // One shot operations
@@ -86,14 +76,39 @@ class ExecutionKPI
return false; return false;
} }
static public function SetGenerateLegacyReport($bReportExtensionsOnly)
{
self::$m_bGenerateLegacyReport = $bReportExtensionsOnly;
}
static public function SetSlowQueries($fSlowQueries)
{
self::$m_fSlowQueries = $fSlowQueries;
}
static public function GetDescription() static public function GetDescription()
{ {
$aFeatures = array(); $aFeatures = array();
if (self::$m_bEnabled_Duration) $aFeatures[] = 'Duration'; if (self::$m_bEnabled_Duration) $aFeatures[] = 'Duration';
if (self::$m_bEnabled_Memory) $aFeatures[] = 'Memory usage'; if (self::$m_bEnabled_Memory) $aFeatures[] = 'Memory usage';
$sFeatures = implode(', ', $aFeatures); $sFeatures = 'Measures: '.implode(', ', $aFeatures);
$sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'"; $sFor = self::$m_sAllowedUser == '*' ? 'EVERYBODY' : "'".trim(self::$m_sAllowedUser)."'";
return "KPI logging is active for $sFor. Measures: $sFeatures"; $sSlowQueries = '';
if (self::$m_fSlowQueries > 0) {
$sSlowQueries = ". Slow Queries: ".self::$m_fSlowQueries."s";
}
$aExtensions = [];
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$aExtensions[] = ModuleService::GetInstance()->GetModuleNameFromObject($oExtensionInstance);
}
$sExtensions = '';
if (count($aExtensions) > 0) {
$sExtensions = '. KPI Extensions: ['.implode(', ', $aExtensions).']';
}
return "KPI logging is active for $sFor. $sFeatures$sSlowQueries$sExtensions";
} }
static public function ReportStats() static public function ReportStats()
@@ -101,7 +116,28 @@ class ExecutionKPI
if (!self::IsEnabled()) return; if (!self::IsEnabled()) return;
global $fItopStarted; global $fItopStarted;
global $iItopInitialMemory;
$sExecId = microtime(); // id to differentiate the hrefs! $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(); $aBeginTimes = array();
foreach (self::$m_aExecData as $aOpStats) foreach (self::$m_aExecData as $aOpStats)
@@ -114,9 +150,9 @@ class ExecutionKPI
$sHtml = "<hr/>"; $sHtml = "<hr/>";
$sHtml .= "<div style=\"background-color: grey; padding: 10px;\">"; $sHtml .= "<div style=\"background-color: grey; padding: 10px;\">";
$sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - ".$_SERVER['REQUEST_URI']." (".$_SERVER['REQUEST_METHOD'].")</h3>"; $sHtml .= "<h3><a name=\"".md5($sExecId)."\">KPIs</a> - $sRequest</h3>";
$oStarted = DateTime::createFromFormat('U.u', $fItopStarted); $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 .= "<p>log_kpi_user_id: ".UserRights::GetUserId()."</p>";
$sHtml .= "<div>"; $sHtml .= "<div>";
$sHtml .= "<table border=\"1\" style=\"$sTableStyle\">"; $sHtml .= "<table border=\"1\" style=\"$sTableStyle\">";
@@ -257,7 +293,7 @@ class ExecutionKPI
$sTotalInter = round($fTotalInter, 3); $sTotalInter = round($fTotalInter, 3);
$sMinInter = round($fMinInter, 3); $sMinInter = round($fMinInter, 3);
$sMaxInter = round($fMaxInter, 3); $sMaxInter = round($fMaxInter, 3);
if (($fTotalInter >= $fSlowQueries)) if (($fTotalInter >= self::$m_fSlowQueries))
{ {
if ($bDisplayHeader) if ($bDisplayHeader)
{ {
@@ -285,37 +321,19 @@ class ExecutionKPI
self::Report($sHtml); 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() public function __construct()
{ {
$this->ResetCounters(); $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 // Get the duration since startup, and reset the counter for the next measure
// //
@@ -323,9 +341,15 @@ class ExecutionKPI
{ {
global $fItopStarted; global $fItopStarted;
if (!self::IsEnabled()) {
return;
}
$aNewEntry = null; $aNewEntry = null;
if (self::$m_bEnabled_Duration) { $fStarted = $this->m_fStarted;
$fStopped = $this->m_fStarted;
if (self::$m_bEnabled_Duration) {
$fStopped = MyHelpers::getmicrotime(); $fStopped = MyHelpers::getmicrotime();
$aNewEntry = array( $aNewEntry = array(
'op' => $sOperationDesc, 'op' => $sOperationDesc,
@@ -336,6 +360,9 @@ class ExecutionKPI
$this->m_fStarted = $fStopped; $this->m_fStarted = $fStopped;
} }
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
$iCurrentMemory = 0;
$iPeakMemory = 0;
if (self::$m_bEnabled_Memory) if (self::$m_bEnabled_Memory)
{ {
$iCurrentMemory = self::memory_get_usage(); $iCurrentMemory = self::memory_get_usage();
@@ -345,40 +372,118 @@ class ExecutionKPI
} }
$aNewEntry['mem_begin'] = $this->m_iInitialMemory; $aNewEntry['mem_begin'] = $this->m_iInitialMemory;
$aNewEntry['mem_end'] = $iCurrentMemory; $aNewEntry['mem_end'] = $iCurrentMemory;
if (function_exists('memory_get_peak_usage')) $iPeakMemory = self::memory_get_peak_usage();
{ $aNewEntry['mem_peak'] = $iPeakMemory;
$aNewEntry['mem_peak'] = memory_get_peak_usage();
}
// Reset for the next operation (if the object is recycled) // Reset for the next operation (if the object is recycled)
$this->m_iInitialMemory = $iCurrentMemory; $this->m_iInitialMemory = $iCurrentMemory;
} }
if (!is_null($aNewEntry)) if (self::$m_bEnabled_Duration || self::$m_bEnabled_Memory) {
// Invoke extensions to log the KPI operation
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach(MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance)
{
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
$oKPILogData = new KpiLogData(
KpiLogData::TYPE_REPORT,
'Step',
$sOperationDesc,
$fStarted,
$fStopped,
$sExtension,
$iInitialMemory,
$iCurrentMemory,
$iPeakMemory);
$oExtensionInstance->LogOperation($oKPILogData);
}
}
if (!is_null($aNewEntry) && self::$m_bGenerateLegacyReport)
{ {
self::$m_aExecData[] = $aNewEntry; self::$m_aExecData[] = $aNewEntry;
} }
$this->ResetCounters(); $this->ResetCounters();
} }
/**
* Compute statistics for a call to an extension
* Note: not working in dev mode (with links to env-production)
*
* @param object|string $object object called
* @param string $sMethod method called on the object
* @param string $sMessage additional message
*
* @return bool true if an extension was found for this object::method
* @throws \ReflectionException
*/
public function ComputeStatsForExtension($object, string $sMethod, string $sMessage = ''): bool
{
if (!self::IsEnabled()) {
return true;
}
$sSignature = ModuleService::GetInstance()->GetModuleMethodSignature($object, $sMethod);
if (utils::StartsWith($sSignature, '[')) {
$this->ComputeStats('Extension', "$sSignature $sMessage");
return true;
}
return false;
}
public function ComputeStats($sOperation, $sArguments) public function ComputeStats($sOperation, $sArguments)
{ {
if (!self::IsEnabled()) {
return;
}
$fDuration = 0; $fDuration = 0;
if (self::$m_bEnabled_Duration) { if (self::$m_bEnabled_Duration) {
$fStopped = MyHelpers::getmicrotime(); $fStopped = MyHelpers::getmicrotime();
$fDuration = $fStopped - $this->m_fStarted; $fDuration = $fStopped - $this->m_fStarted;
$fSelfDuration = $fDuration - $this->m_fChildrenDuration; $aCallstack = [];
if (self::$m_bBlameCaller) { if (self::$m_bGenerateLegacyReport) {
self::$m_aStats[$sOperation][$sArguments][] = array( if (self::$m_bBlameCaller) {
'time' => $fSelfDuration, $aCallstack = MyHelpers::get_callstack(1);
'callers' => MyHelpers::get_callstack(1), self::$m_aStats[$sOperation][$sArguments][] = [
); 'time' => $fDuration,
} else { 'callers' => $aCallstack,
self::$m_aStats[$sOperation][$sArguments][] = array( ];
'time' => $fSelfDuration, } else {
); self::$m_aStats[$sOperation][$sArguments][] = [
} 'time' => $fDuration
} ];
self::Pop($fDuration); }
}
$iInitialMemory = is_null($this->m_iInitialMemory) ? 0 : $this->m_iInitialMemory;
$iCurrentMemory = 0;
$iPeakMemory = 0;
if (self::$m_bEnabled_Memory)
{
$iCurrentMemory = self::memory_get_usage();
$iPeakMemory = self::memory_get_peak_usage();
}
// Invoke extensions to log the KPI operation
/** @var \iKPILoggerExtension $oExtensionInstance */
foreach (MetaModel::EnumPlugins('iKPILoggerExtension') as $oExtensionInstance) {
$sExtension = ModuleService::GetInstance()->GetModuleNameFromCallStack(1);
$oKPILogData = new KpiLogData(
KpiLogData::TYPE_STATS,
$sOperation,
$sArguments,
$this->m_fStarted,
$fStopped,
$sExtension,
$iInitialMemory,
$iCurrentMemory,
$iPeakMemory,
$aCallstack);
$oExtensionInstance->LogOperation($oKPILogData);
}
}
} }
protected function ResetCounters() protected function ResetCounters()
@@ -408,35 +513,7 @@ class ExecutionKPI
static protected function memory_get_usage() static protected function memory_get_usage()
{ {
if (function_exists('memory_get_usage')) return memory_get_usage(true);
{
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) static public function memory_get_peak_usage($bRealUsage = false)

View File

@@ -1138,7 +1138,7 @@ class DeprecatedCallsLog extends LogAPI
parent::Enable($sTargetFile); parent::Enable($sTargetFile);
if ( if (
(false === defined(ITOP_PHPUNIT_RUNNING_CONSTANT_NAME)) (false === defined('ITOP_PHPUNIT_RUNNING_CONSTANT_NAME'))
&& static::IsLogLevelEnabledSafe(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD) && static::IsLogLevelEnabledSafe(self::LEVEL_WARNING, self::ENUM_CHANNEL_PHP_LIBMETHOD)
) { ) {
set_error_handler([static::class, 'DeprecatedNoticesErrorHandler'], E_DEPRECATED | E_USER_DEPRECATED); set_error_handler([static::class, 'DeprecatedNoticesErrorHandler'], E_DEPRECATED | E_USER_DEPRECATED);
@@ -1671,6 +1671,8 @@ class ExceptionLog extends LogAPI
*/ */
private static function GetLastEventIssue() private static function GetLastEventIssue()
{ {
return self::$oLastEventIssue; $oRet = self::$oLastEventIssue;
self::$oLastEventIssue = null;
return $oRet;
} }
} }

View File

@@ -1241,7 +1241,7 @@ abstract class MetaModel
} }
$sTable = self::DBGetTable($sClass); $sTable = self::DBGetTable($sClass);
// Could be completed later with all the classes that are using a given table // Could be completed later with all the classes that are using a given table
if (!array_key_exists($sTable, $aTables)) { if (!array_key_exists($sTable, $aTables)) {
$aTables[$sTable] = array(); $aTables[$sTable] = array();
} }
@@ -3522,7 +3522,7 @@ abstract class MetaModel
} }
// Set the "host class" as soon as possible, since HierarchicalKeys use it for their 'target class' as well // Set the "host class" as soon as possible, since HierarchicalKeys use it for their 'target class' as well
// and this needs to be know early (for Init_IsKnowClass 19 lines below) // and this needs to be know early (for Init_IsKnowClass 19 lines below)
$oAtt->SetHostClass($sTargetClass); $oAtt->SetHostClass($sTargetClass);
// Some attributes could refer to a class // Some attributes could refer to a class
@@ -3564,7 +3564,7 @@ abstract class MetaModel
self::$m_aAttribDefs[$sTargetClass][$oAtt->GetCode()] = $oAtt; self::$m_aAttribDefs[$sTargetClass][$oAtt->GetCode()] = $oAtt;
self::$m_aAttribOrigins[$sTargetClass][$oAtt->GetCode()] = $sTargetClass; self::$m_aAttribOrigins[$sTargetClass][$oAtt->GetCode()] = $sTargetClass;
// Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used // Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used
} }
/** /**
@@ -3764,7 +3764,7 @@ abstract class MetaModel
self::$m_aStimuli[$sTargetClass][$oStimulus->GetCode()] = $oStimulus; self::$m_aStimuli[$sTargetClass][$oStimulus->GetCode()] = $oStimulus;
// I wanted to simplify the syntax of the declaration of objects in the biz model // I wanted to simplify the syntax of the declaration of objects in the biz model
// Therefore, the reference to the host class is set there // Therefore, the reference to the host class is set there
$oStimulus->SetHostClass($sTargetClass); $oStimulus->SetHostClass($sTargetClass);
} }
@@ -4219,40 +4219,77 @@ abstract class MetaModel
} }
else else
{ {
$aCurrentUser = array(); $aCurrentUser = [];
$aCurrentContact = array(); $aCurrentContact = [];
foreach ($aExpectedArgs as $expression) foreach ($aExpectedArgs as $expression)
{ {
$aName = explode('->', $expression->GetName()); $aName = explode('->', $expression->GetName());
if ($aName[0] == 'current_contact_id') { if ($aName[0] == 'current_contact_id') {
$aPlaceholders['current_contact_id'] = UserRights::GetContactId(); $aPlaceholders['current_contact_id'] = UserRights::GetContactId();
} } else if ($aName[0] == 'current_user') {
if ($aName[0] == 'current_user') {
array_push($aCurrentUser, $aName[1]); array_push($aCurrentUser, $aName[1]);
} } else if ($aName[0] == 'current_contact') {
if ($aName[0] == 'current_contact') {
array_push($aCurrentContact, $aName[1]); array_push($aCurrentContact, $aName[1]);
} }
} }
if (count($aCurrentUser) > 0) { if (count($aCurrentUser) > 0) {
$oUser = UserRights::GetUserObject(); static::FillObjectPlaceholders($aPlaceholders, 'current_user', UserRights::GetUserObject(), $aCurrentUser);
$aPlaceholders['current_user->object()'] = $oUser;
foreach ($aCurrentUser as $sField) {
$aPlaceholders['current_user->'.$sField] = $oUser->Get($sField);
}
} }
if (count($aCurrentContact) > 0) { if (count($aCurrentContact) > 0) {
$oPerson = UserRights::GetContactObject(); static::FillObjectPlaceholders($aPlaceholders, 'current_contact', UserRights::GetContactObject(), $aCurrentContact);
$aPlaceholders['current_contact->object()'] = $oPerson;
foreach ($aCurrentContact as $sField) {
$aPlaceholders['current_contact->'.$sField] = $oPerson->Get($sField);
}
} }
} }
return $aPlaceholders; return $aPlaceholders;
} }
/**
* @since 3.1.1 N°6824
* @param array $aPlaceholders
* @param string $sPlaceHolderPrefix
* @param ?\DBObject $oObject
* @param array $aCurrentUser
*
* @return void
*
*/
private static function FillObjectPlaceholders(array &$aPlaceholders, string $sPlaceHolderPrefix, ?\DBObject $oObject, array $aCurrentUser) : void {
$sPlaceHolderKey = $sPlaceHolderPrefix."->object()";
if (is_null($oObject)){
$aContext = [
"current_user_id" => UserRights::GetUserId(),
"null object type" => $sPlaceHolderPrefix,
"fields" => $aCurrentUser,
];
IssueLog::Warning("Unresolved placeholders due to null object in current context", null,
$aContext);
$aPlaceholders[$sPlaceHolderKey] = \Dict::Format("Core:Placeholder:CannotBeResolved", $sPlaceHolderKey);
foreach ($aCurrentUser as $sField) {
$sPlaceHolderKey = $sPlaceHolderPrefix . "->$sField";
$aPlaceholders[$sPlaceHolderKey] = \Dict::Format("Core:Placeholder:CannotBeResolved", $sPlaceHolderKey);
}
} else {
$aPlaceholders[$sPlaceHolderKey] = $oObject;
foreach ($aCurrentUser as $sField) {
$sPlaceHolderKey = $sPlaceHolderPrefix . "->$sField";
if (false === MetaModel::IsValidAttCode(get_class($oObject), $sField)){
$aContext = [
"current_user_id" => UserRights::GetUserId(),
"obj_class" => get_class($oObject),
"placeholder" => $sPlaceHolderKey,
"invalid_field" => $sField,
];
IssueLog::Warning("Unresolved placeholder due to invalid attribute", null,
$aContext);
$aPlaceholders[$sPlaceHolderKey] = \Dict::Format("Core:Placeholder:CannotBeResolved", $sPlaceHolderKey);
continue;
}
$aPlaceholders[$sPlaceHolderKey] = $oObject->Get($sField);
}
}
}
/** /**
* @param \DBSearch $oFilter * @param \DBSearch $oFilter
* *
@@ -6298,6 +6335,13 @@ abstract class MetaModel
*/ */
public static function Startup($config, $bModelOnly = false, $bAllowCache = true, $bTraceSourceFiles = false, $sEnvironment = 'production') public static function Startup($config, $bModelOnly = false, $bAllowCache = true, $bTraceSourceFiles = false, $sEnvironment = 'production')
{ {
// Startup on a new environment is not supported
static $bStarted = false;
if ($bStarted) {
return;
}
$bStarted = true;
self::$m_sEnvironment = $sEnvironment; self::$m_sEnvironment = $sEnvironment;
try { try {
@@ -6376,7 +6420,9 @@ abstract class MetaModel
ExecutionKPI::EnableDuration(self::$m_oConfig->Get('log_kpi_duration')); ExecutionKPI::EnableDuration(self::$m_oConfig->Get('log_kpi_duration'));
ExecutionKPI::EnableMemory(self::$m_oConfig->Get('log_kpi_memory')); ExecutionKPI::EnableMemory(self::$m_oConfig->Get('log_kpi_memory'));
ExecutionKPI::SetAllowedUser(self::$m_oConfig->Get('log_kpi_user_id')); ExecutionKPI::SetAllowedUser(self::$m_oConfig->Get('log_kpi_user_id'));
ExecutionKPI::SetGenerateLegacyReport(self::$m_oConfig->Get('log_kpi_generate_legacy_report'));
ExecutionKPI::SetSlowQueries(self::$m_oConfig->Get('log_kpi_slow_queries'));
self::$m_bSkipCheckToWrite = self::$m_oConfig->Get('skip_check_to_write'); self::$m_bSkipCheckToWrite = self::$m_oConfig->Get('skip_check_to_write');
self::$m_bSkipCheckExtKeys = self::$m_oConfig->Get('skip_check_ext_keys'); self::$m_bSkipCheckExtKeys = self::$m_oConfig->Get('skip_check_ext_keys');
@@ -6470,7 +6516,7 @@ abstract class MetaModel
$aCache['m_aExtensionClassNames'] = self::$m_aExtensionClassNames; $aCache['m_aExtensionClassNames'] = self::$m_aExtensionClassNames;
$aCache['m_Category2Class'] = self::$m_Category2Class; $aCache['m_Category2Class'] = self::$m_Category2Class;
$aCache['m_aRootClasses'] = self::$m_aRootClasses; // array of "classname" => "rootclass" $aCache['m_aRootClasses'] = self::$m_aRootClasses; // array of "classname" => "rootclass"
$aCache['m_aParentClasses'] = self::$m_aParentClasses; // array of ("classname" => array of "parentclass") $aCache['m_aParentClasses'] = self::$m_aParentClasses; // array of ("classname" => array of "parentclass")
$aCache['m_aChildClasses'] = self::$m_aChildClasses; // array of ("classname" => array of "childclass") $aCache['m_aChildClasses'] = self::$m_aChildClasses; // array of ("classname" => array of "childclass")
$aCache['m_aClassParams'] = self::$m_aClassParams; // array of ("classname" => array of class information) $aCache['m_aClassParams'] = self::$m_aClassParams; // array of ("classname" => array of class information)
$aCache['m_aAttribDefs'] = self::$m_aAttribDefs; // array of ("classname" => array of attributes) $aCache['m_aAttribDefs'] = self::$m_aAttribDefs; // array of ("classname" => array of attributes)
@@ -6495,6 +6541,7 @@ abstract class MetaModel
CMDBSource::InitFromConfig(self::$m_oConfig); CMDBSource::InitFromConfig(self::$m_oConfig);
// Later when timezone implementation is correctly done: CMDBSource::SetTimezone($sDBTimezone); // Later when timezone implementation is correctly done: CMDBSource::SetTimezone($sDBTimezone);
ExecutionKPI::InitStats();
} }
/** /**
@@ -6526,6 +6573,19 @@ abstract class MetaModel
return $value; return $value;
} }
/**
* @internal Used for resetting the configuration during automated tests
* @param \Config $oConfiguration
*
* @return void
* @since 3.0.4 3.1.1 3.2.0
*/
public static function SetConfig(Config $oConfiguration)
{
self::$m_oConfig = $oConfiguration;
}
/** /**
* @return Config * @return Config
*/ */
@@ -6714,7 +6774,13 @@ abstract class MetaModel
if ($bMustBeFound && empty($aRow)) if ($bMustBeFound && empty($aRow))
{ {
throw new CoreException("No result for the single row query: '$sSQL'"); $sNotFoundErrorMessage = "No result for the single row query";
IssueLog::Info($sNotFoundErrorMessage, LogChannels::CMDB_SOURCE, [
'class' => $sClass,
'key' => $iKey,
'sql_query' => $sSQL,
]);
throw new CoreException($sNotFoundErrorMessage);
} }
return $aRow; return $aRow;
@@ -6807,25 +6873,21 @@ abstract class MetaModel
* $bMustBeFound=false) * $bMustBeFound=false)
* @throws CoreException if no result found and $bMustBeFound=true * @throws CoreException if no result found and $bMustBeFound=true
* @throws ArchivedObjectException if archive mode disabled and result is archived and $bMustBeFound=true * @throws ArchivedObjectException if archive mode disabled and result is archived and $bMustBeFound=true
* @throws \Exception
*
*/ */
public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null) public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null)
{ {
$oObject = self::GetObjectWithArchive($sClass, $iKey, $bMustBeFound, $bAllowAllData, $aModifierProperties); $oObject = self::GetObjectWithArchive($sClass, $iKey, $bMustBeFound, $bAllowAllData, $aModifierProperties);
if (empty($oObject)) if (empty($oObject)) {
{
return null; return null;
} }
if (!utils::IsArchiveMode() && $oObject->IsArchived()) if (!utils::IsArchiveMode() && $oObject->IsArchived()) {
{
if ($bMustBeFound) { if ($bMustBeFound) {
throw new ArchivedObjectException("The object $sClass::$iKey is archived"); throw new ArchivedObjectException("The object $sClass::$iKey is archived");
} else {
return null;
} }
return null;
} }
return $oObject; return $oObject;
@@ -7608,14 +7670,12 @@ abstract class MetaModel
// Build the list of available extensions // Build the list of available extensions
// //
$aInterfaces = [ $aInterfaces = [
'iApplicationUIExtension',
'iPreferencesExtension',
'iApplicationObjectExtension',
'iLoginFSMExtension', 'iLoginFSMExtension',
'iLoginUIExtension',
'iLogoutExtension', 'iLogoutExtension',
'iQueryModifier', 'iLoginUIExtension',
'iOnClassInitialization', 'iPreferencesExtension',
'iApplicationUIExtension',
'iApplicationObjectExtension',
'iPopupMenuExtension', 'iPopupMenuExtension',
'iPageUIExtension', 'iPageUIExtension',
'iPageUIBlockExtension', 'iPageUIBlockExtension',
@@ -7629,9 +7689,12 @@ abstract class MetaModel
'iBackofficeDictEntriesExtension', 'iBackofficeDictEntriesExtension',
'iBackofficeDictEntriesPrefixesExtension', 'iBackofficeDictEntriesPrefixesExtension',
'iPortalUIExtension', 'iPortalUIExtension',
'iQueryModifier',
'iOnClassInitialization',
'iModuleExtension',
'iKPILoggerExtension',
'ModuleHandlerApiInterface', 'ModuleHandlerApiInterface',
'iNewsroomProvider', 'iNewsroomProvider',
'iModuleExtension',
]; ];
foreach ($aInterfaces as $sInterface) { foreach ($aInterfaces as $sInterface) {
self::$m_aExtensionClassNames[$sInterface] = array(); self::$m_aExtensionClassNames[$sInterface] = array();

View File

@@ -257,7 +257,7 @@ class iTopMutex
$this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false); $this->hDBLink = CMDBSource::GetMysqliInstance($sServer, $sUser, $sPwd, $sSource, $bTlsEnabled, $sTlsCA, false);
if (!$this->hDBLink) { if (!$this->hDBLink) {
throw new Exception("Could not connect to the DB server (host=$sServer, user=$sUser): ".mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno().')'); throw new MySQLException('Could not connect to the DB server '.mysqli_connect_error().' (mysql errno: '.mysqli_connect_errno(), array('host' => $sDBHost, 'user' => $sDBUser));
} }
// Make sure that the server variable `wait_timeout` is at least 86400 seconds for this connection, // Make sure that the server variable `wait_timeout` is at least 86400 seconds for this connection,

View File

@@ -121,7 +121,9 @@ abstract class Trigger extends cmdbAbstractObject
$oAction = MetaModel::GetObject('Action', $iActionId); $oAction = MetaModel::GetObject('Action', $iActionId);
if ($oAction->IsActive()) if ($oAction->IsActive())
{ {
$oKPI = new ExecutionKPI();
$oAction->DoExecute($this, $aContextArgs); $oAction->DoExecute($this, $aContextArgs);
$oKPI->ComputeStatsForExtension($oAction, 'DoExecute');
} }
} }
} }

View File

@@ -761,14 +761,25 @@ class UserRights
protected static $m_aCacheContactPictureAbsUrl = []; protected static $m_aCacheContactPictureAbsUrl = [];
/** @var UserRightsAddOnAPI $m_oAddOn */ /** @var UserRightsAddOnAPI $m_oAddOn */
protected static $m_oAddOn; protected static $m_oAddOn;
protected static $m_oUser; protected static $m_oUser = null;
protected static $m_oRealUser; protected static $m_oRealUser = null;
protected static $m_sSelfRegisterAddOn = null; protected static $m_sSelfRegisterAddOn = null;
protected static $m_aAdmins = array(); protected static $m_aAdmins = array();
protected static $m_aPortalUsers = array(); protected static $m_aPortalUsers = array();
/** @var array array('sName' => $sName, 'bSuccess' => $bSuccess); */ /** @var array array('sName' => $sName, 'bSuccess' => $bSuccess); */
private static $m_sLastLoginStatus = null; private static $m_sLastLoginStatus = null;
/**
* @return void
* @since 3.0.4 3.1.1 3.2.0
*/
protected static function ResetCurrentUserData()
{
self::$m_oUser = null;
self::$m_oRealUser = null;
self::$m_sLastLoginStatus = null;
}
/** /**
* @param string $sModuleName * @param string $sModuleName
* *
@@ -787,8 +798,7 @@ class UserRights
} }
self::$m_oAddOn = new $sModuleName; self::$m_oAddOn = new $sModuleName;
self::$m_oAddOn->Init(); self::$m_oAddOn->Init();
self::$m_oUser = null; self::ResetCurrentUserData();
self::$m_oRealUser = null;
} }
/** /**
@@ -846,6 +856,8 @@ class UserRights
} }
/** /**
* Set the current user (as part of the login process)
*
* @param string $sLogin Login of the concerned user * @param string $sLogin Login of the concerned user
* @param string $sAuthentication * @param string $sAuthentication
* *
@@ -872,6 +884,19 @@ class UserRights
return true; return true;
} }
/**
* Reset current user and cleanup associated SESSION data
*
* @return void
* @since 3.0.4 3.1.1 3.2.0
*/
public static function Logoff()
{
self::ResetCurrentUserData();
Dict::SetUserLanguage(null);
self::_ResetSessionCache();
}
/** /**
* @param string $sLogin Login of the user to check the credentials for * @param string $sLogin Login of the user to check the credentials for
* @param string $sPassword * @param string $sPassword

View File

@@ -20,6 +20,8 @@ $ibo-dashlet-badge--icon--size: 48px !default;
$ibo-dashlet-badge--action-icon--margin-right: $ibo-spacing-300 !default; $ibo-dashlet-badge--action-icon--margin-right: $ibo-spacing-300 !default;
$ibo-dashlet-badge--body--tooltip-title--margin-bottom: $ibo-spacing-500 !default;
/* CSS variables (can be changed directly from the browser) */ /* CSS variables (can be changed directly from the browser) */
:root { :root {
--ibo-dashlet-badge--min-width: #{$ibo-dashlet-badge--min-width}; --ibo-dashlet-badge--min-width: #{$ibo-dashlet-badge--min-width};
@@ -74,18 +76,27 @@ $ibo-dashlet-badge--action-icon--margin-right: $ibo-spacing-300 !default;
@extend %ibo-hyperlink-inherited-colors; @extend %ibo-hyperlink-inherited-colors;
} }
} }
.ibo-dashlet-badge--action-list-count{
margin-right: $ibo-dashlet-badge--action-list-count--margin-right; .ibo-dashlet-badge--action-list-count {
@extend %ibo-font-ral-bol-450; margin-right: $ibo-dashlet-badge--action-list-count--margin-right;
@extend %ibo-font-ral-bol-450;
} }
.ibo-dashlet-badge--action-list-label{
display: inline-block; .ibo-dashlet-badge--action-list-label {
@extend %ibo-text-truncated-with-ellipsis; display: inline-block;
@extend %ibo-text-truncated-with-ellipsis;
} }
.ibo-dashlet-badge--action-create{
@extend %ibo-baseline-centered-content; .ibo-dashlet-badge--action-create {
@extend %ibo-font-size-150; @extend %ibo-baseline-centered-content;
@extend %ibo-font-size-150;
} }
.ibo-dashlet-badge--action-create-icon{
margin-right: $ibo-dashlet-badge--action-icon--margin-right; .ibo-dashlet-badge--action-create-icon {
margin-right: $ibo-dashlet-badge--action-icon--margin-right;
}
.ibo-dashlet-badge--body--tooltip-title {
@extend %ibo-font-weight-600;
margin-bottom: $ibo-dashlet-badge--body--tooltip-title--margin-bottom;
} }

View File

@@ -13,6 +13,9 @@ $body-overflow-x: hidden !default;
$body-overflow-y: auto !default; $body-overflow-y: auto !default;
/*N°5786 - Avoid strong text to always be grey (default Bulma color for this var.) so strong text can keep its color. This is mostly for text within the .ibo-is-html-content. */
$text-strong: inherit !default;
/** /**
* customize Bulma content variables * customize Bulma content variables
* See https://bulma.io/documentation/elements/content/ * See https://bulma.io/documentation/elements/content/

View File

@@ -355,4 +355,26 @@
</presentation> </presentation>
</class> </class>
</classes> </classes>
<meta>
<classes>
<class id="UserInternal" _delta="define_if_not_exists">
<fields>
<field id="contactid" xsi:type="AttributeExternalKey">
<target_class>Contact</target_class>
</field>
<field id="first_name" xsi:type="AttributeExternalField"/>
<field id="last_name" xsi:type="AttributeExternalField"/>
<field id="status" xsi:type="AttributeEnum"/>
<field id="org_id" xsi:type="AttributeExternalField"/>
<field id="email" xsi:type="AttributeExternalField"/>
<field id="login" xsi:type="AttributeString"/>
<field id="language" xsi:type="AttributeApplicationLanguage"/>
<field id="status" xsi:type="AttributeEnum"/>
<field id="allowed_org_list" xsi:type="AttributeLinkedSetIndirect"/>
<field id="profile_list" xsi:type="AttributeLinkedSetIndirect"/>
<field id="log" xsi:type="AttributeCaseLog"/>
</fields>
</class>
</classes>
</meta>
</itop_design> </itop_design>

View File

@@ -344,6 +344,7 @@ class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExt
while ($oAttachment = $oSet->Fetch()) while ($oAttachment = $oSet->Fetch())
{ {
$oTempAttachment = clone $oAttachment; $oTempAttachment = clone $oAttachment;
$oTempAttachment->Set('expire', time() + utils::GetConfig()->Get('draft_attachments_lifetime'));
$oTempAttachment->Set('item_id', null); $oTempAttachment->Set('item_id', null);
$oTempAttachment->Set('temp_id', $sTempId); $oTempAttachment->Set('temp_id', $sTempId);
$oTempAttachment->DBInsert(); $oTempAttachment->DBInsert();

View File

@@ -235,13 +235,16 @@ class DBRestore extends DBBackup
if (in_array($oFileInfo->getFilename(), $aStandardFiles)) { if (in_array($oFileInfo->getFilename(), $aStandardFiles)) {
continue; continue;
} }
if (strncmp($oFileInfo->getPathname(), $sDataDir.'/production-modules', strlen($sDataDir.'/production-modules')) == 0) { // Normalize filenames to cope with Windows backslashes
$sPath = str_replace('\\', '/', $oFileInfo->getPathname());
$sRefPath = str_replace('\\', '/', $sDataDir.'/production-modules');
if (strncmp($sPath, $sRefPath, strlen($sRefPath)) == 0) {
continue; continue;
} }
$aExtraFiles[$oFileInfo->getPathname()] = APPROOT.substr($oFileInfo->getPathname(), strlen($sDataDir)); $aExtraFiles[$oFileInfo->getPathname()] = APPROOT.substr($oFileInfo->getPathname(), strlen($sDataDir));
} }
return $aExtraFiles; return $aExtraFiles;
} }
} }

View File

@@ -6354,17 +6354,7 @@
<attribute id="connectableci_id"/> <attribute id="connectableci_id"/>
</attributes> </attributes>
</reconciliation> </reconciliation>
<uniqueness_rules> <uniqueness_rules/>
<rule id="no_duplicate">
<attributes>
<attribute id="networkdevice_id"/>
<attribute id="connectableci_id"/>
</attributes>
<filter><![CDATA[]]></filter>
<disabled>false</disabled>
<is_blocking>true</is_blocking>
</rule>
</uniqueness_rules>
</properties> </properties>
<fields> <fields>
<field id="networkdevice_id" xsi:type="AttributeExternalKey"> <field id="networkdevice_id" xsi:type="AttributeExternalKey">

View File

@@ -88,7 +88,7 @@ Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
// Errors // Errors
'iTopUpdate:Error:MissingFunction' => 'Lehetetlen elindítani a frissítést, hiányzó funkció', 'iTopUpdate:Error:MissingFunction' => 'Lehetetlen elindítani a frissítést, hiányzó funkció',
'iTopUpdate:Error:MissingFile' => 'Hiányzó fájl: %1$', 'iTopUpdate:Error:MissingFile' => 'Hiányzó fájl: %1$s',
'iTopUpdate:Error:CorruptedFile' => 'A %1$s fájl sérült', 'iTopUpdate:Error:CorruptedFile' => 'A %1$s fájl sérült',
'iTopUpdate:Error:BadFileFormat' => 'A frissítési fájl nem zip fájl', 'iTopUpdate:Error:BadFileFormat' => 'A frissítési fájl nem zip fájl',
'iTopUpdate:Error:BadFileContent' => 'A frissítési fájl nem alkalmazás archívum', 'iTopUpdate:Error:BadFileContent' => 'A frissítési fájl nem alkalmazás archívum',

View File

@@ -58,7 +58,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:FAQ/Attribute:category_id+' => '', 'Class:FAQ/Attribute:category_id+' => '',
'Class:FAQ/Attribute:category_name' => '类别名称', 'Class:FAQ/Attribute:category_name' => '类别名称',
'Class:FAQ/Attribute:category_name+' => '', 'Class:FAQ/Attribute:category_name+' => '',
'Class:FAQ/Attribute:error_code' => '错误码', 'Class:FAQ/Attribute:error_code' => '错误码',
'Class:FAQ/Attribute:error_code+' => '', 'Class:FAQ/Attribute:error_code+' => '',
'Class:FAQ/Attribute:key_words' => '关键字', 'Class:FAQ/Attribute:key_words' => '关键字',
'Class:FAQ/Attribute:key_words+' => '', 'Class:FAQ/Attribute:key_words+' => '',

View File

@@ -66,11 +66,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:Incident/Attribute:status+' => '', 'Class:Incident/Attribute:status+' => '',
'Class:Incident/Attribute:status/Value:new' => '新建', 'Class:Incident/Attribute:status/Value:new' => '新建',
'Class:Incident/Attribute:status/Value:new+' => '', 'Class:Incident/Attribute:status/Value:new+' => '',
'Class:Incident/Attribute:status/Value:escalated_tto' => '已升级响应时间', 'Class:Incident/Attribute:status/Value:escalated_tto' => '已升级TTO',
'Class:Incident/Attribute:status/Value:escalated_tto+' => '', 'Class:Incident/Attribute:status/Value:escalated_tto+' => '',
'Class:Incident/Attribute:status/Value:assigned' => '已分配', 'Class:Incident/Attribute:status/Value:assigned' => '已分配',
'Class:Incident/Attribute:status/Value:assigned+' => '', 'Class:Incident/Attribute:status/Value:assigned+' => '',
'Class:Incident/Attribute:status/Value:escalated_ttr' => '已升级解决时间', 'Class:Incident/Attribute:status/Value:escalated_ttr' => '已升级TTR',
'Class:Incident/Attribute:status/Value:escalated_ttr+' => '', 'Class:Incident/Attribute:status/Value:escalated_ttr+' => '',
'Class:Incident/Attribute:status/Value:waiting_for_approval' => '等待批准', 'Class:Incident/Attribute:status/Value:waiting_for_approval' => '等待批准',
'Class:Incident/Attribute:status/Value:waiting_for_approval+' => '', 'Class:Incident/Attribute:status/Value:waiting_for_approval+' => '',
@@ -90,8 +90,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:Incident/Attribute:impact/Value:3+' => '', 'Class:Incident/Attribute:impact/Value:3+' => '',
'Class:Incident/Attribute:priority' => '优先级', 'Class:Incident/Attribute:priority' => '优先级',
'Class:Incident/Attribute:priority+' => '', 'Class:Incident/Attribute:priority+' => '',
'Class:Incident/Attribute:priority/Value:1' => '非常高', 'Class:Incident/Attribute:priority/Value:1' => '紧急',
'Class:Incident/Attribute:priority/Value:1+' => '非常高', 'Class:Incident/Attribute:priority/Value:1+' => '紧急',
'Class:Incident/Attribute:priority/Value:2' => '高', 'Class:Incident/Attribute:priority/Value:2' => '高',
'Class:Incident/Attribute:priority/Value:2+' => '高', 'Class:Incident/Attribute:priority/Value:2+' => '高',
'Class:Incident/Attribute:priority/Value:3' => '中', 'Class:Incident/Attribute:priority/Value:3' => '中',
@@ -100,8 +100,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:Incident/Attribute:priority/Value:4+' => '低', 'Class:Incident/Attribute:priority/Value:4+' => '低',
'Class:Incident/Attribute:urgency' => '紧急度', 'Class:Incident/Attribute:urgency' => '紧急度',
'Class:Incident/Attribute:urgency+' => '', 'Class:Incident/Attribute:urgency+' => '',
'Class:Incident/Attribute:urgency/Value:1' => '非常高', 'Class:Incident/Attribute:urgency/Value:1' => '紧急',
'Class:Incident/Attribute:urgency/Value:1+' => '非常高', 'Class:Incident/Attribute:urgency/Value:1+' => '紧急',
'Class:Incident/Attribute:urgency/Value:2' => '高', 'Class:Incident/Attribute:urgency/Value:2' => '高',
'Class:Incident/Attribute:urgency/Value:2+' => '高', 'Class:Incident/Attribute:urgency/Value:2+' => '高',
'Class:Incident/Attribute:urgency/Value:3' => '中', 'Class:Incident/Attribute:urgency/Value:3' => '中',
@@ -136,7 +136,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:Incident/Attribute:escalation_flag/Value:no+' => '否', 'Class:Incident/Attribute:escalation_flag/Value:no+' => '否',
'Class:Incident/Attribute:escalation_flag/Value:yes' => '是', 'Class:Incident/Attribute:escalation_flag/Value:yes' => '是',
'Class:Incident/Attribute:escalation_flag/Value:yes+' => '是', 'Class:Incident/Attribute:escalation_flag/Value:yes+' => '是',
'Class:Incident/Attribute:escalation_reason' => '热门', 'Class:Incident/Attribute:escalation_reason' => '升级原因',
'Class:Incident/Attribute:escalation_reason+' => '', 'Class:Incident/Attribute:escalation_reason+' => '',
'Class:Incident/Attribute:assignment_date' => '分配日期', 'Class:Incident/Attribute:assignment_date' => '分配日期',
'Class:Incident/Attribute:assignment_date+' => '', 'Class:Incident/Attribute:assignment_date+' => '',
@@ -146,21 +146,21 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:Incident/Attribute:last_pending_date+' => '', 'Class:Incident/Attribute:last_pending_date+' => '',
'Class:Incident/Attribute:cumulatedpending' => '累计待定', 'Class:Incident/Attribute:cumulatedpending' => '累计待定',
'Class:Incident/Attribute:cumulatedpending+' => '', 'Class:Incident/Attribute:cumulatedpending+' => '',
'Class:Incident/Attribute:tto' => '响应时间', 'Class:Incident/Attribute:tto' => 'TTO',
'Class:Incident/Attribute:tto+' => '', 'Class:Incident/Attribute:tto+' => '响应时间',
'Class:Incident/Attribute:ttr' => '解决时间', 'Class:Incident/Attribute:ttr' => 'TTR',
'Class:Incident/Attribute:ttr+' => '', 'Class:Incident/Attribute:ttr+' => '解决时限',
'Class:Incident/Attribute:tto_escalation_deadline' => '响应时间截止', 'Class:Incident/Attribute:tto_escalation_deadline' => 'TTO截止日期',
'Class:Incident/Attribute:tto_escalation_deadline+' => '', 'Class:Incident/Attribute:tto_escalation_deadline+' => '',
'Class:Incident/Attribute:sla_tto_passed' => '超过SLA响应时间', 'Class:Incident/Attribute:sla_tto_passed' => 'SLA TTO 合格',
'Class:Incident/Attribute:sla_tto_passed+' => '', 'Class:Incident/Attribute:sla_tto_passed+' => '',
'Class:Incident/Attribute:sla_tto_over' => 'SLA响应时间结束', 'Class:Incident/Attribute:sla_tto_over' => 'SLA TTO 超时',
'Class:Incident/Attribute:sla_tto_over+' => '', 'Class:Incident/Attribute:sla_tto_over+' => '',
'Class:Incident/Attribute:ttr_escalation_deadline' => '解决时间截止', 'Class:Incident/Attribute:ttr_escalation_deadline' => 'TTR截止日期',
'Class:Incident/Attribute:ttr_escalation_deadline+' => '', 'Class:Incident/Attribute:ttr_escalation_deadline+' => '',
'Class:Incident/Attribute:sla_ttr_passed' => '超过SLA解决时间', 'Class:Incident/Attribute:sla_ttr_passed' => 'SLA TTR 合格',
'Class:Incident/Attribute:sla_ttr_passed+' => '', 'Class:Incident/Attribute:sla_ttr_passed+' => '',
'Class:Incident/Attribute:sla_ttr_over' => 'SLA解决时间结束', 'Class:Incident/Attribute:sla_ttr_over' => 'SLA TTR 超时',
'Class:Incident/Attribute:sla_ttr_over+' => '', 'Class:Incident/Attribute:sla_ttr_over+' => '',
'Class:Incident/Attribute:time_spent' => '耗时', 'Class:Incident/Attribute:time_spent' => '耗时',
'Class:Incident/Attribute:time_spent+' => '', 'Class:Incident/Attribute:time_spent+' => '',
@@ -182,7 +182,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:Incident/Attribute:resolution_code/Value:training+' => '培训', 'Class:Incident/Attribute:resolution_code/Value:training+' => '培训',
'Class:Incident/Attribute:solution' => '解决方案', 'Class:Incident/Attribute:solution' => '解决方案',
'Class:Incident/Attribute:solution+' => '', 'Class:Incident/Attribute:solution+' => '',
'Class:Incident/Attribute:pending_reason' => '待定原因', 'Class:Incident/Attribute:pending_reason' => '待定原因',
'Class:Incident/Attribute:pending_reason+' => '', 'Class:Incident/Attribute:pending_reason+' => '',
'Class:Incident/Attribute:parent_incident_id' => '父级事件', 'Class:Incident/Attribute:parent_incident_id' => '父级事件',
'Class:Incident/Attribute:parent_incident_id+' => '', 'Class:Incident/Attribute:parent_incident_id+' => '',

View File

@@ -66,7 +66,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:KnownError/Attribute:workaround+' => '', 'Class:KnownError/Attribute:workaround+' => '',
'Class:KnownError/Attribute:solution' => '解决方案', 'Class:KnownError/Attribute:solution' => '解决方案',
'Class:KnownError/Attribute:solution+' => '', 'Class:KnownError/Attribute:solution+' => '',
'Class:KnownError/Attribute:error_code' => '错误码', 'Class:KnownError/Attribute:error_code' => '错误码',
'Class:KnownError/Attribute:error_code+' => '', 'Class:KnownError/Attribute:error_code+' => '',
'Class:KnownError/Attribute:domain' => '类型', 'Class:KnownError/Attribute:domain' => '类型',
'Class:KnownError/Attribute:domain+' => '', 'Class:KnownError/Attribute:domain+' => '',

View File

@@ -230,6 +230,7 @@ HTML
$this->Set('refresh_token', $oAccessToken->getRefreshToken()); $this->Set('refresh_token', $oAccessToken->getRefreshToken());
} }
$this->Set('status', 'active'); $this->Set('status', 'active');
$this->AllowWrite();
$this->DBUpdate(); $this->DBUpdate();
} }
]]></code> ]]></code>

View File

@@ -11,6 +11,7 @@ use Combodo\iTop\Application\TwigBase\Controller\Controller;
use Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProviderFactory; use Combodo\iTop\Core\Authentication\Client\OAuth\OAuthClientProviderFactory;
use Dict; use Dict;
use IssueLog; use IssueLog;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use MetaModel; use MetaModel;
use utils; use utils;
use WebPage; use WebPage;
@@ -65,13 +66,15 @@ class AjaxOauthClientController extends Controller
} }
if (isset($aQuery['code'])) { if (isset($aQuery['code'])) {
$sCode = $aQuery['code']; $sCode = $aQuery['code'];
$oAccessToken = OAuthClientProviderFactory::GetAccessTokenFromCode($oOAuthClient, $sCode); try {
$oAccessToken = OAuthClientProviderFactory::GetAccessTokenFromCode($oOAuthClient, $sCode);
$oOAuthClient->SetAccessToken($oAccessToken); $oOAuthClient->SetAccessToken($oAccessToken);
$aResult['status'] = 'success';
}
catch (IdentityProviderException $e) {
$aResult['status'] = 'success'; $aResult['status'] = 'error';
$aResult['error_description'] = $e->getMessage();
}
} }
} else { } else {
$aResult['status'] = 'error'; $aResult['status'] = 'error';

File diff suppressed because one or more lines are too long

View File

@@ -1803,6 +1803,11 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{
text-decoration: line-through; text-decoration: line-through;
} }
/* Tippy: Handle multi-line content */
.tippy-content {
white-space: pre-line;
}
/**********************************************************/ /**********************************************************/
/* Shameful area (things that should be refactored soon) */ /* Shameful area (things that should be refactored soon) */
/**********************************************************/ /**********************************************************/

View File

@@ -1105,7 +1105,11 @@ class ObjectController extends BrickController
// When reaching to an Attachment, we have to check security on its host object instead of the Attachment itself // When reaching to an Attachment, we have to check security on its host object instead of the Attachment itself
if ($sObjectClass === 'Attachment') if ($sObjectClass === 'Attachment')
{ {
$oAttachment = MetaModel::GetObject($sObjectClass, $sObjectId, true, true);
$oAttachment = MetaModel::GetObject($sObjectClass, $sObjectId, false, true);
if ($oAttachment === null) {
throw new HttpException(Response::HTTP_NOT_FOUND, Dict::S('UI:ObjectDoesNotExist'));
}
$sHostClass = $oAttachment->Get('item_class'); $sHostClass = $oAttachment->Get('item_class');
$sHostId = $oAttachment->Get('item_id'); $sHostId = $oAttachment->Get('item_id');
@@ -1384,7 +1388,7 @@ class ObjectController extends BrickController
$aObjectIds = $oRequestManipulator->ReadParam('aObjectIds', array(), FILTER_UNSAFE_RAW); $aObjectIds = $oRequestManipulator->ReadParam('aObjectIds', array(), FILTER_UNSAFE_RAW);
$aObjectAttCodes = $oRequestManipulator->ReadParam('aObjectAttCodes', array(), FILTER_UNSAFE_RAW); $aObjectAttCodes = $oRequestManipulator->ReadParam('aObjectAttCodes', array(), FILTER_UNSAFE_RAW);
$aLinkAttCodes = $oRequestManipulator->ReadParam('aLinkAttCodes', array(), FILTER_UNSAFE_RAW); $aLinkAttCodes = $oRequestManipulator->ReadParam('aLinkAttCodes', array(), FILTER_UNSAFE_RAW);
$sDateTimePickerWidgetParent = $oRequestManipulator->ReadParam('sDateTimePickerWidgetParent', array(), FILTER_SANITIZE_STRING); $sDateTimePickerWidgetParent = $oRequestManipulator->ReadParam('sDateTimePickerWidgetParent', array(), FILTER_UNSAFE_RAW);
if (empty($sObjectClass) || empty($aObjectIds) || empty($aObjectAttCodes)) { if (empty($sObjectClass) || empty($aObjectIds) || empty($aObjectAttCodes)) {
IssueLog::Info(__METHOD__.' at line '.__LINE__.' : sObjectClass, aObjectIds and aObjectAttCodes expected, "'.$sObjectClass.'", "'.implode('/', IssueLog::Info(__METHOD__.' at line '.__LINE__.' : sObjectClass, aObjectIds and aObjectAttCodes expected, "'.$sObjectClass.'", "'.implode('/',

View File

@@ -67,6 +67,8 @@
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function(){ $(document).ready(function(){
CombodoJsActivity.AddOngoingScript();
// Form field set declaration // Form field set declaration
var oFieldSet_{{ sFormIdSanitized }} = $('#{{ sFormId }} > .form_fields').field_set({{ form.fieldset|json_encode()|raw }}); var oFieldSet_{{ sFormIdSanitized }} = $('#{{ sFormId }} > .form_fields').field_set({{ form.fieldset|json_encode()|raw }});
// Form handler declaration // Form handler declaration
@@ -149,5 +151,7 @@
$('#{{ sFormId }}').closest('.modal').scrollTop(0); $('#{{ sFormId }}').closest('.modal').scrollTop(0);
$('#{{ sFormId }}').closest('.modal').find('.modal-footer').hide(); $('#{{ sFormId }}').closest('.modal').find('.modal-footer').hide();
{% endif %} {% endif %}
CombodoJsActivity.RemoveOngoingScript();
}); });
</script> </script>

View File

@@ -103,8 +103,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:Problem/Attribute:impact/Value:3+' => '', 'Class:Problem/Attribute:impact/Value:3+' => '',
'Class:Problem/Attribute:urgency' => '紧急度', 'Class:Problem/Attribute:urgency' => '紧急度',
'Class:Problem/Attribute:urgency+' => '', 'Class:Problem/Attribute:urgency+' => '',
'Class:Problem/Attribute:urgency/Value:1' => '非常高', 'Class:Problem/Attribute:urgency/Value:1' => '紧急',
'Class:Problem/Attribute:urgency/Value:1+' => '非常高', 'Class:Problem/Attribute:urgency/Value:1+' => '紧急',
'Class:Problem/Attribute:urgency/Value:2' => '高', 'Class:Problem/Attribute:urgency/Value:2' => '高',
'Class:Problem/Attribute:urgency/Value:2+' => '高', 'Class:Problem/Attribute:urgency/Value:2+' => '高',
'Class:Problem/Attribute:urgency/Value:3' => '中', 'Class:Problem/Attribute:urgency/Value:3' => '中',
@@ -113,8 +113,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:Problem/Attribute:urgency/Value:4+' => '低', 'Class:Problem/Attribute:urgency/Value:4+' => '低',
'Class:Problem/Attribute:priority' => '优先级', 'Class:Problem/Attribute:priority' => '优先级',
'Class:Problem/Attribute:priority+' => '', 'Class:Problem/Attribute:priority+' => '',
'Class:Problem/Attribute:priority/Value:1' => '非常高', 'Class:Problem/Attribute:priority/Value:1' => '紧急',
'Class:Problem/Attribute:priority/Value:1+' => '非常高', 'Class:Problem/Attribute:priority/Value:1+' => '紧急',
'Class:Problem/Attribute:priority/Value:2' => '高', 'Class:Problem/Attribute:priority/Value:2' => '高',
'Class:Problem/Attribute:priority/Value:2+' => '高', 'Class:Problem/Attribute:priority/Value:2+' => '高',
'Class:Problem/Attribute:priority/Value:3' => '中', 'Class:Problem/Attribute:priority/Value:3' => '中',

View File

@@ -125,6 +125,7 @@
<group id="Audit" _delta="define"> <group id="Audit" _delta="define">
<classes> <classes>
<!-- This class list is also present in AdminTools group --> <!-- This class list is also present in AdminTools group -->
<class id="AuditDomain"/>
<class id="AuditCategory"/> <class id="AuditCategory"/>
<class id="AuditRule"/> <class id="AuditRule"/>
<class id="ResourceRunQueriesMenu"/> <class id="ResourceRunQueriesMenu"/>
@@ -166,6 +167,7 @@
<class id="URP_UserProfile"/> <class id="URP_UserProfile"/>
<class id="URP_Profiles"/> <class id="URP_Profiles"/>
<!-- Audit group --> <!-- Audit group -->
<class id="AuditDomain"/>
<class id="AuditCategory"/> <class id="AuditCategory"/>
<class id="AuditRule"/> <class id="AuditRule"/>
<!-- Query group --> <!-- Query group -->

View File

@@ -58,11 +58,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:UserRequest/Attribute:status+' => '', 'Class:UserRequest/Attribute:status+' => '',
'Class:UserRequest/Attribute:status/Value:new' => '新建', 'Class:UserRequest/Attribute:status/Value:new' => '新建',
'Class:UserRequest/Attribute:status/Value:new+' => '', 'Class:UserRequest/Attribute:status/Value:new+' => '',
'Class:UserRequest/Attribute:status/Value:escalated_tto' => '已升级响应时间', 'Class:UserRequest/Attribute:status/Value:escalated_tto' => '已升级TTO',
'Class:UserRequest/Attribute:status/Value:escalated_tto+' => '', 'Class:UserRequest/Attribute:status/Value:escalated_tto+' => '',
'Class:UserRequest/Attribute:status/Value:assigned' => '已分配', 'Class:UserRequest/Attribute:status/Value:assigned' => '已分配',
'Class:UserRequest/Attribute:status/Value:assigned+' => '', 'Class:UserRequest/Attribute:status/Value:assigned+' => '',
'Class:UserRequest/Attribute:status/Value:escalated_ttr' => '已升级解决时间', 'Class:UserRequest/Attribute:status/Value:escalated_ttr' => '已升级TTR',
'Class:UserRequest/Attribute:status/Value:escalated_ttr+' => '', 'Class:UserRequest/Attribute:status/Value:escalated_ttr+' => '',
'Class:UserRequest/Attribute:status/Value:waiting_for_approval' => '等待批准', 'Class:UserRequest/Attribute:status/Value:waiting_for_approval' => '等待批准',
'Class:UserRequest/Attribute:status/Value:waiting_for_approval+' => '', 'Class:UserRequest/Attribute:status/Value:waiting_for_approval+' => '',
@@ -90,8 +90,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:UserRequest/Attribute:impact/Value:3+' => '', 'Class:UserRequest/Attribute:impact/Value:3+' => '',
'Class:UserRequest/Attribute:priority' => '优先级', 'Class:UserRequest/Attribute:priority' => '优先级',
'Class:UserRequest/Attribute:priority+' => '', 'Class:UserRequest/Attribute:priority+' => '',
'Class:UserRequest/Attribute:priority/Value:1' => '非常高', 'Class:UserRequest/Attribute:priority/Value:1' => '紧急',
'Class:UserRequest/Attribute:priority/Value:1+' => '非常高', 'Class:UserRequest/Attribute:priority/Value:1+' => '紧急',
'Class:UserRequest/Attribute:priority/Value:2' => '高', 'Class:UserRequest/Attribute:priority/Value:2' => '高',
'Class:UserRequest/Attribute:priority/Value:2+' => '高', 'Class:UserRequest/Attribute:priority/Value:2+' => '高',
'Class:UserRequest/Attribute:priority/Value:3' => '中', 'Class:UserRequest/Attribute:priority/Value:3' => '中',
@@ -100,8 +100,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:UserRequest/Attribute:priority/Value:4+' => '低', 'Class:UserRequest/Attribute:priority/Value:4+' => '低',
'Class:UserRequest/Attribute:urgency' => '紧急度', 'Class:UserRequest/Attribute:urgency' => '紧急度',
'Class:UserRequest/Attribute:urgency+' => '', 'Class:UserRequest/Attribute:urgency+' => '',
'Class:UserRequest/Attribute:urgency/Value:1' => '非常高', 'Class:UserRequest/Attribute:urgency/Value:1' => '紧急',
'Class:UserRequest/Attribute:urgency/Value:1+' => '非常高', 'Class:UserRequest/Attribute:urgency/Value:1+' => '紧急',
'Class:UserRequest/Attribute:urgency/Value:2' => '高', 'Class:UserRequest/Attribute:urgency/Value:2' => '高',
'Class:UserRequest/Attribute:urgency/Value:2+' => '高', 'Class:UserRequest/Attribute:urgency/Value:2+' => '高',
'Class:UserRequest/Attribute:urgency/Value:3' => '中', 'Class:UserRequest/Attribute:urgency/Value:3' => '中',
@@ -134,7 +134,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:UserRequest/Attribute:servicesubcategory_id+' => '', 'Class:UserRequest/Attribute:servicesubcategory_id+' => '',
'Class:UserRequest/Attribute:servicesubcategory_name' => '子服务名称', 'Class:UserRequest/Attribute:servicesubcategory_name' => '子服务名称',
'Class:UserRequest/Attribute:servicesubcategory_name+' => '', 'Class:UserRequest/Attribute:servicesubcategory_name+' => '',
'Class:UserRequest/Attribute:escalation_flag' => '是否升级', 'Class:UserRequest/Attribute:escalation_flag' => '升级标签',
'Class:UserRequest/Attribute:escalation_flag+' => '', 'Class:UserRequest/Attribute:escalation_flag+' => '',
'Class:UserRequest/Attribute:escalation_flag/Value:no' => '否', 'Class:UserRequest/Attribute:escalation_flag/Value:no' => '否',
'Class:UserRequest/Attribute:escalation_flag/Value:no+' => '否', 'Class:UserRequest/Attribute:escalation_flag/Value:no+' => '否',
@@ -150,30 +150,30 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:UserRequest/Attribute:last_pending_date+' => '', 'Class:UserRequest/Attribute:last_pending_date+' => '',
'Class:UserRequest/Attribute:cumulatedpending' => '累计待定', 'Class:UserRequest/Attribute:cumulatedpending' => '累计待定',
'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:cumulatedpending+' => '',
'Class:UserRequest/Attribute:tto' => '响应时间', 'Class:UserRequest/Attribute:tto' => 'TTO',
'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:tto+' => '',
'Class:UserRequest/Attribute:ttr' => '解决时间', 'Class:UserRequest/Attribute:ttr' => 'TTR',
'Class:UserRequest/Attribute:ttr+' => '', 'Class:UserRequest/Attribute:ttr+' => '',
'Class:UserRequest/Attribute:tto_escalation_deadline' => '响应时间截止', 'Class:UserRequest/Attribute:tto_escalation_deadline' => 'TTO截止日期',
'Class:UserRequest/Attribute:tto_escalation_deadline+' => '', 'Class:UserRequest/Attribute:tto_escalation_deadline+' => '',
'Class:UserRequest/Attribute:sla_tto_passed' => '超过SLA响应时间', 'Class:UserRequest/Attribute:sla_tto_passed' => 'SLA TTO 合格',
'Class:UserRequest/Attribute:sla_tto_passed+' => '', 'Class:UserRequest/Attribute:sla_tto_passed+' => '',
'Class:UserRequest/Attribute:sla_tto_over' => 'SLA响应时间超过', 'Class:UserRequest/Attribute:sla_tto_over' => 'SLA TTO 超时',
'Class:UserRequest/Attribute:sla_tto_over+' => '', 'Class:UserRequest/Attribute:sla_tto_over+' => '',
'Class:UserRequest/Attribute:ttr_escalation_deadline' => '解决时间截止', 'Class:UserRequest/Attribute:ttr_escalation_deadline' => 'TTR截止日期',
'Class:UserRequest/Attribute:ttr_escalation_deadline+' => '', 'Class:UserRequest/Attribute:ttr_escalation_deadline+' => '',
'Class:UserRequest/Attribute:sla_ttr_passed' => '超过SLA解决时间', 'Class:UserRequest/Attribute:sla_ttr_passed' => 'SLA TTR 合格',
'Class:UserRequest/Attribute:sla_ttr_passed+' => '', 'Class:UserRequest/Attribute:sla_ttr_passed+' => '',
'Class:UserRequest/Attribute:sla_ttr_over' => 'SLA解决时间超过', 'Class:UserRequest/Attribute:sla_ttr_over' => 'SLA TTR 超时',
'Class:UserRequest/Attribute:sla_ttr_over+' => '', 'Class:UserRequest/Attribute:sla_ttr_over+' => '',
'Class:UserRequest/Attribute:time_spent' => '耗时', 'Class:UserRequest/Attribute:time_spent' => '耗时',
'Class:UserRequest/Attribute:time_spent+' => '', 'Class:UserRequest/Attribute:time_spent+' => '',
'Class:UserRequest/Attribute:resolution_code' => '解决码', 'Class:UserRequest/Attribute:resolution_code' => '解决码',
'Class:UserRequest/Attribute:resolution_code+' => '', 'Class:UserRequest/Attribute:resolution_code+' => '',
'Class:UserRequest/Attribute:resolution_code/Value:assistance' => '帮助', 'Class:UserRequest/Attribute:resolution_code/Value:assistance' => '帮助',
'Class:UserRequest/Attribute:resolution_code/Value:assistance+' => '帮助', 'Class:UserRequest/Attribute:resolution_code/Value:assistance+' => '帮助',
'Class:UserRequest/Attribute:resolution_code/Value:bug fixed' => '缺陷修复', 'Class:UserRequest/Attribute:resolution_code/Value:bug fixed' => 'bug修复',
'Class:UserRequest/Attribute:resolution_code/Value:bug fixed+' => '缺陷修复', 'Class:UserRequest/Attribute:resolution_code/Value:bug fixed+' => 'bug修复',
'Class:UserRequest/Attribute:resolution_code/Value:hardware repair' => '硬件维修', 'Class:UserRequest/Attribute:resolution_code/Value:hardware repair' => '硬件维修',
'Class:UserRequest/Attribute:resolution_code/Value:hardware repair+' => '硬件维修', 'Class:UserRequest/Attribute:resolution_code/Value:hardware repair+' => '硬件维修',
'Class:UserRequest/Attribute:resolution_code/Value:other' => '其它', 'Class:UserRequest/Attribute:resolution_code/Value:other' => '其它',

View File

@@ -62,11 +62,11 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:UserRequest/Attribute:status+' => '', 'Class:UserRequest/Attribute:status+' => '',
'Class:UserRequest/Attribute:status/Value:new' => '新建', 'Class:UserRequest/Attribute:status/Value:new' => '新建',
'Class:UserRequest/Attribute:status/Value:new+' => '', 'Class:UserRequest/Attribute:status/Value:new+' => '',
'Class:UserRequest/Attribute:status/Value:escalated_tto' => '已升级响应时间', 'Class:UserRequest/Attribute:status/Value:escalated_tto' => '已升级TTO',
'Class:UserRequest/Attribute:status/Value:escalated_tto+' => '', 'Class:UserRequest/Attribute:status/Value:escalated_tto+' => '',
'Class:UserRequest/Attribute:status/Value:assigned' => '已分配', 'Class:UserRequest/Attribute:status/Value:assigned' => '已分配',
'Class:UserRequest/Attribute:status/Value:assigned+' => '', 'Class:UserRequest/Attribute:status/Value:assigned+' => '',
'Class:UserRequest/Attribute:status/Value:escalated_ttr' => '已升级解决时间', 'Class:UserRequest/Attribute:status/Value:escalated_ttr' => '已升级TTR',
'Class:UserRequest/Attribute:status/Value:escalated_ttr+' => '', 'Class:UserRequest/Attribute:status/Value:escalated_ttr+' => '',
'Class:UserRequest/Attribute:status/Value:waiting_for_approval' => '等待批准', 'Class:UserRequest/Attribute:status/Value:waiting_for_approval' => '等待批准',
'Class:UserRequest/Attribute:status/Value:waiting_for_approval+' => '', 'Class:UserRequest/Attribute:status/Value:waiting_for_approval+' => '',
@@ -156,21 +156,21 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:UserRequest/Attribute:last_pending_date+' => '', 'Class:UserRequest/Attribute:last_pending_date+' => '',
'Class:UserRequest/Attribute:cumulatedpending' => '累计待定', 'Class:UserRequest/Attribute:cumulatedpending' => '累计待定',
'Class:UserRequest/Attribute:cumulatedpending+' => '', 'Class:UserRequest/Attribute:cumulatedpending+' => '',
'Class:UserRequest/Attribute:tto' => '响应时间', 'Class:UserRequest/Attribute:tto' => 'TTO',
'Class:UserRequest/Attribute:tto+' => '', 'Class:UserRequest/Attribute:tto+' => '响应时间',
'Class:UserRequest/Attribute:ttr' => '解决时间', 'Class:UserRequest/Attribute:ttr' => 'TTR',
'Class:UserRequest/Attribute:ttr+' => '', 'Class:UserRequest/Attribute:ttr+' => '解决时限',
'Class:UserRequest/Attribute:tto_escalation_deadline' => '响应时间期限', 'Class:UserRequest/Attribute:tto_escalation_deadline' => 'TTO截止日期',
'Class:UserRequest/Attribute:tto_escalation_deadline+' => '', 'Class:UserRequest/Attribute:tto_escalation_deadline+' => '',
'Class:UserRequest/Attribute:sla_tto_passed' => '超过SLA响应时间', 'Class:UserRequest/Attribute:sla_tto_passed' => 'SLA TTO 合格',
'Class:UserRequest/Attribute:sla_tto_passed+' => '', 'Class:UserRequest/Attribute:sla_tto_passed+' => '',
'Class:UserRequest/Attribute:sla_tto_over' => 'SLA响应时间结束', 'Class:UserRequest/Attribute:sla_tto_over' => 'SLA TTO 超时',
'Class:UserRequest/Attribute:sla_tto_over+' => '', 'Class:UserRequest/Attribute:sla_tto_over+' => '',
'Class:UserRequest/Attribute:ttr_escalation_deadline' => '解决时间期限', 'Class:UserRequest/Attribute:ttr_escalation_deadline' => 'TTR截止日期',
'Class:UserRequest/Attribute:ttr_escalation_deadline+' => '', 'Class:UserRequest/Attribute:ttr_escalation_deadline+' => '',
'Class:UserRequest/Attribute:sla_ttr_passed' => '超过SLA解决时间', 'Class:UserRequest/Attribute:sla_ttr_passed' => 'SLA TTR 合格',
'Class:UserRequest/Attribute:sla_ttr_passed+' => '', 'Class:UserRequest/Attribute:sla_ttr_passed+' => '',
'Class:UserRequest/Attribute:sla_ttr_over' => 'SLA解决时间结束', 'Class:UserRequest/Attribute:sla_ttr_over' => 'SLA TTR 超时',
'Class:UserRequest/Attribute:sla_ttr_over+' => '', 'Class:UserRequest/Attribute:sla_ttr_over+' => '',
'Class:UserRequest/Attribute:time_spent' => '耗时', 'Class:UserRequest/Attribute:time_spent' => '耗时',
'Class:UserRequest/Attribute:time_spent+' => '', 'Class:UserRequest/Attribute:time_spent+' => '',
@@ -192,7 +192,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:UserRequest/Attribute:resolution_code/Value:training+' => '培训', 'Class:UserRequest/Attribute:resolution_code/Value:training+' => '培训',
'Class:UserRequest/Attribute:solution' => '解决方案', 'Class:UserRequest/Attribute:solution' => '解决方案',
'Class:UserRequest/Attribute:solution+' => '', 'Class:UserRequest/Attribute:solution+' => '',
'Class:UserRequest/Attribute:pending_reason' => '待定原因', 'Class:UserRequest/Attribute:pending_reason' => '待定原因',
'Class:UserRequest/Attribute:pending_reason+' => '', 'Class:UserRequest/Attribute:pending_reason+' => '',
'Class:UserRequest/Attribute:parent_request_id' => '父级需求', 'Class:UserRequest/Attribute:parent_request_id' => '父级需求',
'Class:UserRequest/Attribute:parent_request_id+' => '', 'Class:UserRequest/Attribute:parent_request_id+' => '',

View File

@@ -1748,48 +1748,15 @@ public function PrefillSearchForm(&$aContextParam)
<ext_key_to_remote>slt_id</ext_key_to_remote> <ext_key_to_remote>slt_id</ext_key_to_remote>
<duplicates/> <duplicates/>
</field> </field>
<field id="customercontracts_list" xsi:type="AttributeLinkedSetIndirect"> <field id="customercontracts_list" xsi:type="AttributeLinkedSet">
<linked_class>lnkCustomerContractToService</linked_class> <linked_class>lnkCustomerContractToService</linked_class>
<ext_key_to_me>sla_id</ext_key_to_me> <ext_key_to_me>sla_id</ext_key_to_me>
<count_min>0</count_min> <count_min>0</count_min>
<count_max>0</count_max> <count_max>0</count_max>
<ext_key_to_remote>customercontract_id</ext_key_to_remote> <edit_mode>none</edit_mode>
<duplicates>true</duplicates>
</field> </field>
</fields> </fields>
<methods> <methods/>
<method id="DoCheckToWrite">
<static>false</static>
<access>public</access>
<code><![CDATA[
public function DoCheckToWrite()
{
parent::DoCheckToWrite();
$aCustomerContracts = $this->Get("customercontracts_list");
foreach ($aCustomerContracts as $sAttCode => $oCustomerContracts)
{
// Recurse inside the subdirectories
$sOql = "SELECT lnkCustomerContractToService AS ccs WHERE ccs.customercontract_id=:customercontract_id AND ccs.service_id=:service_id";
$aQueryParams['customercontract_id'] = $oCustomerContracts->Get("customercontract_id");
$aQueryParams['service_id'] = $oCustomerContracts->Get("service_id");
if ($this->Get("id") != null)
{
$sOql = $sOql." AND ccs.sla_id!=:sla_id";
$aQueryParams['sla_id'] = $this->Get("id");
}
$oQuery = DBSearch::FromOQL($sOql, $aQueryParams);
$oResultSql = new DBObjectSet($oQuery);
$oResultSql->OptimizeColumnLoad(['ccs' => ['customercontract_name','service_name']]);
if ($aCurrentRow = $oResultSql->Fetch())
{
$this->m_aCheckIssues[] = Dict::Format('Class:SLA/Error:UniqueLnkCustomerContractToService',$aCurrentRow->Get('customercontract_name'),$aCurrentRow->Get('service_name'));
}
}
}
]]></code>
</method>
</methods>
<presentation> <presentation>
<details> <details>
<items> <items>

View File

@@ -99,9 +99,9 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:Contract/Attribute:organization_name' => 'Nom client', 'Class:Contract/Attribute:organization_name' => 'Nom client',
'Class:Contract/Attribute:organization_name+' => 'Nom commun', 'Class:Contract/Attribute:organization_name+' => 'Nom commun',
'Class:Contract/Attribute:contacts_list' => 'Contacts', 'Class:Contract/Attribute:contacts_list' => 'Contacts',
'Class:Contract/Attribute:contacts_list+' => 'Tous les contacts for ce contrat client', 'Class:Contract/Attribute:contacts_list+' => 'Tous les contacts pour ce contrat client',
'Class:Contract/Attribute:documents_list' => 'Documents', 'Class:Contract/Attribute:documents_list' => 'Documents',
'Class:Contract/Attribute:documents_list+' => 'Tous les documents for ce contrat client', 'Class:Contract/Attribute:documents_list+' => 'Tous les documents pour ce contrat client',
'Class:Contract/Attribute:description' => 'Description', 'Class:Contract/Attribute:description' => 'Description',
'Class:Contract/Attribute:description+' => '', 'Class:Contract/Attribute:description+' => '',
'Class:Contract/Attribute:start_date' => 'Date de début', 'Class:Contract/Attribute:start_date' => 'Date de début',

View File

@@ -389,8 +389,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:SLT/Attribute:name+' => '', 'Class:SLT/Attribute:name+' => '',
'Class:SLT/Attribute:priority' => '优先级', 'Class:SLT/Attribute:priority' => '优先级',
'Class:SLT/Attribute:priority+' => '', 'Class:SLT/Attribute:priority+' => '',
'Class:SLT/Attribute:priority/Value:1' => '非常高', 'Class:SLT/Attribute:priority/Value:1' => '紧急',
'Class:SLT/Attribute:priority/Value:1+' => '非常高', 'Class:SLT/Attribute:priority/Value:1+' => '紧急',
'Class:SLT/Attribute:priority/Value:2' => '高', 'Class:SLT/Attribute:priority/Value:2' => '高',
'Class:SLT/Attribute:priority/Value:2+' => '高', 'Class:SLT/Attribute:priority/Value:2+' => '高',
'Class:SLT/Attribute:priority/Value:3' => '中', 'Class:SLT/Attribute:priority/Value:3' => '中',
@@ -403,15 +403,15 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:SLT/Attribute:request_type/Value:incident+' => '事件', 'Class:SLT/Attribute:request_type/Value:incident+' => '事件',
'Class:SLT/Attribute:request_type/Value:service_request' => '服务需求', 'Class:SLT/Attribute:request_type/Value:service_request' => '服务需求',
'Class:SLT/Attribute:request_type/Value:service_request+' => '服务需求', 'Class:SLT/Attribute:request_type/Value:service_request+' => '服务需求',
'Class:SLT/Attribute:metric' => '指标', 'Class:SLT/Attribute:metric' => '衡量指标',
'Class:SLT/Attribute:metric+' => '', 'Class:SLT/Attribute:metric+' => '',
'Class:SLT/Attribute:metric/Value:tto' => '响应时间', 'Class:SLT/Attribute:metric/Value:tto' => 'TTO',
'Class:SLT/Attribute:metric/Value:tto+' => '响应时间', 'Class:SLT/Attribute:metric/Value:tto+' => '响应时间',
'Class:SLT/Attribute:metric/Value:ttr' => '解决时间', 'Class:SLT/Attribute:metric/Value:ttr' => 'TTR',
'Class:SLT/Attribute:metric/Value:ttr+' => '解决时', 'Class:SLT/Attribute:metric/Value:ttr+' => '解决时',
'Class:SLT/Attribute:value' => '值', 'Class:SLT/Attribute:value' => '值',
'Class:SLT/Attribute:value+' => '', 'Class:SLT/Attribute:value+' => '',
'Class:SLT/Attribute:unit' => '单位', 'Class:SLT/Attribute:unit' => '度量单位',
'Class:SLT/Attribute:unit+' => '', 'Class:SLT/Attribute:unit+' => '',
'Class:SLT/Attribute:unit/Value:hours' => '小时', 'Class:SLT/Attribute:unit/Value:hours' => '小时',
'Class:SLT/Attribute:unit/Value:hours+' => '小时', 'Class:SLT/Attribute:unit/Value:hours+' => '小时',

View File

@@ -157,6 +157,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'Class:ProviderContract/Attribute:contracttype_id+' => '', 'Class:ProviderContract/Attribute:contracttype_id+' => '',
'Class:ProviderContract/Attribute:contracttype_name' => 'Vertragstyp-Name', 'Class:ProviderContract/Attribute:contracttype_name' => 'Vertragstyp-Name',
'Class:ProviderContract/Attribute:contracttype_name+' => '', 'Class:ProviderContract/Attribute:contracttype_name+' => '',
'Class:ProviderContract/Attribute:services_list' => 'Services',
'Class:ProviderContract/Attribute:services_list+' => 'Alle für diesen Vertrag erworbenen Services',
)); ));
// //

View File

@@ -169,6 +169,8 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:ProviderContract/Attribute:contracttype_id+' => '', 'Class:ProviderContract/Attribute:contracttype_id+' => '',
'Class:ProviderContract/Attribute:contracttype_name' => 'Contract type name', 'Class:ProviderContract/Attribute:contracttype_name' => 'Contract type name',
'Class:ProviderContract/Attribute:contracttype_name+' => '', 'Class:ProviderContract/Attribute:contracttype_name+' => '',
'Class:ProviderContract/Attribute:services_list' => 'Services',
'Class:ProviderContract/Attribute:services_list+' => 'All the services purchased with this contract',
)); ));
// //

View File

@@ -157,6 +157,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:ProviderContract/Attribute:contracttype_id+' => '', 'Class:ProviderContract/Attribute:contracttype_id+' => '',
'Class:ProviderContract/Attribute:contracttype_name' => 'Nom Type de contrat', 'Class:ProviderContract/Attribute:contracttype_name' => 'Nom Type de contrat',
'Class:ProviderContract/Attribute:contracttype_name+' => '', 'Class:ProviderContract/Attribute:contracttype_name+' => '',
'Class:ProviderContract/Attribute:services_list' => 'Services',
'Class:ProviderContract/Attribute:services_list+' => 'Tous les services achetés par ce contrat',
)); ));
// //

View File

@@ -362,8 +362,8 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:SLT/Attribute:name+' => '', 'Class:SLT/Attribute:name+' => '',
'Class:SLT/Attribute:priority' => '优先级', 'Class:SLT/Attribute:priority' => '优先级',
'Class:SLT/Attribute:priority+' => '', 'Class:SLT/Attribute:priority+' => '',
'Class:SLT/Attribute:priority/Value:1' => '非常高', 'Class:SLT/Attribute:priority/Value:1' => '紧急',
'Class:SLT/Attribute:priority/Value:1+' => '非常高', 'Class:SLT/Attribute:priority/Value:1+' => '紧急',
'Class:SLT/Attribute:priority/Value:2' => '高', 'Class:SLT/Attribute:priority/Value:2' => '高',
'Class:SLT/Attribute:priority/Value:2+' => '高', 'Class:SLT/Attribute:priority/Value:2+' => '高',
'Class:SLT/Attribute:priority/Value:3' => '中', 'Class:SLT/Attribute:priority/Value:3' => '中',
@@ -376,15 +376,15 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:SLT/Attribute:request_type/Value:incident+' => '事件', 'Class:SLT/Attribute:request_type/Value:incident+' => '事件',
'Class:SLT/Attribute:request_type/Value:service_request' => '服务需求', 'Class:SLT/Attribute:request_type/Value:service_request' => '服务需求',
'Class:SLT/Attribute:request_type/Value:service_request+' => '服务需求', 'Class:SLT/Attribute:request_type/Value:service_request+' => '服务需求',
'Class:SLT/Attribute:metric' => '指标', 'Class:SLT/Attribute:metric' => '衡量指标',
'Class:SLT/Attribute:metric+' => '', 'Class:SLT/Attribute:metric+' => '',
'Class:SLT/Attribute:metric/Value:tto' => '响应时间', 'Class:SLT/Attribute:metric/Value:tto' => 'TTO',
'Class:SLT/Attribute:metric/Value:tto+' => '响应时间', 'Class:SLT/Attribute:metric/Value:tto+' => '响应时间',
'Class:SLT/Attribute:metric/Value:ttr' => '解决时间', 'Class:SLT/Attribute:metric/Value:ttr' => 'TTR',
'Class:SLT/Attribute:metric/Value:ttr+' => '解决时', 'Class:SLT/Attribute:metric/Value:ttr+' => '解决时',
'Class:SLT/Attribute:value' => '值', 'Class:SLT/Attribute:value' => '值',
'Class:SLT/Attribute:value+' => '', 'Class:SLT/Attribute:value+' => '',
'Class:SLT/Attribute:unit' => '单位', 'Class:SLT/Attribute:unit' => '度量单位',
'Class:SLT/Attribute:unit+' => '', 'Class:SLT/Attribute:unit+' => '',
'Class:SLT/Attribute:unit/Value:hours' => '小时', 'Class:SLT/Attribute:unit/Value:hours' => '小时',
'Class:SLT/Attribute:unit/Value:hours+' => '小时', 'Class:SLT/Attribute:unit/Value:hours+' => '小时',

View File

@@ -713,6 +713,7 @@
<linked_class>User</linked_class> <linked_class>User</linked_class>
<ext_key_to_me>contactid</ext_key_to_me> <ext_key_to_me>contactid</ext_key_to_me>
<edit_mode>add_only</edit_mode> <edit_mode>add_only</edit_mode>
<edit_when>on_host_display</edit_when>
<count_min>0</count_min> <count_min>0</count_min>
<count_max>0</count_max> <count_max>0</count_max>
</field> </field>

View File

@@ -251,7 +251,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
Dict::Add('ZH CN', 'Chinese', '简体中文', array( Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:DocumentNote' => '文档笔记', 'Class:DocumentNote' => '文档笔记',
'Class:DocumentNote+' => '', 'Class:DocumentNote+' => '',
'Class:DocumentNote/Attribute:text' => '文', 'Class:DocumentNote/Attribute:text' => '文',
'Class:DocumentNote/Attribute:text+' => '', 'Class:DocumentNote/Attribute:text+' => '',
)); ));

View File

@@ -240,9 +240,9 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
'Class:cmdbAbstractObject/Method:ApplyStimulus+' => 'Apply the specified stimulus to the current object', 'Class:cmdbAbstractObject/Method:ApplyStimulus+' => 'Apply the specified stimulus to the current object',
'Class:cmdbAbstractObject/Method:ApplyStimulus/Param:1' => 'Stimulus code', 'Class:cmdbAbstractObject/Method:ApplyStimulus/Param:1' => 'Stimulus code',
'Class:cmdbAbstractObject/Method:ApplyStimulus/Param:1+' => 'A valid stimulus code for the current class', 'Class:cmdbAbstractObject/Method:ApplyStimulus/Param:1+' => 'A valid stimulus code for the current class',
'Class:ResponseTicketTTO/Interface:iMetricComputer' => '响应时间', 'Class:ResponseTicketTTO/Interface:iMetricComputer' => 'TTO',
'Class:ResponseTicketTTO/Interface:iMetricComputer+' => 'SLT 的响应时间', 'Class:ResponseTicketTTO/Interface:iMetricComputer+' => 'SLT 的响应时间',
'Class:ResponseTicketTTR/Interface:iMetricComputer' => '解决时间', 'Class:ResponseTicketTTR/Interface:iMetricComputer' => 'TTR',
'Class:ResponseTicketTTR/Interface:iMetricComputer+' => 'SLT 的解决时', 'Class:ResponseTicketTTR/Interface:iMetricComputer+' => 'SLT 的解决时',
)); ));

View File

@@ -49,6 +49,7 @@ Dict::Add('EN US', 'English', 'English', array(
'Core:AttributeTagSet' => 'List of tags', 'Core:AttributeTagSet' => 'List of tags',
'Core:AttributeTagSet+' => '', 'Core:AttributeTagSet+' => '',
'Core:AttributeSet:placeholder' => 'click to add', 'Core:AttributeSet:placeholder' => 'click to add',
'Core:Placeholder:CannotBeResolved' => '(%1$s : cannot be resolved)',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromClass' => '%1$s (%2$s)', 'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromClass' => '%1$s (%2$s)',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromOneChildClass' => '%1$s (%2$s from %3$s)', 'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromOneChildClass' => '%1$s (%2$s from %3$s)',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromSeveralChildClasses' => '%1$s (%2$s from child classes)', 'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromSeveralChildClasses' => '%1$s (%2$s from child classes)',

View File

@@ -248,7 +248,7 @@ Dict::Add('EN US', 'English', 'English', array(
Dict::Add('EN US', 'English', 'English', array( Dict::Add('EN US', 'English', 'English', array(
'Class:URP_UserOrg' => 'User organizations', 'Class:URP_UserOrg' => 'User organizations',
'Class:URP_UserOrg+' => 'Allowed organizations', 'Class:URP_UserOrg+' => 'Allowed organizations',
'Class:URP_UserOrg/Name' => 'LinkGG between %1$s and %2$s', 'Class:URP_UserOrg/Name' => 'Link between %1$s and %2$s',
'Class:URP_UserOrg/Attribute:userid' => 'User', 'Class:URP_UserOrg/Attribute:userid' => 'User',
'Class:URP_UserOrg/Attribute:userid+' => 'user account', 'Class:URP_UserOrg/Attribute:userid+' => 'user account',
'Class:URP_UserOrg/Attribute:userlogin' => 'Login', 'Class:URP_UserOrg/Attribute:userlogin' => 'Login',
@@ -837,7 +837,7 @@ We hope youll enjoy this version as much as we enjoyed imagining and creating
'UI:RunQuery:DevelopedOQLCount' => 'Developed OQL for count', 'UI:RunQuery:DevelopedOQLCount' => 'Developed OQL for count',
'UI:RunQuery:ResultSQLCount' => 'Resulting SQL for count', 'UI:RunQuery:ResultSQLCount' => 'Resulting SQL for count',
'UI:RunQuery:ResultSQL' => 'Resulting SQL', 'UI:RunQuery:ResultSQL' => 'Resulting SQL',
'UI:RunQuery:Error' => 'An error occured while running the query', 'UI:RunQuery:Error' => 'An error occured while running the query: %1$s',
'UI:Query:UrlForExcel' => 'URL to use for MS-Excel web queries', 'UI:Query:UrlForExcel' => 'URL to use for MS-Excel web queries',
'UI:Query:UrlV1' => 'The list of fields has been left unspecified. The page <em>export-V2.php</em> cannot be invoked without this information. Therefore, the URL suggested here below points to the legacy page: <em>export.php</em>. This legacy version of the export has the following limitation: the list of exported fields may vary depending on the output format and the data model of '.ITOP_APPLICATION_SHORT.'. <br/>Should you want to guarantee that the list of exported columns will remain stable on the long run, then you must specify a value for the attribute "Fields" and use the page <em>export-V2.php</em>.', 'UI:Query:UrlV1' => 'The list of fields has been left unspecified. The page <em>export-V2.php</em> cannot be invoked without this information. Therefore, the URL suggested here below points to the legacy page: <em>export.php</em>. This legacy version of the export has the following limitation: the list of exported fields may vary depending on the output format and the data model of '.ITOP_APPLICATION_SHORT.'. <br/>Should you want to guarantee that the list of exported columns will remain stable on the long run, then you must specify a value for the attribute "Fields" and use the page <em>export-V2.php</em>.',
'UI:Schema:Title' => ITOP_APPLICATION_SHORT.' objects schema', 'UI:Schema:Title' => ITOP_APPLICATION_SHORT.' objects schema',

View File

@@ -39,6 +39,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Core:AttributeTagSet' => 'Liste d\'étiquettes', 'Core:AttributeTagSet' => 'Liste d\'étiquettes',
'Core:AttributeTagSet+' => '', 'Core:AttributeTagSet+' => '',
'Core:AttributeSet:placeholder' => 'cliquer pour ajouter', 'Core:AttributeSet:placeholder' => 'cliquer pour ajouter',
'Core:Placeholder:CannotBeResolved' => '(%1$s : non remplacé)',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromClass' => '%1$s (%2$s)', 'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromClass' => '%1$s (%2$s)',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromOneChildClass' => '%1$s (%2$s de la classe %3$s)', 'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromOneChildClass' => '%1$s (%2$s de la classe %3$s)',
'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromSeveralChildClasses' => '%1$s (%2$s d\'une sous-classe)', 'Core:AttributeClassAttCodeSet:ItemLabel:AttributeFromSeveralChildClasses' => '%1$s (%2$s d\'une sous-classe)',

View File

@@ -21,7 +21,7 @@
* along with iTop. If not, see <http://www.gnu.org/licenses/> * along with iTop. If not, see <http://www.gnu.org/licenses/>
*/ */
Dict::Add('HU HU', 'Hungarian', 'Magyar', array( Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'Core:DeletedObjectLabel' => '%1s (törölve)', 'Core:DeletedObjectLabel' => '%1$s (törölve)',
'Core:DeletedObjectTip' => 'A %1$s objektum törölve (%2$s)', 'Core:DeletedObjectTip' => 'A %1$s objektum törölve (%2$s)',
'Core:UnknownObjectLabel' => 'Objektum nem található (osztály: %1$s, id: %2$d)', 'Core:UnknownObjectLabel' => 'Objektum nem található (osztály: %1$s, id: %2$d)',
'Core:UnknownObjectTip' => 'Az objektumot nem sikerült megtalálni. Lehet, hogy már törölték egy ideje, és a naplót azóta törölték.', 'Core:UnknownObjectTip' => 'Az objektumot nem sikerült megtalálni. Lehet, hogy már törölték egy ideje, és a naplót azóta törölték.',

View File

@@ -509,7 +509,7 @@ Reméljük, hogy ezt a verziót ugyanúgy kedvelni fogja, mint ahogy mi élvezt
'UI:Error:2ParametersMissing' => 'Hiba: a következő paramétereket meg kell adni ennél a műveletnél: %1$s és %2$s.', 'UI:Error:2ParametersMissing' => 'Hiba: a következő paramétereket meg kell adni ennél a műveletnél: %1$s és %2$s.',
'UI:Error:3ParametersMissing' => 'Hiba: a következő paramétereket meg kell adni ennél a műveletnél: %1$s, %2$s és %3$s.', 'UI:Error:3ParametersMissing' => 'Hiba: a következő paramétereket meg kell adni ennél a műveletnél: %1$s, %2$s és %3$s.',
'UI:Error:4ParametersMissing' => 'Hiba: a következő paramétereket meg kell adni ennél a műveletnél: %1$s, %2$s, %3$s és %4$s.', 'UI:Error:4ParametersMissing' => 'Hiba: a következő paramétereket meg kell adni ennél a műveletnél: %1$s, %2$s, %3$s és %4$s.',
'UI:Error:IncorrectOQLQuery_Message' => 'Hiba: nem megfelelő OQL lekérdezés: %1$', 'UI:Error:IncorrectOQLQuery_Message' => 'Hiba: nem megfelelő OQL lekérdezés: %1$s',
'UI:Error:AnErrorOccuredWhileRunningTheQuery_Message' => 'Hiba történt a lekérdezés futtatása közben: %1$s', 'UI:Error:AnErrorOccuredWhileRunningTheQuery_Message' => 'Hiba történt a lekérdezés futtatása közben: %1$s',
'UI:Error:ObjectAlreadyUpdated' => 'Hiba: az objketum már korábban módosításra került.', 'UI:Error:ObjectAlreadyUpdated' => 'Hiba: az objketum már korábban módosításra került.',
'UI:Error:ObjectCannotBeUpdated' => 'Hiba: az objektum nem frissíthető.', 'UI:Error:ObjectCannotBeUpdated' => 'Hiba: az objektum nem frissíthető.',
@@ -715,7 +715,7 @@ Reméljük, hogy ezt a verziót ugyanúgy kedvelni fogja, mint ahogy mi élvezt
'UI:CSVReport-Value-Issue-Null' => 'A nulla nem engedélyezett', 'UI:CSVReport-Value-Issue-Null' => 'A nulla nem engedélyezett',
'UI:CSVReport-Value-Issue-NotFound' => 'Az objektum nincs meg', 'UI:CSVReport-Value-Issue-NotFound' => 'Az objektum nincs meg',
'UI:CSVReport-Value-Issue-FoundMany' => '%1$d egyezés található', 'UI:CSVReport-Value-Issue-FoundMany' => '%1$d egyezés található',
'UI:CSVReport-Value-Issue-Readonly' => 'A \'%1$\'s attribútum csak olvasható (jelenlegi érték: %2$s, várható érték: %3$s)', 'UI:CSVReport-Value-Issue-Readonly' => 'A \'%1$s attribútum csak olvasható (jelenlegi érték: %2$s, várható érték: %3$s)',
'UI:CSVReport-Value-Issue-Format' => 'A bevitel feldolgozása sikertelen: %1$s', 'UI:CSVReport-Value-Issue-Format' => 'A bevitel feldolgozása sikertelen: %1$s',
'UI:CSVReport-Value-Issue-NoMatch' => 'A \'%1$s\' attribútum nem várt értéket kapott: nincs egyezés, ellenőrizze a beírást', 'UI:CSVReport-Value-Issue-NoMatch' => 'A \'%1$s\' attribútum nem várt értéket kapott: nincs egyezés, ellenőrizze a beírást',
'UI:CSVReport-Value-Issue-AllowedValues' => 'Allowed \'%1$s\' value(s): %2$s~~', 'UI:CSVReport-Value-Issue-AllowedValues' => 'Allowed \'%1$s\' value(s): %2$s~~',

View File

@@ -20,7 +20,7 @@
* @licence http://opensource.org/licenses/AGPL-3.0 * @licence http://opensource.org/licenses/AGPL-3.0
*/ */
Dict::Add('JA JP', 'Japanese', '日本語', array( Dict::Add('JA JP', 'Japanese', '日本語', array(
'Core:DeletedObjectLabel' => '%1s (削除されました)', 'Core:DeletedObjectLabel' => '%1$s (削除されました)',
'Core:DeletedObjectTip' => 'オブジェクトは削除されました %1$s (%2$s)', 'Core:DeletedObjectTip' => 'オブジェクトは削除されました %1$s (%2$s)',
'Core:UnknownObjectLabel' => 'オブジェクトは見つかりません (クラス: %1$s, id: %2$d)', 'Core:UnknownObjectLabel' => 'オブジェクトは見つかりません (クラス: %1$s, id: %2$d)',
'Core:UnknownObjectTip' => 'オブジェクトは見つかりません。しばらく前に削除され、その後ログが削除されたかもしれません。', 'Core:UnknownObjectTip' => 'オブジェクトは見つかりません。しばらく前に削除され、その後ログが削除されたかもしれません。',

View File

@@ -915,7 +915,7 @@ We hope youll enjoy this version as much as we enjoyed imagining and creating
'UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class' => '%2$sクラスの%1$dオブジェクトの削除', 'UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class' => '%2$sクラスの%1$dオブジェクトの削除',
'UI:Delete:CannotDeleteBecause' => '削除できません: %1$s', 'UI:Delete:CannotDeleteBecause' => '削除できません: %1$s',
'UI:Delete:ShouldBeDeletedAtomaticallyButNotPossible' => '自動的に削除されるべきですが、出来ません。: %1$s', 'UI:Delete:ShouldBeDeletedAtomaticallyButNotPossible' => '自動的に削除されるべきですが、出来ません。: %1$s',
'UI:Delete:MustBeDeletedManuallyButNotPossible' => '手動で削除されるべきですが、出来ません。: %1$', 'UI:Delete:MustBeDeletedManuallyButNotPossible' => '手動で削除されるべきですが、出来ません。: %1$s',
'UI:Delete:WillBeDeletedAutomatically' => '自動的に削除されます。', 'UI:Delete:WillBeDeletedAutomatically' => '自動的に削除されます。',
'UI:Delete:MustBeDeletedManually' => '手動で削除されるべきです。', 'UI:Delete:MustBeDeletedManually' => '手動で削除されるべきです。',
'UI:Delete:CannotUpdateBecause_Issue' => '自動的に更新されるべきですが、しかし: %1$s', 'UI:Delete:CannotUpdateBecause_Issue' => '自動的に更新されるべきですが、しかし: %1$s',
@@ -1159,8 +1159,7 @@ We hope youll enjoy this version as much as we enjoyed imagining and creating
'Enum:Undefined' => '未定義', 'Enum:Undefined' => '未定義',
'UI:DurationForm_Days_Hours_Minutes_Seconds' => '%1$s 日 %2$s 時 %3$s 分 %4$s 秒', 'UI:DurationForm_Days_Hours_Minutes_Seconds' => '%1$s 日 %2$s 時 %3$s 分 %4$s 秒',
'UI:ModifyAllPageTitle' => '全てを修正', 'UI:ModifyAllPageTitle' => '全てを修正',
'UI:Modify_ObjectsOf_Class' => 'Modifying objects of class %1$s~~', 'UI:Modify_N_ObjectsOf_Class' => 'クラス%2$sの%1$dオブジェクトを修正',
'UI:Modify_N_ObjectsOf_Class' => 'クラス%2$Sの%1$dオブジェクトを修正',
'UI:Modify_M_ObjectsOf_Class_OutOf_N' => 'クラス%2$sの%3$d中%1$dを修正', 'UI:Modify_M_ObjectsOf_Class_OutOf_N' => 'クラス%2$sの%3$d中%1$dを修正',
'UI:Menu:ModifyAll' => '修正...', 'UI:Menu:ModifyAll' => '修正...',
'UI:Menu:ModifyAll_Class' => 'Modify %1$s objects...~~', 'UI:Menu:ModifyAll_Class' => 'Modify %1$s objects...~~',
@@ -1180,7 +1179,7 @@ We hope youll enjoy this version as much as we enjoyed imagining and creating
'UI:BulkModify_Count_DistinctValues' => '%1$d 個の個別の値:', 'UI:BulkModify_Count_DistinctValues' => '%1$d 個の個別の値:',
'UI:BulkModify:Value_Exists_N_Times' => '%1$s, %2$d 回存在', 'UI:BulkModify:Value_Exists_N_Times' => '%1$s, %2$d 回存在',
'UI:BulkModify:N_MoreValues' => '%1$d 個以上の値...', 'UI:BulkModify:N_MoreValues' => '%1$d 個以上の値...',
'UI:AttemptingToSetAReadOnlyAttribute_Name' => '読み込み専用フィールド %1$にセットしょうとしています。', 'UI:AttemptingToSetAReadOnlyAttribute_Name' => '読み込み専用フィールド %1$sにセットしょうとしています。',
'UI:FailedToApplyStimuli' => 'アクションは失敗しました。', 'UI:FailedToApplyStimuli' => 'アクションは失敗しました。',
'UI:StimulusModify_N_ObjectsOf_Class' => '%1$s: クラス%3$sの%2$dオブジェクトを修正', 'UI:StimulusModify_N_ObjectsOf_Class' => '%1$s: クラス%3$sの%2$dオブジェクトを修正',
'UI:CaseLogTypeYourTextHere' => 'テキストを入力ください:', 'UI:CaseLogTypeYourTextHere' => 'テキストを入力ください:',

View File

@@ -9,7 +9,7 @@
* *
*/ */
Dict::Add('RU RU', 'Russian', 'Русский', array( Dict::Add('RU RU', 'Russian', 'Русский', array(
'Core:DeletedObjectLabel' => '%1ы (удален)', 'Core:DeletedObjectLabel' => '%1$sы (удален)',
'Core:DeletedObjectTip' => 'Объект был удален %1$s (%2$s)', 'Core:DeletedObjectTip' => 'Объект был удален %1$s (%2$s)',
'Core:UnknownObjectLabel' => 'Объект не найден (class: %1$s, id: %2$d)', 'Core:UnknownObjectLabel' => 'Объект не найден (class: %1$s, id: %2$d)',
'Core:UnknownObjectTip' => 'Объект не удается найти. Возможно, он был удален некоторое время назад, и журнал с тех пор был очищен.', 'Core:UnknownObjectTip' => 'Объект не удается найти. Возможно, он был удален некоторое время назад, и журнал с тех пор был очищен.',

View File

@@ -497,7 +497,7 @@ We hope youll enjoy this version as much as we enjoyed imagining and creating
'UI:Error:MandatoryTemplateParameter_group_by' => 'Parameter group_by je povinný. Skontrolujte definíciu šablóny zobrazenia.', 'UI:Error:MandatoryTemplateParameter_group_by' => 'Parameter group_by je povinný. Skontrolujte definíciu šablóny zobrazenia.',
'UI:Error:InvalidGroupByFields' => 'Neplatný zoznam polí pre skupinu podľa: "%1$s".', 'UI:Error:InvalidGroupByFields' => 'Neplatný zoznam polí pre skupinu podľa: "%1$s".',
'UI:Error:UnsupportedStyleOfBlock' => 'Chyba: nepodporovaný štýl bloku: "%1$s".', 'UI:Error:UnsupportedStyleOfBlock' => 'Chyba: nepodporovaný štýl bloku: "%1$s".',
'UI:Error:IncorrectLinkDefinition_LinkedClass_Class' => 'Nesprávna definícia spojenia : trieda objektov na manažovanie : %l$s nebol nájdený ako externý kľúč v triede %2$s', 'UI:Error:IncorrectLinkDefinition_LinkedClass_Class' => 'Nesprávna definícia spojenia : trieda objektov na manažovanie : %1$s nebol nájdený ako externý kľúč v triede %2$s',
'UI:Error:Object_Class_Id_NotFound' => 'Objekt: %1$s:%2$d nebol nájdený.', 'UI:Error:Object_Class_Id_NotFound' => 'Objekt: %1$s:%2$d nebol nájdený.',
'UI:Error:WizardCircularReferenceInDependencies' => 'Chyba: Cyklický odkaz v závislostiach medzi poliami, skontrolujte dátový model.', 'UI:Error:WizardCircularReferenceInDependencies' => 'Chyba: Cyklický odkaz v závislostiach medzi poliami, skontrolujte dátový model.',
'UI:Error:UploadedFileTooBig' => 'Nahraný súbor je príliš veľký. (Max povolená veľkosť je %1$s). Ak chcete zmeniť tento limit, obráťte sa na správcu ITOP . (Skontrolujte, PHP konfiguráciu pre upload_max_filesize a post_max_size na serveri).', 'UI:Error:UploadedFileTooBig' => 'Nahraný súbor je príliš veľký. (Max povolená veľkosť je %1$s). Ak chcete zmeniť tento limit, obráťte sa na správcu ITOP . (Skontrolujte, PHP konfiguráciu pre upload_max_filesize a post_max_size na serveri).',
@@ -1301,7 +1301,7 @@ Keď sú priradené spúštačom, každej akcii je dané číslo "príkazu", šp
'UI:DashletGroupBy:Prop-GroupBy:DayOfMonth' => 'Deň v mesiaci pre %1$s', 'UI:DashletGroupBy:Prop-GroupBy:DayOfMonth' => 'Deň v mesiaci pre %1$s',
'UI:DashletGroupBy:Prop-GroupBy:Select-Hour' => '%1$s (hodina)', 'UI:DashletGroupBy:Prop-GroupBy:Select-Hour' => '%1$s (hodina)',
'UI:DashletGroupBy:Prop-GroupBy:Select-Month' => '%1$s (mesiac)', 'UI:DashletGroupBy:Prop-GroupBy:Select-Month' => '%1$s (mesiac)',
'UI:DashletGroupBy:Prop-GroupBy:Select-DayOfWeek' => '%1$ (deň v týžni)', 'UI:DashletGroupBy:Prop-GroupBy:Select-DayOfWeek' => '%1$s (deň v týžni)',
'UI:DashletGroupBy:Prop-GroupBy:Select-DayOfMonth' => '%1$s (deň v mesiaci)', 'UI:DashletGroupBy:Prop-GroupBy:Select-DayOfMonth' => '%1$s (deň v mesiaci)',
'UI:DashletGroupBy:MissingGroupBy' => 'Prosím zvoľte pole na ktorom objekty budú zoskupené spolu', 'UI:DashletGroupBy:MissingGroupBy' => 'Prosím zvoľte pole na ktorom objekty budú zoskupené spolu',
'UI:DashletGroupByPie:Label' => 'Koláčový graf', 'UI:DashletGroupByPie:Label' => 'Koláčový graf',

View File

@@ -278,7 +278,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
'Change:AttName_SetTo_NewValue_PreviousValue_OldValue' => '%1$s\'nin değeri %2$s olarak atandı (önceki değer: %3$s)', 'Change:AttName_SetTo_NewValue_PreviousValue_OldValue' => '%1$s\'nin değeri %2$s olarak atandı (önceki değer: %3$s)',
'Change:AttName_SetTo' => '%1$s\'nin değeri %2$s olarak atandı', 'Change:AttName_SetTo' => '%1$s\'nin değeri %2$s olarak atandı',
'Change:Text_AppendedTo_AttName' => '%2$s\'ye %1$s eklendi', 'Change:Text_AppendedTo_AttName' => '%2$s\'ye %1$s eklendi',
'Change:AttName_Changed_PreviousValue_OldValue' => '%1$\'nin değeri deiştirildi, önceki değer: %2$s', 'Change:AttName_Changed_PreviousValue_OldValue' => '%1$s nin değeri deiştirildi, önceki değer: %2$s',
'Change:AttName_Changed' => '%1$s değiştirildi', 'Change:AttName_Changed' => '%1$s değiştirildi',
'Change:AttName_EntryAdded' => '%1$s değiştirilmiş, yeni giriş eklendi.', 'Change:AttName_EntryAdded' => '%1$s değiştirilmiş, yeni giriş eklendi.',
'Change:State_Changed_NewValue_OldValue' => 'Changed from %2$s to %1$s~~', 'Change:State_Changed_NewValue_OldValue' => 'Changed from %2$s to %1$s~~',

View File

@@ -18,4 +18,7 @@
*/ */
Dict::Add('CS CZ', 'Czech', 'Čeština', array( Dict::Add('CS CZ', 'Czech', 'Čeština', array(
'UI:Object:Modal:Title' => 'Create an object~~', 'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
)); ));

View File

@@ -18,4 +18,7 @@
*/ */
Dict::Add('DA DA', 'Danish', 'Dansk', array( Dict::Add('DA DA', 'Danish', 'Dansk', array(
'UI:Object:Modal:Title' => 'Create an object~~', 'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
)); ));

View File

@@ -18,4 +18,7 @@
*/ */
Dict::Add('DE DE', 'German', 'Deutsch', array( Dict::Add('DE DE', 'German', 'Deutsch', array(
'UI:Object:Modal:Title' => 'Ein Objekt erstellen', 'UI:Object:Modal:Title' => 'Ein Objekt erstellen',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
)); ));

View File

@@ -19,4 +19,7 @@
Dict::Add('EN US', 'English', 'English', array( Dict::Add('EN US', 'English', 'English', array(
'UI:Object:Modal:Title' => 'Create an object', 'UI:Object:Modal:Title' => 'Create an object',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.',
)); ));

View File

@@ -18,4 +18,7 @@
*/ */
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array( Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'UI:Object:Modal:Title' => 'Create an object~~', 'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
)); ));

View File

@@ -18,4 +18,7 @@
*/ */
Dict::Add('FR FR', 'French', 'Français', array( Dict::Add('FR FR', 'French', 'Français', array(
'UI:Object:Modal:Title' => 'Create an object~~', 'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'Ce formulaire contient un attribut fichier obligatoire qui ne peut pas être renseigné en mode pop-up. La création de cet objet sera incomplète et pourra être complétée dans un formulaire en pleine page.',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'Ce formulaire contient un attribut fichier obligatoire qui ne peut pas être renseigné en mode pop-up. Cet objet est incomplet, il peut être complété dans un formulaire en pleine page.',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'Ce formulaire contient un attribut fichier obligatoire qui ne peut pas être modifié en mode pop-up.',
)); ));

View File

@@ -18,4 +18,7 @@
*/ */
Dict::Add('HU HU', 'Hungarian', 'Magyar', array( Dict::Add('HU HU', 'Hungarian', 'Magyar', array(
'UI:Object:Modal:Title' => 'Create an object~~', 'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
)); ));

View File

@@ -18,4 +18,7 @@
*/ */
Dict::Add('IT IT', 'Italian', 'Italiano', array( Dict::Add('IT IT', 'Italian', 'Italiano', array(
'UI:Object:Modal:Title' => 'Create an object~~', 'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
)); ));

View File

@@ -18,4 +18,7 @@
*/ */
Dict::Add('JA JP', 'Japanese', '日本語', array( Dict::Add('JA JP', 'Japanese', '日本語', array(
'UI:Object:Modal:Title' => 'Create an object~~', 'UI:Object:Modal:Title' => 'Create an object~~',
'UI:Object:Modal:Create:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. The creation of this object will be incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains a mandatory file attribute which cannot be set in modal mode. This object is incomplete, edit it in a full-page form to complete it.~~',
'UI:Object:Modal:Modify:Filled:MandatoryAttributeBlobInputs:Warning:Text' => 'This form contains mandatory file attribute which cannot be modified in modal mode.~~',
)); ));

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