Compare commits

..

322 Commits

Author SHA1 Message Date
acognet
e23c41232d N°4465 - Polishing iTop 3.0 beta6 - fix "configure this list" 2021-11-23 15:34:53 +01:00
Stephen Abello
e25d070a38 N°4465 Fix tagset and enumset dropdown overflowing out of the screen and not autoplacing themselves 2021-11-23 13:33:08 +01:00
Stephen Abello
499c97b914 N°4465 Fix transition button not grouping/ungrouping in some cases 2021-11-23 11:51:31 +01:00
Molkobain
c472b8ad3b N°4466 - Fix line breaks not displayed correctly 2021-11-23 11:40:52 +01:00
Molkobain
94ceb98b9e N°4466 - Fix line breaks not displayed correctly 2021-11-23 11:32:26 +01:00
Stephen Abello
b1de8683f0 N°3523 Adjust buttons and links colors to make application more accessible 2021-11-23 11:26:16 +01:00
Stephen Abello
cff2105908 Remove debug line 2021-11-23 11:06:23 +01:00
Stephen Abello
e11d5c142c N°3523 Adjust buttons and links colors to make application more accessible 2021-11-23 10:39:11 +01:00
Molkobain
f77b3bedaf N°2370 - Change PHPDoc to reflect reality, method will not be removed 2021-11-23 10:15:28 +01:00
Molkobain
3654df1cae N°4466 - SCSS: Improve readability 2021-11-23 09:57:53 +01:00
Molkobain
f3d2e89c68 N°4466 - Revert changes that broke contraints for fields to stay in their column (d0f9868 and c9aa6938) 2021-11-23 09:40:33 +01:00
Molkobain
dd46536f7c N°4477 - Update dataTables lib. to 1.11.3 in iTop 3.0 2021-11-22 17:31:48 +01:00
Molkobain
e249907a9c N°4475 N°4408 - Fix update on external key not refreshing dependents fields 2021-11-22 16:45:35 +01:00
Eric Espie
4ded943a43 N°2527 - Fix require approot 2021-11-22 16:00:45 +01:00
Molkobain
bd52f4fefb N°4468 - Add data-resource-id meta-data on all buttons 2021-11-22 15:44:27 +01:00
Pierre Goiffon
f57785e422 Merge remote-tracking branch 'origin/support/2.7' into develop
# Conflicts:
#	datamodels/2.x/authent-local/dictionaries/es_cr.dict.authent-local.php
#	datamodels/2.x/itop-config-mgmt/dictionaries/es_cr.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-knownerror-mgmt/dictionaries/es_cr.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-service-mgmt/dictionaries/es_cr.dict.itop-service-mgmt.php
#	datamodels/2.x/itop-tickets/dictionaries/es_cr.dict.itop-tickets.php
#	datamodels/2.x/itop-welcome-itil/es_cr.dict.itop-welcome-itil.php
#	dictionaries/es_cr.dictionary.itop.ui.php
#	test/integration/DictionariesConsistencyTest.php
2021-11-22 09:08:34 +01:00
Pierre Goiffon
77880c3675 🌐 N°3635 ES dict : change 'Español, Castellaño' to 'Español, Castellano' 2021-11-22 08:55:08 +01:00
Molkobain
bb369e68b5 N°4283 - Rename some SCSS partials to match conventions 2021-11-20 18:52:54 +01:00
Molkobain
2a2d68e204 N°4283 - Textarea: Make sure to use monospace font for code
- CSV import is now using monospace font
- Simplify SCSS code

Note: TextArea and all input UIBlocks need to be reworked as they are not properly organized, see comment in _input-text.scss
2021-11-20 18:42:41 +01:00
Molkobain
b49fbf1a97 N°4283 - Textarea: Make them take all available width 2021-11-20 18:12:41 +01:00
Molkobain
308bff7875 SCSS: Fix typo in variable name 2021-11-20 14:23:13 +01:00
Molkobain
9444e61856 N°4283 - Improve spacing of HTML inputs with their labels 2021-11-19 18:35:50 +01:00
Molkobain
2f114701a1 N°4283 - AttributeOneWayPassword: Fix spacing between the fields / button 2021-11-19 17:40:48 +01:00
Molkobain
5128db05cf N°4283 - AttributeBlob: Fix extra between the value and the links / input 2021-11-19 17:39:54 +01:00
Molkobain
9675202bb2 N°4283 - Force field labels to wrap even if it is a single giant word 2021-11-19 17:09:23 +01:00
Molkobain
cbe42cd727 N°4283 - Improve textarea and OQL inputs style
- All textarea app. wide avec the same padding
- OQL fields have the same padding as the other textareas
- OQL fields have the monospaced font
2021-11-19 16:34:01 +01:00
Pierre Goiffon
3559425fc1 N°°4463 Trigger : remove user rights check when controlling filter 2021-11-19 15:20:21 +01:00
Molkobain
a246528eec Update DoPostRequest() in itoprest.examples.php to fix optional headers like in utils::DoPostRequest 2021-11-19 14:47:51 +01:00
Stephen Abello
7fbbf1088a N°4460 Fix SCSS variable usage 2021-11-19 12:22:08 +01:00
Molkobain
fa4a5e2990 Merge spanish translations and remove duplicated files due to merge 2021-11-19 11:49:48 +01:00
Molkobain
b662a7e3c6 Remove (some) redundant description in spanish translations 2021-11-19 11:49:36 +01:00
Stephen Abello
cdc452282e Fix dictionnary entries 2021-11-19 11:38:08 +01:00
Molkobain
d8c4251c03 Remove (some) redundant description in spanish translations 2021-11-19 11:27:46 +01:00
Stephen Abello
72d1ab5cbc N°3523 Add accessibility to silo selection and user menu toggler 2021-11-19 11:16:19 +01:00
Stephen Abello
18b8e7093a N°3515 Update Enclosure icon (thanks to icons8 quick response ❤️) 2021-11-19 11:16:19 +01:00
Stephen Abello
aedb9ccbba N°3525 Make colors more accessible 2021-11-19 11:16:18 +01:00
Pierre Goiffon
67fa156c0e Merge remote-tracking branch 'origin/support/2.7' into develop
# Conflicts:
#	application/dashboard.class.inc.php
#	core/action.class.inc.php
#	datamodels/2.x/combodo-db-tools/dictionaries/es_cr.dict.combodo-db-tools.php
#	datamodels/2.x/itop-attachments/dictionaries/es_cr.dict.itop-attachments.php
#	datamodels/2.x/itop-config-mgmt/dictionaries/es_cr.dict.itop-config-mgmt.php
#	datamodels/2.x/itop-core-update/dictionaries/es_cr.dict.itop-core-update.php
#	datamodels/2.x/itop-hub-connector/dictionaries/es_cr.dict.itop-hub-connector.php
#	datamodels/2.x/itop-knownerror-mgmt/dictionaries/es_cr.dict.itop-knownerror-mgmt.php
#	datamodels/2.x/itop-portal-base/dictionaries/es_cr.dict.itop-portal-base.php
#	datamodels/2.x/itop-request-mgmt-itil/dictionaries/es_cr.dict.itop-request-mgmt-itil.php
#	datamodels/2.x/itop-request-mgmt/dictionaries/es_cr.dict.itop-request-mgmt.php
#	datamodels/2.x/itop-sla-computation/dictionaries/es_cr.dict.itop-sla-computation.php
#	datamodels/2.x/itop-storage-mgmt/dictionaries/es_cr.dict.itop-storage-mgmt.php
#	datamodels/2.x/itop-tickets/dictionaries/es_cr.dict.itop-tickets.php
#	datamodels/2.x/itop-virtualization-mgmt/dictionaries/es_cr.dict.itop-virtualization-mgmt.php
#	datamodels/2.x/itop-welcome-itil/es_cr.dict.itop-welcome-itil.php
#	dictionaries/es_cr.dictionary.itop.core.php
#	dictionaries/es_cr.dictionary.itop.ui.php
#	pages/ajax.render.php
#	setup/wizardsteps.class.inc.php
#	synchro/synchro_import.php
2021-11-19 10:28:46 +01:00
Pierre Goiffon
9437e689fd N°3635 Update ES translations
Many thanks to Miguel Turrubiates !
2021-11-18 17:48:43 +01:00
Pierre Goiffon
a79459bc53 N°4162 Portal exception page : restore exception message
Was removed with Silex to Symfony migration
2021-11-18 14:56:17 +01:00
Pierre Goiffon
500bd15843 Merge remote-tracking branch 'origin/support/2.6' into support/2.7 2021-11-18 08:54:32 +01:00
Pierre Goiffon
3e8dd2f4a5 N°4286 Setup : fix loop in first steps
Setup token wasn't removed at the right place :/
2021-11-18 08:54:10 +01:00
Pierre Goiffon
d0fade9ce1 Merge remote-tracking branch 'origin/support/2.6' into support/2.7
# Conflicts:
#	pages/ajax.render.php
#	setup/wizardsteps.class.inc.php
2021-11-17 17:39:36 +01:00
Pierre Goiffon
51a49dfce8 Remove warnings, use finally block, formatting 2021-11-17 16:10:50 +01:00
Molkobain
db8462ccc9 N°2788 - Constraint table to fit as much as possible in its column 2021-11-17 15:55:01 +01:00
Pierre Goiffon
066b71686d N°4286 Setup : restore backup download on WizStepDone
Setup token was put with N°2016 (6b5cc7c)
But later on we refactored the token handling in SetupUtils methods, and we had token removal in WizStepDone (43daa2ef) : so the backup download cannot be done :/
2021-11-17 14:39:44 +01:00
Pierre Goiffon
be633001a5 Revert "N°4360 Security hardening"
This reverts commit 8adf743cc7.

We will implement a different solution later (hopefully for 2.6.5 / 2.7.6 / 3.0.0 as well)
2021-11-17 11:13:29 +01:00
Molkobain
b0904cabfd N°4447 - Update german translations thanks to Martin Raeker from @itomig-de ! 2021-11-17 10:26:12 +01:00
Pierre Goiffon
84426c6634 N°4365 Security hardening 2021-11-17 10:15:12 +01:00
Molkobain
9d19c189e6 N°4420 N°4421 - Decrease transition duration a bit to match other transitions feeling 2021-11-16 22:41:42 +01:00
Molkobain
91d030933b Rename cmdbAbstractObject::EnumDisplayMode() to be consistent with the rest 2021-11-16 21:38:51 +01:00
Molkobain
d0f9868a19 N°2788 - Constraint table with <pre> to fit as much as possible in its column 2021-11-16 18:20:31 +01:00
Molkobain
2a913cd484 Restore prototypes of cmdbAbstractObject methods in which we added $sMode to preserve compatibility with existing extensions (#240)
* Restore prototypes of cmdbAbstractObject methods in which we added $sMode to preserve compatibility with existing extensions

* Rename cmdbAbstractObject::ENUM_OBJECT_MODE_XXX

* Add cmdbAbstractObject::ENUM_OBJECT_MODE_BULK_EDIT

* Refactor usage of $sDisplayMode in cmdbAbstractObject::DisplayModifyForm()
2021-11-16 17:47:09 +01:00
Pierre Goiffon
dbaf924171 N°4363 Security hardening 2021-11-16 17:19:19 +01:00
Pierre Goiffon
1b2d75efd6 N°4420 N°4421 Table selectable lines : restore style, restore script inclusion in object edition (#242) 2021-11-16 16:05:07 +01:00
Stephen Abello
d66a1e8c10 N°3515 Update some classes/dashboard icons to match 3.0 UI 2021-11-16 15:21:53 +01:00
Molkobain
a5ad981d70 N°4408 - Fix visual glitches on transition buttons in object details 2021-11-16 14:42:32 +01:00
Pierre Goiffon
8adf743cc7 N°4360 Security hardening 2021-11-16 12:01:16 +01:00
Molkobain
4e544a8eab Object details: Fix header buttons being too close to the right border when editing with a sticky header 2021-11-15 18:42:11 +01:00
Pierre Goiffon
75450ded1d N°4359 Security hardening 2021-11-15 16:38:11 +01:00
Molkobain
2b97fed5db Fix unclosed TWIG tag in toolbar spacer 2021-11-15 16:24:41 +01:00
Molkobain
62d8a2ba1f Fix crash in activity panel when entry user is unknown 2021-11-15 16:24:41 +01:00
acognet
fe9a442500 N°4370 - Field content overlapping on rest of the UI (Backoffice) - 2 2021-11-15 15:48:51 +01:00
acognet
11d2991286 N°4417 - Fix portal user not seeing support agent avatar on caselog in portal 2021-11-15 15:36:03 +01:00
Pierre Goiffon
bcca6ac720 Merge remote-tracking branch 'origin/support/2.6' into support/2.7 2021-11-15 15:07:19 +01:00
Molkobain
abb63182e3 N°4346 - Fix operator 2021-11-15 12:08:28 +01:00
Molkobain
4409162eb7 N°2875 - Fix mentions not working with some non url encoded "markers" (eg. '#') 2021-11-14 22:53:10 +01:00
Molkobain
0dc3d249da N°3526 - Clean up pass on UIBlocks
- Add ancestors CSS classes on UIBlocks when missing (programmatically)
- Fix SCSS due to some blocks inheriting their ancestors rules
2021-11-14 22:33:48 +01:00
Molkobain
3598da8808 N°3526 - Clean up pass on UIBlocks
- Move UIBlock::ENUM_BLOCK_FILES_TYPE_XXX to iUIBlock
- Add PHPDoc
- Remove Object return type hint on overloadable methods
- Add scalar return type hint
- Add type hint on parameters
2021-11-14 22:33:48 +01:00
Molkobain
a9b30e160f N°3526 - Clean up pass on UIBlockFactories
- Add PHPDoc
- Remove (Object) return type hint on overloadable methods
2021-11-14 22:33:48 +01:00
Stephen Abello
249fa6ca93 N°3515 Update some classes icon to match 3.0 UI 2021-11-13 11:57:00 +01:00
acognet
50d73e7fca N°4346 - Restore HTML metadata (data-xxx) on lists in the backoffice 2021-11-12 11:20:48 +01:00
Molkobain
1b9e67dc6e SCSS: Fix Quick Create / Global Search drawers being slightly visible when closed and with a full history 2021-11-11 11:09:40 +01:00
Molkobain
ca2124130f N°3517 - Clean-up pass on all block factories 2021-11-10 19:51:59 +01:00
Molkobain
fffd4cf962 N°4346 - Remove duplicated loop and factorize code 2021-11-10 19:12:14 +01:00
Molkobain
4a6b351c37 N°4440 - Fix hierarchy tooltip on ext. key widget 2021-11-10 18:49:25 +01:00
Molkobain
206143824b N°4408 - Improve transition buttons UX in object details
Transition buttons are now grouped in the "Apply" button when there is not enough space to display them all
2021-11-10 18:44:25 +01:00
Molkobain
90c866f696 N°4427 - Fix bouncing results in search page 2021-11-09 19:22:50 +01:00
vdumas
0bbdbdb1cd N°3735 revert typo fix on Known Error module as it would remove the module on all iTop having it installed today 2021-11-09 18:03:11 +01:00
Stephen Abello
395c9c288b N°4355/1745 Prevent malformed caselog entries from breaking activity panel DOM 2021-11-09 16:19:16 +01:00
acognet
60a17c9a65 N°4406 - Warning: Missing constant ITOP_DESIGN_LATEST_VERSION 2021-11-09 15:20:42 +01:00
acognet
2de6ba2827 N°3928 - Polishing: Impact analysis 2021-11-09 15:18:08 +01:00
Pierre Goiffon
2beb795f9a N°4304 Security hardening 2021-11-09 11:32:53 +01:00
Pierre Goiffon
6847d8a5c7 N°4414 Startup error handler : fix message logged twice (#243) 2021-11-09 10:25:29 +01:00
Stephen Abello
f6885567eb N°4408 Add correct CSS class to console renderer richtext field 2021-11-09 09:49:37 +01:00
Stephen Abello
4b888a3805 N°4378 Restore keyboard shortcuts behavior for object details 2021-11-09 09:49:37 +01:00
odain
07f470024a N°4367 : renamed the phpunit annotation for ci purpose 2021-11-09 08:55:54 +01:00
Molkobain
4c69865480 N°2875 - Fix error when trigger has an OQL on a root class which is not abstract 2021-11-08 19:21:43 +01:00
Pierre Goiffon
06985d3cf2 N°4387 synchro_import restore previous set_time_limit call
Was changed in b1761e04 (iTop 2.7.0) by mistake

Added noinspection as the IDE warning (https://github.com/kalessil/phpinspectionsea/blob/master/docs/control-flow.md#statement-could-be-decoupled-from-foreach) seems to be a false positive ?
2021-11-08 16:14:19 +01:00
acognet
ab40c67678 N°4346 - Restore HTML metadata (data-xxx) on lists in the backoffice - after configure this list 2021-11-08 15:02:45 +01:00
odain
a286564345 N°4367 : add phpunit annotation for ci 2021-11-08 06:42:45 +01:00
Stephen Abello
c9e592c07d N°3904 Avoid restore buttons from asking for confirmation multiple times 2021-11-05 15:51:04 +01:00
denis.flaven@combodo.com
5bcdcb52b2 N°4534 - creation of a new category 'filter' to hide admins to
non-admins without breaking legacy code.
2021-11-05 11:29:41 +01:00
Pierre Goiffon
456283866c ✏️ Fix typo in code comment 2021-11-05 11:24:18 +01:00
Stephen Abello
821c14ee86 N°3904 Fix scheduled backups not being restorable from user interface 2021-11-05 10:28:09 +01:00
Stephen Abello
30f1ad8da8 N°4412 Activity panel's notification entries weren't expandable since c0fc62b 2021-11-05 10:07:30 +01:00
vdumas
6d0b979bcd N°3735 revert one part of the previous commit done by mistake 2021-11-04 17:58:49 +01:00
Pierre Goiffon
e16425ab8a Revert behavior change in \UserRights::FindUser v2
Was made in 2ab0fab0 by mistake, committed incorrectly in 2e426d37 (wooops)
2021-11-04 17:28:06 +01:00
Pierre Goiffon
2e426d373d Revert behavior change in \UserRights::FindUser
Was made in 2ab0fab0 by mistake
2021-11-04 17:26:09 +01:00
vdumas
a1a38ca224 N°3735 Add missing translation for older methods 2021-11-04 17:10:17 +01:00
vdumas
05830de4e7 N°3735 Add Method Parameters translation for ITSM Designer 2021-11-04 16:42:10 +01:00
Pierre Goiffon
2e8920f3d1 N°4367 Fix \privUITransactionFileTest::testIsTransactionValid
Was crashing because of limitations on loading User objects
2021-11-04 16:15:01 +01:00
Stephen Abello
4450c28295 N°4408 Prevent dependent field from being triggered twice 2021-11-04 16:02:51 +01:00
Stephen Abello
aa43201cbe N°4408 Do not display non-interpreted HTML when richtext fields are loading 2021-11-04 16:02:51 +01:00
Eric Espie
3d7df7600f N°4346 - Restore HTML metadata (data-xxx) on lists in the backoffice 2021-11-04 14:56:06 +01:00
Stephen Abello
aba0d4144c N°4408 Fix inputs flickering when widgets are loading 2021-11-04 10:49:17 +01:00
Stephen Abello
fe82f54066 N°4396 Fix graphs not displaying anymore in Dashboard Attribute 2021-11-03 15:26:33 +01:00
Pierre Goiffon
2d44d9d1c6 N°4367 disable test for now
On develop we have an error on Jenkins :
> Login with user2 throw an error
2021-11-03 15:19:42 +01:00
Molkobain
8c03525fbb N°2875 - Fix missing argument ":this" when submitting an entry in the activity panel 2021-11-03 12:01:20 +01:00
Eric Espie
79c17970f8 Fix tests on packages missing composer.json file 2021-11-03 11:31:43 +01:00
Pierre Goiffon
04f7660267 Merge remote-tracking branch 'origin/support/2.7' into develop
# Conflicts:
#	js/utils.js
2021-11-03 11:19:23 +01:00
Pierre Goiffon
8c7f7abaab Merge remote-tracking branch 'origin/support/2.6' into support/2.7
# Conflicts:
#	test/application/privUITransactionFileTest.php
2021-11-03 11:10:43 +01:00
Eric Espie
e963a6741f N°3985 - avoid sql request for notifications in case of object creation and avoid reloads on requesting triggers 2021-11-03 11:00:41 +01:00
Pierre Goiffon
e8d314e1f6 N°4367 Fix \privUITransactionFileTest::testIsTransactionValid
* change user name for when password policy is active
* admin user doesn't exist on Jenkins : create a second user
* test UserRights::Login return value
* document that we depend on the sample data
2021-11-03 10:50:25 +01:00
Molkobain
ebe493f7f7 Set attribute: Add tooltip on items when attribute is in edition 2021-11-02 18:48:03 +01:00
Pierre Goiffon
e29f1825be N°4367 Fix "redeclaration of const CombodoSanitizer"
The utils.js can be included more than once in old iTop branches :( This is fixed in 3.0.0 (develop branch)

Also add missing ";"
2021-11-02 17:14:16 +01:00
vdumas
2df8bb1707 N°3735 - Add SetComputedDate on DBObject 2021-11-02 16:55:25 +01:00
Eric Espie
27995c14bf N°3985 - avoid sql request for history in case of object creation 2021-11-02 15:35:51 +01:00
Molkobain
890d0f64ce Add @ilya-stukalov to the constributors, thanks! 2021-11-01 18:18:17 +01:00
Vladimir Kunin
a29bb83a20 Fix typos 2021-10-29 18:27:04 +02:00
Vladimir Kunin
413820a22b Update general UI translations: many typos & errors, "iTop" replaced by constant ITOP_APPLICATION_SHORT, add new & improve existing strings. 2021-10-29 18:27:04 +02:00
Vladimir Kunin
860472beea Update CMDBChangeOp UI translations 2021-10-29 18:27:04 +02:00
Vladimir Kunin
dd4283199e Update NavigationMenu translations 2021-10-29 18:27:04 +02:00
Vladimir Kunin
e147aa31aa Add KeyboardShortcuts translations 2021-10-29 18:27:04 +02:00
Vladimir Kunin
b8aa3dfb0c Update Activity panel translations 2021-10-29 18:27:04 +02:00
Vladimir Kunin
83e2dbc30a Update Newsroom translations 2021-10-29 18:27:04 +02:00
Vladimir Kunin
f63dad7a3a Update tab container translations 2021-10-29 18:27:04 +02:00
Vladimir Kunin
c75bb6b892 Update Navigation Menu translations 2021-10-29 18:27:04 +02:00
Vladimir Kunin
15ffb82257 Update preferences translations 2021-10-29 18:27:04 +02:00
Vladimir Kunin
5e2ae18570 Typo 2021-10-29 18:27:04 +02:00
ilya.stukalov
070a23e61b Add UI translations for iTop 3.0. 2021-10-29 18:27:04 +02:00
acognet
0ab6655ae7 N°4385 - MetaModel::GetRelatedObjectsUp does yield correct results, while DBObject->GetRelatedObjectsUp does not 2021-10-29 13:36:32 +02:00
acognet
322371dd9f N°4349 - Drop-down mandatory template field documented still required in modification 2021-10-29 13:33:44 +02:00
Molkobain
b8e974cfd1 N°4398 - Add object name in the tab name when browsing datamodel viewer 2021-10-28 15:58:32 +02:00
odain
e03eb1e279 N°3556: fix broken ci temporarly for test using ItopDataTestCase 2021-10-28 11:48:14 +02:00
bruno-ds
6d2b75df9e N°4261 - test correctness: log deactivation is performed using bool false and not Error level
thanks @BenGrenoble
2021-10-26 15:54:44 +02:00
Stephen Abello
ab1b6837a2 N°3800 Fix line spacing in rich text editor and in its displayed content 2021-10-26 15:26:29 +02:00
Stephen Abello
5c02b5c38c N°4396 Fix graphs not displaying anymore in Dashboard Attribute 2021-10-26 10:50:32 +02:00
Stephen Abello
dd486dd015 N°3911 Fix datamodel viewer table overflowing in some case 2021-10-26 10:11:12 +02:00
Molkobain
b7e8c0de8a N°4400 - Restore PHP debug output being displayed in a modal 2021-10-25 21:34:03 +02:00
Molkobain
32dae59b9b N°4400 - Move inline JS functions to corresponding JS file 2021-10-25 19:51:07 +02:00
acognet
5540988001 N°4349 - Drop-down mandatory template field documented still required in modification 2021-10-25 15:50:43 +02:00
Stephen Abello
f8c59e6171 N°3911 Allow datamodel viewer graph to be horizontally scrolled when overflowing out of page 2021-10-25 15:35:33 +02:00
acognet
ab23ea1da0 N°3928 - Polishing: Impact analysis 2021-10-25 14:12:11 +02:00
Molkobain
3d49cd199a Fix Panel not being collapsible anymore (regression from 1c11cef2f, thanks @accognet !) 2021-10-25 12:15:02 +02:00
denis.flaven@combodo.com
ee848129b7 New methods for creating/import test data from XML files. 2021-10-25 11:49:56 +02:00
Molkobain
63448276cc Tooltips: Add possibility to define theme through meta-data 2021-10-24 11:12:07 +02:00
Stephen Abello
d5af6e9061 N°4319 Backup page datatable no longer grow off limits, even on Chrome, also fix restore button 2021-10-22 14:39:30 +02:00
Molkobain
2fc3e7ad38 N°2875 - Fix empty mention results when host object is being created 2021-10-22 13:23:24 +02:00
Stephen Abello
465ed58471 N°4319 Backup page datatable no longer grow off limits 2021-10-22 10:35:43 +02:00
Eric Espie
fac454595a removing meaningless information 2021-10-22 10:10:14 +02:00
acognet
89ea4adbce N°4366 - RCSS in /pages/ajax.searchform.php on develop 2021-10-21 17:15:27 +02:00
bruno-ds
427f107ddf N°4261 - Fix CI and add a test case 2021-10-21 17:04:08 +02:00
Pierre Goiffon
59d674d744 Fix broken export
Argument 1 passed to Combodo\iTop\Controller\AjaxRenderController::ExportBuild() must be an instance of Combodo\iTop\Controller\ajax_page, instance of AjaxPage given,

Caused by merge of support/2.7 with 2 conflicts on the same line (347cbca5)
2021-10-21 17:03:36 +02:00
Molkobain
54db7243bf N°2875 - Restore correct app. version check in the module installer 🥶 2021-10-21 16:36:41 +02:00
Eric Espie
e6d2b0bc18 N°4346 - Restore HTML metadata (data-xxx) on lists in the backoffice 2021-10-21 16:18:57 +02:00
bruno-ds
9e1f5a1b63 N°4261 - ExceptionLog restore Portal log level to Error as for iTop 2.7 (no more BC break)
- default level is now Error,
 - for "write in DB": each level (including error) are ignored by default
2021-10-21 16:18:27 +02:00
Stephen Abello
5b42b8983a N°3515 Update some classes icon to match 3.0 UI 2021-10-21 16:00:18 +02:00
Molkobain
5bae964064 Setup: Fix spacing between module's label and module's more info link 2021-10-21 15:58:19 +02:00
Molkobain
ef9c18e393 Fix setup crash on upgrade when env-production / data/cache-production have been deleted (missing dict files) 2021-10-21 15:16:15 +02:00
Pierre Goiffon
ed43d00afe Merge remote-tracking branch 'origin/support/2.7' into develop
# Conflicts:
#	application/transaction.class.inc.php
#	core/userrights.class.inc.php
2021-10-21 15:12:23 +02:00
Pierre Goiffon
908a48e0a1 Merge remote-tracking branch 'origin/support/2.6' into support/2.7
# Conflicts:
#	application/transaction.class.inc.php
#	test/application/privUITransactionFileTest.php
2021-10-21 15:09:50 +02:00
Pierre Goiffon
9b854dbcc7 N°4289 skip test (not working on Jenkins) 2021-10-21 14:52:59 +02:00
Pierre Goiffon
7757f1f2d2 N°4289 Security hardening 2021-10-21 12:43:03 +02:00
bruno-ds
5e2f8a4ea6 N°4261 - ExceptionLog: improve readability 2021-10-21 11:07:44 +02:00
bruno-ds
62f7eca0a8 N°4261 - Log in db: change default level.
In order to preserve the existing behavior: not EventIssue creation (unless configured otherwise).
2021-10-21 11:07:44 +02:00
Stephen Abello
820f2cbed6 N°4015 Set a minimum height for last tab in scrollable mode and allow customizing tabs minimum height in themes 2021-10-21 10:37:38 +02:00
bruno-ds
d03718681f N°4261 - Log in db: change default configuration.
As since the latest changes by Pir, the default value is handled directly in the class.
2021-10-21 10:11:26 +02:00
Pierre Goiffon
a353317746 N°4289 Fix privUITransactionFile generating error if MetaModel not loaded 2021-10-20 17:26:32 +02:00
Pierre Goiffon
723eb90160 N°4289 privUITransactionSession phpDoc 2021-10-20 17:25:58 +02:00
Pierre Goiffon
0e14be8b15 N°4261 Refactor ExceptionLog (#239)
Doing a code review with Bruno, we agreed to do some little refactoring :

* Level per exception class
  - Before the whole ExceptionLog::Log method was a total rewrite of its parent, with some code duplicates... not a good idea : we should better improve LogAPI to make other similar uses possible in the future !
  - The logic to get level from config must be in a GetMinLogLevel override
* Write to DB
  - Pull up this functionnality in LogAPI
  - Add a sCode parameter in GetLevelDefault

Doing this refactoring, I also improved :

* Test the attributes set when creating the EventIssue object : during my dev I had crashes because I didn't filled all the mandatory fields... Having a PHPUnit test checking this will prevent future bugs to happen if attributes are modified in the class or in the object creation method
* Use Throwable instead of Exception : this was added in PHP 7.0 and will allow to catch both Exception and Error
* Because we need to have 2 statements on the same line in \Combodo\iTop\Test\UnitTest\Core\Log\ExceptionLogTest::testLogInFile, I modified the editorConfig file to allow disabling the formatter using comments.
2021-10-20 16:01:08 +02:00
Pierre Goiffon
ef6d7925fc Log : always log the same structure, and add a delimiter at the end
This will allow to parse the logs more easily !
2021-10-20 15:16:57 +02:00
acognet
ec8c2ca122 N°4374 - Add sanitizer helper for front end (JS) - Rollback 2021-10-20 10:31:41 +02:00
acognet
ebe50b319a N°4357 - Autocomplete issue with accent in a list 2021-10-20 10:31:40 +02:00
Eric Espie
48b1a15bf7 N°4369 - updated autoloader 2021-10-20 09:52:32 +02:00
Eric Espie
5102400be4 N°4369 - Return product and version in status rest ws 2021-10-20 08:52:25 +02:00
Molkobain
4bde828dbb 🌐 N°2875 - Fix missing word in TriggerOnObjectMention:mentioned_filter(+) thanks to @jbostoen 2021-10-19 21:36:23 +02:00
Eric Espie
e0929f4d0d N°4375 - Change CKEditor plugins init 2021-10-19 17:16:17 +02:00
Eric Espie
ef1903dabe N°4369 - Return product and version in status rest ws 2021-10-19 16:20:06 +02:00
Molkobain
e53a45ec5d N°2875 - Update config. param. 2021-10-19 15:40:42 +02:00
Molkobain
0811fd4aa7 N°2875 - Default triggers/action are created during first install/upgrade of 3.0.0 for DM classes with a log attribute in order to allow Person mention out of the box 2021-10-19 15:16:43 +02:00
Molkobain
cdf5789d62 N°2875 - Add $bRootFirst param. to MetaModel::EnumChildClasses() to be iso with MetaModel::EnumParentClasses() 2021-10-19 15:16:43 +02:00
Molkobain
8f2b5ad8e2 N°2875 - Invert TriggerOnObjectMention approach
- Trigger is now positioned on a specific "host" class (eg. Ticket)
- Trigger now has a "mentioned objects" filter which determines the scope of mentioned objects which will activate the trigger
2021-10-19 15:16:42 +02:00
Molkobain
df49e9c3b5 N°2875 - Add class icon to Trigger and Action 2021-10-19 15:16:42 +02:00
Molkobain
6915b0b824 N°2875 - RichText: Fix config property being an array instead of a string (also add a setter) 2021-10-19 15:16:42 +02:00
Molkobain
0b0c99c935 🌐 N°2875 - Add TriggerOnObjectMention:mentioned_filter(+) translations 2021-10-19 15:16:42 +02:00
Molkobain
b9a68f4aeb 🌐 N°2875 - Add TriggerOnObject:filter description
Note: The new translations are based on the russian one as it seemed pretty good, hence the russian translation is not "reset".
2021-10-19 15:16:41 +02:00
Eric Espie
c611b11981 N°4369 - About box with reduced information for non-administrators 2021-10-19 14:52:57 +02:00
Stephen Abello
25763c2d2e N°4351 Fix case log own entries overflowing when unnecessary 2021-10-19 14:49:44 +02:00
acognet
c8f3d23d30 N°4361 - XSS in csvimport on develop 2021-10-19 11:32:05 +02:00
acognet
88fda1466e N°4374 - Add sanitizer helper for front end (JS) 2021-10-19 11:26:29 +02:00
Eric Espie
8a7f0d346d N°4375 - Change CKEditor plugins init 2021-10-19 11:04:33 +02:00
acognet
3e5440aa3b N°3928 - Polishing: Impact analysis - remove icons in pdf list 2021-10-19 10:19:41 +02:00
acognet
6f08038f34 N°4347 - Fix JS errors in custom dashboards for list dahslets 2021-10-19 09:50:53 +02:00
Pierre Goiffon
216e62c448 N°4367 Replace uses of utils.js EncodeHtml() 2021-10-19 08:48:58 +02:00
Eric Espie
f4856150ed N°4375 - Upgrade to CKEditor 4.16.2 2021-10-19 08:46:58 +02:00
Pierre Goiffon
d73d39e71a utils.js : deprecate EncodeHtml and copy it to CombodoSanitizer.EscapeHtml 2021-10-19 08:43:27 +02:00
Pierre Goiffon
6abdabd197 Removing config file writing in OQL PHPUnit tests (#238) 2021-10-18 16:37:24 +02:00
Pierre Goiffon
020460d048 N°4367 update test to use new utils constants 2021-10-18 15:24:10 +02:00
Pierre Goiffon
347cbca5cf Merge remote-tracking branch 'origin/support/2.7' into develop
# Conflicts:
#	application/transaction.class.inc.php
#	application/ui.extkeywidget.class.inc.php
#	composer.json
#	composer.lock
#	js/utils.js
#	lib/composer/InstalledVersions.php
#	lib/composer/installed.json
#	lib/composer/installed.php
#	lib/pear/archive_tar/Archive/Tar.php
#	lib/pear/archive_tar/package.xml
#	setup/wizardsteps.class.inc.php
#	sources/Controller/AjaxRenderController.php
2021-10-18 14:57:19 +02:00
Pierre Goiffon
8ea5be4ead Merge remote-tracking branch 'origin/support/2.6' into support/2.7
# Conflicts:
#	application/transaction.class.inc.php
2021-10-18 14:32:27 +02:00
Pierre Goiffon
b3f827ed5e N°4367 Security hardening 2021-10-18 14:27:58 +02:00
Pierre Goiffon
eaf8a187aa N°3332 report function rename
The method was renamed in 18d52319 but only on support/2.7 and above
2021-10-18 11:36:17 +02:00
Pierre Goiffon
34f64c61f6 privUITransaction fix inspections errors + formatting 2021-10-18 11:32:38 +02:00
acognet
1d28bbe3f4 N°3928 - Polishing: Impact analysis 2021-10-18 11:15:22 +02:00
Molkobain
54c89dcbb5 PHPDoc 2021-10-14 18:03:27 +02:00
acognet
06dac2417a Rollback N°4361 - XSS in csvimport on develop 2021-10-14 16:25:16 +02:00
acognet
83125d9ae1 N°4362 - XSS in ajax.render.php?operation=wizard_helper on develop 2021-10-14 15:46:46 +02:00
odain
20f4419062 ci: add phpunit annotation to exclude/include test 2021-10-14 14:26:07 +02:00
acognet
0f4ca4237d N°4362 - XSS in ajax.render.php?operation=wizard_helper on develop 2021-10-14 10:14:01 +02:00
acognet
bc2c12a8c3 N°3928 - Polishing: Impact analysis 2021-10-14 09:31:21 +02:00
Pierre Goiffon
8154e718a1 N°4356 modifications after code review
Thanks @Molkobain !
2021-10-13 17:23:29 +02:00
Eric Espie
9ec6c2098b N°3928 - Fix iTop logo 2021-10-13 17:21:44 +02:00
Eric Espie
4305e40ae5 Fix warning 2021-10-13 17:15:58 +02:00
Pierre Goiffon
0f149ed852 N°4381 remove old deprecations.md file
Content wasn't up to date
Check the wiki instead (https://www.itophub.io/wiki/page?id=3_0_0%3Ainstall%3A270_to_300_migration_notes)
2021-10-13 16:50:27 +02:00
Pierre Goiffon
d5f9ca9c4b N°4381 CONTRIBUTING : move badge image 2021-10-13 16:48:11 +02:00
Pierre Goiffon
198c63dd79 N°4381 Replace documentation/itop-tickets.htm by a wiki link
Page wasn't maintained anymore
A link was displayed at the end of the setup
2021-10-13 16:40:06 +02:00
acognet
96ae91494b N°3928 - Polishing: Impact analysis 2021-10-13 14:25:55 +02:00
acognet
ceaa98f4ef N°4349 - Drop-down mandatory template field documented still required in modification 2021-10-13 14:25:38 +02:00
acognet
af4b9aaa52 N°4370 - Field content overlapping on rest of the UI (Backoffice) - 2 2021-10-13 14:25:23 +02:00
acognet
d2662b608b N°4349 - Drop-down mandatory template field documented still required in modification 2021-10-13 14:12:23 +02:00
Molkobain
591d189a33 N°3935 - Increase size a bit 2021-10-13 13:36:08 +02:00
Molkobain
36bea54bac N°3935 - Improve transition form UI with different style for cancel button and wider inputs 2021-10-13 13:11:11 +02:00
Pierre Goiffon
b5369a0c03 N°4356 Fix portal attachment download
Was opening the attachment directly in the browser (HTTP header Content-Disposition set to 'inline' instead of 'download')
2021-10-13 12:10:22 +02:00
Molkobain
1c11cef2f8 N°3939 - Keep current tab when editing an object (regression from 1e99ad436) 2021-10-12 21:51:51 +02:00
Molkobain
d8bd5528d3 Merge remote-tracking branch 'origin/support/3.0.0-beta5' into develop 2021-10-12 20:11:36 +02:00
Molkobain
9ed8cf7970 Setup: Ease usage of the UI when zoomed up to 150% (licenses fieldset being too height) 2021-10-12 18:44:47 +02:00
Molkobain
be24d409ed Config: Move security.hide_administrators parameter with other security.xxx parameters 2021-10-12 18:01:56 +02:00
Molkobain
64d91b194f PHPDoc 2021-10-12 18:01:45 +02:00
Eric
2bc61caab1 N°4207 N°4298 Fix data/.maintenance flag not removed by setup anymore
Was already fixed in the develop branch
(cherry picked from commit d0986c048a)
(cherry picked from commit 9126635cf2)
2021-10-12 12:23:49 +02:00
Pierre Goiffon
8f0a5fcaf9 N°4231 Security hardening 2021-10-12 11:11:11 +02:00
Eric Espie
b6df73bdcc N°2527 - Fix dependencies 2021-10-12 09:04:52 +02:00
Eric Espie
836a35d0dd N°4352 - Fix unlock message 2021-10-12 08:55:59 +02:00
acognet
c7a7bfcd68 N°3928 - Polishing: Impact analysis 2021-10-12 08:17:50 +02:00
Molkobain
e7e09b5023 N°4372 - Fix AttributeText limited dimensions in fullscreen when height/width is defined in the DM 2021-10-11 18:08:52 +02:00
Molkobain
3f24b80043 Code cleanup 2021-10-11 18:00:33 +02:00
Pierre Goiffon
fe3512cb5f N°4335 Fix export with PHP < 7.0+ 2021-10-11 17:05:30 +02:00
acognet
2a32c5691b N°4370 - Field content overlapping on rest of the UI (Backoffice) - 2 2021-10-11 16:17:19 +02:00
acognet
dce244f5aa N°4370 - Field content overlapping on rest of the UI (Backoffice) - 2 2021-10-11 15:28:37 +02:00
acognet
63bd1643ce N°4370 - Field content overlapping on rest of the UI (Backoffice) - 2 2021-10-11 09:11:38 +02:00
acognet
3c84a74ea4 N°4357 - Autocomplete issue with accent in a list 2021-10-11 09:09:59 +02:00
acognet
d9870c2513 N°4361 - XSS in csvimport on develop 2021-10-11 09:09:13 +02:00
acognet
7f0493c91d N°3928 - Polishing: Impact analysis 2021-10-07 18:11:04 +02:00
acognet
26e32b1759 Remove console.log 2021-10-07 14:04:22 +02:00
acognet
1b1a6321d7 N°4349 - Drop-down mandatory template field documented still required in modification 2021-10-07 14:04:22 +02:00
acognet
0eba00259a N°3928 - Polishing: Impact analysis 2021-10-07 11:45:53 +02:00
denis.flaven@combodo.com
a7a9e5f0eb N°4354 - Fixed the test which was using a (now fixed) edge case! 2021-10-06 18:45:41 +02:00
denis.flaven@combodo.com
bf4835eec0 N°4354 - Hide Administrator profile to non-admins 2021-10-06 15:34:23 +02:00
acognet
9fbc631b07 N°4349 - Drop-down mandatory template field documented still required in modification 2021-10-05 14:18:28 +02:00
acognet
c6cb7c41cd N°4349 - Drop-down mandatory template field documented still required in modification 2021-10-05 11:47:45 +02:00
acognet
0cb1583688 N°1731 - Allow Transitions without unnecessary confirmation - add force_transition_confirmation param 2021-10-05 11:36:08 +02:00
acognet
370b42d1fd N°3835 - Make global pass on all inputs (objects, dashlets, ...) to ensure XSS and double encoding have been dealt with 2021-10-04 17:28:05 +02:00
Eric Espie
0da8c761c7 N°2527 - Manage hierarchical keys in database maintenance tools 2021-10-04 16:26:16 +02:00
Eric Espie
8c39374abb N°2527 - Manage hierarchical keys in database maintenance tools 2021-10-04 16:23:35 +02:00
acognet
eb239843aa N°4347 - Fix JS errors in custom dashboards for list dahslets 2021-10-04 15:57:17 +02:00
acognet
e38ca54691 N°3835 - Make global pass on all inputs (objects, dashlets, ...) to ensure XSS and double encoding have been dealt with 2021-10-04 15:57:17 +02:00
Eric Espie
94d99a4109 N°2527 - Manage hierarchical keys in database maintenance tools 2021-10-04 11:25:20 +02:00
Eric Espie
f39e801719 N°2527 - Manage hierarchical keys in database maintenance tools 2021-10-04 11:24:30 +02:00
acognet
645f20742c N°3934 - Polishing: User Preferences 2021-10-04 11:00:23 +02:00
acognet
d86065b4a6 N°3934 - Polishing: User Preferences 2021-10-04 10:01:10 +02:00
Molkobain
8af54efd44 N°4068 - Object details: Avoid image attributes to overlap other attributes when they are very large
- Keep its width contained in the column
- Maintain aspect ratio of the container div to hint the user on which size the image should be
- Keep image real size as long as it fits in the container
2021-10-02 10:25:19 +02:00
Molkobain
c96ee4fafc Fix typo 2021-10-01 18:30:03 +02:00
Molkobain
ced4d1c5f1 Fix dictionary entry verbal form 2021-10-01 18:28:24 +02:00
acognet
6d3e8df3e4 N°3934 - Polishing: User Preferences 2021-10-01 18:22:17 +02:00
acognet
963fae243c N°3835 - Make global pass on all inputs (objects, dashlets, ...) to ensure XSS and double encoding have been dealt with 2021-10-01 18:22:17 +02:00
Molkobain
5a09365221 N°3925 - External key: Use more vertical space when possible (30% of the window tops) and fit within the window if not enough space (below or above the input) 2021-10-01 17:38:55 +02:00
Eric Espie
49c5f75c6c N°3520 - Fix static datatables rows 2021-10-01 15:18:29 +02:00
Molkobain
bd67bc8838 N°3882 - Handle use of transparent colors in dynamic header dashlet 2021-10-01 11:48:05 +02:00
Molkobain
c9aa693872 AttributeSet: Avoid items' label to wrap on display in the backoffice (regression from 42be0c20) 2021-10-01 09:37:51 +02:00
acognet
776c03ef6a fix loading of ckeditor 2021-10-01 09:15:34 +02:00
Molkobain
ae6f8fba5c N°3520 - Add missing data-role on UIBlocks 2021-09-30 22:07:39 +02:00
Molkobain
a139dc7e6e N°3520 - HTML metadata: Add data-role to backoffice logos 2021-09-30 21:31:27 +02:00
Molkobain
7d46626d9c N°3520 - HTML metadata: Add object state in object details if present 2021-09-30 21:30:56 +02:00
Molkobain
9ea25188ba N°3925 - Fix state flags not taken into account correctly by a child class (regression from f2ff5a4e) 2021-09-30 19:41:56 +02:00
Molkobain
fadafa8267 N°3925 - Fix stimulus not applied after submission of multiple logs in an object (read-only) 2021-09-30 19:13:33 +02:00
Molkobain
e0d6bc18be N°3925 - Improve display of long external keys in autocomplete / dropdowns
- Show ellipsis if label is overflowing
- Show tooltip on hover (useful for overflowing labels)
2021-09-30 19:13:33 +02:00
Molkobain
2ae01c19e1 Advanced search: Fix checkbox / label alignment on multi choices criteria 2021-09-30 17:49:33 +02:00
Stephen Abello
a5e2831e31 N°3931 Allow HTML in DataTables' Panel title 2021-09-30 15:53:40 +02:00
acognet
e86454ff74 N°3912 - Polishing: Export 2021-09-30 15:06:21 +02:00
acognet
62be3f9f58 fix loading of ckeditor 2021-09-30 15:06:21 +02:00
acognet
cddbe27182 N°4315 - Polish bulk modify screens 2021-09-30 15:06:21 +02:00
Stephen Abello
1f56e39577 N°3931 Make object deletion errors more visible 2021-09-30 15:01:30 +02:00
Stephen Abello
a35c80de57 Align FormTable row metadata forwarding with Datatables/StaticTables ones 2021-09-30 15:01:29 +02:00
Eric Espie
4b8ef4f919 N°3572 - Fix Data Integrity tab results display
- fix synchrodatasource SQL definitions
- fix Hierarchical keys warnings due to bad parameters
2021-09-30 14:53:00 +02:00
Pierre Goiffon
c3d23981fb 💡 Document limitation in \LogAPI::GetConfig 2021-09-30 14:45:06 +02:00
Pierre Goiffon
9c6d8253f4 Fix \Combodo\iTop\Test\UnitTest\Core\CMDBSourceTest::testIsOpenedDbConnectionUsingTls 2021-09-30 10:49:11 +02:00
acognet
4d6a8a76aa N°3904 - Polishing: Backup 2021-09-30 10:20:11 +02:00
Pierre Goiffon
8fa6ae6703 Merge remote-tracking branch 'origin/support/2.7' into develop
# Conflicts:
#	core/cmdbsource.class.inc.php
2021-09-30 09:51:13 +02:00
Pierre Goiffon
fdc987f367 Merge remote-tracking branch 'origin/support/2.7.5' into support/2.7 2021-09-29 17:51:17 +02:00
Eric Espie
be9bb10606 N°4339 - Fix page title for object creation 2021-09-29 17:03:53 +02:00
Eric Espie
11d3d5af49 N°3908 - Fix button location (moved to the right of the screen) 2021-09-29 15:56:52 +02:00
Eric Espie
b630492d7a N°4337 - Fix "Missing TWIG template" error 2021-09-29 15:16:13 +02:00
Molkobain
1db32c6dee N°4315 - Cleanup in SCSS 2021-09-29 14:21:24 +02:00
acognet
b1597f7d90 recompile styles 2021-09-29 11:24:09 +02:00
acognet
e2904fb0ee N°4230 - Printable version issues 2021-09-29 11:21:00 +02:00
acognet
8dbbc9a124 N°4315 - Polish bulk modify screens 2021-09-29 11:21:00 +02:00
bruno-ds
4297b854e1 N°4034 - Add the get_module_setting function to TwigBase.
This is temporary, as in the 3.1, this file will be deprecated in favor of `sources/application/TwigBase/Twig/Extension.php`.
More info in the N°4034.
2021-09-29 10:58:23 +02:00
Eric Espie
c53c1d5b9c N°4332 - Fix table name 2021-09-29 10:33:46 +02:00
Molkobain
a3db7f0e83 Merge remote-tracking branch 'origin/support/3.0.0-beta5' into develop 2021-09-29 10:18:53 +02:00
Stephen Abello
157c031236 N°4296 Correctly unescape dashboard title tooltip when switching dashboard 2021-09-29 10:15:57 +02:00
denis.flaven@combodo.com
78b1ee04e8 N°4336 - Improve tooltip pertinence, by hiding useless entries 2021-09-28 18:17:16 +02:00
Molkobain
30da7d2ea4 Add deprecated annotations on duplicated TwigExtension files to ease choose for developers 2021-09-28 17:38:59 +02:00
denis.flaven@combodo.com
d467cb5c79 N°4078 - Use the icon AND tooltip when the hyperlink is put as 'shortcut' 2021-09-28 17:25:35 +02:00
Eric Espie
9dcb789cfd N°3908 - Polishing: Application Upgrade - move the buttons at the bottom 2021-09-28 17:04:08 +02:00
Stephen Abello
aac504ec0b N°3917 Fix object details icon cover method when using class highlight icon as object icon 2021-09-28 16:25:47 +02:00
denis.flaven@combodo.com
a7a7ce77fb N°4096 - Automatic email sending retry in case of error. 2021-09-28 15:42:28 +02:00
Eric Espie
6caf78fdcd N°4326 - Fix error in dashboard when no tooltip exist on Pill 2021-09-28 15:07:27 +02:00
Stephen Abello
5680d9579c N°3917 Refactor algorithm to get object details semantic image icon in object's GetIcon method in order to propagate this feature anywhere where GetIcon is called 2021-09-28 15:01:06 +02:00
Molkobain
ec47645ef7 Merge branch 'support/3.0.0-beta5' into develop 2021-09-28 12:58:08 +02:00
Molkobain
a70acf29ae Simplify double if condition 2021-09-28 12:35:02 +02:00
Molkobain
90906912cc N°3556 - Fix CaptureWebPage not retrieve parts from UIBlocks 2021-09-28 10:09:43 +02:00
Eric Espie
3064d868b7 N°4328 - Fix Overview tab displayed before object creation 2021-09-28 10:01:03 +02:00
denis.flaven@combodo.com
f226916bb8 N°4078 - Use the icon when the hyperlink is put as 'shortcut' 2021-09-27 19:06:46 +02:00
Molkobain
86b03b9e92 Set back @covers annotation 2021-09-27 17:34:48 +02:00
acognet
9811d6f8ea N°3912 - Polishing: Export 2021-09-27 15:08:04 +02:00
odain
0b4d4764bd ci testing: use proper annotation to include/exlude test linked to composer.json 2021-09-27 11:57:25 +02:00
acognet
580deb655d Remove unused tablesorter 2021-09-27 09:25:20 +02:00
acognet
5e2bfdf660 N°3912 - Polishing: Export 2021-09-27 09:23:52 +02:00
Pierre Goiffon
ec1dcc8df6 💡 N°3513 PHPDoc 2021-09-23 14:42:16 +02:00
Pierre Goiffon
47ed863da9 N°4215 N°3513 Fix DB errors fetch from the wrong object n°2 2021-09-23 14:32:43 +02:00
Pierre Goiffon
88290f9e91 N°4215 N°3513 Fix DB errors fetch from the wrong object 2021-09-23 13:55:23 +02:00
Pierre Goiffon
cfdbc8ae62 N°4215 When checking for TLS cnx, don't set anymore CMDBSource mysql attributes ! 2021-09-23 11:59:44 +02:00
Pierre Goiffon
aaa8f6d311 N°4215 Fix call to a function on null error when setting TLS connection in the setup
Regression introduced by b1ca1f2630 / N°3513
2021-09-22 15:59:39 +02:00
774 changed files with 19604 additions and 6611 deletions

View File

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 2.1 MiB

View File

@@ -11,7 +11,7 @@ tab_width = 4
ij_continuation_indent_size = 8
ij_formatter_off_tag = @formatter:off
ij_formatter_on_tag = @formatter:on
ij_formatter_tags_enabled = false
ij_formatter_tags_enabled = true
ij_smart_tabs = false
ij_visual_guides = 300
ij_wrap_on_typing = true
@@ -78,7 +78,7 @@ ij_editorconfig_space_before_colon = false
ij_editorconfig_space_before_comma = false
ij_editorconfig_spaces_around_assignment_operators = true
[{*.ant, *.fxml, *.jhm, *.jnlp, *.jrxml, *.rng, *.tld, *.wsdl, *.xml, *.xsd, *.xsl, *.xslt, *.xul, phpunit.xml.dist}]
[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.rng,*.tld,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}]
indent_size = 2
tab_width = 2
ij_smart_tabs = true
@@ -280,16 +280,17 @@ ij_javascript_while_brace_force = always
ij_javascript_while_on_new_line = false
ij_javascript_wrap_comments = false
[{*.ctp, *.hphp, *.inc, *.module, *.php, *.php4, *.php5, *.phtml}]
[{*.ctp,*.hphp,*.inc,*.module,*.php,*.php4,*.php5,*.phtml}]
indent_style = tab
ij_continuation_indent_size = 4
ij_smart_tabs = true
ij_wrap_on_typing = false
ij_php_align_assignments = false
ij_php_align_class_constants = false
ij_php_align_class_constants = true
ij_php_align_group_field_declarations = false
ij_php_align_inline_comments = false
ij_php_align_key_value_pairs = true
ij_php_align_match_arm_bodies = false
ij_php_align_multiline_array_initializer_expression = true
ij_php_align_multiline_binary_operation = false
ij_php_align_multiline_chained_methods = false
@@ -298,6 +299,7 @@ ij_php_align_multiline_for = true
ij_php_align_multiline_parameters = false
ij_php_align_multiline_parameters_in_calls = false
ij_php_align_multiline_ternary_operation = false
ij_php_align_named_arguments = false
ij_php_align_phpdoc_comments = false
ij_php_align_phpdoc_param_names = false
ij_php_anonymous_brace_style = end_of_line
@@ -417,6 +419,7 @@ ij_php_see_weight = 3
ij_php_since_weight = 28
ij_php_sort_phpdoc_elements = true
ij_php_space_after_colon = true
ij_php_space_after_colon_in_enum_backed_type = true
ij_php_space_after_colon_in_named_argument = true
ij_php_space_after_colon_in_return_type = true
ij_php_space_after_comma = true
@@ -431,6 +434,7 @@ ij_php_space_before_catch_parentheses = true
ij_php_space_before_class_left_brace = true
ij_php_space_before_closure_left_parenthesis = true
ij_php_space_before_colon = true
ij_php_space_before_colon_in_enum_backed_type = false
ij_php_space_before_colon_in_named_argument = false
ij_php_space_before_colon_in_return_type = false
ij_php_space_before_comma = false
@@ -466,6 +470,7 @@ ij_php_spaces_around_equality_operators = true
ij_php_spaces_around_logical_operators = true
ij_php_spaces_around_multiplicative_operators = true
ij_php_spaces_around_null_coalesce_operator = true
ij_php_spaces_around_pipe_in_union_type = false
ij_php_spaces_around_relational_operators = true
ij_php_spaces_around_shift_operators = true
ij_php_spaces_around_unary_operator = false
@@ -540,7 +545,6 @@ ij_html_space_after_tag_name = false
ij_html_space_around_equality_in_attribute = false
ij_html_space_inside_empty_tag = false
ij_html_text_wrap = normal
ij_html_uniform_ident = false
[{*.markdown,*.md}]
ij_visual_guides = none

View File

@@ -157,4 +157,4 @@ Stickers' design might change from one year to another. For the first year we wa
* Beta tester: Test and give feedback on beta releases
* Extension developer: Develop and publish an extension
![](documentation/contributing-guide/contributing-stickers-side-by-side.png)
![](.doc/contributing-guide/contributing-stickers-side-by-side.png)

View File

@@ -99,9 +99,11 @@ We would like to give a special thank you 🤗 to the people from the community
- Lucas, Jonathan
- Malik, Remie
- Mindêllo de Andrade, Lucas (a.k.a @rokam)
- Raenker, Martin
- Rosenke, Stephan
- Seki, Shoji
- Shilov, Vladimir
- Stukalov, Ilya (a.k.a @ilya-stukalov)
- Tulio, Marco
- Turrubiates, Miguel

View File

@@ -34,7 +34,7 @@ class URP_Profiles extends UserRightsBaseClassGUI
{
$aParams = array
(
"category" => "addon/userrights,grant_by_profile",
"category" => "addon/userrights,grant_by_profile,filter",
"key_type" => "autoincrement",
"name_attcode" => "name",
"state_attcode" => "",
@@ -219,7 +219,7 @@ class URP_UserProfile extends UserRightsBaseClassGUI
{
$aParams = array
(
"category" => "addon/userrights,grant_by_profile",
"category" => "addon/userrights,grant_by_profile,filter",
"key_type" => "autoincrement",
"name_attcode" => array("userlogin", "profile"),
"state_attcode" => "",
@@ -610,30 +610,115 @@ class UserRightsProfile extends UserRightsAddOnAPI
{
$this->LoadCache();
$aObjectPermissions = $this->GetUserActionGrant($oUser, $sClass, UR_ACTION_READ);
if ($aObjectPermissions['permission'] == UR_ALLOWED_NO)
// Let us pass an administrator for bypassing the grant matrix check in order to test this method without the need to set up a complex profile
// In the nominal case Administrators never end up here (since they completely bypass GetSelectFilter)
if (!static::IsAdministrator($oUser) && (MetaModel::HasCategory($sClass, 'silo') || MetaModel::HasCategory($sClass, 'bizmodel')))
{
return false;
// N°4354 - Categories 'silo' and 'bizmodel' do check the grant matrix. Whereas 'filter' always allows to read (but the result can be filtered)
$aObjectPermissions = $this->GetUserActionGrant($oUser, $sClass, UR_ACTION_READ);
if ($aObjectPermissions['permission'] == UR_ALLOWED_NO)
{
return false;
}
}
// Determine how to position the objects of this class
//
$oFilter = true;
$aConditions = array();
// Determine if this class is part of a silo and build the filter for it
$sAttCode = self::GetOwnerOrganizationAttCode($sClass);
if (is_null($sAttCode))
if (!is_null($sAttCode))
{
// No filtering for this object
return true;
$aUserOrgs = $this->GetUserOrgs($oUser, $sClass);
if (count($aUserOrgs) > 0)
{
$oFilter = $this->MakeSelectFilter($sClass, $aUserOrgs, $aSettings, $sAttCode);
}
// else: No org means 'any org'
}
// Position the user
//
$aUserOrgs = $this->GetUserOrgs($oUser, $sClass);
if (count($aUserOrgs) == 0)
// else: No silo for this class
// Specific conditions to hide, for non-administrators, the Administrator Users, the Administrator Profile and related links
// Note: when logged as an administrator, GetSelectFilter is completely bypassed.
if ($this->AdministratorsAreHidden())
{
// No org means 'any org'
return true;
if ($sClass == 'URP_Profiles')
{
$oExpression = new FieldExpression('id', $sClass);
$oScalarExpr = new ScalarExpression(1);
$aConditions[] = new BinaryExpression($oExpression, '!=', $oScalarExpr);
}
else if (($sClass == 'URP_UserProfile') || ($sClass == 'User') || (is_subclass_of($sClass, 'User')))
{
$aAdministrators = $this->GetAdministrators();
if (count($aAdministrators) > 0)
{
$sAttCode = ($sClass == 'URP_UserProfile') ? 'userid' : 'id';
$oExpression = new FieldExpression($sAttCode, $sClass);
$oListExpr = ListExpression::FromScalars($aAdministrators);
$aConditions[] = new BinaryExpression($oExpression, 'NOT IN', $oListExpr);
}
}
}
return $this->MakeSelectFilter($sClass, $aUserOrgs, $aSettings, $sAttCode);
// Handling of the added conditions
if (count($aConditions) > 0)
{
if($oFilter === true)
{
// No 'silo' filter, let's build a clean one
$oFilter = new DBObjectSearch($sClass);
}
// Add the conditions to the filter
foreach($aConditions as $oCondition)
{
$oFilter->AddConditionExpression($oCondition);
}
}
return $oFilter;
}
/**
* Retrieve (and memoize) the list of administrator accounts.
* Note that there should always be at least one administrator account
* @return number[]
*/
private function GetAdministrators()
{
static $aAdministrators = null;
if ($aAdministrators === null)
{
// Find all administrators
$aAdministrators = array();
$oAdministratorsFilter = new DBObjectSearch('User');
$oLnkFilter = new DBObjectSearch('URP_UserProfile');
$oExpression = new FieldExpression('profileid', 'URP_UserProfile');
$oScalarExpr = new ScalarExpression(1);
$oCondition = new BinaryExpression($oExpression, '=', $oScalarExpr);
$oLnkFilter->AddConditionExpression($oCondition);
$oAdministratorsFilter->AddCondition_ReferencedBy($oLnkFilter, 'userid');
$oAdministratorsFilter->AllowAllData(true); // Mandatory to prevent infinite recursion !!
$oSet = new DBObjectSet($oAdministratorsFilter);
$oSet->OptimizeColumnLoad(array('User' => array('login')));
while($oUser = $oSet->Fetch())
{
$aAdministrators[] = $oUser->GetKey();
}
}
return $aAdministrators;
}
/**
* Whether or not to hide the 'Administrator' profile and the administrator accounts
* @return boolean
*/
private function AdministratorsAreHidden()
{
return ((bool)MetaModel::GetConfig()->Get('security.hide_administrators'));
}

View File

@@ -10,6 +10,7 @@ use Combodo\iTop\Application\Search\SearchForm;
use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Button\Button;
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\ButtonGroup\ButtonGroupUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\CollapsibleSection\CollapsibleSection;
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableSettings;
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
@@ -27,7 +28,8 @@ use Combodo\iTop\Application\UI\Base\Component\Input\Select\SelectOptionUIBlockF
use Combodo\iTop\Application\UI\Base\Component\Input\SelectUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\MedallionIcon\MedallionIcon;
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Title\Title;
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenuItem\JsPopoverMenuItem;
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\ActivityPanel\ActivityPanel;
@@ -69,16 +71,42 @@ require_once(APPROOT.'sources/application/search/criterionconversion/criterionto
*/
abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
{
/** @var string ENUM_OBJECT_MODE_VIEW */
public const ENUM_OBJECT_MODE_VIEW = 'view';
/** @var string ENUM_OBJECT_MODE_EDIT */
public const ENUM_OBJECT_MODE_EDIT = 'edit';
/** @var string ENUM_OBJECT_MODE_CREATE */
public const ENUM_OBJECT_MODE_CREATE = 'create';
/** @var string ENUM_OBJECT_MODE_STIMULUS */
public const ENUM_OBJECT_MODE_STIMULUS = 'stimulus';
/** @var string ENUM_OBJECT_MODE_PRINT */
public const ENUM_OBJECT_MODE_PRINT = 'print';
/**
* @var string
* @see static::$sDisplayMode
* @since 3.0.0
*/
public const ENUM_DISPLAY_MODE_VIEW = 'view';
/**
* @var string
* @see static::$sDisplayMode
* @since 3.0.0
*/
public const ENUM_DISPLAY_MODE_EDIT = 'edit';
/**
* @var string
* @see static::$sDisplayMode
* @since 3.0.0
*/
public const ENUM_DISPLAY_MODE_CREATE = 'create';
/**
* @var string
* @see static::$sDisplayMode
* @since 3.0.0
*/
public const ENUM_DISPLAY_MODE_STIMULUS = 'stimulus';
/**
* @var string
* @see static::$sDisplayMode
* @since 3.0.0
*/
public const ENUM_DISPLAY_MODE_PRINT = 'print';
/**
* @var string
* @see static::$sDisplayMode
* @since 3.0.0
*/
public const ENUM_DISPLAY_MODE_BULK_EDIT = self::ENUM_DISPLAY_MODE_EDIT;
// N°3750 rendering used
/** @var string */
@@ -113,10 +141,11 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
public const ENUM_INPUT_TYPE_LINKEDSET = 'linkedset';
/**
* @var string DEFAULT_OBJECT_MODE
* @var string DEFAULT_DISPLAY_MODE
* @see static::$sDisplayMode
* @since 3.0.0
*/
public const DEFAULT_OBJECT_MODE = self::ENUM_OBJECT_MODE_VIEW;
public const DEFAULT_DISPLAY_MODE = self::ENUM_DISPLAY_MODE_VIEW;
/**
* @var string Prefix for tags in the subtitle, allows to identify them more easily
@@ -127,6 +156,11 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
protected $m_iFormId; // The ID of the form used to edit the object (when in edition mode !)
protected static $iGlobalFormId = 1;
/**
* @var string Mode in which the object is displayed {@see static::ENUM_DISPLAY_MODE_VIEW}, ...)
* @since 3.0.0
*/
protected $sDisplayMode;
protected $aFieldsMap;
/**
@@ -153,28 +187,55 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
public function __construct($aRow = null, $sClassAlias = '', $aAttToLoad = null, $aExtendedDataSpec = null)
{
parent::__construct($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec);
$this->sDisplayMode = static::DEFAULT_DISPLAY_MODE;
$this->bAllowWrite = false;
$this->bAllowDelete = false;
}
/**
* Return the allowed object modes
* Return the allowed display modes
*
* @see static::ENUM_OBJECT_MODE_XXX
* @see static::ENUM_DISPLAY_MODE_XXX
*
* @return string[]
* @since 3.0.0
*/
public static function EnumObjectModes(): array
public static function EnumDisplayModes(): array
{
return [
static::ENUM_OBJECT_MODE_VIEW,
static::ENUM_OBJECT_MODE_EDIT,
static::ENUM_OBJECT_MODE_CREATE,
static::ENUM_OBJECT_MODE_STIMULUS,
static::ENUM_DISPLAY_MODE_VIEW,
static::ENUM_DISPLAY_MODE_EDIT,
static::ENUM_DISPLAY_MODE_CREATE,
static::ENUM_DISPLAY_MODE_STIMULUS,
static::ENUM_DISPLAY_MODE_PRINT,
static::ENUM_DISPLAY_MODE_BULK_EDIT,
];
}
/**
* @see static::$sDisplayMode
* @return string
* @since 3.0.0
*/
public function GetDisplayMode(): string
{
return $this->sDisplayMode;
}
/**
* @param string $sMode
*
* @see static::$sDisplayMode
* @return $this
* @since 3.0.0
*/
public function SetDisplayMode(string $sMode)
{
$this->sDisplayMode = $sMode;
return $this;
}
/**
* returns what will be the next ID for the forms
*/
@@ -283,8 +344,7 @@ JS
* To insert something IN the panel, we now need to add UIBlocks in either the "subtitle" or "toolbar" sections of the array that will be returned.
*
* @param \WebPage $oPage
* @param bool $bEditMode
* @param string $sMode Mode in which the object is displayed (see static::ENUM_OBJECT_MODE_XXX)
* @param bool $bEditMode Note that this parameter is no longer used in this method. Use {@see static::$sDisplayMode} instead
*
* @return array UIBlocks to be inserted in the "subtitle" and the "toolbar" sections of the ObjectDetails block. eg. ['subtitle' => [<BLOCK1>, <BLOCK2>], 'toolbar' => [<BLOCK3>]]
*
@@ -294,10 +354,10 @@ JS
* @throws \CoreUnexpectedValue
* @throws \MySQLException
* @throws \OQLException
* @todo 3.0.0: This has to be discussed within the R&D when I come back in 10 days
*
* @since 3.0.0 $bEditMode is deprecated and no longer used
*/
public function DisplayBareHeader(WebPage $oPage, $bEditMode = false, $sMode = self::ENUM_OBJECT_MODE_VIEW)
public function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
{
$aHeaderBlocks = [
'subtitle' => [],
@@ -325,7 +385,7 @@ JS
$oPage->AddSessionMessages($sMessageKey, $aRanks, $aMessages);
}
if (!$oPage->IsPrintableVersion() && ($sMode === static::ENUM_OBJECT_MODE_VIEW)) {
if (!$oPage->IsPrintableVersion() && ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_VIEW)) {
// action menu
$oSingletonFilter = new DBObjectSearch(get_class($this));
$oSingletonFilter->AddCondition('id', $this->GetKey(), '=');
@@ -477,12 +537,14 @@ HTML
* Display properties tab of an object
*
* @param \WebPage $oPage
* @param bool $bEditMode
* @param bool $bEditMode Note that this parameter is no longer used in this method. Use {@see static::$sDisplayMode} instead
* @param string $sPrefix
* @param array $aExtraParams
*
* @return array
* @throws \CoreException
*
* @since 3.0.0 $bEditMode is deprecated and no longer used
*/
public function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{
@@ -555,7 +617,7 @@ HTML
/**
* @param \WebPage $oPage
* @param bool $bEditMode
* @param bool $bEditMode Note that this parameter is no longer used in this method. Use {@see static::$sDisplayMode} instead
*
* @throws \CoreException
* @throws \CoreUnexpectedValue
@@ -565,6 +627,8 @@ HTML
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @throws \Exception
*
* @since 3.0.0 $bEditMode is deprecated and no longer used
*/
public function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
{
@@ -582,12 +646,16 @@ HTML
foreach($aList as $sAttCode) {
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
if ($oAttDef instanceof AttributeDashboard) {
$sHostContainerInEditionUrlParam = ($bEditMode) ? '&host_container_in_edition=true' : '';
$oPage->AddAjaxTab($oAttDef->GetLabel(),
utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=dashboard&class='.get_class($this).'&id='.$this->GetKey().'&attcode='.$oAttDef->GetCode().$sHostContainerInEditionUrlParam,
true,
'Class:'.$sClass.'/Attribute:'.$sAttCode,
AjaxTab::ENUM_TAB_PLACEHOLDER_DASHBOARD);
if (!$this->IsNew()) {
$sHostContainerInEditionUrlParam = ($bEditMode) ? '&host_container_in_edition=true' : '';
$oPage->AddAjaxTab($oAttDef->GetLabel(),
utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=dashboard&class='.get_class($this).'&id='.$this->GetKey().'&attcode='.$oAttDef->GetCode().$sHostContainerInEditionUrlParam,
true,
'Class:'.$sClass.'/Attribute:'.$sAttCode,
AjaxTab::ENUM_TAB_PLACEHOLDER_DASHBOARD);
// Add graphs dependencies
WebResourcesHelper::EnableC3JSToWebPage($oPage);
}
continue;
}
@@ -782,33 +850,35 @@ HTML
$oPage->SetCurrentTab('');
// Look for any trigger that considers this object as "In Scope"
// If any trigger has been found then display a tab with notifications
//
$aTriggers = $this->GetRelatedTriggersIDs();
if (count($aTriggers) > 0) {
$iId = $this->GetKey();
$aParams = array('triggers' => $aTriggers, 'id' => $iId);
$aNotifSearches = array();
$iNotifsCount = 0;
$aNotificationClasses = MetaModel::EnumChildClasses('EventNotification', ENUM_CHILD_CLASSES_EXCLUDETOP);
foreach ($aNotificationClasses as $sNotifClass) {
$aNotifSearches[$sNotifClass] = DBObjectSearch::FromOQL("SELECT $sNotifClass AS Ev JOIN Trigger AS T ON Ev.trigger_id = T.id WHERE T.id IN (:triggers) AND Ev.object_id = :id");
$aNotifSearches[$sNotifClass]->SetInternalParams($aParams);
$oNotifSet = new DBObjectSet($aNotifSearches[$sNotifClass], array());
$iNotifsCount += $oNotifSet->Count();
}
// Display notifications regarding the object: on block per subclass to have the interesting columns
$sCount = ($iNotifsCount > 0) ? ' ('.$iNotifsCount.')' : '';
$oPage->SetCurrentTab('UI:NotificationsTab', Dict::S('UI:NotificationsTab').$sCount);
if (!$this->IsNew()) {
// Look for any trigger that considers this object as "In Scope"
// If any trigger has been found then display a tab with notifications
//
$aTriggers = $this->GetRelatedTriggersIDs();
if (count($aTriggers) > 0) {
$iId = $this->GetKey();
$aParams = array('triggers' => $aTriggers, 'id' => $iId);
$aNotifSearches = array();
$iNotifsCount = 0;
$aNotificationClasses = MetaModel::EnumChildClasses('EventNotification');
foreach ($aNotificationClasses as $sNotifClass) {
$aNotifSearches[$sNotifClass] = DBObjectSearch::FromOQL("SELECT $sNotifClass AS Ev JOIN Trigger AS T ON Ev.trigger_id = T.id WHERE T.id IN (:triggers) AND Ev.object_id = :id");
$aNotifSearches[$sNotifClass]->SetInternalParams($aParams);
$oNotifSet = new DBObjectSet($aNotifSearches[$sNotifClass], array());
$iNotifsCount += $oNotifSet->Count();
}
// Display notifications regarding the object: on block per subclass to have the interesting columns
$sCount = ($iNotifsCount > 0) ? ' ('.$iNotifsCount.')' : '';
$oPage->SetCurrentTab('UI:NotificationsTab', Dict::S('UI:NotificationsTab').$sCount);
foreach($aNotificationClasses as $sNotifClass) {
$oClassIcon = new MedallionIcon(MetaModel::GetClassIcon($sNotifClass, false));
$oClassIcon->SetDescription(MetaModel::GetName($sNotifClass))->AddCSSClass('ibo-block-list--medallion');
$oPage->AddUiBlock($oClassIcon);
foreach ($aNotificationClasses as $sNotifClass) {
$oClassIcon = new MedallionIcon(MetaModel::GetClassIcon($sNotifClass, false));
$oClassIcon->SetDescription(MetaModel::GetName($sNotifClass))->AddCSSClass('ibo-block-list--medallion');
$oPage->AddUiBlock($oClassIcon);
$oBlock = new DisplayBlock($aNotifSearches[$sNotifClass], 'list', false);
$oBlock->Display($oPage, 'notifications_'.$sNotifClass, array('menu' => false));
$oBlock = new DisplayBlock($aNotifSearches[$sNotifClass], 'list', false);
$oBlock->Display($oPage, 'notifications_'.$sNotifClass, array('menu' => false));
}
}
}
}
@@ -822,11 +892,17 @@ HTML
*/
public function GetRelatedTriggersIDs(): array
{
$oTriggerSet = new CMDBObjectSet(new DBObjectSearch('Trigger'));
$aTriggers = [];
while ($oTrigger = $oTriggerSet->Fetch()) {
if ($oTrigger->IsInScope($this)) {
$aTriggers[] = $oTrigger->GetKey();
// Request only "leaf" classes to avoid reloads
$aTriggerClasses = MetaModel::EnumChildClasses('Trigger');
foreach ($aTriggerClasses as $sTriggerClass) {
if (MetaModel::IsLeafClass($sTriggerClass)) {
$oTriggerSet = new CMDBObjectSet(new DBObjectSearch($sTriggerClass));
while ($oTrigger = $oTriggerSet->Fetch()) {
if ($oTrigger->IsInScope($this)) {
$aTriggers[] = $oTrigger->GetKey();
}
}
}
}
@@ -835,7 +911,7 @@ HTML
/**
* @param \WebPage $oPage
* @param bool $bEditMode
* @param bool $bEditMode Note that this parameter is no longer used in this method. Use {@see static::$sDisplayMode} instead
* @param string $sPrefix
* @param array $aExtraParams
*
@@ -847,6 +923,8 @@ HTML
* @throws \MySQLException
* @throws \OQLException
* @throws \Exception
*
* @since 3.0.0 $bEditMode is deprecated and no longer used
*/
public function GetBareProperties(WebPage $oPage, $bEditMode, $sPrefix, $aExtraParams = array())
{
@@ -1028,8 +1106,7 @@ HTML
/**
* @param \WebPage $oPage
* @param bool $bEditMode Note that this parameter is no longer used in ths method, $sMode is used instead, but we cannot remove it as it part of the base interface (iDisplay)...
* @param string $sMode Mode in which the object will be displayed (see static::ENUM_OBJECT_MODE_XXX)
* @param bool $bEditMode Note that this parameter is no longer used in this method, {@see static::$sDisplayMode} is used instead, but we cannot remove it as it part of the base interface (iDisplay)...
*
* @throws \ApplicationException
* @throws \ArchivedObjectException
@@ -1040,8 +1117,10 @@ HTML
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*
* @since 3.0.0 $bEditMode is deprecated and no longer used
*/
public function DisplayDetails(WebPage $oPage, $bEditMode = false, $sMode = self::ENUM_OBJECT_MODE_VIEW)
public function DisplayDetails(WebPage $oPage, $bEditMode = false)
{
// N°3786: As this can now be call recursively from the self::ReloadAndDisplay(), we need to make sure we don't fall into an infinite loop
static $bBlockReentrance = false;
@@ -1049,15 +1128,12 @@ HTML
$sClass = get_class($this);
$iKey = $this->GetKey();
if ($sMode === static::ENUM_OBJECT_MODE_VIEW)
{
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_VIEW) {
// The concurrent access lock makes sense only for already existing objects
$LockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
if ($LockEnabled)
{
if ($LockEnabled) {
$aLockInfo = iTopOwnershipLock::IsLocked($sClass, $iKey);
if ($aLockInfo['locked'] === true && $aLockInfo['owner']->GetKey() == UserRights::GetUserId() && $bBlockReentrance === false)
{
if ($aLockInfo['locked'] === true && $aLockInfo['owner']->GetKey() == UserRights::GetUserId() && $bBlockReentrance === false) {
// If the object is locked by the current user, it's worth trying again, since
// the lock may be released by 'onunload' which is called AFTER loading the current page.
//$bTryAgain = $oOwner->GetKey() == UserRights::GetUserId();
@@ -1070,11 +1146,14 @@ HTML
}
// Object's details
$oObjectDetails = ObjectFactory::MakeDetails($this);
$oObjectDetails = ObjectFactory::MakeDetails($this, $this->GetDisplayMode());
if ($oPage->IsPrintableVersion()) {
$oObjectDetails->SetIsHeaderVisibleOnScroll(false);
}
// Note: DisplayBareHeader is called before adding $oObjectDetails to the page, so it can inject HTML before it through $oPage.
/** @var \iTopWebPage $oPage */
$aHeadersBlocks = $this->DisplayBareHeader($oPage, $bEditMode, $sMode);
$aHeadersBlocks = $this->DisplayBareHeader($oPage, $bEditMode);
if (false === empty($aHeadersBlocks['subtitle'])) {
$oObjectDetails->AddSubTitleBlocks($aHeadersBlocks['subtitle']);
}
@@ -1189,6 +1268,25 @@ HTML
return "";
}
/**
* @param \WebPage $oPage
* @param \DBObjectSet $oSet
* @param array $aExtraParams
*
* @return \Combodo\iTop\Application\UI\Base\Layout\UIContentBlock|string
* @throws \ApplicationException
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \DictExceptionMissingString
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
* @throws \ReflectionException
*
* @since 3.0.0
*/
public static function GetDisplaySetBlock(WebPage $oPage, DBObjectSet $oSet, $aExtraParams = array())
{
if ($oPage->IsPrintableVersion() || $oPage->is_pdf()) {
@@ -2056,10 +2154,8 @@ HTML;
$sHours = "<input class=\"ibo-input ibo-input-duration\" title=\"$sHelpText\" type=\"text\" size=\"2\" name=\"attr_{$sFieldPrefix}{$sAttCode}[h]{$sNameSuffix}\" value=\"{$aVal['hours']}\" id=\"{$iId}_h\"/>";
$sMinutes = "<input class=\"ibo-input ibo-input-duration\" title=\"$sHelpText\" type=\"text\" size=\"2\" name=\"attr_{$sFieldPrefix}{$sAttCode}[m]{$sNameSuffix}\" value=\"{$aVal['minutes']}\" id=\"{$iId}_m\"/>";
$sSeconds = "<input class=\"ibo-input ibo-input-duration\" title=\"$sHelpText\" type=\"text\" size=\"2\" name=\"attr_{$sFieldPrefix}{$sAttCode}[s]{$sNameSuffix}\" value=\"{$aVal['seconds']}\" id=\"{$iId}_s\"/>";
$sHidden = "<input type=\"hidden\" id=\"{$iId}\" value=\"".htmlentities($value, ENT_QUOTES,
'UTF-8')."\"/>";
$sHTMLValue = Dict::Format('UI:DurationForm_Days_Hours_Minutes_Seconds', $sDays, $sHours, $sMinutes,
$sSeconds).$sHidden."&nbsp;".$sValidationSpan.$sReloadSpan;
$sHidden = "<input type=\"hidden\" id=\"{$iId}\" value=\"".htmlentities($value, ENT_QUOTES, 'UTF-8')."\"/>";
$sHTMLValue = Dict::Format('UI:DurationForm_Days_Hours_Minutes_Seconds', $sDays, $sHours, $sMinutes, $sSeconds).$sHidden."&nbsp;".$sValidationSpan.$sReloadSpan;
$oPage->add_ready_script("$('#{$iId}').on('update', function(evt, sFormId) { return ToggleDurationField('$iId'); });");
break;
@@ -2097,7 +2193,11 @@ HTML;
$sStyle = 'style="'.implode('; ', $aStyles).'"';
}
$aTextareaCssClasses = [];
if ($oAttDef->GetEditClass() == 'OQLExpression') {
$aTextareaCssClasses[] = 'ibo-query-oql';
$aTextareaCssClasses[] = 'ibo-is-code';
// N°3227 button to open predefined queries dialog
$sPredefinedBtnId = 'predef_btn_'.$sFieldPrefix.$sAttCode.$sNameSuffix;
$sSearchQueryLbl = Dict::S('UI:Edit:SearchQuery');
@@ -2167,14 +2267,16 @@ JS
} else {
$sAdditionalStuff = '';
}
// Ok, the text area is drawn here
$sTextareCssClassesAsString = implode(' ', $aTextareaCssClasses);
$sHTMLValue = <<<HTML
{$sAdditionalStuff}
<div class="field_input_zone field_input_text ibo-input-wrapper ibo-input-text-wrapper" data-validation="untouched">
<div class="f_i_text_header">
<span class="fullscreen_button" title="{$sFullscreenLabelForHtml}"></span>
</div>
<textarea class="ibo-input ibo-input-text" title="{$sHelpText}" name="attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}" rows="8" cols="40" id="{$iId}" {$sStyle} >{$sEditValueForHtml}</textarea>
<textarea class="ibo-input ibo-input-text {$sTextareCssClassesAsString}" title="{$sHelpText}" name="attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}" rows="8" cols="40" id="{$iId}" {$sStyle} >{$sEditValueForHtml}</textarea>
</div>
{$sValidationSpan}{$sReloadSpan}
HTML;
@@ -2318,7 +2420,6 @@ EOF
<span class="fas fa-trash"></span>
</button>
</div>
<br/>
<input title="{$sHelpText}" name="attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}[fcontents]" type="file" id="file_{$iId}" onChange="UpdateFileName('{$iId}', this.value)"/>
{$sValidationSpan}{$sReloadSpan}
HTML;
@@ -2666,10 +2767,11 @@ JS
$sOwnershipToken = null;
$iKey = $this->GetKey();
$sClass = get_class($this);
$sMode = ($iKey > 0) ? static::ENUM_OBJECT_MODE_EDIT : static::ENUM_OBJECT_MODE_CREATE;
$this->SetDisplayMode(($iKey > 0) ? static::ENUM_DISPLAY_MODE_EDIT : static::ENUM_DISPLAY_MODE_CREATE);
$sDisplayMode = $this->GetDisplayMode();
if ($sMode === static::ENUM_OBJECT_MODE_EDIT)
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT)
{
// The concurrent access lock makes sense only for already existing objects
$LockEnabled = MetaModel::GetConfig()->Get('concurrent_lock_enabled');
@@ -2719,7 +2821,7 @@ JS
if (isset($aExtraParams['custom_button'])) {
$sApplyButton = $aExtraParams['custom_button'];
} else {
if ($sMode === static::ENUM_OBJECT_MODE_EDIT) {
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
$sApplyButton = Dict::S('UI:Button:Apply');
} else {
$sApplyButton = Dict::S('UI:Button:Create');
@@ -2729,7 +2831,7 @@ JS
if (isset($aExtraParams['custom_operation'])) {
$sOperation = $aExtraParams['custom_operation'];
} else {
if ($sMode === static::ENUM_OBJECT_MODE_EDIT) {
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
$sOperation = 'apply_modify';
} else {
$sOperation = 'apply_new';
@@ -2744,7 +2846,7 @@ JS
->SetOnSubmitJsCode("return OnSubmit('form_{$this->m_iFormId}');");
$oContentBlock->AddSubBlock($oForm);
if ($sMode === static::ENUM_OBJECT_MODE_EDIT) {
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
// The object already exists in the database, it's a modification
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('id', $iKey, "{$sPrefix}_id"));
}
@@ -2759,7 +2861,11 @@ JS
// TODO 3.0.0: Is this (the if condition, not the code inside) still necessary?
if (isset($aExtraParams['wizard_container']) && $aExtraParams['wizard_container']) {
$sClassLabel = MetaModel::GetName($sClass);
$oPage->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $this->GetRawName(), $sClassLabel)); // Set title will take care of the encoding
if ($this->GetDisplayMode() == static::ENUM_DISPLAY_MODE_CREATE) {
$oPage->set_title(Dict::Format('UI:CreationPageTitle_Class', $sClassLabel)); // Set title will take care of the encoding
} else {
$oPage->set_title(Dict::Format('UI:ModificationPageTitle_Object_Class', $this->GetRawName(), $sClassLabel)); // Set title will take care of the encoding
}
}
$oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null);
@@ -2773,24 +2879,41 @@ JS
$aTransitions = $this->EnumTransitions();
if (!isset($aExtraParams['custom_operation']) && count($aTransitions)) {
// transitions are displayed only for the standard new/modify actions, not for modify_all or any other case...
// Transitions are displayed only for the standard new/modify actions, not for modify_all or any other case...
$oSetToCheckRights = DBObjectSet::FromObject($this);
$oTransitionPopoverMenu = new PopoverMenu();
$sTPMSectionId = 'transitions';
$oTransitionPopoverMenu->AddSection($sTPMSectionId);
$aStimuli = Metamodel::EnumStimuli($sClass);
foreach ($aTransitions as $sStimulusCode => $aTransitionDef) {
$iActionAllowed = (get_class($aStimuli[$sStimulusCode]) == 'StimulusUserAction') ? UserRights::IsStimulusAllowed($sClass,
$sStimulusCode, $oSetToCheckRights) : UR_ALLOWED_NO;
switch ($iActionAllowed) {
case UR_ALLOWED_YES:
// Button to be displayed on its own on large screens
$oButton = ButtonUIBlockFactory::MakeForPrimaryAction($aStimuli[$sStimulusCode]->GetLabel(), 'next_action', $sStimulusCode, true);
$oButton->AddCSSClass('action');
$oButton->SetColor(Button::ENUM_COLOR_SCHEME_NEUTRAL);
$oToolbarButtons->AddSubBlock($oButton);
// Button to be displayed in a grouped button on smaller screens
$oTPMPopupMenuItem = new JSPopupMenuItem('next_action--'.$oButton->GetId(), $oButton->GetLabel(), "$(`#{$oButton->GetId()}`).trigger(`click`);");
$oTransitionPopoverMenu->AddItem($sTPMSectionId, new JsPopoverMenuItem($oTPMPopupMenuItem));
break;
default:
// Do nothing
}
}
// If there are some allowed transitions, build the grouped button
if ($oTransitionPopoverMenu->HasItems()) {
$oApplyForButtonGroup = ButtonUIBlockFactory::MakeForPrimaryAction($oApplyButton->GetLabel(), null, null, true);
$oApplyAndTransitionsButtonGroup = ButtonGroupUIBlockFactory::MakeButtonWithOptionsMenu($oApplyForButtonGroup, $oTransitionPopoverMenu)
->SetIsHidden(true);
$oToolbarButtons->AddSubBlock($oApplyAndTransitionsButtonGroup);
}
}
$sStatesSelection = '';
@@ -2848,7 +2971,7 @@ EOF
$oObjectDetails->SetIcon($sClassIcon);
$oToolbarButtons->AddCSSClass('ibo-toolbar--button');
} else {
$oObjectDetails = ObjectFactory::MakeDetails($this, $sMode);
$oObjectDetails = ObjectFactory::MakeDetails($this, $this->GetDisplayMode());
$oToolbarButtons->AddCSSClass('ibo-toolbar-top');
$oObjectDetails->AddToolbarBlock($oToolbarButtons);
}
@@ -2879,7 +3002,7 @@ EOF
if (!is_array($aFieldsMap)) {
$aFieldsMap = array();
}
if ($sMode === static::ENUM_OBJECT_MODE_EDIT) {
if ($this->GetDisplayMode() === static::ENUM_DISPLAY_MODE_EDIT) {
$aFieldsMap['id'] = $sPrefix.'_id';
}
// Now display the relations, one tab per relation
@@ -3070,6 +3193,7 @@ EOF
} else {
$oObj = clone $oSourceObject;
}
$oObj->SetDisplayMode(static::ENUM_DISPLAY_MODE_CREATE);
// Pre-fill the object with default values, when there is only on possible choice
// AND the field is mandatory (otherwise there is always the possiblity to let it empty)
@@ -3125,10 +3249,10 @@ EOF
}
/**
* @param \WebPage $oPage
* @param string $sStimulus
* @param null $aPrefillFormParam
* @param bool $bDisplayBareProperties Whether to display the object details or not
* @param \WebPage $oPage
* @param string $sStimulus
* @param array|null $aPrefillFormParam
* @param bool $bDisplayBareProperties Whether to display the object details or not
*
* @throws \ApplicationException
* @throws \ArchivedObjectException
@@ -3142,9 +3266,11 @@ EOF
*/
public function DisplayStimulusForm(WebPage $oPage, $sStimulus, $aPrefillFormParam = null, $bDisplayBareProperties = true)
{
$this->SetDisplayMode(static::ENUM_DISPLAY_MODE_STIMULUS);
$sClass = get_class($this);
$iKey = $this->GetKey();
$sMode = static::ENUM_OBJECT_MODE_STIMULUS;
$sDisplayMode = $this->GetDisplayMode();
$iTransactionId = utils::GetNewTransactionId();
$aTransitions = $this->EnumTransitions();
@@ -3296,11 +3422,11 @@ EOF
}
}
if ($bExistFieldToDisplay) {
if ($bExistFieldToDisplay || MetaModel::GetConfig()->Get('force_transition_confirmation')) {
$oPage->set_title($sActionLabel);
$oPage->add(<<<HTML
<!-- Beginning of object-transition -->
<div class="object-transition" data-object-class="$sClass" data-object-id="$iKey" data-object-mode="$sMode" data-object-current-state="$sCurrentState" data-object-target-state="$sTargetState">
<div class="object-transition" data-object-class="$sClass" data-object-id="$iKey" data-object-mode="$sDisplayMode" data-object-current-state="$sCurrentState" data-object-target-state="$sTargetState">
HTML
);
@@ -3326,8 +3452,10 @@ HTML
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('ownership_token', utils::HtmlEntities($sOwnershipToken)));
}
// Note: Remove the table if we want fields to occupy the whole width of the container
$sHtml = '<table><tr><td>';
// Note: Remove the table if we want fields to occupy the whole width of the container, BUT with today's layout, fields' label will occupy way too much space. This should be part of the field layout rework.
// Note 2: The hardcoded width allows the fields to be a bit wider (useful for long values) while still working on different screen sizes
// Note 3: The inline style is not ideal but we are still wondring how transition form should be displayed
$sHtml = '<table style="width: min(100%, 35rem); margin-bottom: 12px;"><tr><td>';
$sHtml .= $oPage->GetDetails($aDetails);
$sHtml .= '</td></tr></table>';
@@ -3335,8 +3463,10 @@ HTML
$sHtml .= $oAppContext->GetForForm();
$oForm->AddHtml($sHtml);
$oCancelButton = ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Cancel'), 'cancel', 'cancel');
$oCancelButton->SetOnClickJsCode("BackToDetails('{$sClass}', '{$this->GetKey()}', '', '{$sOwnershipToken}');");
$oCancelButton = ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Cancel'), 'cancel', 'cancel')
// Action type is changed on purpose so the button is more visible in the form.
->SetActionType(Button::ENUM_ACTION_TYPE_REGULAR)
->SetOnClickJsCode("BackToDetails('{$sClass}', '{$this->GetKey()}', '', '{$sOwnershipToken}');");
$oForm->AddSubBlock($oCancelButton);
$oSubmitButton = ButtonUIBlockFactory::MakeForPrimaryAction($sActionLabel, 'submit', 'submit', true);
@@ -3513,7 +3643,7 @@ HTML;
$sDownloadUrl = $oDocument->GetDownloadURL(get_class($this), $this->GetKey(), $sAttCode);
$sDisplayValue = <<<HTML
{$sFieldAsHtml}<br>
{$sFieldAsHtml}
<a href="{$sDisplayUrl}" target="_blank">{$sDisplayLabel}</a> / <a href="{$sDownloadUrl}">{$sDownloadLabel}</a>
HTML;
} else {
@@ -4698,7 +4828,9 @@ HTML
$aFieldsMap[$sAttCode] = $sInputId;
$oFieldset = FieldSetUIBlockFactory::MakeStandard($sAttLabel);
$sCommentAsHtml = ($sComment != '') ? ' <div class="ibo-field--comments">'.$sComment.'</div>' : '';
$oFieldset = FieldSetUIBlockFactory::MakeStandard($sAttLabel.$sCommentAsHtml);
$oPage->AddSubBlock($oFieldset);
$oDivField = FieldUIBlockFactory::MakeLarge("");
@@ -4712,10 +4844,9 @@ HTML
$oDivField->AddDataAttribute("attribute-flag-must-prompt", $sAttMetaDataFlagMustPrompt);
$oDivField->AddDataAttribute("attribute-flag-slave", false);
$oFieldset->AddSubBlock($oDivField);
$sCommentAsHtml = ($sComment != '') ? '<span>'.$sComment.'</span><br/>' : '';
//$oDivField->SetComments($sComment);
$sFieldAsHtml = self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sInputId, '', $iFlags, $aArgs);
$sHTMLValue = $sCommentAsHtml.$sFieldAsHtml;
$sHTMLValue = $sFieldAsHtml;
$oDivField->AddSubBlock(new Html($sHTMLValue));
}
}
@@ -4853,6 +4984,7 @@ HTML
// Now create an object that has values for the homogeneous values only
/** @var \cmdbAbstractObject $oDummyObj */
$oDummyObj = new $sClass(); // @@ What if the class is abstract ?
$oDummyObj->SetDisplayMode(static::ENUM_DISPLAY_MODE_BULK_EDIT);
$aComments = array();
function MyComparison($a, $b) // Sort descending
{
@@ -5178,6 +5310,7 @@ EOF
foreach ($aDeletes as $iId => $aData) {
$oToDelete = $aData['to_delete'];
$bAutoDel = (($aData['mode'] == DEL_SILENT) || ($aData['mode'] == DEL_AUTO));
$sRowCssClass = '';
if (array_key_exists('issue', $aData))
{
if ($bAutoDel)
@@ -5197,6 +5330,7 @@ EOF
$sConsequence = Dict::Format('UI:Delete:MustBeDeletedManuallyButNotPossible',
$aData['issue']);
}
$sRowCssClass = 'ibo-is-alert';
}
else
{
@@ -5214,9 +5348,11 @@ EOF
else
{
$sConsequence = Dict::S('UI:Delete:MustBeDeletedManually');
$sRowCssClass = 'ibo-is-warning';
}
}
$aDisplayData[] = array(
'@class' => $sRowCssClass,
'class' => MetaModel::GetName(get_class($oToDelete)),
'object' => $oToDelete->GetHyperLink(),
'consequence' => $sConsequence,
@@ -5228,9 +5364,11 @@ EOF
foreach($aToUpdate as $iId => $aData)
{
$oToUpdate = $aData['to_reset'];
$sRowCssClass = '';
if (array_key_exists('issue', $aData))
{
$sConsequence = Dict::Format('UI:Delete:CannotUpdateBecause_Issue', $aData['issue']);
$sRowCssClass = 'ibo-is-alert';
}
else
{
@@ -5238,6 +5376,7 @@ EOF
$aData['attributes_list']);
}
$aDisplayData[] = array(
'@class' => $sRowCssClass,
'class' => MetaModel::GetName(get_class($oToUpdate)),
'object' => $oToUpdate->GetHyperLink(),
'consequence' => $sConsequence,
@@ -5281,13 +5420,16 @@ EOF
if ($oDeletionPlan->FoundStopper()) {
if ($oDeletionPlan->FoundSecurityIssue()) {
$oP->p(Dict::S('UI:Delete:SorryDeletionNotAllowed'));
} elseif ($oDeletionPlan->FoundManualOperation()) {
$oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations'));
} else // $bFoundManualOp
{
$oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations'));
$oFailAlertBlock = AlertUIBlockFactory::MakeForDanger('', Dict::S('UI:Delete:SorryDeletionNotAllowed'));
$oFailAlertBlock->SetIsClosable(false);
$oP->AddUiBlock($oFailAlertBlock);
}
else {
$oWarningAlertBlock = AlertUIBlockFactory::MakeForWarning('', Dict::S('UI:Delete:PleaseDoTheManualOperations'));
$oWarningAlertBlock->SetIsClosable(false);
$oP->AddUiBlock($oWarningAlertBlock);
}
$oForm = FormUIBlockFactory::MakeStandard('');
$oP->AddSubBlock($oForm);
$oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', utils::ReadParam('transaction_id', '', false, 'transaction_id')));
@@ -5321,6 +5463,7 @@ EOF
'menu' => false,
'surround_with_panel' => true,
'panel_title' => $sSubtitle,
'panel_title_is_html' => true,
'panel_icon' => MetaModel::GetClassIcon($sClass, false),
'panel_class' => $sClass,
)));

View File

@@ -552,7 +552,7 @@ EOF
$oToolbar->AddHtml($sHtml);
} else {
$oPage->add_script(<<<JS
$(".ibo-top-bar--toolbar-dashboard-title").html("$sTitleForHTML").attr("title", "$sTitleForHTML");
$(".ibo-top-bar--toolbar-dashboard-title").html("$sTitleForHTML").attr("title", $('<div>').html("$sTitleForHTML").text());
JS
);
}
@@ -860,28 +860,29 @@ class RuntimeDashboard extends Dashboard
{
$bCustomized = false;
if (!appUserPreferences::GetPref('display_original_dashboard_'.$sDashBoardId, false))
{
$sDashboardFileSanitized = utils::RealPath($sDashboardFile, APPROOT);
if (false === $sDashboardFileSanitized) {
throw new SecurityException('Invalid dashboard file !');
}
if (!appUserPreferences::GetPref('display_original_dashboard_'.$sDashBoardId, false)) {
// Search for an eventual user defined dashboard
$oUDSearch = new DBObjectSearch('UserDashboard');
$oUDSearch->AddCondition('user_id', UserRights::GetUserId(), '=');
$oUDSearch->AddCondition('menu_code', $sDashBoardId, '=');
$oUDSet = new DBObjectSet($oUDSearch);
if ($oUDSet->Count() > 0)
{
if ($oUDSet->Count() > 0) {
// Assuming there is at most one couple {user, menu}!
$oUserDashboard = $oUDSet->Fetch();
$sDashboardDefinition = $oUserDashboard->Get('contents');
$bCustomized = true;
}
else
{
$sDashboardDefinition = @file_get_contents($sDashboardFile);
} else {
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
}
}
else
{
$sDashboardDefinition = @file_get_contents($sDashboardFile);
$sDashboardDefinition = @file_get_contents($sDashboardFileSanitized);
}
if ($sDashboardDefinition !== false)
@@ -889,7 +890,7 @@ class RuntimeDashboard extends Dashboard
$oDashboard = new RuntimeDashboard($sDashBoardId);
$oDashboard->FromXml($sDashboardDefinition);
$oDashboard->SetCustomFlag($bCustomized);
$oDashboard->SetDefinitionFile($sDashboardFile);
$oDashboard->SetDefinitionFile($sDashboardFileSanitized);
} else {
$oDashboard = null;
}

View File

@@ -1008,7 +1008,8 @@ HTML;
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletObjectList:Prop-Query'), $this->aProperties['query']);
$oField->SetMandatory();
$oField->AddCSSClass("ibo-queryoql");
$oField->AddCSSClass("ibo-query-oql");
$oField->AddCSSClass("ibo-is-code");
$oForm->AddField($oField);
$oField = new DesignerBooleanField('menu', Dict::S('UI:DashletObjectList:Prop-Menu'), $this->aProperties['menu']);
@@ -1045,7 +1046,8 @@ HTML;
$oField = new DesignerHiddenField('query', Dict::S('UI:DashletObjectList:Prop-Query'), $sOQL);
$oField->SetMandatory();
$oField->AddCSSClass("ibo-queryoql");
$oField->AddCSSClass("ibo-query-oql");
$oField->AddCSSClass("ibo-is-code");
$oForm->AddField($oField);
$oField = new DesignerBooleanField('menu', Dict::S('UI:DashletObjectList:Prop-Menu'), $this->aProperties['menu']);
@@ -1395,7 +1397,8 @@ abstract class DashletGroupBy extends Dashlet
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletGroupBy:Prop-Query'), $this->aProperties['query']);
$oField->SetMandatory();
$oField->AddCSSClass("ibo-queryoql");
$oField->AddCSSClass("ibo-query-oql");
$oField->AddCSSClass("ibo-is-code");
$oForm->AddField($oField);
try {
@@ -1652,7 +1655,8 @@ abstract class DashletGroupBy extends Dashlet
$oField = new DesignerHiddenField('query', Dict::S('UI:DashletGroupBy:Prop-Query'), $sOQL);
$oField->SetMandatory();
$oField->AddCSSClass("ibo-queryoql");
$oField->AddCSSClass("ibo-query-oql");
$oField->AddCSSClass("ibo-is-code");
$oForm->AddField($oField);
if (!is_null($sOQL)) {
@@ -2225,7 +2229,8 @@ class DashletHeaderDynamic extends Dashlet
$oField = new DesignerLongTextField('query', Dict::S('UI:DashletHeaderDynamic:Prop-Query'), $this->aProperties['query']);
$oField->SetMandatory();
$oField->AddCSSClass("ibo-queryoql");
$oField->AddCSSClass("ibo-query-oql");
$oField->AddCSSClass("ibo-is-code");
$oForm->AddField($oField);
try

View File

@@ -258,6 +258,66 @@
</argument>
</arguments>
</method>
<method id="SetComputedDate">
<arguments>
<argument id="1">
<type>attcode</type>
<mandatory>true</mandatory>
<type_restrictions>
<operation>allow</operation>
<types>
<type id="AttributeDate"/>
<type id="AttributeDateTime"/>
</types>
</type_restrictions>
</argument>
<argument id="2">
<type>string</type>
<mandatory>false</mandatory>
</argument>
<argument id="3">
<type>attcode</type>
<mandatory>false</mandatory>
<type_restrictions>
<operation>allow</operation>
<types>
<type id="AttributeDate"/>
<type id="AttributeDateTime"/>
</types>
</type_restrictions>
</argument>
</arguments>
</method>
<method id="SetComputedDateIfNull">
<arguments>
<argument id="1">
<type>attcode</type>
<mandatory>true</mandatory>
<type_restrictions>
<operation>allow</operation>
<types>
<type id="AttributeDate"/>
<type id="AttributeDateTime"/>
</types>
</type_restrictions>
</argument>
<argument id="2">
<type>string</type>
<mandatory>false</mandatory>
</argument>
<argument id="3">
<type>attcode</type>
<mandatory>false</mandatory>
<type_restrictions>
<operation>allow</operation>
<types>
<type id="AttributeDate"/>
<type id="AttributeDateTime"/>
</types>
</type_restrictions>
</argument>
</arguments>
</method>
<method id="SetCurrentDate">
<arguments>
<argument id="1">
@@ -409,30 +469,6 @@
</argument>
</arguments>
</method>
<method id="PrefillCreationForm">
<arguments>
<argument id="1">
<type>reference</type>
<mandatory>true</mandatory>
</argument>
</arguments>
</method>
<method id="PrefillTransitionForm">
<arguments>
<argument id="1">
<type>reference</type>
<mandatory>true</mandatory>
</argument>
</arguments>
</method>
<method id="PrefillSearchForm">
<arguments>
<argument id="1">
<type>reference</type>
<mandatory>true</mandatory>
</argument>
</arguments>
</method>
</methods>
</class>
</classes>

View File

@@ -268,6 +268,8 @@ class DisplayBlock
'surround_with_panel',
/** string title of panel block */
'panel_title',
/** string true if panel title should be displayed as html */
'panel_title_is_html',
/** string class for panel block style */
'panel_class',
/** string class for panel block style */
@@ -467,10 +469,8 @@ class DisplayBlock
$oHtml->AddSubBlock($this->GetRenderContent($oPage, $aExtraParams, $sId));
} catch (Exception $e) {
if (UserRights::IsAdministrator()) {
$sExceptionContent = <<<HTML
Exception thrown:<br>
<code>{$e->getMessage()}</code>
HTML;
$sExceptionContent = 'Exception thrown:<br><code>'.utils::Sanitize($e->getMessage(), '', utils::ENUM_SANITIZATION_FILTER_STRING).'</code>';
$oExceptionAlert = AlertUIBlockFactory::MakeForFailure('Cannot display results', $sExceptionContent);
$oHtml->AddSubBlock($oExceptionAlert);
}
@@ -1015,7 +1015,7 @@ JS
$oAttDef = MetaModel::GetAttributeDef($sClass, $sStateAttrCode);
$aValues = $oAttDef->GetAllowedValues();
foreach ($aStates as $sStateValue) {
$aStateLabels[$sStateValue] = $aValues[$sStateValue];
$aStateLabels[$sStateValue] = $aValues[$sStateValue] ?? '';
$aCounts[$sStateValue] = (array_key_exists($sStateValue, $aCountsQueryResults))
? $aCountsQueryResults[$sStateValue]
: 0;
@@ -2322,13 +2322,25 @@ class MenuBlock extends DisplayBlock
$sIconClass = 'fas fa-share-alt';
$sLabel = '';
break;
default:
if (isset($aAction['icon_class']) && (strlen($aAction['icon_class']) > 0)) {
$sIconClass = $aAction['icon_class'];
$sLabel = '';
}
}
$sTarget = isset($aAction['target']) ? $aAction['target'] : '';
$oActionButton = ButtonUIBlockFactory::MakeLinkNeutral($sUrl, $sLabel, $sIconClass, $sTarget, $sActionId);
$oActionButton = ButtonUIBlockFactory::MakeLinkNeutral($sUrl, $sLabel, $sIconClass, $sTarget, utils::Sanitize($sActionId, '', utils::ENUM_SANITIZATION_FILTER_ELEMENT_IDENTIFIER));
// ResourceId should not be sanitized
$oActionButton->AddDataAttribute('resource-id', $sActionId);
$oActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']);
if (empty($sLabel)) {
$oActionButton->SetTooltip(Dict::S($sActionId));
if (empty($aAction['tooltip'])) {
$oActionButton->SetTooltip(Dict::S($sActionId));
} else {
$oActionButton->SetTooltip($aAction['tooltip']);
}
}
$oActionsToolbar->AddSubBlock($oActionButton);
}

View File

@@ -126,7 +126,7 @@ class QueryOQL extends Query
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
{
$aFieldsMap = parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams);
$oPage->add_script("$('[name=\"attr_oql\"]').addClass('ibo-queryoql'); $('[data-attribute-code=\"oql\"]').addClass('ibo-queryoql');");
$oPage->add_script("$('[name=\"attr_oql\"]').addClass('ibo-query-oql ibo-is-code'); $('[data-attribute-code=\"oql\"]').addClass('ibo-query-oql ibo-is-code');");
if (!$bEditMode) {
$sFields = trim($this->Get('fields'));

View File

@@ -41,29 +41,25 @@ register_shutdown_function(function()
$sReservedMemory = null;
if (!is_null($err = error_get_last()) && ($err['type'] == E_ERROR))
{
// Remove stack trace from MySQLException
// Remove stack trace from MySQLException (since 2.7.2 see N°3174)
$sMessage = $err['message'];
if (strpos($sMessage, 'MySQLException') !== false)
{
if (strpos($sMessage, 'MySQLException') !== false) {
$iStackTracePos = strpos($sMessage, 'Stack trace:');
if ($iStackTracePos !== false)
{
if ($iStackTracePos !== false) {
$sMessage = substr($sMessage, 0, $iStackTracePos);
}
}
IssueLog::error($sMessage, null, $err);
if (strpos($err['message'], 'Allowed memory size of') !== false)
{
// Log additional info but message from $err (since 2.7.6 N°4174)
$aErrToLog = $err;
unset($aErrToLog['message']);
IssueLog::error($sMessage, null, $aErrToLog);
if (strpos($err['message'], 'Allowed memory size of') !== false) {
$sLimit = ini_get('memory_limit');
echo "<p>iTop: Allowed memory size of $sLimit exhausted, contact your administrator to increase 'memory_limit' in php.ini</p>\n";
}
elseif (strpos($err['message'], 'Maximum execution time') !== false)
{
} elseif (strpos($err['message'], 'Maximum execution time') !== false) {
$sLimit = ini_get('max_execution_time');
echo "<p>iTop: Maximum execution time of $sLimit exceeded, contact your administrator to increase 'max_execution_time' in php.ini</p>\n";
}
else
{
} else {
echo "<p>iTop: An error occurred, check server error log for more information.</p>\n";
}
}

View File

@@ -27,8 +27,6 @@ use Combodo\iTop\Application\Helper\Session;
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
class privUITransaction
{
/**
@@ -100,10 +98,12 @@ class privUITransaction
}
/**
* The original (and by default) mechanism for storing transaction information
* as an array in the _SESSION variable
* @see \Combodo\iTop\Application\Helper\Session
* The original mechanism for storing transaction information as an array in the $_SESSION variable
*
* Warning, since 2.6.0 the session is regenerated on each login (see PR #20) !
* Also, we saw some problems when using memcached as the PHP session implementation (see N°1835)
*
* @see \Combodo\iTop\Application\Helper\Session
*/
class privUITransactionSession
{
@@ -197,8 +197,31 @@ class privUITransactionSession
class privUITransactionFile
{
/**
* @return int
* @throws \SecurityException if no connected user
*
* @since 2.6.5 2.7.6 3.0.0 N°4289 method creation
*/
private static function GetCurrentUserId() {
$iCurrentUserId = UserRights::GetConnectedUserId();
if ('' === $iCurrentUserId) {
throw new SecurityException('Cannot creation transaction_id when no user logged');
}
return $iCurrentUserId;
}
/**
* Create a new transaction id, store it in the session and return its id
*
* @param void
*
* @return int The new transaction identifier
*
* @throws \SecurityException
* @throws \Exception
*
* @since 2.6.5 2.7.6 3.0.0 security hardening + throws SecurityException if no user logged
*/
public static function GetNewTransactionId()
{
@@ -215,24 +238,36 @@ class privUITransactionFile
throw new Exception('Failed to create the directory "'.APPROOT.'data/transactions". Ajust the rights on the parent directory or let an administrator create the transactions directory and give the web sever enough rights to write into it.');
}
}
if (!is_writable(APPROOT.'data/transactions'))
{
throw new Exception('The directory "'.APPROOT.'data/transactions" must be writable to the application.');
}
self::CleanupOldTransactions();
$id = basename(tempnam(APPROOT.'data/transactions', static::GetUserPrefix()));
self::Info('GetNewTransactionId: Created transaction: '.$id);
return (string)$id;
$iCurrentUserId = static::GetCurrentUserId();
self::CleanupOldTransactions();
$sTransactionIdFullPath = tempnam(APPROOT.'data/transactions', static::GetUserPrefix());
file_put_contents($sTransactionIdFullPath, $iCurrentUserId, LOCK_EX);
$sTransactionIdFileName = basename($sTransactionIdFullPath);
self::Info('GetNewTransactionId: Created transaction: '.$sTransactionIdFileName);
return $sTransactionIdFileName;
}
/**
* Check whether a transaction is valid or not and (optionally) remove the valid transaction from
* the session so that another call to IsTransactionValid for the same transaction id
* will return false
*
* @param int $id Identifier of the transaction, as returned by GetNewTransactionId
* @param bool $bRemoveTransaction True if the transaction must be removed
*
* @return bool True if the transaction is valid, false otherwise
*
* @since 2.6.5 2.7.6 3.0.0 N°4289 security hardening
*/
public static function IsTransactionValid($id, $bRemoveTransaction = true)
{
@@ -246,53 +281,53 @@ class privUITransactionFile
clearstatcache(true, $sFilepath);
$bResult = file_exists($sFilepath);
if ($bResult)
if (false === $bResult) {
self::Info("IsTransactionValid: Transaction '$id' not found. Pending transactions:\n".implode("\n", self::GetPendingTransactions()));
return false;
}
$iCurrentUserId = static::GetCurrentUserId();
$sTransactionIdUserId = file_get_contents($sFilepath);
if ($iCurrentUserId != $sTransactionIdUserId) {
self::Info("IsTransactionValid: Transaction '$id' not existing for current user. Pending transactions:\n".implode("\n", self::GetPendingTransactions()));
return false;
}
if ($bRemoveTransaction)
{
if ($bRemoveTransaction)
$bResult = @unlink($sFilepath);
if (!$bResult)
{
$bResult = @unlink($sFilepath);
if (!$bResult)
{
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
}
else
{
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
}
self::Error('IsTransactionValid: FAILED to remove transaction '.$id);
}
else
{
self::Info('IsTransactionValid: OK. Removed transaction: '.$id);
}
}
else
{
self::Info("IsTransactionValid: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
}
return $bResult;
}
/**
* Removes the transaction specified by its id
* @param int $id The Identifier (as returned by GetNewTransactionId) of the transaction to be removed.
* @return void
* @return bool true if the token can be removed
*
* @since 2.6.5 2.7.6 3.0.0 N°4289 security hardening
*/
public static function RemoveTransaction($id)
{
$bSuccess = true;
$sFilepath = APPROOT.'data/transactions/'.$id;
clearstatcache(true, $sFilepath);
if(!file_exists($sFilepath))
{
$bSuccess = false;
self::Error("RemoveTransaction: Transaction '$id' not found. Pending transactions for this user:\n".implode("\n", self::GetPendingTransactions()));
/** @noinspection PhpRedundantOptionalArgumentInspection */
$bResult = static::IsTransactionValid($id, true);
if (false === $bResult) {
self::Error("RemoveTransaction: Transaction '$id' is invalid. Pending transactions:\n"
.implode("\n", self::GetPendingTransactions()));
return false;
}
$bSuccess = @unlink($sFilepath);
if (!$bSuccess)
{
self::Error('RemoveTransaction: FAILED to remove transaction '.$id);
}
else
{
self::Info('RemoveTransaction: OK '.$id);
}
return $bSuccess;
return true;
}
/**
@@ -365,22 +400,35 @@ class privUITransactionFile
{
self::Write('Error | '.$sText);
}
protected static function IsLogEnabled() {
$oConfig = MetaModel::GetConfig();
if (is_null($oConfig)) {
return false;
}
$bLogTransactions = $oConfig->Get('log_transactions');
if (true === $bLogTransactions) {
return true;
}
return false;
}
protected static function Write($sText)
{
$bLogEnabled = MetaModel::GetConfig()->Get('log_transactions');
if ($bLogEnabled)
{
if (false === static::IsLogEnabled()) {
return;
}
$hLogFile = @fopen(APPROOT.'log/transactions.log', 'a');
if ($hLogFile !== false)
{
if ($hLogFile !== false) {
flock($hLogFile, LOCK_EX);
$sDate = date('Y-m-d H:i:s');
fwrite($hLogFile, "$sDate | $sText\n");
fflush($hLogFile);
flock($hLogFile, LOCK_UN);
fclose($hLogFile);
}
}
}
}

View File

@@ -11,6 +11,13 @@ use Twig_SimpleFilter;
use Twig_SimpleFunction;
use utils;
/**
* Class TwigExtension
*
* @author Guillaume Lajarige <guillaume.lajarige@combodo.com>
* @package Combodo\iTop
* @deprecated 3.1.0 N°4034
*/
class TwigExtension
{
/**
@@ -107,6 +114,14 @@ class TwigExtension
return $oConfig->Get($sParamName);
}));
// Function to get a module setting
// Usage in twig: {{ get_module_setting(<MODULE_CODE>, <PROPERTY_CODE> [, <DEFAULT_VALUE>]) }}
// since 3.0.0, but see N°4034 for upcoming evolutions in the 3.1
$oTwigEnv->addFunction(new Twig_SimpleFunction('get_module_setting', function (string $sModuleCode, string $sPropertyCode, $defaultValue = null) {
$oConfig = MetaModel::GetConfig();
return $oConfig->GetModuleSetting($sModuleCode, $sPropertyCode, $defaultValue);
}));
// Function to get the URL of a static page in a module
// Usage in twig: {{ get_static_page_module_url('itop-my-module', 'path-to-my-page') }}
$oTwigEnv->addFunction(new Twig_SimpleFunction('get_static_page_module_url', function($sModuleName, $sPage)

View File

@@ -196,7 +196,7 @@ class UIExtKeyWidget
$bIsAutocomplete = $oAllowedValues->CountExceeds($iMaxComboLength);
$sWrapperCssClass = $bIsAutocomplete ? 'ibo-input-select-autocomplete-wrapper' : 'ibo-input-select-wrapper';
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey ibo-input-wrapper ibo-input-select-wrapper--with-buttons $sWrapperCssClass\" data-attcode=\"".$this->sAttCode."\" data-validation=\"untouched\">";
$sHTMLValue = "<div class=\"field_input_zone field_input_extkey ibo-input-wrapper ibo-input-select-wrapper--with-buttons $sWrapperCssClass\" data-attcode=\"".$this->sAttCode."\" data-validation=\"untouched\" data-accessibility-selectize-label=\"$sTitle\">";
// We just need to compare the number of entries with MaxComboLength, so no need to get the real count.
if (!$bIsAutocomplete) {
@@ -226,7 +226,8 @@ class UIExtKeyWidget
while ($oObj = $oAllowedValues->Fetch()) {
$aOption = [];
$aOption['value'] = $oObj->GetKey();
$aOption['label'] = $oObj->GetName();//.'<span class=\"object-ref-icon fas fa-eye-slash object-obsolete fa-1x fa-fw\"></span>';
$aOption['label'] = $oObj->GetName();
$aOption['search_label'] = utils::HtmlEntityDecode($oObj->GetName());
if (($oAllowedValues->Count() == 1) && ($bMandatory == 'true')) {
// When there is only once choice, select it by default
@@ -243,7 +244,7 @@ class UIExtKeyWidget
foreach ($aAdditionalField as $sAdditionalField) {
array_push($aArguments, $oObj->Get($sAdditionalField));
}
$aOption['additional_field'] = vsprintf($sFormatAdditionalField, $aArguments);
$aOption['additional_field'] = utils::HtmlEntities(vsprintf($sFormatAdditionalField, $aArguments));
}
if (!empty($sObjectImageAttCode)) {
// Try to retrieve image for contact
@@ -259,8 +260,8 @@ class UIExtKeyWidget
array_push($aOptions, $aOption);
}
$sInputType = CmdbAbstractObject::ENUM_INPUT_TYPE_DROPDOWN_DECORATED;
$sHTMLValue .= "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\" tabindex=\"0\"></select>";
$sJsonOptions = str_replace('\\', '\\\\', json_encode($aOptions));
$sHTMLValue .= "<select class=\"ibo-input-select-placeholder\" title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\" tabindex=\"0\"></select>";
$sJsonOptions = str_replace("'", "\'", str_replace('\\', '\\\\', json_encode($aOptions)));
$oPage->add_ready_script(
<<<EOF
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode, $sJSDoSearch);
@@ -537,7 +538,7 @@ JS
$sHTMLValue .= "<option value=\"\">$sDisplayValue</option>\n";
}
} else {
$sHTMLValue .= "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
$sHTMLValue .= "<select class=\"ibo-input-select-placeholder\" title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
$sHTMLValue .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>\n";
}
@@ -703,14 +704,14 @@ JS
HTML
);
$sDialogTitle = addslashes($sTitle);
$sDialogTitleSanitized = utils::HtmlToText($sTitle);
$oPage->add_ready_script(<<<JS
$('#ac_dlg_{$this->iId}').dialog({
width: $(window).width()*0.8,
height: $(window).height()*0.8,
autoOpen: false,
modal: true,
title: '$sDialogTitle',
title: '$sDialogTitleSanitized',
resizeStop: oACWidget_{$this->iId}.UpdateSizes,
close: oACWidget_{$this->iId}.OnClose,
buttons: [
@@ -823,9 +824,9 @@ JS
$aJsonMap = array();
foreach ($aValues as $sKey => $aValue) {
$aElt = ['value' => $sKey, 'label' => $aValue['label'], 'obsolescence_flag' => $aValue['obsolescence_flag']];
$aElt = ['value' => $sKey, 'label' => utils::HtmlEntities($aValue['label']), 'obsolescence_flag' => $aValue['obsolescence_flag']];
if ($aValue['additional_field'] != '') {
$aElt['additional_field'] = $aValue['additional_field'];
$aElt['additional_field'] = utils::HtmlEntities($aValue['additional_field']);
}
if (array_key_exists('initials', $aValue)) {

View File

@@ -65,7 +65,7 @@ class UIHTMLEditorWidget
$sHelpText = $this->m_sHelpText;
$sValidationField = $this->m_sValidationField;
$sHtmlValue = "<div class=\"field_input_zone field_input_html\"><textarea class=\"htmlEditor\" title=\"$sHelpText\" name=\"attr_{$this->m_sFieldPrefix}{$sCode}\" rows=\"10\" cols=\"10\" id=\"$iId\">$sValue</textarea></div>$sValidationField";
$sHtmlValue = "<div class=\"field_input_zone field_input_html ibo-input-wrapper\"><textarea class=\"htmlEditor ibo-input-richtext-placeholder\" title=\"$sHelpText\" name=\"attr_{$this->m_sFieldPrefix}{$sCode}\" id=\"$iId\">$sValue</textarea></div>$sValidationField";
// Replace the text area with CKEditor
// To change the default settings of the editor,

View File

@@ -59,7 +59,7 @@ class UIPasswordWidget
$sConfirmPasswordValue = $aPasswordValues ? $aPasswordValues['confirm'] : '*****';
$sChangedValue = (($sPasswordValue != '*****') || ($sConfirmPasswordValue != '*****')) ? 1 : 0;
$sHtmlValue = '';
$sHtmlValue .= '<div class="field_input_zone field_input_onewaypassword ibo-input-wrapper">';
$sHtmlValue .= '<div class="field_input_zone field_input_onewaypassword ibo-input-wrapper ibo-input-one-way-password-wrapper">';
$sHtmlValue .= '<input class="ibo-input" type="password" maxlength="255" name="attr_'.$sCode.'[value]" id="'.$this->iId.'" value="'.htmlentities($sPasswordValue, ENT_QUOTES, 'UTF-8').'"/>';
$sHtmlValue .= '<div class="ibo-input-wrapper ibo-input-wrapper--with-buttons"><input class="ibo-input" type="password" maxlength="255" id="'.$this->iId.'_confirm" value="'.htmlentities($sConfirmPasswordValue, ENT_QUOTES, 'UTF-8').'" name="attr_'.$sCode.'[confirm]"/>';
$sHtmlValue .= '<div class="ibo-input-select--action-buttons"><div class="ibo-input-select--action-button ibo-input-select--action-button--create" data-tooltip-content="'.Dict::S('UI:PasswordConfirm').'"><i class="fas fa-question-circle"></i></div></div></div>';

View File

@@ -1319,6 +1319,16 @@ class utils
return Session::Get('itop_env', ITOP_DEFAULT_ENV);
}
/**
* @return string Absolute path to the folder into which the current environment has been compiled.
* The corresponding folder is created or cleaned upon code compilation
* @since 3.0.0
*/
public static function GetCompiledEnvironmentPath(): string
{
return APPROOT . 'env-' . MetaModel::GetEnvironment() . '/';
}
/**
* @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
@@ -1327,6 +1337,7 @@ class utils
{
return APPROOT.'data/cache-'.MetaModel::GetEnvironment().'/';
}
/**
* @return string A path to a folder into which any module can store log
* @since 2.7.0
@@ -2627,7 +2638,9 @@ class utils
$aDefaultConf = array(
'language'=> $sLanguage,
'contentsLanguage' => $sLanguage,
'extraPlugins' => 'disabler,codesnippet,mentions,objectshortcut',
'extraPlugins' => 'disabler,codesnippet,mentions,objectshortcut,font,uploadimage',
'uploadUrl' => utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php',
'contentsCss' => array(utils::GetAbsoluteUrlAppRoot().'js/ckeditor/contents.css', utils::GetAbsoluteUrlAppRoot().'css/ckeditor/contents.css'),
);
// Mentions
@@ -2649,7 +2662,7 @@ class utils
}
// Note: Endpoints are defaults only and should be overloaded by other GUIs such as the end-users portal
$sMentionEndpoint = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=cke_mentions&marker='.$sMentionMarker.'&needle={encodedQuery}';
$sMentionEndpoint = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=cke_mentions&marker='.urlencode($sMentionMarker).'&needle={encodedQuery}';
$sMentionItemUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class='.$sMentionClass.'&id={id}';
$sMentionItemPictureTemplate = (empty(MetaModel::GetImageAttributeCode($sMentionClass))) ? '' : <<<HTML

View File

@@ -350,7 +350,7 @@ class WizardHelper
*/
public function GetJsForUpdateFields()
{
$sWizardHelperJsVar = ($this->m_aData['m_sWizHelperJsVarName']) ?? 'oWizardHelper'.$this->GetFormPrefix();
$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();
return <<<JS

View File

@@ -3,4 +3,9 @@
define('APPROOT', dirname(__FILE__).'/');
define('APPCONF', APPROOT.'conf/');
/**
* iTop framework Version
*/
define('ITOP_DESIGN_LATEST_VERSION', '3.0');
require_once APPROOT.'bootstrap.inc.php';

View File

@@ -12,7 +12,7 @@
"ext-soap": "*",
"combodo/tcpdf": "6.3.5",
"nikic/php-parser": "^4.12.0",
"pear/archive_tar": "1.4.13",
"pear/archive_tar": "1.4.14",
"pelago/emogrifier": "3.1.0",
"scssphp/scssphp": "1.0.6",
"swiftmailer/swiftmailer": "5.4.12",

12
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "fb56686981ee4945791fe5b93735b022",
"content-hash": "4c5cdd1e0feb4abaab8f86959ffc7a64",
"packages": [
{
"name": "combodo/tcpdf",
@@ -173,16 +173,16 @@
},
{
"name": "pear/archive_tar",
"version": "1.4.13",
"version": "1.4.14",
"source": {
"type": "git",
"url": "https://github.com/pear/Archive_Tar.git",
"reference": "2b87b41178cc6d4ad3cba678a46a1cae49786011"
"reference": "4d761c5334c790e45ef3245f0864b8955c562caa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pear/Archive_Tar/zipball/19bb8e95490d3e3ad92fcac95500ca80bdcc7495",
"reference": "19bb8e95490d3e3ad92fcac95500ca80bdcc7495",
"url": "https://api.github.com/repos/pear/Archive_Tar/zipball/4d761c5334c790e45ef3245f0864b8955c562caa",
"reference": "4d761c5334c790e45ef3245f0864b8955c562caa",
"shasum": ""
},
"require": {
@@ -249,7 +249,7 @@
"type": "patreon"
}
],
"time": "2021-01-18T19:32:54+00:00"
"time": "2021-07-20T13:53:39+00:00"
},
{
"name": "pear/console_getopt",

View File

@@ -49,7 +49,7 @@ class DbConnectionWrapper
return static::$oDbCnxStandard;
}
public static function SetDbConnection(mysqli $oMysqli): void
public static function SetDbConnection(?mysqli $oMysqli): void
{
static::$oDbCnxStandard = $oMysqli;
static::SetDbConnectionMockForQuery($oMysqli);
@@ -58,9 +58,9 @@ class DbConnectionWrapper
/**
* Use this to register a mock that will handle {@see mysqli::query()}
*
* @param \mysqli $oMysqli
* @param \mysqli|null $oMysqli
*/
public static function SetDbConnectionMockForQuery(mysqli $oMysqli): void
public static function SetDbConnectionMockForQuery(?mysqli $oMysqli): void
{
static::$oDbCnxMockableForQuery = $oMysqli;
}

View File

@@ -51,6 +51,7 @@ abstract class Action extends cmdbAbstractObject
"db_table" => "priv_action",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-in-transit.svg'),
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
@@ -366,8 +367,7 @@ class ActionEmail extends ActionNotification
{
$this->m_iRecipients = 0;
$this->m_aMailErrors = array();
$bRes = false; // until we do succeed in sending the email
// Determine recipients
//
$sTo = $this->FindRecipients('to', $aContextArgs);
@@ -381,29 +381,42 @@ class ActionEmail extends ActionNotification
$sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs);
$sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs);
$oObj = $aContextArgs['this->object()'];
$sMessageId = sprintf('iTop_%s_%d_%f@%s.openitop.org', get_class($oObj), $oObj->GetKey(), microtime(true /* get as float*/), MetaModel::GetEnvironmentId());
$sMessageId = sprintf('iTop_%s_%d_%f@%s.openitop.org', get_class($oObj), $oObj->GetKey(), microtime(true /* get as float*/),
MetaModel::GetEnvironmentId());
$sReference = '<'.$sMessageId.'>';
}
catch(Exception $e)
{
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
throw $e;
}
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
if (!is_null($oLog))
{
catch (Exception $e) {
/** @noinspection PhpUnhandledExceptionInspection */
throw $e;
}
finally {
ApplicationContext::SetUrlMakerClass($sPreviousUrlMaker);
}
if (!is_null($oLog)) {
// Note: we have to secure this because those values are calculated
// inside the try statement, and we would like to keep track of as
// many data as we could while some variables may still be undefined
if (isset($sTo)) $oLog->Set('to', $sTo);
if (isset($sCC)) $oLog->Set('cc', $sCC);
if (isset($sBCC)) $oLog->Set('bcc', $sBCC);
if (isset($sFrom)) $oLog->Set('from', empty($sFromLabel) ? $sFrom : "$sFromLabel <$sFrom>");
if (isset($sSubject)) $oLog->Set('subject', $sSubject);
if (isset($sBody)) $oLog->Set('body', $sBody);
if (isset($sTo)) {
$oLog->Set('to', $sTo);
}
if (isset($sCC)) {
$oLog->Set('cc', $sCC);
}
if (isset($sBCC)) {
$oLog->Set('bcc', $sBCC);
}
if (isset($sFrom)) {
$oLog->Set('from', $sFrom);
}
if (isset($sSubject)) {
$oLog->Set('subject', $sSubject);
}
if (isset($sBody)) {
$oLog->Set('body', $sBody);
}
}
$sStyles = file_get_contents(APPROOT.'css/email.css');
$sStyles .= MetaModel::GetConfig()->Get('email_css');

View File

@@ -157,7 +157,7 @@ abstract class AsyncTask extends DBObject
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
$iRetryDelay = $aConfig['retry_delay'];
$iRetryDelay = $aConfig['retry_delay'] ?? $iRetryDelay;
}
return $iRetryDelay;
}
@@ -169,11 +169,71 @@ abstract class AsyncTask extends DBObject
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
$iMaxRetries = $aConfig['max_retries'];
$iMaxRetries = $aConfig['max_retries'] ?? $iMaxRetries;
}
return $iMaxRetries;
}
public function IsRetryDelayExponential()
{
$bExponential = false;
$aRetries = MetaModel::GetConfig()->Get('async_task_retries');
if (is_array($aRetries) && array_key_exists(get_class($this), $aRetries))
{
$aConfig = $aRetries[get_class($this)];
$bExponential = (bool)$aConfig['exponential_delay'] ?? $bExponential;
}
return $bExponential;
}
public static function CheckRetryConfig(Config $oConfig, $sAsyncTaskClass)
{
$aMessages = [];
$aRetries = $oConfig->Get('async_task_retries');
if (is_array($aRetries) && array_key_exists($sAsyncTaskClass, $aRetries))
{
$aValidKeys = array("retry_delay", "max_retries", "exponential_delay");
$aConfig = $aRetries[$sAsyncTaskClass];
if (!is_array($aConfig))
{
$aMessages[] = Dict::Format('Class:AsyncTask:InvalidConfig_Class_Keys', $sAsyncTaskClass, implode(', ', $aValidKeys));
}
else
{
foreach($aConfig as $key => $value)
{
if (!in_array($key, $aValidKeys))
{
$aMessages[] = Dict::Format('Class:AsyncTask:InvalidConfig_Class_InvalidKey_Keys', $sAsyncTaskClass, $key, implode(', ', $aValidKeys));
}
}
}
}
return $aMessages;
}
/**
* Compute the delay to wait for the "next retry", based on the given parameters
* @param bool $bIsExponential
* @param int $iRetryDelay
* @param int $iMaxRetries
* @param int $iRemainingRetries
* @return int
*/
public static function GetNextRetryDelay($bIsExponential, $iRetryDelay, $iMaxRetries, $iRemainingRetries)
{
if ($bIsExponential)
{
$iExponent = $iMaxRetries - $iRemainingRetries;
if ($iExponent < 0) $iExponent = 0; // Safety net in case on configuration change in the middle of retries
return $iRetryDelay * (2 ** $iExponent);
}
else
{
return $iRetryDelay;
}
}
/**
* Override to notify people that a task cannot be performed
*/
@@ -241,25 +301,26 @@ abstract class AsyncTask extends DBObject
if ($iRemaining > 0)
{
$iRetryDelay = $this->GetRetryDelay($iErrorCode);
IssueLog::Info('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage.' - remaining retries: '.$iRemaining.' - next retry in '.$iRetryDelay.'s');
$iNextRetryDelay = static::GetNextRetryDelay($this->IsRetryDelayExponential(), $iRetryDelay, $this->GetMaxRetries($iErrorCode), $iRemaining);
IssueLog::Info('Failed to process async task #'.$this->GetKey().' - reason: '.$sErrorMessage.' - remaining retries: '.$iRemaining.' - next retry in '.$iNextRetryDelay.'s');
if ($this->Get('event_id') != 0)
{
$oEventLog = MetaModel::GetObject('Event', $this->Get('event_id'));
$oEventLog->Set('message', "$sErrorMessage\nFailed to process async task. Remaining retries: '.$iRemaining.'. Next retry in '.$iRetryDelay.'s'");
$oEventLog->Set('message', "$sErrorMessage\nFailed to process async task. Remaining retries: $iRemaining. Next retry in {$iNextRetryDelay}s");
try
{
$oEventLog->DBUpdate();
}
catch (Exception $e)
{
$oEventLog->Set('message', "Failed to process async task. Remaining retries: '.$iRemaining.'. Next retry in '.$iRetryDelay.'s', more details in the log");
$oEventLog->Set('message', "Failed to process async task. Remaining retries: $iRemaining. Next retry in {$iNextRetryDelay}s, more details in the log");
$oEventLog->DBUpdate();
}
}
$this->Set('remaining_retries', $iRemaining - 1);
$this->Set('status', 'planned');
$this->Set('started', null);
$this->Set('planned', time() + $iRetryDelay);
$this->Set('planned', time() + $iNextRetryDelay);
}
else
{

View File

@@ -1793,7 +1793,7 @@ class AttributeLinkedSet extends AttributeDefinition
public function GetImportColumns()
{
$aColumns = array();
$aColumns[$this->GetCode()] = 'TEXT';
$aColumns[$this->GetCode()] = 'TEXT'.CMDBSource::GetSqlStringColumnDefinition();
return $aColumns;
}
@@ -4159,6 +4159,7 @@ class AttributeText extends AttributeString
{
$sValue = parent::GetAsHTML($sValue, $oHostObject, $bLocalize);
$sValue = self::RenderWikiHtml($sValue);
$sValue = nl2br($sValue);
return "<div $sStyle>$sValue</div>";
}
@@ -5994,7 +5995,7 @@ class AttributeDateTime extends AttributeDBField
{
// Allow an empty string to be a valid value (synonym for "reset")
$aColumns = array();
$aColumns[$this->GetCode()] = 'VARCHAR(19)';
$aColumns[$this->GetCode()] = 'VARCHAR(19)'.CMDBSource::GetSqlStringColumnDefinition();
return $aColumns;
}
@@ -6482,7 +6483,7 @@ class AttributeDate extends AttributeDateTime
{
// Allow an empty string to be a valid value (synonym for "reset")
$aColumns = array();
$aColumns[$this->GetCode()] = 'VARCHAR(10)';
$aColumns[$this->GetCode()] = 'VARCHAR(10)'.CMDBSource::GetSqlStringColumnDefinition();
return $aColumns;
}
@@ -8159,24 +8160,27 @@ class AttributeImage extends AttributeBlob
$sRet = '';
$bIsCustomImage = false;
$iMaxWidthPx = $this->Get('display_max_width').'px';
$iMaxHeightPx = $this->Get('display_max_height').'px';
$iMaxWidth = $this->Get('display_max_width');
$sMaxWidthPx = $iMaxWidth.'px';
$iMaxHeight = $this->Get('display_max_height');
$sMaxHeightPx = $iMaxHeight.'px';
$sDefaultImageUrl = $this->Get('default_image');
if ($sDefaultImageUrl !== null) {
$sRet = $this->GetHtmlForImageUrl($sDefaultImageUrl, $iMaxWidthPx, $iMaxHeightPx);
$sRet = $this->GetHtmlForImageUrl($sDefaultImageUrl, $sMaxWidthPx, $sMaxHeightPx);
}
$sCustomImageUrl = $this->GetAttributeImageFileUrl($value, $oHostObject);
if ($sCustomImageUrl !== null) {
$bIsCustomImage = true;
$sRet = $this->GetHtmlForImageUrl($sCustomImageUrl, $iMaxWidthPx, $iMaxHeightPx);
$sRet = $this->GetHtmlForImageUrl($sCustomImageUrl, $sMaxWidthPx, $sMaxHeightPx);
}
$sCssClasses = 'ibo-input-image--image-view attribute-image';
$sCssClasses .= ' '.(($bIsCustomImage) ? 'attribute-image-custom' : 'attribute-image-default');
return '<div class="'.$sCssClasses.'" style="width: '.$iMaxWidthPx.'; height: '.$iMaxHeightPx.';">'.$sRet.'</div>';
// Important: If you change this, mind updating edit_image.js as well
return '<div class="'.$sCssClasses.'" style="max-width: '.$sMaxWidthPx.'; max-height: '.$sMaxHeightPx.'; aspect-ratio: '.$iMaxWidth.' / '.$iMaxHeight.'">'.$sRet.'</div>';
}
/**

View File

@@ -1199,9 +1199,6 @@ EOF
$.get(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?{$sAppContext}', {operation: 'displayCSVHistory', showall: bShowAll}, function(data)
{
$('#$sAjaxDivId').html(data);
var table = $('#$sAjaxDivId .listResults');
table.tableHover(); // hover tables
table.tablesorter( { widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
}
);
}

View File

@@ -147,6 +147,8 @@ class CMDBSource
*
* @return \mysqli
* @throws \MySQLException
*
* @uses IsOpenedDbConnectionUsingTls when asking for a TLS connection, to check if it was really opened using TLS
*/
public static function GetMysqliInstance(
$sDbHost, $sUser, $sPwd, $sSource = '', $bTlsEnabled = false, $sTlsCa = null, $bCheckTlsAfterConnection = false
@@ -249,41 +251,41 @@ class CMDBSource
* parameters were used.<br>
* This method can be called to ensure that the DB connection really uses TLS.
*
* <p>We're using this object connection : {@see self::$m_oMysqli}
* <p>We're using our own mysqli instance to do the check as this check is done when creating the mysqli instance : the consumer
* might want a dedicated object, and if so we don't want to overwrite the one saved in CMDBSource !<br>
* This is the case for example with {@see \iTopMutex} !
*
* @param \mysqli $oMysqli
*
* @return boolean true if the connection was really established using TLS
* @return boolean true if the connection was really established using TLS, false otherwise
* @throws \MySQLException
*
* @used-by GetMysqliInstance
* @uses IsMySqlVarNonEmpty
* @uses 'ssl_version' MySQL var
* @uses 'ssl_cipher' MySQL var
*/
private static function IsOpenedDbConnectionUsingTls($oMysqli)
{
if (self::$m_oMysqli == null)
{
self::$m_oMysqli = $oMysqli;
}
$bNonEmptySslVersionVar = self::IsMySqlVarNonEmpty('ssl_version');
$bNonEmptySslCipherVar = self::IsMySqlVarNonEmpty('ssl_cipher');
$bNonEmptySslVersionVar = self::IsMySqlVarNonEmpty('ssl_version', $oMysqli);
$bNonEmptySslCipherVar = self::IsMySqlVarNonEmpty('ssl_cipher', $oMysqli);
return ($bNonEmptySslVersionVar && $bNonEmptySslCipherVar);
}
/**
* @param string $sVarName
* @param mysqli $oMysqli connection to use for the query
*
* @return bool
* @throws \MySQLException
*
* @uses SHOW STATUS queries
* @uses 'SHOW SESSION STATUS' queries
*/
private static function IsMySqlVarNonEmpty($sVarName)
private static function IsMySqlVarNonEmpty($sVarName, $oMysqli)
{
try
{
$sResult = self::QueryToScalar("SHOW SESSION STATUS LIKE '$sVarName'", 1);
$sResult = self::QueryToScalar("SHOW SESSION STATUS LIKE '$sVarName'", 1, $oMysqli);
}
catch (MySQLQueryHasNoResultException $e)
{
@@ -838,27 +840,28 @@ class CMDBSource
/**
* @param string $sSql
* @param int $iCol beginning at 0
* @param mysqli $oMysqli if not null will query using this connection, otherwise will use {@see GetMySQLiForQuery}
*
* @return string corresponding cell content on the first line
* @throws \MySQLException
* @throws \MySQLQueryHasNoResultException
* @since 2.7.5-2 2.7.6 3.0.0 N°4215 new optional mysqli param
*/
public static function QueryToScalar($sSql, $iCol = 0)
public static function QueryToScalar($sSql, $iCol = 0, $oMysqli = null)
{
$oMysqliForQuery = $oMysqli ?: DbConnectionWrapper::GetDbConnection(true);
$oKPI = new ExecutionKPI();
try
{
/** @noinspection NullPointerExceptionInspection this shouldn't be called with un-init DB */
$oResult = DbConnectionWrapper::GetDbConnection(true)->query($sSql);
try {
/** @noinspection NullPointerExceptionInspection this shouldn't happen : either cnx is passed or the DB was init */
$oResult = $oMysqliForQuery->query($sSql);
}
catch(mysqli_sql_exception $e)
{
catch (mysqli_sql_exception $e) {
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql, $e));
}
$oKPI->ComputeStats('Query exec (mySQL)', $sSql);
if ($oResult === false)
{
if ($oResult === false) {
throw new MySQLException('Failed to issue SQL query', array('query' => $sSql));
}

View File

@@ -107,7 +107,7 @@ class Config
protected $m_aSettings = [
'log_level_min' => [
'type' => 'array',
'description' => 'Optional min log level per channel',
'description' => 'Optional min log level, per channel.',
'default' => '',
'value' => '',
'source_of_value' => '',
@@ -115,9 +115,9 @@ class Config
],
'log_level_min.write_in_db' => [
'type' => 'array',
'description' => 'Additional configuration that enable "in DB" logs for Exception on compatible code.',
'default' => [ 'Exception' => 'Error', ],
'value' => [ 'Exception' => 'Error', ],
'description' => 'Optional min log level IN DB, per channel.',
'default' => '',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
@@ -1070,72 +1070,80 @@ class Config
'show_in_conf_sample' => false,
],
'transactions_gc_threshold' => [
'type' => 'integer',
'description' => 'probability in percent for the garbage collector to be triggered (100 mean always)',
'default' => 10, // added in itop 2.7.4, before the GC was always called
'value' => '',
'source_of_value' => '',
'type' => 'integer',
'description' => 'probability in percent for the garbage collector to be triggered (100 mean always)',
'default' => 10, // added in itop 2.7.4, before the GC was always called
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'log_transactions' => [
'type' => 'bool',
'description' => 'Whether or not to enable the debug log for the transactions.',
'default' => false,
'value' => '',
'source_of_value' => '',
'type' => 'bool',
'description' => 'Whether or not to enable the debug log for the transactions.',
'default' => false,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'concurrent_lock_enabled' => [
'type' => 'bool',
'description' => 'Whether or not to activate the locking mechanism in order to prevent concurrent edition of the same object.',
'default' => false,
'value' => '',
'source_of_value' => '',
'type' => 'bool',
'description' => 'Whether or not to activate the locking mechanism in order to prevent concurrent edition of the same object.',
'default' => false,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'concurrent_lock_expiration_delay' => [
'type' => 'integer',
'description' => 'Delay (in seconds) for a concurrent lock to expire',
'default' => 120,
'value' => '',
'source_of_value' => '',
'type' => 'integer',
'description' => 'Delay (in seconds) for a concurrent lock to expire',
'default' => 120,
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'concurrent_lock_override_profiles' => [
'type' => 'array',
'description' => 'The list of profiles allowed to "kill" a lock',
'default' => ['Administrator'],
'value' => '',
'source_of_value' => '',
'type' => 'array',
'description' => 'The list of profiles allowed to "kill" a lock',
'default' => ['Administrator'],
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'force_transition_confirmation' => [
'type' => 'bool',
'description' => 'If set to true force confirmation in all transition even if there is no field to complete',
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'html_sanitizer' => [
'type' => 'string',
'description' => 'The class to use for HTML sanitization: HTMLDOMSanitizer, HTMLPurifierSanitizer or HTMLNullSanitizer',
'default' => 'HTMLDOMSanitizer',
'value' => '',
'source_of_value' => '',
'type' => 'string',
'description' => 'The class to use for HTML sanitization: HTMLDOMSanitizer, HTMLPurifierSanitizer or HTMLNullSanitizer',
'default' => 'HTMLDOMSanitizer',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'inline_image_max_display_width' => [
'type' => 'integer',
'description' => 'The maximum width (in pixels) when displaying images inside an HTML formatted attribute. Images will be displayed using this this maximum width.',
'default' => '250',
'value' => '',
'source_of_value' => '',
'type' => 'integer',
'description' => 'The maximum width (in pixels) when displaying images inside an HTML formatted attribute. Images will be displayed using this this maximum width.',
'default' => '250',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => true,
],
'inline_image_max_storage_width' => [
'type' => 'integer',
'description' => 'The maximum width (in pixels) when uploading images to be used inside an HTML formatted attribute. Images larger than the given size will be downsampled before storing them in the database.',
'default' => '1600',
'value' => '',
'source_of_value' => '',
'type' => 'integer',
'description' => 'The maximum width (in pixels) when uploading images to be used inside an HTML formatted attribute. Images larger than the given size will be downsampled before storing them in the database.',
'default' => '1600',
'value' => '',
'source_of_value' => '',
'show_in_conf_sample' => true,
],
'draft_attachments_lifetime' => [
'type' => 'integer',
'description' => 'Lifetime (in seconds) of drafts\' attachments and inline images: after this duration, the garbage collector will delete them.',
'type' => 'integer',
'description' => 'Lifetime (in seconds) of drafts\' attachments and inline images: after this duration, the garbage collector will delete them.',
'default' => 86400,
'value' => '',
'source_of_value' => '',
@@ -1327,9 +1335,9 @@ class Config
],
'mentions.allowed_classes' => [
'type' => 'array',
'description' => 'Classes which can be mentioned through the autocomplete in the caselogs. Key of the array must be a single character that will trigger the autocomplete, value can be either a DM class or a valid OQL (eg. "@" => "Person", "?" => "SELECT FAQ WHERE status = \'published\'")',
'description' => 'Classes which can be mentioned through the autocomplete in the caselogs. Key of the array must be a single character that will trigger the autocomplete, value must be a DM class (eg. "@" => "Person", "?" => "FAQ")',
'default' => [
'@' => 'SELECT Person WHERE status = \'active\'',
'@' => 'Person',
],
'value' => false,
'source_of_value' => '',
@@ -1463,6 +1471,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'security.hide_administrators' => [
'type' => 'bool',
'description' => 'If true, non-administrator users will not be able to see the administrator accounts, the Administrator profile and the links between the administrator accounts and their profiles.',
'default' => false,
'value' => false,
'source_of_value' => '',
'show_in_conf_sample' => false,
],
'behind_reverse_proxy' => [
'type' => 'bool',
'description' => 'If true, then proxies custom header (X-Forwarded-*) are taken into account. Use only if the webserver is not publicly accessible (reachable only by the reverse proxy)',

View File

@@ -1470,24 +1470,78 @@ abstract class DBObject implements iDisplay
*/
public function GetIcon($bImgTag = true)
{
$sClass = get_class($this);
if($this->HasHighlightIcon()) {
$sIconUrl = MetaModel::GetHighlightScale($sClass)[$this->ComputeHighlightCode()]['icon'];
if($bImgTag) {
return "<img src=\"$sIconUrl\" style=\"vertical-align:middle\"/>";
}
else {
return $sIconUrl;
}
}
// Get object instance own image if it exists
$sImageAttCode = MetaModel::GetImageAttributeCode($sClass);
$sIconUrl = $this->HasInstanceIcon() ? $this->Get($sImageAttCode)->GetDisplayURL($sClass, $this->GetKey(), $sImageAttCode) : '';
if (strlen($sIconUrl) > 0) {
if($bImgTag) {
return "<img src=\"$sIconUrl\"/>";
}
else {
return $sIconUrl;
}
}
return MetaModel::GetClassIcon(get_class($this), $bImgTag);
}
/**
* @return bool True if the object has an image attribute as semantic attribute and its value is not empty
* @throws \ArchivedObjectException
* @throws \CoreException
* @since 3.0.0
*/
public function HasInstanceIcon(): bool
{
$bHasInstanceIcon = false;
$sClass = get_class($this);
if (!$this->IsNew() && MetaModel::HasImageAttributeCode($sClass)) {
$sImageAttCode = MetaModel::GetImageAttributeCode($sClass);
if (!empty($sImageAttCode)) {
/** @var \ormDocument $oImage */
$oImage = $this->Get($sImageAttCode);
$bHasInstanceIcon = !$oImage->IsEmpty();
}
}
return $bHasInstanceIcon;
}
/**
* @return bool True if the class has an highlight icon declared for the current object state
* @throws \ArchivedObjectException
* @throws \CoreException
* @since 3.0.0
*/
public function HasHighlightIcon(): bool
{
$bHasHighlightIcon = false;
$sCode = $this->ComputeHighlightCode();
$sClass = get_class($this);
if($sCode != '')
{
$aHighlightScale = MetaModel::GetHighlightScale(get_class($this));
$aHighlightScale = MetaModel::GetHighlightScale($sClass);
if (array_key_exists($sCode, $aHighlightScale))
{
$sIconUrl = $aHighlightScale[$sCode]['icon'];
if($bImgTag)
{
return "<img src=\"$sIconUrl\" style=\"vertical-align:middle\"/>";
}
else
{
return $sIconUrl;
}
$bHasHighlightIcon = true;
}
}
return MetaModel::GetClassIcon(get_class($this), $bImgTag);
}
return $bHasHighlightIcon;
}
/**
@@ -3166,17 +3220,19 @@ abstract class DBObject implements iDisplay
foreach ($aMentionedIds as $sMentionedId) {
/** @var \DBObject $oMentionedObject */
$oMentionedObject = MetaModel::GetObject($sMentionedClass, $sMentionedId);
// Important: Here the "$this->object()$" placeholder is actually the mentioned object and not the current object. The current object can be used through the $source->object()$ placeholder.
// This is due to the current implementation of triggers, the events will only be visible on the object the trigger's OQL is based on... 😕
$aTriggerArgs = $this->ToArgs('source') + array('this->object()' => $oMentionedObject);
$aTriggerArgs = $this->ToArgs('this') + array('mentioned->object()' => $oMentionedObject);
$aParams = array('class_list' => MetaModel::EnumParentClasses($sMentionedClass, ENUM_PARENT_CLASSES_ALL));
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectMention AS t WHERE t.target_class IN (:class_list)"),
array(), $aParams);
$aParams = array('class_list' => MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnObjectMention AS t WHERE t.target_class IN (:class_list)"), array(), $aParams);
while ($oTrigger = $oSet->Fetch())
{
/** @var \TriggerOnObjectMention $oTrigger */
try {
// Ensure to handle only mentioned object in the trigger's scope
if ($oTrigger->IsMentionedObjectInScope($oMentionedObject) === false) {
continue;
}
$oTrigger->DoActivate($aTriggerArgs);
}
catch (Exception $e) {
@@ -4005,6 +4061,58 @@ abstract class DBObject implements iDisplay
return true;
}
/**
* Helper to set a date computed from another date with extra logic
*
* @api
*
* @param string $sAttCode attribute code of a date or date-time which will be set
* @param string $sModifier string specifying how to modify the date time
* @param string $sAttCodeSource attribute code of a date or date-time used as source
*
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @since 3.0.0
*/
public function SetComputedDate($sAttCode, $sModifier = '', $sAttCodeSource = '')
{
$oDate = new DateTime(); // Use now if no Source provided
if ($sAttCodeSource != "") {
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCodeSource);
$oSourceValue = $this->Get($sAttCodeSource);
if (!$oAttDef->IsNull($oSourceValue)) {
// Use the existing value as the Source date
$oDate = new DateTime($oSourceValue);
}
}
$oDate->modify($sModifier);
$this->Set($sAttCode, $oDate->format('Y-m-d H:i:s'));
}
/**
* Helper to set a date computed from another date with extra logic
* Call SetComputedDate() only of the internal representation of the attribute is null.
*
* @api
* @see SetComputedDate()
*
* @param string $sAttCode attribute code which will be set
* @param string $sModifier string specifying how to modify the date time
* @param string $sAttCodeSource attribute code used as source
*
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @since 3.0.0
*/
public function SetComputedDateIfNull($sAttCode, $sModifier = '', $sAttCodeSource = '')
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
$oCurrentValue = $this->Get($sAttCode);
if ($oAttDef->IsNull($oCurrentValue)) {
$this->SetComputedDate($sAttCode, $sModifier, $sAttCodeSource);
}
}
/**
* Helper to set a value only if it is currently undefined
*
@@ -4023,40 +4131,35 @@ abstract class DBObject implements iDisplay
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
$oCurrentValue = $this->Get($sAttCode);
if ($oAttDef->IsNull($oCurrentValue))
{
if ($oAttDef->IsNull($oCurrentValue)) {
$this->SetCurrentDate($sAttCode);
}
}
/**
* Helper to set the current logged in user for the given attribute
* Suitable for use as a lifecycle action
*
* @api
*
* @param string $sAttCode
*
* @return bool
*
* @throws CoreException
* @throws CoreUnexpectedValue
*/
/**
* Helper to set the current logged in user for the given attribute
* Suitable for use as a lifecycle action
*
* @api
*
* @param string $sAttCode
*
* @return bool
*
* @throws CoreException
* @throws CoreUnexpectedValue
*/
public function SetCurrentUser($sAttCode)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode);
if ($oAttDef instanceof AttributeString)
{
if ($oAttDef instanceof AttributeString) {
// Note: the user friendly name is the contact friendly name if a contact is attached to the logged in user
$this->Set($sAttCode, UserRights::GetUserFriendlyName());
}
else
{
if ($oAttDef->IsExternalKey())
{
} else {
if ($oAttDef->IsExternalKey()) {
/** @var \AttributeExternalKey $oAttDef */
if ($oAttDef->GetTargetClass() != 'User')
{
if ($oAttDef->GetTargetClass() != 'User') {
throw new Exception("SetCurrentUser: the attribute $sAttCode must be an external key to 'User', found '".$oAttDef->GetTargetClass()."'");
}
}
@@ -4575,8 +4678,9 @@ abstract class DBObject implements iDisplay
public function GetRelatedObjectsUp($sRelCode, $iMaxDepth = 99, $bEnableRedundancy = true)
{
$oGraph = new RelationGraph();
$oGraph->AddSourceObject($this);
$oGraph->AddSinkObject($this);
$oGraph->ComputeRelatedObjectsUp($sRelCode, $iMaxDepth, $bEnableRedundancy);
return $oGraph;
}

View File

@@ -127,10 +127,8 @@ class DisplayableNode extends GraphNode
if ($this->GetProperty('source')) {
$oPdf->SetLineStyle(array('width' => 2 * $fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => array(204, 51, 51)));
$oPdf->Circle($this->x * $fScale, $this->y * $fScale, 16 * 1.25 * $fScale, 0, 360, 'D');
}
else if ($this->GetProperty('sink'))
{
$oPdf->SetLineStyle(array('width' => 2*$fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => array(51, 51, 204)));
} else if ($this->GetProperty('sink')) {
$oPdf->SetLineStyle(array('width' => 2 * $fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => array(51, 51, 204)));
$oPdf->Circle($this->x * $fScale, $this->y * $fScale, 16 * 1.25 * $fScale, 0, 360, 'D');
}
@@ -409,7 +407,7 @@ class DisplayableNode extends GraphNode
{
$oNewNode = new DisplayableGroupNode($oGraph, $sNewId);
$oNewNode->SetProperty('label', 'x'.$aGroupProps['count']);
$oNewNode->SetProperty('icon_url', $aGroupProps['icon_url']);
$oNewNode->SetProperty('icon_url', MetaModel::GetClassIcon($sClass, false));
$oNewNode->SetProperty('class', $sClass);
$oNewNode->SetProperty('is_reached', ($sStatus == 'reached'));
$oNewNode->SetProperty('count', $aGroupProps['count']);
@@ -589,7 +587,7 @@ class DisplayableRedundancyNode extends DisplayableNode
{
$oNewNode = new DisplayableGroupNode($oGraph, '-'.$this->GetId().'::'.$sClass.'/'.$sStatus);
$oNewNode->SetProperty('label', 'x'.count($aGroupProps['nodes']));
$oNewNode->SetProperty('icon_url', $aGroupProps['icon_url']);
$oNewNode->SetProperty('icon_url', MetaModel::GetClassIcon($sClass, false));
$oNewNode->SetProperty('is_reached', ($sStatus == 'is_reached'));
$oNewNode->SetProperty('class', $sClass);
$oNewNode->SetProperty('count', count($aGroupProps['nodes']));
@@ -767,12 +765,9 @@ class DisplayableGroupNode extends DisplayableNode
{
$bReached = $this->GetProperty('is_reached');
$oPdf->SetFillColor(255, 255, 255);
if ($bReached)
{
if ($bReached) {
$aBorderColor = array(100, 100, 100);
}
else
{
} else {
$aBorderColor = array(200, 200, 200);
}
$oPdf->SetLineStyle(array('width' => 2 * $fScale, 'cap' => 'round', 'join' => 'miter', 'dash' => 0, 'color' => $aBorderColor));
@@ -780,14 +775,11 @@ class DisplayableGroupNode extends DisplayableNode
$sIconUrl = $this->GetProperty('icon_url');
$sIconPath = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-'.utils::GetCurrentEnvironment().'/', $sIconUrl);
$oPdf->SetAlpha(1);
$oPdf->Circle($this->x*$fScale, $this->y*$fScale, $this->GetWidth() / 2 * $fScale, 0, 360, 'DF');
if ($bReached)
{
$oPdf->Circle($this->x * $fScale, $this->y * $fScale, $this->GetWidth() / 2 * $fScale, 0, 360, 'DF');
if ($bReached) {
$oPdf->SetAlpha(1);
}
else
{
} else {
$oPdf->SetAlpha(0.4);
}
$oPdf->AddImage($sIconPath, ($this->x - 17) * $fScale, ($this->y - 17) * $fScale, 16 * $fScale, 16 * $fScale);
@@ -801,9 +793,8 @@ class DisplayableGroupNode extends DisplayableNode
public function GetTooltip($aContextDefs)
{
$sHtml = '';
$iGroupIdx = $this->GetProperty('group_index');
$sHtml .= '<a href="#" onclick="$(\'.itop-simple-graph\').simple_graph(\'show_group\', \'relation_group_'.$iGroupIdx.'\');">'.Dict::Format('UI:RelationGroupNumber_N', (1+$iGroupIdx))."</a>";
$sHtml = '<a href="#" id="a_'.$iGroupIdx.'" onclick="$(this).closest(\'.tippy-box\').parent().hide();$(\'.itop-simple-graph\').simple_graph(\'show_group\', \'relation_group_'.$iGroupIdx.'\');return false;">'.Dict::Format('UI:RelationGroupNumber_N', (1+$iGroupIdx))."</a>";
$sHtml .= '<hr/>';
$sHtml .= '<table><tbody><tr>';
$sHtml .= '<td style="vertical-align:top;padding-right: 0.5em;"><img class="ibo-class-icon ibo-is-small" src="'.$this->GetProperty('icon_url').'"></td><td style="vertical-align:top">'.MetaModel::GetName($this->GetObjectClass()).'<br/>';
@@ -855,66 +846,68 @@ class DisplayableGraph extends SimpleGraph
@unlink($sTempFile);
}
}
/**
* Build a DisplayableGraph from a RelationGraph
*
* @param RelationGraph $oGraph
* @param number $iGroupingThreshold
* @param string $bDirectionDown
*
* @return DisplayableGraph
*/
public static function FromRelationGraph(RelationGraph $oGraph, $iGroupingThreshold = 20, $bDirectionDown = true)
public static function FromRelationGraph(RelationGraph $oGraph, $iGroupingThreshold = 20, $bDirectionDown = true, $bForPdf = false)
{
$oNewGraph = new DisplayableGraph();
$oNewGraph->bDirectionDown = $bDirectionDown;
$iPreviousTimeLimit = ini_get('max_execution_time');
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
$oNodesIter = new RelationTypeIterator($oGraph, 'Node');
foreach($oNodesIter as $oNode)
{
foreach ($oNodesIter as $oNode) {
set_time_limit(intval($iLoopTimeLimit));
switch(get_class($oNode))
{
case 'RelationObjectNode':
$oNewNode = new DisplayableNode($oNewGraph, $oNode->GetId(), 0, 0);
$oObj = $oNode->GetProperty('object');
$sClass = get_class($oObj);
if ($oNode->GetProperty('source'))
{
if (!array_key_exists($sClass, $oNewGraph->aSourceObjects))
{
$oNewGraph->aSourceObjects[$sClass] = array();
switch (get_class($oNode)) {
case 'RelationObjectNode':
$oNewNode = new DisplayableNode($oNewGraph, $oNode->GetId(), 0, 0);
$oObj = $oNode->GetProperty('object');
$sClass = get_class($oObj);
if ($oNode->GetProperty('source')) {
if (!array_key_exists($sClass, $oNewGraph->aSourceObjects)) {
$oNewGraph->aSourceObjects[$sClass] = array();
}
$oNewGraph->aSourceObjects[$sClass][] = $oObj->GetKey();
$oNewNode->SetProperty('source', true);
}
$oNewGraph->aSourceObjects[$sClass][] = $oObj->GetKey();
$oNewNode->SetProperty('source', true);
}
if ($oNode->GetProperty('sink'))
{
if (!array_key_exists($sClass, $oNewGraph->aSinkObjects))
{
$oNewGraph->aSinkObjects[$sClass] = array();
if ($oNode->GetProperty('sink')) {
if (!array_key_exists($sClass, $oNewGraph->aSinkObjects)) {
$oNewGraph->aSinkObjects[$sClass] = array();
}
$oNewGraph->aSinkObjects[$sClass][] = $oObj->GetKey();
$oNewNode->SetProperty('sink', true);
}
$oNewGraph->aSinkObjects[$sClass][] = $oObj->GetKey();
$oNewNode->SetProperty('sink', true);
}
$oNewNode->SetProperty('object', $oObj);
$oNewNode->SetProperty('icon_url', $oObj->GetIcon(false));
$oNewNode->SetProperty('label', $oObj->GetRawName());
$oNewNode->SetProperty('is_reached', $bDirectionDown ? $oNode->GetProperty('is_reached') : true); // When going "up" is_reached does not matter
$oNewNode->SetProperty('is_reached_allowed', $oNode->GetProperty('is_reached_allowed'));
$oNewNode->SetProperty('context_root_causes', $oNode->GetProperty('context_root_causes'));
break;
$oNewNode->SetProperty('object', $oObj);
$sIconUrl = $oObj->GetIcon(false);
//when icons are displayed in a PDF file, puts the image data in place of the url
if ($bForPdf && strpos($sIconUrl, 'display_document') > 0) {
$sImageAttCode = MetaModel::GetImageAttributeCode($sClass);
$sIconUrl = '@'.$oObj->Get($sImageAttCode)->GetData();
}
$oNewNode->SetProperty('icon_url', $sIconUrl);
$oNewNode->SetProperty('label', $oObj->GetRawName());
$oNewNode->SetProperty('is_reached', $bDirectionDown ? $oNode->GetProperty('is_reached') : true); // When going "up" is_reached does not matter
$oNewNode->SetProperty('is_reached_allowed', $oNode->GetProperty('is_reached_allowed'));
$oNewNode->SetProperty('context_root_causes', $oNode->GetProperty('context_root_causes'));
break;
default:
$oNewNode = new DisplayableRedundancyNode($oNewGraph, $oNode->GetId(), 0, 0);
$iNbReached = (is_null($oNode->GetProperty('is_reached_count'))) ? 0 : $oNode->GetProperty('is_reached_count');
$oNewNode->SetProperty('label', $iNbReached."/".($oNode->GetProperty('min_up') + $oNode->GetProperty('threshold')));
$oNewNode->SetProperty('min_up', $oNode->GetProperty('min_up'));
$oNewNode->SetProperty('threshold', $oNode->GetProperty('threshold'));
$oNewNode->SetProperty('is_reached_count', $iNbReached);
$oNewNode->SetProperty('is_reached', true);
$oNewNode = new DisplayableRedundancyNode($oNewGraph, $oNode->GetId(), 0, 0);
$iNbReached = (is_null($oNode->GetProperty('is_reached_count'))) ? 0 : $oNode->GetProperty('is_reached_count');
$oNewNode->SetProperty('label', $iNbReached."/".($oNode->GetProperty('min_up') + $oNode->GetProperty('threshold')));
$oNewNode->SetProperty('min_up', $oNode->GetProperty('min_up'));
$oNewNode->SetProperty('threshold', $oNode->GetProperty('threshold'));
$oNewNode->SetProperty('is_reached_count', $iNbReached);
$oNewNode->SetProperty('is_reached', true);
}
}
$oEdgesIter = new RelationTypeIterator($oGraph, 'Edge');

View File

@@ -548,8 +548,6 @@ JS
// Hook the file upload of all CKEditor instances
$('.htmlEditor').each(function() {
var oEditor = $(this).ckeditorGet();
oEditor.config.extraPlugins = 'font,uploadimage';
oEditor.config.uploadUrl = '$sAbsoluteUrlAppRoot'+'pages/ajax.render.php';
oEditor.config.filebrowserBrowseUrl = '$sAbsoluteUrlAppRoot'+'pages/ajax.render.php?operation=cke_browse&temp_id=$sTempId&obj_class=$sObjClass&obj_key=$iObjKey';
oEditor.on( 'fileUploadResponse', function( evt ) {
var fileLoader = evt.data.fileLoader;

View File

@@ -502,20 +502,22 @@ class FileLog
protected function Write($sText, $sLevel = '', $sChannel = '', $aContext = array())
{
$sTextPrefix = empty($sLevel) ? '' : (str_pad($sLevel, 7).' | ');
$sTextPrefix .= str_pad(UserRights::GetUserId(), 5)." | ";
$sTextSuffix = empty($sChannel) ? '' : " | $sChannel";
$sText = "{$sTextPrefix}{$sText}{$sTextSuffix}";
$sLogFilePath = $this->oFileNameBuilder->GetLogFilePath();
$sTextPrefix = empty($sLevel) ? '' : (str_pad($sLevel, 7));
$sTextPrefix .= ' | ';
$sTextPrefix .= str_pad(LogAPI::GetUserInfo(), 5)." | ";
if (empty($sLogFilePath))
{
$sTextSuffix = ' | '.(empty($sChannel) ? '' : $sChannel);
$sTextSuffix .= ' |||';
$sText = "{$sTextPrefix}{$sText}{$sTextSuffix}";
$sLogFilePath = $this->oFileNameBuilder->GetLogFilePath();
if (empty($sLogFilePath)) {
return;
}
$hLogFile = @fopen($sLogFilePath, 'a');
if ($hLogFile !== false)
{
if ($hLogFile !== false) {
flock($hLogFile, LOCK_EX);
$sDate = date('Y-m-d H:i:s');
if (empty($aContext)) {
@@ -540,12 +542,13 @@ class FileLog
*/
class LogChannels
{
public const CLI = 'CLI';
public const CONSOLE = 'console';
public const DEADLOCK = 'DeadLock';
public const CLI = 'CLI';
public const CONSOLE = 'console';
public const DEADLOCK = 'DeadLock';
public const INLINE_IMAGE = 'InlineImage';
public const PORTAL = 'portal';
public const CMDB_SOURCE = 'cmdbsource';
public const PORTAL = 'portal';
public const CMDB_SOURCE = 'cmdbsource';
public const CORE = 'core';
}
@@ -559,28 +562,42 @@ abstract class LogAPI
public const LEVEL_OK = 'Ok';
public const LEVEL_DEBUG = 'Debug';
public const LEVEL_TRACE = 'Trace';
/**
* @var string default log level
* @see GetMinLogLevel
* @see GetMinLogLevel
* @used-by GetLevelDefault
* @var string default log level.
* @since 2.7.1 N°2977
*/
public const LEVEL_DEFAULT = self::LEVEL_OK;
/**
* @see GetMinLogLevel
* @used-by GetLevelDefault
* @var string|bool default log level when writing to DB: false by default in order to disable EventIssue creation, and so on, do not change the behavior.
* @since 3.0.0 N°4261
*/
public const LEVEL_DEFAULT_DB = false;
protected static $aLevelsPriority = array(
self::LEVEL_ERROR => 400,
self::LEVEL_ERROR => 400,
self::LEVEL_WARNING => 300,
self::LEVEL_INFO => 200,
self::LEVEL_OK => 200,
self::LEVEL_DEBUG => 100,
self::LEVEL_TRACE => 50,
self::LEVEL_INFO => 200,
self::LEVEL_OK => 200,
self::LEVEL_DEBUG => 100,
self::LEVEL_TRACE => 50,
);
public const ENUM_CONFIG_PARAM_FILE = 'log_level_min';
public const ENUM_CONFIG_PARAM_DB = 'log_level_min.write_in_db';
/**
* @var \Config attribute allowing to mock config in the tests
*/
protected static $m_oMockMetaModelConfig = null;
protected static $oLastEventIssue = null;
public static function Enable($sTargetFile)
{
// m_oFileLog is not defined as a class attribute so that each impl will have its own
@@ -631,10 +648,6 @@ abstract class LogAPI
*/
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array())
{
if (!static::$m_oFileLog) {
return;
}
if (!isset(self::$aLevelsPriority[$sLevel])) {
IssueLog::Error("invalid log level '{$sLevel}'");
@@ -645,22 +658,46 @@ abstract class LogAPI
$sChannel = static::CHANNEL_DEFAULT;
}
if (!static::IsLogLevelEnabled($sLevel, $sChannel)) {
return;
static::WriteLog($sLevel, $sMessage, $sChannel, $aContext);
}
/**
* @throws \ConfigException
*/
protected static function WriteLog(string $sLevel, string $sMessage, ?string $sChannel = null, ?array $aContext = array()): void
{
if (
(null !== static::$m_oFileLog)
&& static::IsLogLevelEnabled($sLevel, $sChannel, static::ENUM_CONFIG_PARAM_FILE)
) {
static::$m_oFileLog->$sLevel($sMessage, $sChannel, $aContext);
}
static::$m_oFileLog->$sLevel($sMessage, $sChannel, $aContext);
if (static::IsLogLevelEnabled($sLevel, $sChannel, static::ENUM_CONFIG_PARAM_DB)) {
self::WriteToDb($sMessage, $sChannel, $aContext);
}
}
public static function GetUserInfo(): ?string
{
$oConnectedUser = UserRights::GetUserObject();
if (is_null($oConnectedUser)) {
return '';
}
return $oConnectedUser->GetKey();
}
/**
* @throws \ConfigException if log wrongly configured
* @uses GetMinLogLevel
*/
final public static function IsLogLevelEnabled(string $sLevel, string $sChannel, string $sCode = 'log_level_min'): bool
final public static function IsLogLevelEnabled(string $sLevel, string $sChannel, string $sConfigKey = self::ENUM_CONFIG_PARAM_FILE): bool
{
$sMinLogLevel = self::GetMinLogLevel($sChannel, $sCode);
$sMinLogLevel = self::GetMinLogLevel($sChannel, $sConfigKey);
if ($sMinLogLevel === false || $sMinLogLevel === 'false') {
// the is_bool call is to remove a IDE O:) warning as $sMinLogLevel is typed as string
if ((is_bool($sMinLogLevel) && ($sMinLogLevel === false)) || $sMinLogLevel === 'false') {
return false;
}
if (!is_string($sMinLogLevel)) {
@@ -678,7 +715,7 @@ abstract class LogAPI
/**
* @param string $sChannel
* @param string $sCode
* @param string $sConfigKey
*
* @return string one of the LEVEL_* const value : the one configured it if exists, otherwise default log level for this channel
* Config can be set :
@@ -694,21 +731,44 @@ abstract class LogAPI
*
* @uses \LogAPI::GetConfig()
* @uses `log_level_min` config parameter
* @uses `log_level_min.write_to_db` config parameter
* @uses \LogAPI::GetLevelDefault
*
* @link https://www.itophub.io/wiki/page?id=3_0_0%3Aadmin%3Alog iTop log reference
*/
protected static function GetMinLogLevel($sChannel, $sCode = 'log_level_min')
protected static function GetMinLogLevel($sChannel, $sConfigKey = self::ENUM_CONFIG_PARAM_FILE)
{
$sLogLevelMin = static::GetLogConfig($sConfigKey);
$sConfiguredLevelForChannel = static::GetMinLogLevelFromChannel($sLogLevelMin, $sChannel, $sConfigKey);
if (!is_null($sConfiguredLevelForChannel)) {
return $sConfiguredLevelForChannel;
}
return static::GetMinLogLevelFromDefault($sLogLevelMin, $sChannel, $sConfigKey);
}
final protected static function GetLogConfig($sConfigKey)
{
$oConfig = static::GetConfig();
if (!$oConfig instanceof Config) {
return static::GetLevelDefault();
return static::GetLevelDefault($sConfigKey);
}
$sLogLevelMin = $oConfig->Get($sCode);
return $oConfig->Get($sConfigKey);
}
/**
* @param string|array $sLogLevelMin log config parameter value
* @param string $sChannel
* @param string $sConfigKey config option key
*
* @return string|null null if not defined
*/
protected static function GetMinLogLevelFromChannel($sLogLevelMin, $sChannel, $sConfigKey)
{
if (empty($sLogLevelMin)) {
return static::GetLevelDefault();
return static::GetLevelDefault($sConfigKey);
}
if (!is_array($sLogLevelMin)) {
@@ -719,19 +779,81 @@ abstract class LogAPI
return $sLogLevelMin[$sChannel];
}
return null;
}
protected static function GetMinLogLevelFromDefault($sLogLevelMin, $sChannel, $sConfigKey)
{
if (isset($sLogLevelMin[static::CHANNEL_DEFAULT])) {
return $sLogLevelMin[static::CHANNEL_DEFAULT];
return $sLogLevelMin[static::CHANNEL_DEFAULT];
}
// Even though the *self*::CHANNEL_DEFAULT is set to '' in the current class (LogAPI), the test below is necessary as the CHANNEL_DEFAULT constant can be (and is!) overloaded in derivated classes, don't remove this test to factorize it with the previous one.
// Even though the *self*::CHANNEL_DEFAULT is set to '' in the current class (LogAPI), the test below is necessary as the CHANNEL_DEFAULT constant can be (and is!) overloaded in children classes, don't remove this test to factorize it with the previous one.
if (isset($sLogLevelMin[''])) {
return $sLogLevelMin[''];
return $sLogLevelMin[''];
}
return static::GetLevelDefault();
return static::GetLevelDefault($sConfigKey);
}
protected static function WriteToDb(string $sMessage, string $sChannel, array $aContext): void
{
if (false === MetaModel::IsLogEnabledIssue()) {
return;
}
if (false === MetaModel::IsValidClass('EventIssue')) {
return;
}
// Protect against reentrance
static $bWriteToDbReentrance;
if ($bWriteToDbReentrance === true) {
return;
}
$bWriteToDbReentrance = true;
try {
self::$oLastEventIssue = static::GetEventIssue($sMessage, $sChannel, $aContext);
self::$oLastEventIssue->DBInsertNoReload();
}
catch (Exception $e) {
// calling low level methods : if we would call Error() for example we would try to write to DB again...
static::$m_oFileLog->Error('Failed to log issue into the DB', LogChannels::CORE, [
'exception message' => $e->getMessage(),
'exception stack' => $e->getTraceAsString(),
]);
}
finally {
$bWriteToDbReentrance = false;
}
}
/**
* @throws \CoreException
* @throws \CoreUnexpectedValue
* @throws \OQLException
*/
protected static function GetEventIssue(string $sMessage, string $sChannel, array $aContext): EventIssue
{
$sDate = date('Y-m-d H:i:s');
$aStack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5);
$sCurrentCallStack = var_export($aStack, true);
$oEventIssue = new EventIssue();
$oEventIssue->Set('issue', $sMessage);
$oEventIssue->Set('message', $sMessage);
$oEventIssue->Set('date', $sDate);
$oEventIssue->Set('userinfo', static::GetUserInfo());
$oEventIssue->Set('callstack', $sCurrentCallStack);
$oEventIssue->Set('data', $aContext);
return $oEventIssue;
}
/**
* **Warning** : during \MFCompiler::Compile the config will be partial, so when logging in this method you won't get the proper log config !
* See N°4345
*
* @uses m_oMockMetaModelConfig if defined
* @uses \MetaModel::GetConfig()
*/
@@ -741,16 +863,29 @@ abstract class LogAPI
}
/**
* A method to override if default log level needs to be computed. Otherwise simply override the {@see LEVEL_DEFAULT} constant
* A method to override if default log level needs to be computed. Otherwise, simply override the corresponding constants
*
* @used-by GetMinLogLevel
* @uses \LogAPI::LEVEL_DEFAULT
*
* @since 3.0.0 N°3731
* @param string $sConfigKey config key used for log
*
* @return string|bool if false, then disable log for any level
*
* @uses \LogAPI::LEVEL_DEFAULT
* @uses \LogAPI::LEVEL_DEFAULT_DB
*
* @since 3.0.0 N°3731 Method creation
* @since 3.0.0 N°4261 add specific default level for DB write
*/
protected static function GetLevelDefault(): string
protected static function GetLevelDefault(string $sConfigKey)
{
return static::LEVEL_DEFAULT;
switch ($sConfigKey) {
case static::ENUM_CONFIG_PARAM_DB:
return static::LEVEL_DEFAULT_DB;
case static::ENUM_CONFIG_PARAM_FILE:
default:
return static::LEVEL_DEFAULT;
}
}
}
@@ -765,6 +900,16 @@ class SetupLog extends LogAPI
const LEVEL_DEFAULT = self::LEVEL_INFO;
protected static $m_oFileLog = null;
/**
* In the setup there is no user logged...
*
* @return string|null
*/
public static function GetUserInfo(): ?string
{
return 'SETUP';
}
}
class IssueLog extends LogAPI
@@ -803,6 +948,7 @@ class DeadLockLog extends LogAPI
parent::Enable($sTargetFile);
}
/** @noinspection PhpUnreachableStatementInspection we want to keep the break statements to keep clarity and avoid errors */
private static function GetChannelFromMysqlErrorNo($iMysqlErrorNo)
{
switch ($iMysqlErrorNo)
@@ -962,13 +1108,25 @@ class DeprecatedCallsLog extends LogAPI
return true;
}
protected static function GetLevelDefault(): string
/**
* Override so that :
* - if we are in dev mode ({@see \utils::IsDevelopmentEnvironment()}), the level for file will be DEBUG
* - else call parent method
*
* In other words, when in dev mode all deprecated calls will be logged to file
*
*/
protected static function GetLevelDefault(string $sConfigKey)
{
if ($sConfigKey === self::ENUM_CONFIG_PARAM_DB) {
return parent::GetLevelDefault($sConfigKey);
}
if (utils::IsDevelopmentEnvironment()) {
return static::LEVEL_DEBUG;
}
return static::LEVEL_DEFAULT;
return parent::GetLevelDefault($sConfigKey);
}
/**
@@ -1130,14 +1288,12 @@ class LogFileRotationProcess implements iScheduledProcess
*
* Please use {@see ExceptionLog::LogException()} to log exceptions
*
* @since 3.0.0
* @since 3.0.0 N°4261 class creation to ease logging when an exception occurs
*/
class ExceptionLog extends LogAPI
{
const CHANNEL_DEFAULT = 'Exception';
const CONTEXT_EXCEPTION = '__exception';
private static $oLastEventIssue = null;
public const CHANNEL_DEFAULT = 'Exception';
public const CONTEXT_EXCEPTION = '__exception';
protected static $m_oFileLog = null;
@@ -1145,90 +1301,141 @@ class ExceptionLog extends LogAPI
* This method should be used to write logs.
*
* As it encapsulate the operations performed using the Exception, you should prefer it to the standard API inherited from LogApi `ExceptionLog::Error($oException->getMessage(), get_class($oException), ['__exception' => $oException]);`
* The parameter order is not standard, but in our use case, the resulting API is way more convenient this way.
* The parameter order is not standard, but in our use case, the resulting API is way more convenient this way !
*/
public static function LogException(Exception $oException, $aContext = array(), $sLevel = self::LEVEL_WARNING)
public static function LogException(Throwable $oException, $aContext = array(), $sLevel = self::LEVEL_ERROR): void
{
if (empty($aContext[self::CONTEXT_EXCEPTION])) {
$aContext[self::CONTEXT_EXCEPTION] = $oException;
}
if (empty($aContext['exception class'])) {
$aContext['exception class'] = get_class($oException);
}
if (empty($aContext['file'])) {
$aContext['file'] = $oException->getFile();
}
if (empty($aContext['line'])) {
$aContext['line'] =$oException->getLine();
}
self::Log($sLevel, $oException->getMessage(), get_class($oException), $aContext);
}
/**
* @inheritDoc
* @throws \ConfigException if log wrongly configured
*/
public static function Log($sLevel, $sMessage, $sClass = null, $aContext = array())
{
if (!static::$m_oFileLog) {
return;
}
if (!isset(self::$aLevelsPriority[$sLevel])) {
IssueLog::Error("invalid log level '{$sLevel}'");
return;
}
$sExceptionClass = get_class($oException);
$sChannel = self::FindClassChannel($sClass);
if (static::IsLogLevelEnabled($sLevel, $sChannel)) {
static::$m_oFileLog->$sLevel($sMessage, $sChannel, array_diff_key($aContext, [self::CONTEXT_EXCEPTION => null])); //The exception should not be included in the error.log because of its verbosity.
}
$sDbChannel = self::FindClassChannel($sClass, 'log_level_min.write_in_db');
if (static::IsLogLevelEnabled($sLevel, $sDbChannel, 'log_level_min.write_in_db')) {
self::WriteToDb($aContext);
}
$aDefaultValues = [
self::CONTEXT_EXCEPTION => $oException,
'exception class' => $sExceptionClass,
'file' => $oException->getFile(),
'line' => $oException->getLine(),
];
$aContext = array_merge($aDefaultValues, $aContext);
parent::Log($sLevel, $oException->getMessage(), $sExceptionClass, $aContext);
}
protected static function FindClassChannel($sClass, $sCode = 'log_level_min')
/** @noinspection PhpUnhandledExceptionInspection */
public static function Log($sLevel, $sMessage, $sChannel = null, $aContext = array())
{
$oConfig = static::GetConfig();
if (!$oConfig instanceof Config) {
return static::GetLevelDefault();
throw new ApplicationException('Do not call this directly, prefer using ExceptionLog::LogException() instead');
}
/** @noinspection PhpParameterNameChangedDuringInheritanceInspection */
protected static function WriteLog(string $sLevel, string $sMessage, ?string $sExceptionClass = null, ?array $aContext = array()): void
{
if (
(null !== static::$m_oFileLog)
&& static::IsLogLevelEnabled($sLevel, $sExceptionClass, static::ENUM_CONFIG_PARAM_FILE)
) {
$sExceptionClassConfiguredForFile = static::ExceptionClassFromHierarchy($sExceptionClass, static::ENUM_CONFIG_PARAM_FILE);
if (null === $sExceptionClassConfiguredForFile) {
$sExceptionClassConfiguredForFile = $sExceptionClass;
}
// clearing the Exception object as it is too verbose to write to a file !
$aContextForFile = array_diff_key($aContext, [self::CONTEXT_EXCEPTION => null]);
static::$m_oFileLog->$sLevel($sMessage, $sExceptionClassConfiguredForFile, $aContextForFile);
}
$sLogLevelMin = $oConfig->Get($sCode);
if (static::IsLogLevelEnabled($sLevel, $sExceptionClass, static::ENUM_CONFIG_PARAM_DB)) {
$sExceptionClassConfiguredForDb = static::ExceptionClassFromHierarchy($sExceptionClass, static::ENUM_CONFIG_PARAM_DB);
if (null === $sExceptionClassConfiguredForDb) {
$sExceptionClassConfiguredForDb = $sExceptionClass;
}
self::WriteToDb($sMessage, $sExceptionClassConfiguredForDb, $aContext);
}
}
if (empty($sLogLevelMin)) {
return $sClass;
/**
* Will seek for the configuration based on the exception class, using {@see \ExceptionLog::ExceptionClassFromHierarchy()}
*
* @param string $sExceptionClass
* @param string $sConfigKey
*
* @return string
* @noinspection PhpParameterNameChangedDuringInheritanceInspection
*/
protected static function GetMinLogLevel($sExceptionClass, $sConfigKey = self::ENUM_CONFIG_PARAM_FILE)
{
$sLogLevelMin = static::GetLogConfig($sConfigKey);
$sExceptionClassInConfig = static::ExceptionClassFromHierarchy($sExceptionClass, $sConfigKey);
if (null !== $sExceptionClassInConfig) {
return $sConfigKey[$sExceptionClassInConfig];
}
if (!is_array($sLogLevelMin)) {
return $sClass;
return static::GetMinLogLevelFromDefault($sLogLevelMin, $sExceptionClass, $sConfigKey);
}
/**
* Searching config first for the current exception class
* If not found we are seeking for config for all the parent classes
*
* That means if we are logging a UnknownClassOqlException, we will seek log config all the way the class hierarchy :
* 1. UnknownClassOqlException
* 2. OqlNormalizeException
* 3. OQLException
* 4. CoreException
* 5. Exception
*
* @param string $sExceptionClass
* @param string $sConfigKey
*
* @return string|null the current or parent class name defined in the config, otherwise null if no class of the hierarchy found in the config
*/
protected static function ExceptionClassFromHierarchy($sExceptionClass, $sConfigKey = self::ENUM_CONFIG_PARAM_FILE)
{
$sLogLevelMin = static::GetLogConfig($sConfigKey);
if (false === is_array($sLogLevelMin)) {
return null;
}
$sParentClass = $sClass;
while (
(!isset($sLogLevelMin[$sParentClass]))
&&
($sParentClass !== false)
)
{
$sParentClass = get_parent_class($sParentClass);
$sExceptionClassInHierarchy = $sExceptionClass;
while ($sExceptionClassInHierarchy !== false) {
$sConfiguredLevelForExceptionClass = static::GetMinLogLevelFromChannel($sLogLevelMin, $sExceptionClassInHierarchy, $sConfigKey);
if (!is_null($sConfiguredLevelForExceptionClass)) {
break;
}
$sExceptionClassInHierarchy = get_parent_class($sExceptionClassInHierarchy);
}
if (isset($sLogLevelMin[$sParentClass])) {
return $sParentClass;
if ($sExceptionClassInHierarchy === false) {
return null;
}
return $sClass;
return $sExceptionClassInHierarchy;
}
protected static function GetEventIssue(string $sMessage, string $sChannel, array $aContext): EventIssue
{
$oEventIssue = parent::GetEventIssue($sMessage, $sChannel, $aContext);
$oContextException = $aContext[self::CONTEXT_EXCEPTION];
unset($aContext[self::CONTEXT_EXCEPTION]);
$sIssue = ($oContextException instanceof CoreException) ? $oContextException->GetIssue() : 'PHP Exception';
$sErrorStackTrace = ($oContextException instanceof CoreException) ? $oContextException->getFullStackTraceAsString() : $oContextException->getTraceAsString();
$aContextData = ($oContextException instanceof CoreException) ? $oContextException->getContextData() : [];
$oEventIssue->Set('issue', $sIssue);
$oEventIssue->Set('message', $oContextException->getMessage());
$oEventIssue->Set('callstack', $sErrorStackTrace);
$oEventIssue->Set('data', array_merge($aContextData, $aContext));
return $oEventIssue;
}
/**
@@ -1236,43 +1443,12 @@ class ExceptionLog extends LogAPI
*/
public static function Enable($sTargetFile = null)
{
if (empty($sTargetFile))
{
if (empty($sTargetFile)) {
$sTargetFile = APPROOT.'log/error.log';
}
parent::Enable($sTargetFile);
}
private static function WriteToDb(array $aContext): void
{
$oContextException = $aContext[self::CONTEXT_EXCEPTION];
unset($aContext[self::CONTEXT_EXCEPTION]);
if (MetaModel::IsLogEnabledIssue()) {
if (MetaModel::IsValidClass('EventIssue')) {
try {
self::$oLastEventIssue = new EventIssue();
$sIssue = ($oContextException instanceof CoreException) ? $oContextException->GetIssue() : 'PHP Exception';
$sErrorStackTrace = ($oContextException instanceof CoreException) ? $oContextException->getFullStackTraceAsString() : $oContextException->getTraceAsString();
$aContextData = ($oContextException instanceof CoreException) ? $oContextException->getContextData() : [];
self::$oLastEventIssue->Set('message', $oContextException->getMessage());
self::$oLastEventIssue->Set('userinfo', '');
self::$oLastEventIssue->Set('issue', $sIssue);
self::$oLastEventIssue->Set('impact', '');
self::$oLastEventIssue->Set('callstack', $sErrorStackTrace);
self::$oLastEventIssue->Set('data', array_merge($aContextData, $aContext));
self::$oLastEventIssue->DBInsertNoReload();
}
catch (Exception $e) {
IssueLog::Error("Failed to log issue into the DB");
}
}
}
}
/**
* @internal Used by the tests
*/

View File

@@ -3083,8 +3083,7 @@ abstract class MetaModel
// Set attribute code
self::$m_aClassParams[$sPHPClass]['state_attcode'] = self::$m_aClassParams[$sParent]['state_attcode'];
// Set states
self::$m_aStates[$sPHPClass] = self::$m_aStates[$sParent];
// Note: Don't set self::$m_aStates[$sPHPClass], it has already been done by self::Init_DefineState()
}
// - Image attribute
$bParentHasImageAttribute = (isset(self::$m_aClassParams[$sParent]['image_attcode']) && !empty(self::$m_aClassParams[$sParent]['image_attcode']));
@@ -4132,19 +4131,26 @@ abstract class MetaModel
/**
* @param string $sClass
* @param int $iOption one of ENUM_CHILD_CLASSES_EXCLUDETOP, ENUM_CHILD_CLASSES_ALL
* @param bool $bRootFirst Only when $iOption NOT set to ENUM_CHILD_CLASSES_EXCLUDETOP. If true, the $sClass will be the first element of the returned array, otherwise it will be the last (legacy behavior)
*
* @return array
* @throws \CoreException
* @since 3.0.0 Added $bRootFirst param.
*/
public static function EnumChildClasses($sClass, $iOption = ENUM_CHILD_CLASSES_EXCLUDETOP)
public static function EnumChildClasses($sClass, $iOption = ENUM_CHILD_CLASSES_EXCLUDETOP, $bRootFirst = false)
{
self::_check_subclass($sClass);
$aRes = self::$m_aChildClasses[$sClass];
if ($iOption != ENUM_CHILD_CLASSES_EXCLUDETOP)
{
// Add it to the list
$aRes[] = $sClass;
if ($bRootFirst) {
// Root class on top
array_unshift($aRes, $sClass);
} else {
// Root class at the end, legacy behavior
$aRes[] = $sClass;
}
}
return $aRes;
@@ -4531,15 +4537,15 @@ abstract class MetaModel
/**
* Check (and updates if needed) the hierarchical keys
*
* @param boolean $bDiagnosticsOnly If true only a diagnostic pass will be run, returning true or false
* @param boolean $bVerbose Displays some information about what is done/what needs to be done
* @param boolean $bForceComputation If true, the _left and _right parameters will be recomputed even if some
* @param bool $bDiagnosticsOnly If true only a diagnostic pass will be run, returning true or false
* @param bool $bVerbose Displays some information about what is done/what needs to be done
* @param bool $bForceComputation If true, the _left and _right parameters will be recomputed even if some
* values already exist in the DB
*
* @return bool
* @throws \CoreException
* @throws \Exception
*/
public static function CheckHKeys($bDiagnosticsOnly = false, $bVerbose = false, $bForceComputation = false)
public static function CheckHKeys(bool $bDiagnosticsOnly = false, bool $bVerbose = false, bool $bForceComputation = false)
{
$bChangeNeeded = false;
foreach(self::GetClasses() as $sClass)
@@ -5964,7 +5970,8 @@ abstract class MetaModel
/**
* @deprecated 2.7.0 N°2369 will be removed in 2.8
* @deprecated 2.7.0 N°2369 Method will not be removed any time soon as we still need to drop view if the instance is migrating from an iTop 2.x to an iTop 3.0 or newer, even if they skip iTop 3.0.
* @since 3.0.0 Does not recreate SQL views, only drops them. Method has not been renamed to avoid regressions
*
* @return array
* @throws \CoreException
@@ -7457,9 +7464,11 @@ abstract class MetaModel
* @param string $sInput
* @param array $aParams
*
* @return mixed
* @return string
*
* @throws \Exception
*/
static public function ApplyParams($sInput, $aParams)
public static function ApplyParams($sInput, $aParams)
{
$aParams = static::AddMagicPlaceholders($aParams);
@@ -7469,7 +7478,7 @@ abstract class MetaModel
$aSearches = array();
$aReplacements = array();
foreach($aParams as $sSearch => $replace)
foreach ($aParams as $sSearch => $replace)
{
// Some environment parameters are objects, we just need scalars
if (is_object($replace))

View File

@@ -41,6 +41,7 @@ abstract class Trigger extends cmdbAbstractObject
"db_table" => "priv_trigger",
"db_key_field" => "id",
"db_finalclass_field" => "realclass",
'style' => new ormStyle(null, null, null, null, null, '../images/icons/icons8-conflict.svg'),
);
MetaModel::Init_Params($aParams);
//MetaModel::Init_InheritAttributes();
@@ -238,7 +239,8 @@ abstract class TriggerOnObject extends Trigger
* @param $iObjectId
* @param array $aChanges
*
* @return bool
* @return bool True if the object of ID $iObjectId is within the scope of the OQL defined by the "filter" attribute
*
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
@@ -252,6 +254,7 @@ abstract class TriggerOnObject extends Trigger
{
$oSearch = DBObjectSearch::FromOQL($sFilter);
$oSearch->AddCondition('id', $iObjectId, '=');
$oSearch->AllowAllData();
$oSet = new DBObjectSet($oSearch);
$bRet = ($oSet->Count() > 0);
}
@@ -582,13 +585,56 @@ class TriggerOnObjectMention extends TriggerOnObject
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeOQL("mentioned_filter", array("allowed_values" => null, "sql" => "mentioned_filter", "default_value" => null, "is_null_allowed" => true, "depends_on" => array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('description', 'context', 'target_class', 'filter', 'mentioned_filter', 'action_list')); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('description', 'target_class')); // Criteria of the std search form
}
/**
* @param \DBObject $oObject
*
* @return bool True if $oObject is within the scope of the OQL defined by the "mentioned_filter" attribute OR if no mentioned_filter defined. Otherwise, returns false.
*
* @throws \ArchivedObjectException
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
* @throws \OQLException
*/
public function IsMentionedObjectInScope(DBObject $oObject)
{
$sFilter = trim($this->Get('mentioned_filter'));
if (strlen($sFilter) > 0)
{
$oSearch = DBObjectSearch::FromOQL($sFilter);
$sSearchClass = $oSearch->GetClass();
// If filter not on current object class (or descendants), consider it as not in scope
if (is_a($oObject, $sSearchClass, true) === false) {
return false;
}
$oSearch->AddCondition('id', $oObject->GetKey(), '=');
if (MetaModel::IsAbstract($oSearch->GetClass())) {
$oSearch->AddCondition('finalclass', get_class($oObject), '=');
}
$aParams = $oObject->ToArgs('this');
$oSet = new DBObjectSet($oSearch, [], $aParams);
$bRet = $oSet->CountExceeds(0);
}
else
{
$bRet = true;
}
return $bRet;
}
}
/**

View File

@@ -638,7 +638,7 @@ abstract class UserInternal extends User
{
$aParams = array
(
"category" => "core,grant_by_profile",
"category" => "core,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",
@@ -1437,6 +1437,21 @@ class UserRights
return self::$m_oRealUser;
}
/**
* @return int|string ID of the connected user : if impersonate then use {@see m_oRealUser}, else {@see m_oUser}. If no user set then return ''
* @since 2.6.5 2.7.6 3.0.0 N°4289 method creation
*/
public static function GetConnectedUserId() {
if (false === is_null(static::$m_oRealUser)) {
return static::$m_oRealUser->GetKey();
}
if (false === is_null(static::$m_oUser)) {
return static::$m_oUser->GetKey();
}
return '';
}
/**
* @return string
*/
@@ -1492,7 +1507,7 @@ class UserRights
try
{
// Check Bug 1436 for details
if (MetaModel::HasCategory($sClass, 'bizmodel') || MetaModel::HasCategory($sClass, 'silo'))
if (MetaModel::HasCategory($sClass, 'bizmodel') || MetaModel::HasCategory($sClass, 'silo') || MetaModel::HasCategory($sClass, 'filter'))
{
return self::$m_oAddOn->GetSelectFilter(self::$m_oUser, $sClass, $aSettings);
}
@@ -1844,15 +1859,15 @@ class UserRights
{
self::$m_aCacheUsers = array('internal' => array(), 'external' => array());
}
if (!array_key_exists($sLogin, self::$m_aCacheUsers[$sAuthentication]))
if (!isset(self::$m_aCacheUsers[$sAuthentication][$sLogin]))
{
switch($sAuthentication)
{
case 'external':
$sBaseClass = 'UserExternal';
break;
case 'internal':
$sBaseClass = 'UserInternal';
break;
@@ -1862,6 +1877,7 @@ class UserRights
assert(false); // should never happen
}
$oSearch = DBObjectSearch::FromOQL("SELECT $sBaseClass WHERE login = :login");
$oSearch->AllowAllData();
if (!$bAllowDisabledUsers)
{
$oSearch->AddCondition('status', 'enabled');

View File

@@ -22,4 +22,8 @@ fieldset {
legend {
@extend .ibo-fieldset-legend;
}
textarea {
@extend .ibo-input-text;
}

View File

@@ -11,9 +11,9 @@ $ibo-scrollbar--scrollbar-thumb-background-color: $ibo-color-grey-300 !default;
$ibo-scrollbar--scrollbar-thumb-border: none !default;
$ibo-scrollbar--scrollbar-thumb-border-radius: $ibo-border-radius-500 !default;
$ibo-hyperlink-color: $ibo-color-primary-500 !default;
$ibo-hyperlink-color--on-hover: $ibo-color-primary-600 !default;
$ibo-hyperlink-color--on-active: $ibo-color-primary-700 !default;
$ibo-hyperlink-color: $ibo-color-primary-700 !default;
$ibo-hyperlink-color--on-hover: $ibo-color-primary-800 !default;
$ibo-hyperlink-color--on-active: $ibo-color-primary-900 !default;
$ibo-svg-illustration--fill: $ibo-color-primary-500 !default;
@@ -86,4 +86,11 @@ a{
&:active{
color: var(--ibo-hyperlink-color--on-active);
}
}
/* Input reset */
/* - Standard spacing between label and input */
select + label, label + select, label > select,
input + label, label + input, label > input {
margin-left: 0.5rem;
}

View File

@@ -6,8 +6,8 @@
@import "dashlet-within-dashboard";
@import "add-to-dashboard";
@import "caselog-entry-form-within-activity-panel";
@import "panel-with-datatable";
@import "panel-with-tab-container";
@import "datatable-within-panel";
@import "tab-container-within-panel";
@import "panel-within-main-content";
@import "panel-within-modal";
@import "object-details-with-tab-container";

View File

@@ -4,15 +4,21 @@
*/
$ibo-input-within-datatable--attribute-set-item--padding-x: $ibo-input-set--item--padding-x !default;
$ibo-input-within-datatable--attribute-set-item--padding-y: $ibo-input-set--item--padding-y !default;
$ibo-input-within-datatable--attribute-set-item--box-shadow: $ibo-elevation-100 !default;
$ibo-input-within-datatable--attribute-set-item--siblings-spacing: 0.5rem !default;
.ibo-datatable {
.attribute-set {
.attribute-set-item {
display: inline;
margin: 0;
padding: 0 $ibo-input-within-datatable--attribute-set-item--padding-x;
padding: $ibo-input-within-datatable--attribute-set-item--padding-y $ibo-input-within-datatable--attribute-set-item--padding-x;
box-shadow: $ibo-input-within-datatable--attribute-set-item--box-shadow;
+ .attribute-set-item {
margin-left: $ibo-input-within-datatable--attribute-set-item--siblings-spacing;
}
}
}
}

View File

@@ -49,7 +49,10 @@ $ibo-alert-colors: (
.ibo-alert.ibo-is-#{$sColor} {
background-color: $bg-color;
color: $text-color;
a {
color: $text-color;
@extend %ibo-font-ral-bol-150;
}
&::before {
background-color: $highlight-color;
}

View File

@@ -54,22 +54,22 @@ $ibo-button-colors: (
/* Primary action does not have the colors from the primary brand color, at least not for now */
'primary': (
'': (
$ibo-color-cyan-700,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-cyan-900,
$ibo-color-cyan-800,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-cyan-950,
$ibo-color-cyan-900,
),
':hover': (
$ibo-color-cyan-800,
$ibo-color-cyan-900,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-cyan-900,
$ibo-color-cyan-800,
$ibo-button--box-shadow-bottom $ibo-color-cyan-950,
$ibo-color-cyan-900,
),
':active': (
$ibo-color-cyan-800,
$ibo-color-cyan-900,
$ibo-color-white-100,
$ibo-button--box-shadow-top $ibo-color-cyan-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-cyan-800,
$ibo-color-cyan-800,
$ibo-button--box-shadow-top $ibo-color-cyan-950 #{','} $ibo-button--box-shadow-bottom $ibo-color-cyan-900,
$ibo-color-cyan-900,
),
':disabled': (
$ibo-color-grey-300,
@@ -106,22 +106,22 @@ $ibo-button-colors: (
),
'danger': (
'': (
$ibo-color-danger-600,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-danger-800,
$ibo-color-danger-700,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-danger-900,
$ibo-color-danger-800,
),
':hover': (
$ibo-color-danger-700,
$ibo-color-danger-800,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-danger-800,
$ibo-color-danger-700,
$ibo-button--box-shadow-bottom $ibo-color-danger-900,
$ibo-color-danger-800,
),
':active': (
$ibo-color-danger-700,
$ibo-color-danger-800,
$ibo-color-white-100,
$ibo-button--box-shadow-top $ibo-color-danger-800 #{','} $ibo-button--box-shadow-bottom $ibo-color-danger-700,
$ibo-color-danger-700,
$ibo-button--box-shadow-top $ibo-color-danger-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-danger-800,
$ibo-color-danger-800,
),
':disabled': (
$ibo-color-grey-300,
@@ -132,22 +132,22 @@ $ibo-button-colors: (
),
'success': (
'': (
$ibo-color-success-700,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-success-900,
$ibo-color-success-800,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-success-950,
$ibo-color-success-900,
),
':hover': (
$ibo-color-success-800,
$ibo-color-success-900,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-success-900,
$ibo-color-success-800,
$ibo-button--box-shadow-bottom $ibo-color-success-950,
$ibo-color-success-900,
),
':active': (
$ibo-color-success-800,
$ibo-color-success-900,
$ibo-color-white-100,
$ibo-button--box-shadow-top $ibo-color-success-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-success-800,
$ibo-color-success-800,
$ibo-button--box-shadow-top $ibo-color-success-950 #{','} $ibo-button--box-shadow-bottom $ibo-color-success-900,
$ibo-color-success-900,
),
':disabled': (
$ibo-color-grey-300,
@@ -159,22 +159,22 @@ $ibo-button-colors: (
/* Colors */
'red': (
'': (
$ibo-color-red-600,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-red-800,
$ibo-color-red-700,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-red-900,
$ibo-color-red-800,
),
':hover': (
$ibo-color-red-700,
$ibo-color-red-800,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-red-800,
$ibo-color-red-700,
$ibo-button--box-shadow-bottom $ibo-color-red-900,
$ibo-color-red-800,
),
':active': (
$ibo-color-red-700,
$ibo-color-red-800,
$ibo-color-white-100,
$ibo-button--box-shadow-top $ibo-color-red-800 #{','} $ibo-button--box-shadow-bottom $ibo-color-red-700,
$ibo-color-red-700,
$ibo-button--box-shadow-top $ibo-color-red-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-red-800,
$ibo-color-red-800,
),
':disabled': (
$ibo-color-grey-300,
@@ -185,22 +185,22 @@ $ibo-button-colors: (
),
'green': (
'': (
$ibo-color-green-700,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-green-900,
$ibo-color-green-800,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-green-950,
$ibo-color-green-900,
),
':hover': (
$ibo-color-green-800,
$ibo-color-green-900,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-green-900,
$ibo-color-green-800,
$ibo-button--box-shadow-bottom $ibo-color-green-950,
$ibo-color-green-900,
),
':active': (
$ibo-color-green-800,
$ibo-color-green-900,
$ibo-color-white-100,
$ibo-button--box-shadow-top $ibo-color-green-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-green-800,
$ibo-color-green-800,
$ibo-button--box-shadow-top $ibo-color-green-950 #{','} $ibo-button--box-shadow-bottom $ibo-color-green-900,
$ibo-color-green-900,
),
':disabled': (
$ibo-color-grey-300,
@@ -211,22 +211,22 @@ $ibo-button-colors: (
),
'cyan': (
'': (
$ibo-color-cyan-500,
$ibo-color-cyan-800,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-cyan-900,
$ibo-button--box-shadow-bottom $ibo-color-cyan-950,
$ibo-color-cyan-900,
),
':hover': (
$ibo-color-cyan-700,
$ibo-color-cyan-900,
$ibo-color-white-100,
$ibo-button--box-shadow-bottom $ibo-color-cyan-900,
$ibo-button--box-shadow-bottom $ibo-color-cyan-950,
$ibo-color-cyan-900,
),
':active': (
$ibo-color-cyan-700,
$ibo-color-cyan-900,
$ibo-color-white-100,
$ibo-button--box-shadow-top $ibo-color-cyan-900 #{','} $ibo-button--box-shadow-bottom $ibo-color-cyan-700,
$ibo-color-cyan-700,
$ibo-button--box-shadow-top $ibo-color-cyan-950 #{','} $ibo-button--box-shadow-bottom $ibo-color-cyan-900,
$ibo-color-cyan-900,
),
':disabled': (
$ibo-color-grey-300,
@@ -268,9 +268,9 @@ $ibo-button-colors: (
'primary': (
'': (
transparent,
$ibo-color-cyan-800,
$ibo-color-cyan-900,
$ibo-button--box-shadow-bottom transparent,
$ibo-color-cyan-700,
$ibo-color-cyan-800,
),
':hover': (
$ibo-color-white-100,

View File

@@ -5,7 +5,7 @@
$ibo-datatable--toolbar--padding-x: 6px !default;
$ibo-datatable--toolbar--padding-y: 0 !default;
$ibo-datatable--toolbar--text-color: $ibo-color-grey-700 !default;
$ibo-datatable--toolbar--text-color: $ibo-color-grey-800 !default;
$ibo-datatable--toolbar--elements-spacing: 1rem !default;
$ibo-datatable--toolbar--table-spacing: 18px !default;
@@ -14,6 +14,9 @@ $ibo-datatable-header--text-color: $ibo-base-variable--text-color !default;
$ibo-datatable-panel--table-spacing: 48px !default;
$ibo-datatable-panel--body--padding: $ibo-panel--body--padding-top 0px $ibo-panel--body--padding-bottom !default;
$ibo-datatable--row--background-color--is-hover: $ibo-color-primary-200 !default;
$ibo-datatable--row--background-color--is-selected: $ibo-color-primary-300 !default;
$ibo-datatable--selection-validation-buttons-toolbar--margin-top: 10px !default;
$ibo-list-column--max-height: 150px !default;
@@ -91,6 +94,7 @@ $ibo-fieldsorter--selected--background-color: $ibo-color-blue-200 !default;
&.ibo-is-ascending::after{
content: '\f0de';
}
&.ibo-is-none::after{
content: '\f0dc';
}
@@ -100,4 +104,20 @@ $ibo-fieldsorter--selected--background-color: $ibo-color-blue-200 !default;
>.selected{
background-color: $ibo-fieldsorter--selected--background-color;
}
}
.ibo-datatable {
tbody > tr {
transition: background-color 300ms linear;
&:hover, &.selected:hover {
cursor: pointer;
background-color: $ibo-datatable--row--background-color--is-hover;
}
&.selected {
background-color: $ibo-datatable--row--background-color--is-selected;
}
}
}

View File

@@ -5,7 +5,7 @@
/* SCSS variables */
$ibo-field--sibling-spacing: 16px !default;
$ibo-field--value--color: $ibo-color-grey-700 !default;
$ibo-field--value--color: $ibo-color-grey-800 !default;
$ibo-field--label--description--content: "?" !default;
$ibo-field--label--description--padding-left: 4px !default;
@@ -23,7 +23,11 @@ $ibo-field--fullscreen-toggler--size: 20px !default;
$ibo-field--fullscreen-toggler--border-radius: $ibo-border-radius-500 !default;
$ibo-field--fullscreen-toggler--background-color--on-hover: $ibo-color-white-200 !default;
$ibo-field--comments--size: 5em !default;
$ibo-field--comments--font-size: $ibo-font-size-150 !default;
$ibo-field--value--scrollbar-track-background-color: $ibo-color-white-200 !default;
$ibo-field--value--margin-top--for-large: 2px !default;
$ibo-field--value--padding-x--is-fullscreen: $ibo-field--label--padding-x--is-fullscreen !default;
$ibo-field--value--padding-top--is-fullscreen: $ibo-field--label--padding-y--is-fullscreen + 32px !default;
$ibo-field--value--padding-bottom--is-fullscreen: $ibo-field--label--padding-y--is-fullscreen !default;
@@ -44,19 +48,24 @@ $ibo-field--enable-bulk--checkbox--margin-left: 8px !default;
/* Avoid value to overflow from its container with very long strings (typically URLs) */
/* Note: Some types of attribute must be excluding as it can alter their rendering */
&:not([data-attribute-type="AttributeBlob"]):not([data-attribute-type="AttributeFile"]):not([data-attribute-type="AttributeImage"]):not([data-attribute-type="AttributeCustomFields"]):not(.ibo-input-file-select--container) {
/* We need the rule to apply for the class and all its descendants, hence the "&, & *" */
&:not(
[data-attribute-type="AttributeBlob"],
[data-attribute-type="AttributeFile"],
[data-attribute-type="AttributeImage"],
[data-attribute-type="AttributeCustomFields"],
[data-attribute-type="AttributeTagSet"],
[data-attribute-type="AttributeEnumSet"],
[data-attribute-type="AttributeClassAttCodeSet"],
[data-attribute-type="AttributeQueryAttCodeSet"],
.ibo-input-file-select--container
) {
/* We need the rule to apply for the class and all its descendants, hence the "& *" */
.ibo-field--value {
&, & * {
word-break: break-word;
white-space: inherit; /* Here we don't put break-spaces as it would put ".ibo-field-small .ibo-field-value" on a new line due to the spaces/indentation of the HTML templates. For now we rather have this rule than diminish the templates readability/maintenability */
& * {
word-break: break-word;
white-space: inherit; /* Here we don't put break-spaces as it would put ".ibo-field-small .ibo-field-value" on a new line due to the spaces/indentation of the HTML templates. For now we rather have this rule than diminish the templates readability/maintenability */
}
}
&.ibo-field-large {
.ibo-field--value {
&, & * {
white-space: break-spaces; /* For large fields we don't have the issue stated above */
}
white-space: inherit;
}
}
}
@@ -88,6 +97,10 @@ $ibo-field--enable-bulk--checkbox--margin-left: 8px !default;
width: 30%;
}
.ibo-field--value {
margin-top: $ibo-field--value--margin-top--for-large; /* Mostly used to have a clear separation from elements in .ibo-field--comments */
}
/* N°4318 - Visible scrollbar background for large fields overflowing to ease "limits" visualization by the user */
.ibo-field--value > * {
--ibo-scrollbar--scrollbar-track-background-color: $ibo-field--value--scrollbar-track-background-color;
@@ -109,6 +122,11 @@ $ibo-field--enable-bulk--checkbox--margin-left: 8px !default;
.ibo-field--value {
padding: $ibo-field--value--padding-top--is-fullscreen $ibo-field--value--padding-x--is-fullscreen $ibo-field--value--padding-bottom--is-fullscreen $ibo-field--value--padding-x--is-fullscreen;
> * {
height: initial !important; /* !important is necessary to overload the inline style put by the lib */
width: initial !important;
}
}
}
}
@@ -144,6 +162,7 @@ $ibo-field--enable-bulk--checkbox--margin-left: 8px !default;
min-width: 100px;
max-width: 145px;
width: 30%;
word-break: break-word; /* We want labels to wrap if it is very long if it has no spaces */
@extend %ibo-font-weight-600;
> .ibo-has-description {
@@ -162,29 +181,42 @@ $ibo-field--enable-bulk--checkbox--margin-left: 8px !default;
width: 20em;
}
.ibo-field--value {
display: table;
width: 100%;
color: $ibo-field--value--color;
/* Hack to force a table to fit its container without overflow (see N°2127) */
/* Note that along with with, we now display a "expand" icon on large fields so we can have a better view of its content */
.HTML {
@extend .ibo-vendors-ckeditor--display-content;
table {
table-layout: fixed;
width: 100%;
}
}
}
.ibo-field--label > .ibo-field--comments {
flex: auto;
}
/* For log fields in the bulk operation screens */
.ibo-fieldset-legend > .ibo-field--comments {
padding-bottom: $ibo-field--value--margin-top--for-large;
font-size: $ibo-field--comments--font-size;
}
.ibo-field--comments {
display: table-cell;
vertical-align: top;
width: $ibo-field--comments--size; /* Ensure that inputs have all the same width (this is due to the of display table / table-cell) */
> input[type="checkbox"] {
margin-left: 5px;
float: right;
}
> .multi_values, > .mono_value, > .ibo-field--comments--synchro{
> .multi_values, > .mono_value, > .ibo-field--comments--synchro {
float: right;
}
}
@@ -223,6 +255,6 @@ $ibo-field--enable-bulk--checkbox--margin-left: 8px !default;
margin-left: $ibo-field--enable-bulk--checkbox--margin-left;
}
.form_field ~ .form_field {
.form_field ~ .form_field {
margin-top: $ibo-field--sibling-spacing;
}

View File

@@ -18,6 +18,14 @@ $ibo-fieldset--legend--border-bottom-style: solid !default;
}
}
.ibo-column > .ibo-fieldset {
// display: contents;
}
.ibo-multi-column ~ .ibo-fieldset {
margin-top: $ibo-fieldset--sibling-spacing;
}
.ibo-fieldset-legend {
width: $ibo-fieldset--legend--width;
margin-bottom: $ibo-fieldset--legend--margin-bottom;

View File

@@ -8,6 +8,10 @@ $ibo-global-search--head--background-color: $ibo-color-white-100 !default;
$ibo-global-search--icon-padding-x: 16px !default;
$ibo-global-search--icon-padding-y: 0 !default;
$ibo-global-search--icon--color: $ibo-color-primary-600 !default;
$ibo-global-search--icon--color--on-hover: $ibo-color-primary-700 !default;
$ibo-global-search--icon--color--on-active: $ibo-color-primary-800 !default;
$ibo-global-search--input--padding: 0 !default;
$ibo-global-search--input--padding-x--is-opened: 8px !default;
@@ -20,13 +24,13 @@ $ibo-global-search--input--placeholder-color: $ibo-color-grey-600 !default;
$ibo-global-search--drawer--max-height: 300px !default;
$ibo-global-search--drawer--padding-x: $ibo-global-search--icon-padding-x !default;
$ibo-global-search--drawer--padding-y: 16px !default;
$ibo-global-search--drawer--top: -1 * ($ibo-global-search--drawer--max-height) !default;
$ibo-global-search--drawer--top: -1 * ($ibo-global-search--drawer--max-height) - 10 !default; /* 10px of margin to avoid to be slightly visible when closed and has a lot of history */
$ibo-global-search--drawer--top--is-opened: 100% !default;
$ibo-global-search--drawer--background-color: $ibo-color-white-100 !default;
$ibo-global-search--compartment-title--margin-bottom: 8px !default;
$ibo-global-search--compartment-title--padding-left: 32px !default;
$ibo-global-search--compartment-title--text-color: $ibo-color-grey-600 !default;
$ibo-global-search--compartment-title--text-color: $ibo-color-grey-800 !default;
$ibo-global-search--compartment-title--line-spacing: 8px !default;
$ibo-global-search--compartment-content--text-color: $ibo-color-grey-900 !default;
@@ -81,9 +85,17 @@ $ibo-global-search--compartment--placeholder-hint--text-color: $ibo-color-grey-7
background-color: $ibo-global-search--head--background-color;
}
.ibo-global-search--icon{
color: $ibo-global-search--icon--color;
align-self: center;
padding: $ibo-global-search--icon-padding-y $ibo-global-search--icon-padding-x;
@extend %ibo-font-ral-nor-400;
&:hover{
color: $ibo-global-search--icon--color--on-hover;
}
&:active{
color: $ibo-global-search--icon--color--on-active;
}
}
.ibo-global-search--input{
padding: $ibo-global-search--input--padding;

View File

@@ -8,6 +8,9 @@ $ibo-quick-create--head--background-color: $ibo-color-white-100 !default;
$ibo-quick-create--icon-padding-x: 16px !default;
$ibo-quick-create--icon-padding-y: 0 !default;
$ibo-quick-create--icon--color: $ibo-color-primary-600 !default;
$ibo-quick-create--icon--color--on-hover: $ibo-color-primary-700 !default;
$ibo-quick-create--icon--color--on-active: $ibo-color-primary-800 !default;
$ibo-quick-create--input--padding: 0 default;
$ibo-quick-create--input--padding-x--is-opened: 8px !default;
@@ -24,14 +27,14 @@ $ibo-quick-create--input-options--border-radius: 0 !default;
$ibo-quick-create--drawer--max-height: 300px !default;
$ibo-quick-create--drawer--padding-x: $ibo-quick-create--icon-padding-x !default;
$ibo-quick-create--drawer--padding-y: 16px !default;
$ibo-quick-create--drawer--top: -1 * ($ibo-quick-create--drawer--max-height) !default;
$ibo-quick-create--drawer--top: -1 * ($ibo-quick-create--drawer--max-height) - 10 !default; /* 10px of margin to avoid to be slightly visible when closed and has a lot of history */
$ibo-quick-create--drawer--top--is-opened: 100% !default;
$ibo-quick-create--drawer--background-color: $ibo-color-white-100 !default;
$ibo-quick-create--compartment-title--margin-top: 8px !default;
$ibo-quick-create--compartment-title--margin-bottom: $ibo-quick-create--compartment-title--margin-top !default;
$ibo-quick-create--compartment-title--padding-left: 32px !default;
$ibo-quick-create--compartment-title--text-color: $ibo-color-grey-600 !default;
$ibo-quick-create--compartment-title--text-color: $ibo-color-grey-800 !default;
$ibo-quick-create--compartment-title--line-spacing: 8px !default;
$ibo-quick-create--compartment-content--text-color: $ibo-color-grey-900 !default;
@@ -91,9 +94,17 @@ $ibo-quick-create--compartment--placeholder-hint--text-color: $ibo-color-grey-70
background-color: $ibo-quick-create--head--background-color;
}
.ibo-quick-create--icon{
color: $ibo-quick-create--icon--color;
align-self: center;
padding: $ibo-quick-create--icon-padding-y $ibo-quick-create--icon-padding-x;
@extend %ibo-font-ral-nor-400;
&:hover{
color: $ibo-quick-create--icon--color--on-hover;
}
&:active{
color: $ibo-quick-create--icon--color--on-active;
}
}
.ibo-quick-create--input{
width: $ibo-quick-create--input--width;

View File

@@ -9,6 +9,7 @@ $ibo-search-form-panel--body--padding-top: 18px !default;
$ibo-search-form-panel--body--padding-bottom: 10px !default;
$ibo-search-form-panel--body--padding-x: 14px !default;
$ibo-search-form-panel--criteria--margin-bottom: 5px !default;
$ibo-search-form-panel--criteria--color: $ibo-color-grey-900 !default;
$ibo-search-form-panel--criteria--background-color: $ibo-color-white-200 !default;
$ibo-search-form-panel--criteria--border-color: $ibo-color-grey-300 !default;
@@ -19,10 +20,15 @@ $ibo-search-form-panel--more-criteria--background-color: $ibo-color-white-100 !d
$ibo-search-form-panel--more-criteria--icon--color: $ibo-color-primary-600 !default;
$ibo-search-form-panel--more-criteria--border-color: $ibo-search-form-panel--criteria--border-color !default;
$ibo-search-form-panel--items--hover--color: $ibo-color-grey-200 !default;
$ibo-search-form-panel--multiple-choice--hover--color: $ibo-color-grey-200 !default;
$ibo-search-form-panel--misc-button--background-color: $ibo-search-form-panel--more-criteria--background-color !default;
$ibo-search-form-panel--misc-button--icon--color: $ibo-search-form-panel--more-criteria--icon--color !default;
$ibo-search-results-area--z-index: $ibo-search-form-panel--z-index - 2 !default; /* Minus 2 because the criteria expands between the search form panel and the results area */
$ibo-search-results-area--margin-bottom: 300px !default; /* Arbitrary value, must be greater than the position (y) of the first result line in non sticking state, should be discussed to find a clever way to adjust it in both search page and search modals (linkedsets) */
$ibo-search-results-area--datatable-toolbar--background-color--is-sticking: $ibo-panel--body--background-color !default;
$ibo-search-results-area--datatable-toolbar--border--is-sticking: $ibo-panel--body--border !default;
@@ -197,7 +203,8 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
position: relative;
display: inline-block;
margin-right: 10px;
margin-bottom: 5px;
margin-top: 3px;
margin-bottom: 3px; /* Bottom spacing (6px) for next criteria line is actually splitted between top and bottom margin in order to center the criteria within the container. The cumulated margins fo the lines will do the trick. */
vertical-align: top;
&.opened {
@@ -380,7 +387,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
.sfc_opc_multichoices {
label > input {
vertical-align: middle;
vertical-align: text-top;
margin-left: 0px;
margin-right: 8px;
}
@@ -422,7 +429,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
padding: 4px 8px; /* Putting back the padding remove by .sfc_opc_mc_items */
&:hover {
background-color: $ibo-color-grey-200;
background-color: $ibo-search-form-panel--multiple-choice--hover--color;
}
label {
@@ -686,7 +693,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
.sfl_items {
> li {
&:hover {
background-color: $ibo-color-grey-200;
background-color: $ibo-search-form-panel--items--hover--color;
}
}
}
@@ -745,7 +752,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
white-space: nowrap;
&:hover {
background-color: $ibo-color-white-100;
background-color: $ibo-search-form-panel--items--hover--color;
}
&.sfl_i_placeholder {
@@ -869,9 +876,6 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
#ibo-main-content .search_form_handler .sf_criterion_area{
padding: 0;
}
.ibo-search-form{
padding-top: $ibo-panel--spacing-top ;
}
.sfm_tg_title{
display: none;
@@ -881,6 +885,7 @@ $ibo-search-results-area--datatable-scrollhead--border--is-sticking: $ibo-search
}
.sf_results_area {
z-index: $ibo-search-results-area--z-index;
margin-bottom: $ibo-search-results-area--margin-bottom; /* This is mostly to avoid glitches when the result list's bottom limit is just around the window bottom limit and the user scrolls down (boucing effect, see N°4427) */
}
/***********************/

View File

@@ -9,8 +9,10 @@
@import "input-datetime";
@import "input-duration";
@import "input-image";
@import "input-richtext";
@import "input-select";
@import "input-select-icon";
@import "input-string";
@import "input-one-way-password";
@import "input-set";
@import "input-text";

View File

@@ -0,0 +1,12 @@
/*!
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
$ibo-input-one-way-password--elements-spacing-y: 6px !default;
.ibo-input-one-way-password-wrapper {
> *:not(first-child) {
margin-top: $ibo-input-one-way-password--elements-spacing-y;
}
}

View File

@@ -0,0 +1,5 @@
.ibo-input-richtext-placeholder{
height: 200px;
width: 100%;
visibility: hidden;
}

View File

@@ -14,6 +14,9 @@ $ibo-input-select-selectize--value--min-midth: 150px;
$ibo-input-select-selectize--value--height: 100% !default;
$ibo-input-select-selectize--value--line-height: $ibo-input--height !default;
$ibo-input-select-selectize--dropdown--max-height: 50vh !default;
$ibo-input-select-selectize--dropdown--max-width: 50em !default;
$ibo-input-select-selectize--item--active--text-color: $ibo-color-grey-800 !default;
$ibo-input-select-selectize--item--active--background-color: $ibo-color-blue-100 !default;
$ibo-input-select-wrapper--width: 100% !default;
@@ -84,6 +87,7 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60
}
.ibo-input-select-autocomplete{
min-width: $ibo-input-select-autocomplete--value--min-midth !important;
text-overflow: ellipsis;
}
.ibo-input-selectize {
min-width: $ibo-input-select-selectize--value--min-midth !important;
@@ -104,7 +108,7 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60
}
.ibo-input-select-wrapper--with-buttons .selectize-control
{
display: inline-block;
display: grid;
width: $ibo-input-select-wrapper--width;
}
.ibo-input-select-wrapper::after{
@@ -178,16 +182,18 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60
/*2 types of selectize to manage (select + autocomplete)*/
.selectize-dropdown.ui-autocomplete,
.selectize-dropdown.set-dropdown,
.selectize-dropdown.plugin-custom_itop {
z-index: 2000; /* Note: This is not great as it does not take into account other elements z-index, but as selectize puts its dropdown under the <body> tag, we cannot have a z-index relative to input container. */
max-height: $ibo-input-select-selectize--dropdown--max-height;
max-width: $ibo-input-select-selectize--dropdown--max-width ;
overflow-y: auto;
}
.selectize-dropdown.ui-menu .ui-state-active {
margin: unset;
background-color: #f5fafd;
color: #495c68;
background-color: $ibo-input-select-selectize--item--active--background-color;
color: $ibo-input-select-selectize--item--active--text-color;
}
.ibo-input-select--autocomplete-item {
@@ -213,5 +219,11 @@ $ibo-input-select--autocomplete-item-image--border: 1px solid $ibo-color-grey-60
}
.ibo-input-select--autocomplete-item-txt {
white-space: nowrap;
@extend %ibo-text-truncated-with-ellipsis;
}
/* Use "select" in order to avoid stylizing selectize wrapper that copy select classes*/
select.ibo-input-select-placeholder{
@extend .ibo-input;
@extend .ibo-input-select;
}

View File

@@ -3,20 +3,26 @@
* @license http://opensource.org/licenses/AGPL-3.0
*/
$ibo-input-text--width: 100% !default;
$ibo-input-text--min-height: 12rem !default;
$ibo-input-text--padding-x: 12px !default;
$ibo-input-text--padding-y: 10px !default;
.ibo-input-text {
width: $ibo-input-text--width;
min-height: $ibo-input-text--min-height;
padding: $ibo-input-text--padding-y $ibo-input-text--padding-x;
&.ibo-is-code {
@extend %ibo-font-code-150;
/* Note: There is a mic-mac in the inputs SCSS partials / PHP classes / TWIG templates. All base style should be defined in a common ancestor (AbstractInput) which should have its own BLOCK_CODE and SCSS partial; which is not the case right now.
* We should correct this later as now is too close to the RC to make such changes.
*/
background-color: $ibo-input--background-color;
}
}
.ibo-input-text--export {
width: 100%;
min-height: 15em;
}
textarea.ibo-queryoql, .ibo-queryoql:not(:nth-child(n)), .ibo-queryoql > :not(.ibo-field--label) {
@extend %ibo-font-code-150;
}

View File

@@ -49,11 +49,22 @@ $ibo-input--margin-x: 5px !default;
color: $ibo-input--placeholder--color;
}
}
.ibo-input-wrapper.is-error {
.ibo-input{
background-color: $ibo-input-wrapper--is-error--background-color;
textarea.ibo-input{
height:unset;
}
.ibo-input-wrapper.is-error, .ibo-input-field-wrapper.is-error {
.ibo-input, .ibo-input-vanilla, .cke, textarea {
border: 1px solid $ibo-input-wrapper--is-error--border-color;
background-color: $ibo-input-wrapper--is-error--background-color;
}
.ibo-input-vanilla input{
border: 0;
background-color: #11ffee00;
}
}
input.ibo-input-vanilla{
width: unset;
display:initial;
}
.ibo-input-wrapper--with-buttons{
position: relative;

View File

@@ -11,9 +11,9 @@ $ibo-popover-menu--item--text-color: $ibo-color-grey-900 !default;
$ibo-popover-menu--item--hyperlink-color: $ibo-popover-menu--item--text-color !default;
$ibo-popover-menu--item--hover-background-color: $ibo-color-grey-200 !default;
$ibo-popover-menu--separator--padding: 0 !default;
$ibo-popover-menu--separator--margin: 0 !default;
$ibo-popover-menu--separator--background-color: $ibo-color-grey-200 !default;
$ibo-popover-menu--item-separator--padding: 0 !default;
$ibo-popover-menu--item-separator--margin: 0 !default;
$ibo-popover-menu--item-separator--background-color: $ibo-color-grey-200 !default;
$ibo-popover-menu--item--icon--color: $ibo-color-grey-700 !default;
$ibo-popover-menu--item--icon--font-size: $ibo-font-size-200 !default;
@@ -31,10 +31,10 @@ $ibo-popover-menu--item--icon--padding-right: 5px !default;
background-color: $ibo-popover-menu--item--hover-background-color;
color: inherit;
}
&.ibo-popover-menu--separator{
padding: $ibo-popover-menu--separator--padding;
margin: $ibo-popover-menu--separator--margin;
background-color: $ibo-popover-menu--separator--background-color
&.ibo-popover-menu--item-separator{
padding: $ibo-popover-menu--item-separator--padding;
margin: $ibo-popover-menu--item-separator--margin;
background-color: $ibo-popover-menu--item-separator--background-color
}
}

View File

@@ -105,7 +105,7 @@ $ibo-navigation-menu--drawer--border-right: 1px solid $ibo-color-grey-300 !defau
$ibo-navigation-menu--menu-filter-input--padding-x: 10px !default;
$ibo-navigation-menu--menu-filter-input--padding-y: 8px !default;
$ibo-navigation-menu--menu-filter-input--width: 100% !default;
$ibo-navigation-menu--menu-filter-input--placeholder-color: $ibo-color-grey-500 !default;
$ibo-navigation-menu--menu-filter-input--placeholder-color: $ibo-color-grey-700 !default;
$ibo-navigation-menu--menu-filter-input--text-color: $ibo-color-grey-900 !default;
$ibo-navigation-menu--menu-filter-input--background-color: $ibo-color-white-100 !default;
$ibo-navigation-menu--menu-filter-input--border: 1px solid $ibo-color-grey-300 !default;

View File

@@ -81,7 +81,7 @@ $ibo-activity-panel--load-all-entries--is-hover--margin-left: ($ibo-activity-pan
/* Current or not user specificities */
&.ibo-is-current-user{
flex-direction: row-reverse;
min-width: max-content;
min-width: min-content;
.ibo-activity-entry--medallion{
margin-right: initial;

View File

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

View File

@@ -24,7 +24,7 @@ a.ibo-notification-entry--short-description {
}
/* - Long desc. opened */
.ibo-notification-entry{
&.ibo-is-opened{
&:not(.ibo-is-closed){
.ibo-notification-entry--long-description-toggler-icon{
transform: rotateX(180deg);
}

View File

@@ -28,6 +28,8 @@ $ibo-object-details--tag--separator--diameter: 5px !default;
$ibo-object-details--tag--separator--margin-right: 12px !default;
$ibo-object-details--tag--separator--border-radius: $ibo-border-radius-full !default;
$ibo-object-details--header-right--padding-right--is-sticking: 8px !default;
/* Parent block overloads (!= than blocks integrations) */
.ibo-object-details {
&.ibo-has-icon.ibo-has-medallion-icon {
@@ -51,6 +53,13 @@ $ibo-object-details--tag--separator--border-radius: $ibo-border-radius-full !def
}
}
}
// Note: Direct child selector is mandatory, otherwise a panel within a panel will have be impacted (eg. n:n relations tabs)
> .ibo-panel--header {
.ibo-panel--subtitle {
@extend %ibo-text-truncated-with-ellipsis;
}
}
}
.ibo-object-details--status {
@@ -138,6 +147,9 @@ $ibo-object-details--tag--separator--border-radius: $ibo-border-radius-full !def
.ibo-panel--header-left {
padding-left: $ibo-object-details--icon--size--is-sticking;
}
.ibo-panel--header-right {
padding-right: $ibo-object-details--header-right--padding-right--is-sticking;
}
.ibo-panel--titles {
padding-left: $ibo-object-details--icon--spacing--as-medallion--is-sticking;

View File

@@ -9,7 +9,7 @@ $ibo-tab-container--tabs-list--padding-x: 24px !default;
$ibo-tab-container--tabs-list--background-color: $ibo-color-grey-100 !default;
$ibo-tab-container--tab-header--max-width: 110px !default;
$ibo-tab-container--tab-header--text-color: $ibo-color-grey-700 !default;
$ibo-tab-container--tab-header--text-color: $ibo-color-grey-800 !default;
$ibo-tab-container--tab-header--text-color--is-active: $ibo-color-blue-800 !default;
$ibo-tab-container--tab-header--text-color--on-hover: $ibo-color-blue-800 !default;
$ibo-tab-container--tab-header--background-color--on-hover: $ibo-color-grey-200 !default;
@@ -49,6 +49,8 @@ $ibo-tab--temporary-remote-content--button--hover--opacity: 0.5 !default;
$ibo-tab--temporary-remote-content--button--hover--background-color: $ibo-color-grey-900 !default;
$ibo-tab--temporary-remote-content--button--hover--color: $ibo-color-grey-200 !default;
$ibo-tab-container--tab-container--min-height: auto !default;
$ibo-tab-container--tab-container--last--min-height: 60vh !default;
/* Rules */
.ibo-tab-container--tabs-list {
@@ -140,8 +142,16 @@ $ibo-tab--temporary-remote-content--button--hover--color: $ibo-color-grey-200 !d
overflow-x: auto;
}
.ibo-is-scrollable .ibo-tab-container--tab-container--label {
display: block;
.ibo-tab-container--tab-container-list.ibo-is-scrollable {
.ibo-tab-container--tab-container--label {
display: block;
}
.ibo-tab-container--tab-container {
min-height: $ibo-tab-container--tab-container--min-height;
}
.ibo-tab-container--tab-container:last-child {
min-height: $ibo-tab-container--tab-container--last--min-height;
}
}
.ibo-tab-container--tab-container--label {

View File

@@ -70,8 +70,9 @@ body{
height: 100%;
}
}
#ibo-main-content{
padding-top: $ibo-main-content--padding-top;
padding-bottom: $ibo-main-content--padding-bottom;
#ibo-main-content {
padding-top: $ibo-main-content--padding-top;
padding-bottom: $ibo-main-content--padding-bottom;
overflow: auto; /* For scroll to happen in the main content instead of the center content */
}

View File

@@ -33,14 +33,7 @@ $ibo-datamodel-viewer--schema--tooltip--span--margin: 3px !default;
$ibo-datamodel-viewer--schema--tooltip-top--border-color: $ibo-color-grey-700 !default;
$ibo-datamodel-viewer--schema--tooltip-top--padding: 3px !default;
#ibo-datamodel-viewer{
display: flex;
flex-direction: row;
}
.ibo-datamodel-viewer--details{
flex-grow: 1;
.ibo-panel--subtitle{
@extend %ibo-font-ral-nor-150;
}

View File

@@ -69,19 +69,28 @@ $ibo-keyboard-shortcut--input--is-focus--border-color: $ibo-color-primary-600 !d
width: auto;
text-transform: capitalize;
text-align: center;
color: $ibo-keyboard-shortcut--input--color;
background-color: $ibo-keyboard-shortcut--input--background-color;
border: 1px solid $ibo-keyboard-shortcut--input--border-color;
border-bottom: 2px solid $ibo-keyboard-shortcut--input--border-color;
border-radius: $ibo-keyboard-shortcut--input--border-radius;
padding: $ibo-keyboard-shortcut--input--padding-y $ibo-keyboard-shortcut--input--padding-x;
margin-bottom: $ibo-keyboard-shortcut--input--margin-bottom;
&.ibo-is-focus{
&.ibo-is-focus {
text-transform: none;
color: $ibo-keyboard-shortcut--input--is-focus--color;
border-color: $ibo-keyboard-shortcut--input--is-focus--border-color;
}
}
#ibo-favorite-organizations .ibo-toolbar {
float: right;
vertical-align: top;
}
#ibo-favorite-organizations .ibo-datatable--toolbar {
padding-top: $ibo-button--padding-y/2;
}

View File

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

View File

@@ -18,6 +18,7 @@ $ibo-color-grey-600: hsla(215, 16.6%, 63.3%, 1) !default;
$ibo-color-grey-700: hsla(214, 11.3%, 48.6%, 1) !default;
$ibo-color-grey-800: hsla(215, 16.9%, 30.2%, 1) !default;
$ibo-color-grey-900: hsla(215, 22.4%, 16.7%, 1) !default;
$ibo-color-grey-950: hsla(215, 26.4%, 10.7%, 1) !default;
$ibo-color-blue-grey-50: hsla(210, 36%, 96%, 1) !default;
$ibo-color-blue-grey-100: hsla(198, 15.7%, 83.7%, 1) !default;
@@ -29,6 +30,7 @@ $ibo-color-blue-grey-600: hsla(199, 18.4%, 40.4%, 1) !default;
$ibo-color-blue-grey-700: hsla(199, 18.3%, 33.1%, 1) !default;
$ibo-color-blue-grey-800: hsla(200, 17.9%, 26.3%, 1) !default;
$ibo-color-blue-grey-900: hsla(200, 19.1%, 18.4%, 1) !default;
$ibo-color-blue-grey-950: hsla(200, 20.2%, 13.1%, 1) !default;
$ibo-color-blue-100: hsla(201, 100%, 96.1%, 1) !default;
$ibo-color-blue-200: hsla(202, 80.6%, 85.9%, 1) !default;
@@ -39,6 +41,7 @@ $ibo-color-blue-600: hsla(209, 61.6%, 50%, 1) !default;
$ibo-color-blue-700: hsla(211, 60.7%, 42.9%, 1) !default;
$ibo-color-blue-800: hsla(213, 49.4%, 34.1%, 1) !default;
$ibo-color-blue-900: hsla(215, 41.3%, 28%, 1) !default;
$ibo-color-blue-950: hsla(215, 36.8%, 23%, 1) !default;
$ibo-color-cyan-100: hsla(186, 61.2%, 86.9%, 1) !default;
$ibo-color-cyan-200: hsla(187, 71.6%, 71%, 1) !default;
@@ -49,6 +52,7 @@ $ibo-color-cyan-600: hsla(187, 100%, 37.8%, 1) !default;
$ibo-color-cyan-700: hsla(186, 100%, 32.7%, 1) !default;
$ibo-color-cyan-800: hsla(185, 100%, 28%, 1) !default;
$ibo-color-cyan-900: hsla(182, 100%, 19.6%, 1) !default;
$ibo-color-cyan-950: hsla(180, 100%, 10.6%, 1) !default;
$ibo-color-green-100: hsla(88, 50.7%, 85.7%, 1) !default;
$ibo-color-green-200: hsla(88, 50%, 76.5%, 1) !default;
@@ -59,6 +63,8 @@ $ibo-color-green-600: hsla(89, 46.1%, 48%, 1) !default;
$ibo-color-green-700: hsla(92, 47.9%, 42.2%, 1) !default;
$ibo-color-green-800: hsla(95, 49.5%, 36.5%, 1) !default;
$ibo-color-green-900: hsla(103, 55.6%, 26.5%, 1) !default;
$ibo-color-green-950: hsla(108, 59.6%, 21.5%, 1) !default;
$ibo-color-orange-100: hsla(40, 100%, 97.1%, 1) !default;
$ibo-color-orange-200: hsla(39, 96.4%, 89%, 1) !default;
@@ -69,6 +75,7 @@ $ibo-color-orange-600: hsla(24, 74.7%, 49.6%, 1) !default;
$ibo-color-orange-700: hsla(20, 70.7%, 44.1%, 1) !default;
$ibo-color-orange-800: hsla(16, 65.1%, 37.1%, 1) !default;
$ibo-color-orange-900: hsla(14, 60.8%, 30%, 1) !default;
$ibo-color-orange-950: hsla(14, 55.1%, 22%, 1) !default;
$ibo-color-red-100: hsla(0, 76.9%, 94.9%, 1) !default;
$ibo-color-red-200: hsla(0, 95.1%, 92%, 1) !default;
@@ -79,6 +86,7 @@ $ibo-color-red-600: hsla(0, 76.3%, 57.1%, 1) !default;
$ibo-color-red-700: hsla(0, 60.8%, 48%, 1) !default;
$ibo-color-red-800: hsla(0, 55.8%, 39%, 1) !default;
$ibo-color-red-900: hsla(0, 46.8%, 31%, 1) !default;
$ibo-color-red-950: hsla(0, 42.9%, 20%, 1) !default;
$ibo-color-pink-100: hsla(348, 100%, 98%, 1) !default;
$ibo-color-pink-200: hsla(343, 95%, 92%, 1) !default;
@@ -89,6 +97,7 @@ $ibo-color-pink-600: hsla(329, 64%, 54%, 1) !default;
$ibo-color-pink-700: hsla(325, 57%, 46%, 1) !default;
$ibo-color-pink-800: hsla(322, 60%, 37%, 1) !default;
$ibo-color-pink-900: hsla(318, 51%, 29%, 1) !default;
$ibo-color-pink-950: hsla(318, 51%, 21%, 1) !default;
$ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pink', 'primary', 'secondary', 'information', 'success', 'warning', 'danger');
@@ -108,6 +117,7 @@ $ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pi
--ibo-color-grey-700: #{$ibo-color-grey-700};
--ibo-color-grey-800: #{$ibo-color-grey-800};
--ibo-color-grey-900: #{$ibo-color-grey-900};
--ibo-color-grey-950: #{$ibo-color-grey-950};
--ibo-color-blue-grey-50: #{$ibo-color-blue-grey-50};
--ibo-color-blue-grey-100: #{$ibo-color-blue-grey-100};
@@ -119,6 +129,7 @@ $ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pi
--ibo-color-blue-grey-700: #{$ibo-color-blue-grey-700};
--ibo-color-blue-grey-800: #{$ibo-color-blue-grey-800};
--ibo-color-blue-grey-900: #{$ibo-color-blue-grey-900};
--ibo-color-blue-grey-950: #{$ibo-color-blue-grey-950};
--ibo-color-blue-100: #{$ibo-color-blue-100};
--ibo-color-blue-200: #{$ibo-color-blue-200};
@@ -129,6 +140,7 @@ $ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pi
--ibo-color-blue-700: #{$ibo-color-blue-700};
--ibo-color-blue-800: #{$ibo-color-blue-800};
--ibo-color-blue-900: #{$ibo-color-blue-900};
--ibo-color-blue-950: #{$ibo-color-blue-950};
--ibo-color-cyan-100: #{$ibo-color-cyan-100};
--ibo-color-cyan-200: #{$ibo-color-cyan-200};
@@ -139,6 +151,7 @@ $ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pi
--ibo-color-cyan-700: #{$ibo-color-cyan-700};
--ibo-color-cyan-800: #{$ibo-color-cyan-800};
--ibo-color-cyan-900: #{$ibo-color-cyan-900};
--ibo-color-cyan-950: #{$ibo-color-cyan-950};
--ibo-color-green-100: #{$ibo-color-green-100};
--ibo-color-green-200: #{$ibo-color-green-200};
@@ -149,6 +162,7 @@ $ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pi
--ibo-color-green-700: #{$ibo-color-green-700};
--ibo-color-green-800: #{$ibo-color-green-800};
--ibo-color-green-900: #{$ibo-color-green-900};
--ibo-color-green-950: #{$ibo-color-green-950};
--ibo-color-orange-100: #{$ibo-color-orange-100};
--ibo-color-orange-200: #{$ibo-color-orange-200};
@@ -159,6 +173,7 @@ $ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pi
--ibo-color-orange-700: #{$ibo-color-orange-700};
--ibo-color-orange-800: #{$ibo-color-orange-800};
--ibo-color-orange-900: #{$ibo-color-orange-900};
--ibo-color-orange-950: #{$ibo-color-orange-950};
--ibo-color-red-100: #{$ibo-color-red-100};
--ibo-color-red-200: #{$ibo-color-red-200};
@@ -169,6 +184,7 @@ $ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pi
--ibo-color-red-700: #{$ibo-color-red-700};
--ibo-color-red-800: #{$ibo-color-red-800};
--ibo-color-red-900: #{$ibo-color-red-900};
--ibo-color-red-950: #{$ibo-color-red-950};
--ibo-color-pink-100: #{$ibo-color-pink-100};
--ibo-color-pink-200: #{$ibo-color-pink-200};
@@ -179,4 +195,5 @@ $ibo-colors: ('grey', 'blue-grey', 'blue', 'cyan', 'green', 'orange', 'red', 'pi
--ibo-color-pink-700: #{$ibo-color-pink-700};
--ibo-color-pink-800: #{$ibo-color-pink-800};
--ibo-color-pink-900: #{$ibo-color-pink-900};
--ibo-color-pink-950: #{$ibo-color-pink-950};
}

View File

@@ -6,4 +6,7 @@
/* Base helpers for colors */
/* - These lightness vars are used to force a certain lightness on HSLA colors */
$ibo-color-base-lightness-100: 93% !default;
$ibo-color-base-lightness-900: 15% !default;
$ibo-color-base-lightness-900: 15% !default;
$ibo-color-base-opacity-for-lightness-100: 1 !default;
$ibo-color-base-opacity-for-lightness-900: 1 !default;

View File

@@ -14,6 +14,7 @@ $ibo-color-primary-600: $ibo-color-orange-600 !default;
$ibo-color-primary-700: $ibo-color-orange-700 !default;
$ibo-color-primary-800: $ibo-color-orange-800 !default;
$ibo-color-primary-900: $ibo-color-orange-900 !default;
$ibo-color-primary-950: $ibo-color-orange-950 !default;
/* - Secondary color of the brand */
$ibo-color-secondary-100: $ibo-color-grey-100 !default;
@@ -25,6 +26,7 @@ $ibo-color-secondary-600: $ibo-color-grey-600 !default;
$ibo-color-secondary-700: $ibo-color-grey-700 !default;
$ibo-color-secondary-800: $ibo-color-grey-800 !default;
$ibo-color-secondary-900: $ibo-color-grey-900 !default;
$ibo-color-secondary-950: $ibo-color-grey-950 !default;
/* - Information: messages / actions that should neither seem as success, warning or failure */
$ibo-color-information-100: $ibo-color-blue-100 !default;
@@ -36,6 +38,7 @@ $ibo-color-information-600: $ibo-color-blue-600 !default;
$ibo-color-information-700: $ibo-color-blue-700 !default;
$ibo-color-information-800: $ibo-color-blue-800 !default;
$ibo-color-information-900: $ibo-color-blue-900 !default;
$ibo-color-information-950: $ibo-color-blue-950 !default;
/* Success: messages of success, safe actions, ... */
$ibo-color-success-100: $ibo-color-green-100 !default;
@@ -47,6 +50,7 @@ $ibo-color-success-600: $ibo-color-green-600 !default;
$ibo-color-success-700: $ibo-color-green-700 !default;
$ibo-color-success-800: $ibo-color-green-800 !default;
$ibo-color-success-900: $ibo-color-green-900 !default;
$ibo-color-success-950: $ibo-color-green-950 !default;
/* Warning: messages of warning, actions that would be done carefully, ... */
$ibo-color-warning-100: $ibo-color-orange-100 !default;
@@ -58,6 +62,7 @@ $ibo-color-warning-600: $ibo-color-orange-600 !default;
$ibo-color-warning-700: $ibo-color-orange-700 !default;
$ibo-color-warning-800: $ibo-color-orange-800 !default;
$ibo-color-warning-900: $ibo-color-orange-900 !default;
$ibo-color-warning-950: $ibo-color-orange-950 !default;
/* Danger: messages of failure, error, ... */
$ibo-color-error-100: $ibo-color-red-100 !default;
@@ -69,6 +74,7 @@ $ibo-color-error-600: $ibo-color-red-600 !default;
$ibo-color-error-700: $ibo-color-red-700 !default;
$ibo-color-error-800: $ibo-color-red-800 !default;
$ibo-color-error-900: $ibo-color-red-900 !default;
$ibo-color-error-950: $ibo-color-red-950 !default;
/* Danger: messages of danger, actions that cannot be undone, ... */
$ibo-color-danger-100: $ibo-color-red-100 !default;
@@ -80,6 +86,7 @@ $ibo-color-danger-600: $ibo-color-red-600 !default;
$ibo-color-danger-700: $ibo-color-red-700 !default;
$ibo-color-danger-800: $ibo-color-red-800 !default;
$ibo-color-danger-900: $ibo-color-red-900 !default;
$ibo-color-danger-950: $ibo-color-red-950 !default;
$ibo-semantic-colors: ('primary', 'secondary', 'information', 'success', 'warning', 'danger');
@@ -103,6 +110,7 @@ $ibo-caselog-highlight-colors: ($ibo-caselog-highlight-color-1, $ibo-caselog-hig
--ibo-color-primary-700: #{$ibo-color-primary-700};
--ibo-color-primary-800: #{$ibo-color-primary-800};
--ibo-color-primary-900: #{$ibo-color-primary-900};
--ibo-color-primary-950: #{$ibo-color-primary-950};
--ibo-color-secondary-100: #{$ibo-color-secondary-100};
--ibo-color-secondary-200: #{$ibo-color-secondary-200};
@@ -113,6 +121,7 @@ $ibo-caselog-highlight-colors: ($ibo-caselog-highlight-color-1, $ibo-caselog-hig
--ibo-color-secondary-700: #{$ibo-color-secondary-700};
--ibo-color-secondary-800: #{$ibo-color-secondary-800};
--ibo-color-secondary-900: #{$ibo-color-secondary-900};
--ibo-color-secondary-950: #{$ibo-color-secondary-950};
--ibo-color-information-100: #{$ibo-color-information-100};
--ibo-color-information-200: #{$ibo-color-information-200};
@@ -123,6 +132,7 @@ $ibo-caselog-highlight-colors: ($ibo-caselog-highlight-color-1, $ibo-caselog-hig
--ibo-color-information-700: #{$ibo-color-information-700};
--ibo-color-information-800: #{$ibo-color-information-800};
--ibo-color-information-900: #{$ibo-color-information-900};
--ibo-color-information-950: #{$ibo-color-information-950};
--ibo-color-success-100: #{$ibo-color-success-100};
--ibo-color-success-200: #{$ibo-color-success-200};
@@ -133,6 +143,7 @@ $ibo-caselog-highlight-colors: ($ibo-caselog-highlight-color-1, $ibo-caselog-hig
--ibo-color-success-700: #{$ibo-color-success-700};
--ibo-color-success-800: #{$ibo-color-success-800};
--ibo-color-success-900: #{$ibo-color-success-900};
--ibo-color-success-950: #{$ibo-color-success-950};
--ibo-color-warning-100: #{$ibo-color-warning-100};
--ibo-color-warning-200: #{$ibo-color-warning-200};
@@ -143,6 +154,7 @@ $ibo-caselog-highlight-colors: ($ibo-caselog-highlight-color-1, $ibo-caselog-hig
--ibo-color-warning-700: #{$ibo-color-warning-700};
--ibo-color-warning-800: #{$ibo-color-warning-800};
--ibo-color-warning-900: #{$ibo-color-warning-900};
--ibo-color-warning-950: #{$ibo-color-warning-950};
--ibo-color-danger-100: #{$ibo-color-danger-100};
--ibo-color-danger-200: #{$ibo-color-danger-200};
@@ -153,6 +165,7 @@ $ibo-caselog-highlight-colors: ($ibo-caselog-highlight-color-1, $ibo-caselog-hig
--ibo-color-danger-700: #{$ibo-color-danger-700};
--ibo-color-danger-800: #{$ibo-color-danger-800};
--ibo-color-danger-900: #{$ibo-color-danger-900};
--ibo-color-danger-950: #{$ibo-color-danger-950};
--ibo-color-error-100: #{$ibo-color-error-100};
--ibo-color-error-200: #{$ibo-color-error-200};
@@ -163,6 +176,7 @@ $ibo-caselog-highlight-colors: ($ibo-caselog-highlight-color-1, $ibo-caselog-hig
--ibo-color-error-700: #{$ibo-color-error-700};
--ibo-color-error-800: #{$ibo-color-error-800};
--ibo-color-error-900: #{$ibo-color-error-900};
--ibo-color-error-950: #{$ibo-color-error-950};
--ibo-caselog-color-highlight-1: #{$ibo-caselog-highlight-color-1};
--ibo-caselog-color-highlight-2: #{$ibo-caselog-highlight-color-2};

View File

@@ -15,4 +15,5 @@
@import "datatables";
@import "jquery-treeview";
@import "jquery-blockui";
@import "magnific-popup";
@import "magnific-popup";
@import "selectize";

View File

@@ -85,4 +85,19 @@ ul.cke_autocomplete_panel{
color: $ibo-vendors-ckeditor--autocomplete-item-title--text-color;
@extend %ibo-font-weight-700;
}
}
/* These rules should be the same as those in css/ckeditor/contents.css */
.ibo-vendors-ckeditor--display-content{
.cke_editable{
line-height: 1.4;
}
figure{
border: solid 1px #ccc;
border-radius: 2px;
}
p{
margin-top: 0.25em;
margin-bottom: 0.25em;
}
}

View File

@@ -4,15 +4,15 @@
*/
$ibo-vendors-blockui--blockoverlay--background-color: $ibo-color-white-200 !default;
$ibo-vendors-blockui--blockomsg--color: $ibo-color-grey-700 !default;
$ibo-vendors-blockui--blockmsg--color: $ibo-color-grey-700 !default;
.blockUI.blockOverlay{
background-color: $ibo-color-white-200;
background-color: $ibo-vendors-blockui--blockoverlay--background-color;
}
.blockUI.blockMsg{
font-size: 6em;
text-align: center;
color: $ibo-color-grey-700;
color: $ibo-vendors-blockui--blockmsg--color;
border: none;
background-color: transparent;
}

View File

@@ -21,15 +21,16 @@ $ibo-vendors-jquery-multiselect--ui-multiselect-menu--z-index: 10000 !default;
.ui-multiselect {
.ui-multiselect {
@extend .ibo-input;
@extend .ibo-input-select;
width: auto !important;
padding-left: $ibo-vendors-jquery-multiselect--ui-multiselect--padding-left !important;
padding-right: $ibo-vendors-jquery-multiselect--ui-multiselect--padding-right !important;
text-align:left;
white-space:nowrap;
overflow:hidden;
text-align: left;
white-space: nowrap;
overflow: hidden;
display: grid;
}
button.ui-multiselect > span {

View File

@@ -66,6 +66,7 @@ $ibo-vendors-jqueryui--ui-slider--ui-slider-handle--hover--border-color: $ibo-co
// Tabs
.ui-dialog{
box-sizing: content-box; /* Necessary to overload our theme default setting */
display: flex;
flex-direction: column;
position: absolute;
@@ -104,6 +105,7 @@ $ibo-vendors-jqueryui--ui-slider--ui-slider-handle--hover--border-color: $ibo-co
height: 20px;
}
.ui-dialog-content {
box-sizing: content-box; /* Necessary to overload our theme default setting */
position: relative;
padding: $ibo-vendors-jqueryui--ui-dialog-content--padding-y $ibo-vendors-jqueryui--ui-dialog-content--padding-x;
overflow: auto;
@@ -185,7 +187,7 @@ $ibo-vendors-jqueryui--ui-slider--ui-slider-handle--hover--border-color: $ibo-co
}
&.ui-dialog-titlebar-close{
@extend .ibo-is-alternative;
@extend .ibo-is-grey;
@extend .ibo-is-neutral;
}
}
@@ -578,7 +580,7 @@ $ibo-vendors-jqueryui--ui-slider--ui-slider-handle--hover--border-color: $ibo-co
}
.ui-autocomplete-category{
@extend .ibo-popover-menu--separator
@extend .ibo-popover-menu--item-separator
}
// Menu

View File

@@ -0,0 +1,8 @@
/*!
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
.selectize-dropdown-content {
max-height: unset; /* Overloaded as it will be handled by the _input-select.scss partial */
}

20
css/ckeditor/contents.css Normal file
View File

@@ -0,0 +1,20 @@
/*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/* These rules should be the same as those in css/backoffice/vendors/ckeditor.css */
body{
margin: 5px 10px;
}
.cke_editable{
line-height: 1.4;
}
figure{
border: solid 1px #ccc;
border-radius: 2px;
}
p{
margin-top: 0.25em;
margin-bottom: 0.25em;
}

View File

@@ -2,12 +2,35 @@
@media print {
html {
font-size: 10px;
break-after:auto;
break-after: auto;
}
.not-printable {
display: none;
}
.ibo-panel--icon {
-webkit-print-color-adjust: exact !important; /* Chrome, Safari, Edge */
color-adjust: exact !important; /*Firefox*/
}
.printable-content legend::before,
.printable-content .ibo-activity-panel--tab-toolbar-action::before {
display: none;
}
.printable-content .strikethrough {
display: none;
}
#hiddeable_chapters .ibo-block-print--tab.strikethrough {
display: none;
}
#hiddeable_chapters .ibo-block-print--tab span::before {
display: none;
}
span.ui-layout-resizer {
display: none;
}
@@ -97,6 +120,10 @@
background-image: none;
}
.ibo-field--label > .ibo-has-description::after {
display: none;
}
/**
* PRINT Stylesheet
*

File diff suppressed because one or more lines are too long

View File

@@ -391,10 +391,6 @@ body {
height: 100%;
.ibo-setup--wizard--content{
overflow: auto;
input#db_backup {
margin-right: 0.4rem;
}
}
.ibo-setup--wizard--buttons-container{
margin-top: auto;
@@ -565,9 +561,6 @@ body {
}
.setup-accept-licenses{
margin-top: 18px;
input{
margin-right: 8px;
}
}
.module-selection-banner{
display: flex;
@@ -599,6 +592,9 @@ body {
color: $ibo-color-blue-700;
font-size: $ibo-font-size-200;
}
.setup--wizard-choice--label + .setup--wizard-choice--more-info {
margin-left: 0.5rem;
}
/* integrityCheck: end (do not remove/edit) */
/* Legacy inline stuff */

View File

@@ -1,12 +1,15 @@
<?php
/**
* Localized data
/**
* Spanish Localized data
*
* @copyright Copyright (C) 2013 XXXXX
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
* @notas Utilizar codificación UTF-8 para mostrar acentos y otros caracteres especiales
*/
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'CAS:Error:UserNotAllowed' => 'User not allowed~~',
'CAS:Login:SignIn' => 'Sign in with CAS~~',
'CAS:Login:SignInTooltip' => 'Click here to authenticate yourself with the CAS server~~',
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'CAS:Error:UserNotAllowed' => 'Usuario no permitido',
'CAS:Login:SignIn' => 'Iniciar sesión con CAS',
'CAS:Login:SignInTooltip' => 'Click para autenticarse con servidor CAS',
));

View File

@@ -15,8 +15,8 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Localized data
/**
* Spanish Localized data
*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
@@ -34,7 +34,7 @@
//
// Class: UserExternal
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:UserExternal' => 'Usuario Externo',
'Class:UserExternal+' => 'Usuario Autenticado fuera de '.ITOP_APPLICATION_SHORT,
));

View File

@@ -37,7 +37,7 @@ class UserExternal extends User
{
$aParams = array
(
"category" => "addon/authentication,grant_by_profile",
"category" => "addon/authentication,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",

View File

@@ -14,7 +14,7 @@
* @copyright Copyright (C) 2010-2020 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/</comment>
<category>addon/authentication,grant_by_profile</category>
<category>addon/authentication,grant_by_profile,silo</category>
<abstract>false</abstract>
<key_type>autoincrement</key_type>
<db_table>priv_user_ldap</db_table>

View File

@@ -15,8 +15,8 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Localized data
/**
* Spanish Localized data
*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
@@ -34,7 +34,7 @@
//
// Class: UserLDAP
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:UserLDAP' => 'Usuario LDAP',
'Class:UserLDAP+' => 'Usuario Autenticado vía LDAP',
));

View File

@@ -15,8 +15,8 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Localized data
/**
* Spanish Localized data
*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
@@ -34,24 +34,24 @@
//
// Class: UserLocal
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:UserLocal' => 'Usuario de '.ITOP_APPLICATION_SHORT,
'Class:UserLocal+' => 'Usuario Autenticado vía '.ITOP_APPLICATION_SHORT,
'Class:UserLocal/Attribute:password' => 'Contraseña',
'Class:UserLocal/Attribute:password+' => 'Contraseña',
'Class:UserLocal/Attribute:expiration' => 'Password expiration~~',
'Class:UserLocal/Attribute:expiration+' => 'Password expiration status (requires an extension to have an effect)~~',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Can expire~~',
'Class:UserLocal/Attribute:expiration/Value:can_expire+' => '~~',
'Class:UserLocal/Attribute:expiration/Value:never_expire' => 'Never expire~~',
'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '~~',
'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'Expired~~',
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '~~',
'Class:UserLocal/Attribute:password_renewed_date' => 'Password renewal~~',
'Class:UserLocal/Attribute:password_renewed_date+' => 'When the password was last changed~~',
'Class:UserLocal/Attribute:expiration' => 'Expiración de contraseña',
'Class:UserLocal/Attribute:expiration+' => 'Estatus de expiración de contraseña (requiere de una extensión para que tenga efecto)',
'Class:UserLocal/Attribute:expiration/Value:can_expire' => 'Puede expirar',
'Class:UserLocal/Attribute:expiration/Value:can_expire+' => '',
'Class:UserLocal/Attribute:expiration/Value:never_expire' => 'Nunca expirar',
'Class:UserLocal/Attribute:expiration/Value:never_expire+' => '',
'Class:UserLocal/Attribute:expiration/Value:force_expire' => 'Expirado',
'Class:UserLocal/Attribute:expiration/Value:force_expire+' => '',
'Class:UserLocal/Attribute:password_renewed_date' => 'Renovación de contraseña',
'Class:UserLocal/Attribute:password_renewed_date+' => 'Cuando fue el último cambio de contraseña',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'Password must be at least 8 characters and include uppercase, lowercase, numeric and special characters.~~',
'Error:UserLocalPasswordValidator:UserPasswordPolicyRegex:ValidationFailed' => 'La contraseña debe ser de al menos 8 caracteres e incluír mayúsculas, minúsculas, números y caracteres especiales.',
'UserLocal:password:expiration' => 'The fields below require an extension~~'
'UserLocal:password:expiration' => 'El siguiente campo requiere una extensión'
));

View File

@@ -77,7 +77,7 @@ class UserLocal extends UserInternal
{
$aParams = array
(
"category" => "addon/authentication,grant_by_profile",
"category" => "addon/authentication,grant_by_profile,silo",
"key_type" => "autoincrement",
"name_attcode" => "login",
"state_attcode" => "",

View File

@@ -0,0 +1,34 @@
<?php
/*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/**
* Rebuild the hierarchical keys control data
*/
use Combodo\iTop\Core\MetaModel\HierarchicalKey;
require_once ('../../../approot.inc.php');
require_once APPROOT.'application/startup.inc.php';
foreach(MetaModel::GetClasses() as $sClass)
{
if (!MetaModel::HasTable($sClass))
{
continue;
}
foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
{
// Check (once) all the attributes that are hierarchical keys
if ((MetaModel::GetAttributeOrigin($sClass, $sAttCode) == $sClass) && $oAttDef->IsHierarchicalKey())
{
echo "Rebuild hierarchical key $sAttCode from $sClass.\n";
HierarchicalKey::Rebuild($sClass, $sAttCode, $oAttDef);
}
}
}
echo "Done\n";

View File

@@ -6,10 +6,7 @@
use Combodo\iTop\DBTools\Service\DBAnalyzerUtils;
@include_once('../approot.inc.php');
@include_once('../../approot.inc.php');
@include_once('../../../approot.inc.php');
require_once ('../../../approot.inc.php');
require_once(APPROOT.'application/startup.inc.php');
require_once('../db_analyzer.class.inc.php');

View File

@@ -17,6 +17,8 @@
// along with iTop. If not, see <http://www.gnu.org/licenses/>
//
use Combodo\iTop\Core\MetaModel\HierarchicalKey;
class DatabaseAnalyzer
{
var $iTimeLimitPerOperation;
@@ -162,6 +164,9 @@ class DatabaseAnalyzer
if ($oAttDef->IsExternalKey())
{
$this->CheckExternalKeys($oAttDef, $sTable, $sKeyField, $sAttCode, $sClass, $aErrorsAndFixes);
if ((MetaModel::GetAttributeOrigin($sClass, $sAttCode) == $sClass) && $oAttDef->IsHierarchicalKey()) {
$this->CheckHK($sClass, $sAttCode, $aErrorsAndFixes);
}
}
elseif ($oAttDef->IsDirectField() && !($oAttDef instanceof AttributeTagSet))
{
@@ -506,5 +511,24 @@ class DatabaseAnalyzer
$this->ExecQuery($sSelWrongRecs, $sFixit, $sErrorDesc, $sClass, $aErrorsAndFixes);
}
/**
* Check hierarchical keys
*
* @param $sClass
* @param $sAttCode
* @param $aErrorsAndFixes
*
* @throws \Exception
*/
private function CheckHK($sClass, $sAttCode, &$aErrorsAndFixes)
{
try {
HierarchicalKey::VerifyIntegrity($sClass, $sAttCode, MetaModel::GetAttributeDef($sClass, $sAttCode));
} catch (CoreException $e) {
$sErrorDesc = Dict::Format('DBAnalyzer-Integrity-HKInvalid', $sAttCode);
$aErrorsAndFixes[$sClass][$sErrorDesc]['count'] = 1;
$aErrorsAndFixes[$sClass][$sErrorDesc]['query'] = '-- N/A';
$aErrorsAndFixes[$sClass][$sErrorDesc]['fixit'] = ['-- Run script env-'.utils::GetCurrentEnvironment().DIRECTORY_SEPARATOR.'combodo-db-tools'.DIRECTORY_SEPARATOR.'bin'.DIRECTORY_SEPARATOR.'rebuildhk.php' ];
}
}
}

View File

@@ -234,17 +234,19 @@ function DisplayErrorDetails($aResults, $bVerbose)
$oFieldSet = FieldSetUIBlockFactory::MakeStandard(Dict::S('DBTools:SQLquery'));
$oCollapsible->AddSubBlock($oFieldSet);
$oCode = UIContentBlockUIBlockFactory::MakeForPreformatted($aError['query']);
$oFieldSet->AddSubBlock($oCode);
if (array_key_exists('query', $aError)) {
$oCode = UIContentBlockUIBlockFactory::MakeForPreformatted($aError['query']);
$oFieldSet->AddSubBlock($oCode);
if (isset($aError['fixit'])) {
$oFieldSet = FieldSetUIBlockFactory::MakeStandard(Dict::S('DBTools:FixitSQLquery'));
$oCollapsible->AddSubBlock($oFieldSet);
if (isset($aError['fixit'])) {
$oFieldSet = FieldSetUIBlockFactory::MakeStandard(Dict::S('DBTools:FixitSQLquery'));
$oCollapsible->AddSubBlock($oFieldSet);
$aQueries = $aError['fixit'];
foreach ($aQueries as $sFixQuery) {
$oCode = UIContentBlockUIBlockFactory::MakeForPreformatted($sFixQuery);
$oFieldSet->AddSubBlock($oCode);
$aQueries = $aError['fixit'];
foreach ($aQueries as $sFixQuery) {
$oCode = UIContentBlockUIBlockFactory::MakeForPreformatted($sFixQuery);
$oFieldSet->AddSubBlock($oCode);
}
}
}

View File

@@ -24,8 +24,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
'DBTools:Class' => 'Klasse',
'DBTools:Title' => 'Datenbankpflege-Tools',
'DBTools:ErrorsFound' => 'Fehler gefunden',
'DBTools:Indication' => 'Important: after fixing errors in the database you\'ll have to run the analysis again as new inconsistencies will be generated~~',
'DBTools:Disclaimer' => 'DISCLAIMER: BACKUP YOUR DATABASE BEFORE RUNNING THE FIXES~~',
'DBTools:Indication' => 'Wichtig: Nach dem Fixen der Errors in der Datenbank müssen Sie die Analyse erneut laufen lassen, weil durch die Fixes eventuell weitere Inkonsistenzen enstanden sind',
'DBTools:Disclaimer' => 'DISCLAIMER: FERTIGEN SIE EIN BACKUP IHRER DATENBANK AN, BEVOR SIE DIE FIXES ANWENDEN!',
'DBTools:Error' => 'Fehler',
'DBTools:Count' => 'Anzahl',
'DBTools:SQLquery' => 'SQL Query',

View File

@@ -55,6 +55,7 @@ Dict::Add('EN US', 'English', 'English', array(
'DBAnalyzer-Integrity-MissingExtKey' => 'Missing external key %1$s (column: `%2$s.%3$s`)',
'DBAnalyzer-Integrity-InvalidValue' => 'Invalid value for %1$s (column: `%2$s.%3$s`)',
'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Some user accounts have no profile at all',
'DBAnalyzer-Integrity-HKInvalid' => 'Broken hierarchical key `%1$s`',
'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted',
'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`',
'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class',

View File

@@ -1,9 +1,10 @@
<?php
/**
* Localized data
* Spanish localized data
*
* @copyright Copyright (C) 2010-2018 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
*
* This file is part of iTop.
*
@@ -21,11 +22,11 @@
* along with iTop. If not, see <http://www.gnu.org/licenses/>
*/
// Database inconsistencies
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
// Dictionary entries go here
'Menu:DBToolsMenu' => 'Herramientas de bases de datos',
'Menu:DBToolsMenu' => 'Integridad de Base de Datos',
'DBTools:Class' => 'Clase',
'DBTools:Title' => 'Herramientas de mantenimiento de base de datos~~',
'DBTools:Title' => 'Herramientas de Mantenimiento de Base de Datos',
'DBTools:ErrorsFound' => 'Errores encontrados',
'DBTools:Indication' => 'Important: after fixing errors in the database you\'ll have to run the analysis again as new inconsistencies will be generated~~',
'DBTools:Disclaimer' => 'DISCLAIMER: BACKUP YOUR DATABASE BEFORE RUNNING THE FIXES~~',
@@ -39,14 +40,14 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'DBTools:ShowIds' => 'Vista detallada',
'DBTools:ShowReport' => 'Reporte',
'DBTools:IntegrityCheck' => 'Verificación de integridad',
'DBTools:FetchCheck' => 'Fetch Check (long)~~',
'DBTools:FetchCheck' => 'Verificación de búsqueda (larga)',
'DBTools:SelectAnalysisType' => 'Select analysis type~~',
'DBTools:Analyze' => 'Analizar',
'DBTools:Details' => 'Mostrar detalles',
'DBTools:ShowAll' => 'Mostrar todos los errores',
'DBTools:Inconsistencies' => 'Inconsistencias de base de datos',
'DBTools:Inconsistencies' => 'Inconsistencias de Base de Datos',
'DBTools:DetailedErrorTitle' => '%2$s error(s) in class %1$s: %3$s~~',
'DBAnalyzer-Integrity-OrphanRecord' => 'Registro huérfano en `%1$s`, debería tener su contraparte en la tabla `%2$s`',
@@ -54,32 +55,32 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'DBAnalyzer-Integrity-MissingExtKey' => 'Llave externa perdida %1$s (columna: `%2$s.%3$s`)',
'DBAnalyzer-Integrity-InvalidValue' => 'Valor inválido para %1$s (columna: `%2$s.%3$s`)',
'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Algunas cuentas de usuario no tienen perfil asignado',
'DBAnalyzer-Fetch-Count-Error' => 'Fetch count error in `%1$s`, %2$d entries fetched / %3$d counted~~',
'DBAnalyzer-Integrity-FinalClass' => 'Field `%2$s`.`%1$s` must have the same value as `%3$s`.`%1$s`~~',
'DBAnalyzer-Integrity-RootFinalClass' => 'Field `%2$s`.`%1$s` must contains a valid class~~',
'DBAnalyzer-Fetch-Count-Error' => 'Obtener cuenta de errores en `%1$s`, %2$d entradas recuperadas / %3$d contadas',
'DBAnalyzer-Integrity-FinalClass' => 'Campo `%2$s`.`%1$s` debe tener los mismos valores que `%3$s`.`%1$s`',
'DBAnalyzer-Integrity-RootFinalClass' => 'Campo `%2$s`.`%1$s` debe contener un caracter válido',
));
// Database Info
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'DBTools:DatabaseInfo' => 'Información de base de datos',
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'DBTools:DatabaseInfo' => 'Información de Base de Datos',
'DBTools:Base' => 'Base',
'DBTools:Size' => 'Tamaño',
));
// Lost attachments
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'DBTools:LostAttachments' => 'Adjuntos perdidos',
'DBTools:LostAttachments:Disclaimer' => 'Aquí usted puede buscar adjuntos perdidos o desplazados. Esta NO es una herramienta de recuperación de datos, no obtiene datos borrados.',
'DBTools:LostAttachments:Disclaimer' => 'Aquí usted puede buscar anexos perdidos o fuera de lugar. Esta NO es una herramienta de recuperación de datos, no obtiene datos borrados.',
'DBTools:LostAttachments:Button:Analyze' => 'Analizar',
'DBTools:LostAttachments:Button:Restore' => 'Restaurar',
'DBTools:LostAttachments:Button:Restore:Confirm' => 'Esta acción no se puede deshacer, por favor confirme que quiere restaurar los archivos seleccionados.',
'DBTools:LostAttachments:Button:Busy' => 'Por favor espere...',
'DBTools:LostAttachments:Step:Analyze' => 'Primero, buscaremos adjuntos perdidos/desplazados analizando la base de datos.',
'DBTools:LostAttachments:Step:Analyze' => 'Primero, buscar anexos perdidos o fuera de lugar analizando la base de datos.',
'DBTools:LostAttachments:Step:AnalyzeResults' => 'Analizar resultados:',
'DBTools:LostAttachments:Step:AnalyzeResults:None' => 'Genial! Todo parece estar en el lugar correcto.',
'DBTools:LostAttachments:Step:AnalyzeResults:None' => '¡Genial! Todo parece estar en el lugar correcto.',
'DBTools:LostAttachments:Step:AnalyzeResults:Some' => 'Algunos adjuntos (%1$d) parecen estar desplazados. Mire la siguiente lista y verifique los que quiera mover.',
'DBTools:LostAttachments:Step:AnalyzeResults:Item:Filename' => 'Nombre de archivo',
'DBTools:LostAttachments:Step:AnalyzeResults:Item:CurrentLocation' => 'Ubicación actual',

View File

@@ -46,10 +46,11 @@ Dict::Add('FR FR', 'French', 'Français', array(
'DBTools:DetailedErrorTitle' => '%2$s erreur(s) dans la classe %1$s : %3$s',
'DBAnalyzer-Integrity-OrphanRecord' => 'Enregistrement orphelin dans `%1$s`, il devrait avoir son équivalent dans la table `%2$s`',
'DBAnalyzer-Integrity-InvalidExtKey' => 'Clef externe invalide %1$s (colonne: `%2$s.%3$s`)',
'DBAnalyzer-Integrity-MissingExtKey' => 'Clef externe manquante %1$s (colonne: `%2$s.%3$s`)',
'DBAnalyzer-Integrity-InvalidExtKey' => 'Clé externe invalide %1$s (colonne: `%2$s.%3$s`)',
'DBAnalyzer-Integrity-MissingExtKey' => 'Clé externe manquante %1$s (colonne: `%2$s.%3$s`)',
'DBAnalyzer-Integrity-InvalidValue' => 'Valeur invalide pour %1$s (colonne: `%2$s.%3$s`)',
'DBAnalyzer-Integrity-UsersWithoutProfile' => 'Certains comptes utilisateurs n\'ont aucun profile',
'DBAnalyzer-Integrity-HKInvalid' => 'Clé hiérarchique `%1$s` invalide',
'DBAnalyzer-Fetch-Count-Error' => 'Erreur de récupération dans `%1$s`, %2$d enregistrements récupérés / %3$d comptés',
'DBAnalyzer-Integrity-FinalClass' => 'Le champ `%2$s`.`%1$s` doit avoir la même valeur que `%3$s`.`%1$s`',
'DBAnalyzer-Integrity-RootFinalClass' => 'Le champ `%2$s`.`%1$s` doit contenir une classe valide',

View File

@@ -34,6 +34,7 @@ SetupWebPage::AddModule(
// Setup
//
'dependencies' => array(
'itop-structure/3.0.0',
),
'mandatory' => false,
'visible' => true,

View File

@@ -40,9 +40,11 @@ class DBAnalyzerUtils
$iCount = $aError['count'];
fwrite($fReport, '-- Count: '.$iCount."\r\n");
fwrite($fReport, '-- Error: '.$sErrorLabel."\r\n");
$sQuery = $aError['query'];
fwrite($fReport, '-- Query: '.$sQuery."\r\n");
if (array_key_exists('query', $aError)) {
$sQuery = $aError['query'];
fwrite($fReport, '-- Query: '.$sQuery."\r\n");
}
if (isset($aError['fixit']))
{
fwrite($fReport, "\r\n-- Fix it (indication):\r\n\r\n");

View File

@@ -195,14 +195,15 @@
<banner>/images/icons/icons8-important-book.svg</banner>
<options type="array">
<choice>
<extension_code>itop-kown-error-mgmt</extension_code>
<title>Known Errors Management and FAQ</title>
<description>Select this option to track "Known Errors" and FAQs in iTop.</description>
<modules type="array">
<module>itop-faq-light</module>
<module>itop-knownerror-mgmt</module>
</modules>
</choice>
<!-- Extension code has a typo but fixing it would remove that setup option for all existing iTop -->
<extension_code>itop-kown-error-mgmt</extension_code>
<title>Known Errors Management and FAQ</title>
<description>Select this option to track "Known Errors" and FAQs in iTop.</description>
<modules type="array">
<module>itop-faq-light</module>
<module>itop-knownerror-mgmt</module>
</modules>
</choice>
<choice>
<extension_code>itop-problem-mgmt</extension_code>
<title>Problem Management</title>

View File

@@ -15,14 +15,14 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with iTop. If not, see <http://www.gnu.org/licenses/>
/**
* Localized data
/**
* Spanish Localized data
*
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
* @traductor Miguel Turrubiates <miguel_tf@yahoo.com>
*/
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Attachments:TabTitle_Count' => 'Anexos (%1$d)',
'Attachments:EmptyTabTitle' => 'Anexos',
'Attachments:FieldsetTitle' => 'Anexos',
@@ -36,12 +36,12 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Attachment:Max_Ko' => '(Tamaño Máximo de Archivo: %1$s Kb)',
'Attachments:NoAttachment' => 'No hay Anexo. ',
'Attachments:PreviewNotAvailable' => 'Vista preliminar no disponible para este tipo de Anexo.',
'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s~~',
'Attachments:Error:UploadedFileEmpty' => 'The received file is empty and cannot be attached.
Either you have pushed an empty file,
or ask your iTop administrator if the iTop server disk is full.~~',
'Attachments:Render:Icons' => 'Display as icons~~',
'Attachments:Render:Table' => 'Display as list~~',
'Attachments:Error:FileTooLarge' => 'El archivo es demasiado grande para ser cargado. %1$s',
'Attachments:Error:UploadedFileEmpty' => 'El archivo recibido está vacío y no puede ser anexado.
Puede ser que haya enviado un archivo vació,
o pregunte al administador de iTop si el servidor que ha quedado sin espacio en disco.',
'Attachments:Render:Icons' => 'Desplegar como icono',
'Attachments:Render:Table' => 'Desplegar como lista',
'UI:Attachments:DropYourFileHint' => 'Drop files anywhere in this area~~',
));
@@ -49,41 +49,41 @@ or ask your iTop administrator if the iTop server disk is full.~~',
// Class: Attachment
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:Attachment' => 'Attachment~~',
'Class:Attachment+' => '~~',
'Class:Attachment/Attribute:expire' => 'Expire~~',
'Class:Attachment/Attribute:expire+' => '~~',
'Class:Attachment/Attribute:temp_id' => 'Temporary id~~',
'Class:Attachment/Attribute:temp_id+' => '~~',
'Class:Attachment/Attribute:item_class' => 'Item class~~',
'Class:Attachment/Attribute:item_class+' => '~~',
'Class:Attachment/Attribute:item_id' => 'Item~~',
'Class:Attachment/Attribute:item_id+' => '~~',
'Class:Attachment/Attribute:item_org_id' => 'Item organization~~',
'Class:Attachment/Attribute:item_org_id+' => '~~',
'Class:Attachment/Attribute:contents' => 'Contents~~',
'Class:Attachment/Attribute:contents+' => '~~',
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:Attachment' => 'Anexo',
'Class:Attachment+' => 'Anexo',
'Class:Attachment/Attribute:expire' => 'Expira',
'Class:Attachment/Attribute:expire+' => '',
'Class:Attachment/Attribute:temp_id' => 'Id Temporal',
'Class:Attachment/Attribute:temp_id+' => '',
'Class:Attachment/Attribute:item_class' => 'Clase de Elemento',
'Class:Attachment/Attribute:item_class+' => '',
'Class:Attachment/Attribute:item_id' => 'Elemento',
'Class:Attachment/Attribute:item_id+' => '',
'Class:Attachment/Attribute:item_org_id' => 'Organización de Elemento',
'Class:Attachment/Attribute:item_org_id+' => '',
'Class:Attachment/Attribute:contents' => 'Contenido',
'Class:Attachment/Attribute:contents+' => '',
));
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Attachments:File:Thumbnail' => 'Icon~~',
'Attachments:File:Name' => 'File name~~',
'Attachments:File:Date' => 'Upload date~~',
'Attachments:File:Uploader' => 'Uploaded by~~',
'Attachments:File:Size' => 'Size~~',
'Attachments:File:MimeType' => 'Type~~',
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Attachments:File:Thumbnail' => 'Ícono',
'Attachments:File:Name' => 'Nombre de Archivo',
'Attachments:File:Date' => 'Fecha de Carga',
'Attachments:File:Uploader' => 'Cargado por',
'Attachments:File:Size' => 'Tamaño',
'Attachments:File:MimeType' => 'Tipo',
));
//
// Class: Attachment
//
Dict::Add('ES CR', 'Spanish', 'Español, Castellaño', array(
'Class:Attachment/Attribute:creation_date' => 'Creation date~~',
'Class:Attachment/Attribute:creation_date+' => '~~',
'Class:Attachment/Attribute:user_id' => 'User id~~',
'Class:Attachment/Attribute:user_id+' => '~~',
'Class:Attachment/Attribute:contact_id' => 'Contact id~~',
'Class:Attachment/Attribute:contact_id+' => '~~',
Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
'Class:Attachment/Attribute:creation_date' => 'Fecha de Creación',
'Class:Attachment/Attribute:creation_date+' => '',
'Class:Attachment/Attribute:user_id' => 'Id del Usuario',
'Class:Attachment/Attribute:user_id+' => '',
'Class:Attachment/Attribute:contact_id' => 'Id del Contacto',
'Class:Attachment/Attribute:contact_id+' => '',
));

View File

@@ -28,6 +28,7 @@ use Combodo\iTop\Application\UI\Base\Component\Button\Button;
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Input\FileSelect\FileSelectUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
use Combodo\iTop\Renderer\BlockRenderer;
define('ATTACHMENT_DOWNLOAD_URL', 'pages/ajax.document.php?operation=download_document&class=Attachment&field=contents&id=');
@@ -448,10 +449,13 @@ class TableDetailsAttachmentsRenderer extends AbstractAttachmentsRenderer
if ($bWithDeleteButton) {
$aAttribs['delete'] = array('label' => '', 'description' => '');
}
$oPanel = PanelUIBlockFactory::MakeNeutral('');
$oPanel->AddCSSClass('ibo-datatable-panel');
$oAttachmentTableBlock = DataTableUIBlockFactory::MakeForStaticData('', $aAttribs, $aData);
$oAttachmentTableBlock->AddCSSClass('ibo-attachment--datatable');
$this->oPage->AddUiBlock($oAttachmentTableBlock);
$oPanel->AddSubBlock($oAttachmentTableBlock);
$this->oPage->AddUiBlock($oPanel);
$sTableId = $oAttachmentTableBlock->GetId();

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