diff --git a/css/backoffice/components/_datatable.scss b/css/backoffice/components/_datatable.scss index 08ae5d00a..fb644ca0e 100644 --- a/css/backoffice/components/_datatable.scss +++ b/css/backoffice/components/_datatable.scss @@ -151,17 +151,3 @@ $ibo-fieldsorter--selected--background-color: $ibo-color-blue-200 !default; } - -#table-row-action-confirmation-dialog{ - - .ibo-row-action--confirmation--explanation{ - margin-bottom: 16px; - } - - .ibo-row-action--confirmation--do-not-show-again--checkbox{ - height: auto; - display: inline-block; - width: auto; - } - -} \ No newline at end of file diff --git a/css/backoffice/components/_modal.scss b/css/backoffice/components/_modal.scss index 6054677e4..0cb0f44de 100644 --- a/css/backoffice/components/_modal.scss +++ b/css/backoffice/components/_modal.scss @@ -1,4 +1,18 @@ /* * @copyright Copyright (C) 2010-2021 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 - */ \ No newline at end of file + */ + +/* SCSS variables */ +$ibo-modal-option--do-not-show-again--margin-top: $ibo-spacing-500 !default; + +// Modal Option - Do not show again +.ibo-modal-option--do-not-show-again{ + margin-top: $ibo-modal-option--do-not-show-again--margin-top; + + .ibo-modal-option--do-not-show-again--checkbox{ + height: auto; + display: inline-block; + width: auto; + } +} \ No newline at end of file diff --git a/datamodels/2.x/itop-portal-base/portal/public/js/toolbox.js b/datamodels/2.x/itop-portal-base/portal/public/js/toolbox.js index 9714a3c79..e6a1afaed 100644 --- a/datamodels/2.x/itop-portal-base/portal/public/js/toolbox.js +++ b/datamodels/2.x/itop-portal-base/portal/public/js/toolbox.js @@ -126,7 +126,7 @@ CombodoModal._InstantiateModal = function(oModalElem, oOptions) { oModalElem.find('.modal-content').html(oOptions.content); // Internal callbacks - this._OnContentLoaded(oModalElem, oOptions.callbackOnContentLoaded); + this._OnContentLoaded(oModalElem, oOptions.callback_on_content_loaded); // Manually triggers bootstrap event in order to keep listeners working oModalElem.trigger('loaded.bs.modal'); @@ -148,7 +148,7 @@ CombodoModal._InstantiateModal = function(oModalElem, oOptions) { } // Internal callbacks - me._OnContentLoaded(oModalElem, oOptions.callbackOnContentLoaded); + me._OnContentLoaded(oModalElem, oOptions.callback_on_content_loaded); //Manually triggers bootstrap event in order to keep listeners working oModalElem.trigger('loaded.bs.modal'); diff --git a/dictionaries/ui/components/modal/en.dictionary.itop.modal.php b/dictionaries/ui/components/modal/en.dictionary.itop.modal.php new file mode 100644 index 000000000..52f8a3cb2 --- /dev/null +++ b/dictionaries/ui/components/modal/en.dictionary.itop.modal.php @@ -0,0 +1,22 @@ + 'Confirmation', +)); \ No newline at end of file diff --git a/dictionaries/ui/components/modal/fr.dictionary.itop.modal.php b/dictionaries/ui/components/modal/fr.dictionary.itop.modal.php new file mode 100644 index 000000000..60a3ffb7b --- /dev/null +++ b/dictionaries/ui/components/modal/fr.dictionary.itop.modal.php @@ -0,0 +1,22 @@ + 'Confirmation', +)); \ No newline at end of file diff --git a/js/dataTables.row-actions.js b/js/dataTables.row-actions.js index a7993a5e2..98f6c12fd 100644 --- a/js/dataTables.row-actions.js +++ b/js/dataTables.row-actions.js @@ -3,9 +3,6 @@ * @license http://opensource.org/licenses/AGPL-3.0 */ -const TABLE_ACTION_CONFIRMATION_PREFIX = 'table_action_row'; -const TABLE_ACTION_CONFIRMATION_DIALOG_SELECTOR = '#table-row-action-confirmation-dialog'; - /** * Return column JSON declaration for row actions. * Could be part of column or columnDefs declaration of datatable.js. @@ -30,77 +27,4 @@ function getRowActionsColumnDefinition(sTableId, iColumnTargetIndex = -1) } return aColumn; -} - - -/** - * HandleActionRowConfirmation. - * - * @param sTitle title for confirmation dialog - * @param sMessage message of the confirmation dialog - * @param sDoNotShowAgainPreferenceKey iTop preference key to store "do not show again" flag - * @param oConfirmHandler confirm button handler - * @param aConfirmHandlerData confirm button handler data - * @constructor - */ -const HandleActionRowConfirmation = function (sTitle, sMessage, sDoNotShowAgainPreferenceKey, oConfirmHandler, aConfirmHandlerData){ - - // confirmation preference - if(sDoNotShowAgainPreferenceKey != null){ - - // retrieve need confirmation user preference - let bNeedConfirmation = GetUserPreferenceAsBoolean(`${TABLE_ACTION_CONFIRMATION_PREFIX}.${sDoNotShowAgainPreferenceKey}`, true); - - // confirm handler if no confirmation requested - if(!bNeedConfirmation){ - oConfirmHandler(aConfirmHandlerData.datatable, aConfirmHandlerData.tr_element, aConfirmHandlerData.action_id, aConfirmHandlerData.row_data); - return; - } - } - - // fill confirmation dialog - $('.ibo-row-action--confirmation--explanation', $(TABLE_ACTION_CONFIRMATION_DIALOG_SELECTOR)).html(sMessage); - $('.ibo-row-action--confirmation--do-not-show-again', $(TABLE_ACTION_CONFIRMATION_DIALOG_SELECTOR)).toggle(sDoNotShowAgainPreferenceKey != null); - - // open confirmation dialog - $(TABLE_ACTION_CONFIRMATION_DIALOG_SELECTOR).dialog({ - autoOpen: false, - minWidth: 400, - modal: true, - title: sTitle, - autoOpen: true, - position: {my: "center center", at: "center center", of: $('body')}, - close: function () { - // destroy dialog object - $(TABLE_ACTION_CONFIRMATION_DIALOG_SELECTOR).dialog( "destroy" ); - }, - buttons: [ - { - text: Dict.S('UI:Button:Cancel'), - class: 'ibo-is-alternative', - click: function () { - // close dialog - $(TABLE_ACTION_CONFIRMATION_DIALOG_SELECTOR).dialog('close'); - } - }, - { - text: Dict.S('UI:Button:Ok'), - class: 'ibo-is-primary', - click: function () { - // handle "do not show again" user preference - if(sDoNotShowAgainPreferenceKey != null){ - // save preference - const bDoNotShowAgain = $(this).find($('.ibo-row-action--confirmation--do-not-show-again--checkbox')).prop('checked'); - if (bDoNotShowAgain) { - SetUserPreference(`${TABLE_ACTION_CONFIRMATION_PREFIX}.${sDoNotShowAgainPreferenceKey}`, 'false', true); - } - } - // call confirm handler and close dialog - if(oConfirmHandler(aConfirmHandlerData.datatable, aConfirmHandlerData.tr_element, aConfirmHandlerData.action_id, aConfirmHandlerData.row_data)){ - $(TABLE_ACTION_CONFIRMATION_DIALOG_SELECTOR).dialog('close'); - } - } - }, - ], - }); -} +} \ No newline at end of file diff --git a/js/dataTables.settings.js b/js/dataTables.settings.js index 52754a972..335012933 100644 --- a/js/dataTables.settings.js +++ b/js/dataTables.settings.js @@ -111,6 +111,13 @@ $(function () { } }); + // Append row actions column + if (me.options.bHasRowActions) { + sThead += ""; + let iColumnCount = aOptions['columns'].length; + aOptions["columns"][iColumnCount] = getRowActionsColumnDefinition(oParams.list_id); + } + parentElt.append(""+ ""+sThead+"
"); aOptions["lengthMenu"] = [[oParams.end, oParams.end * 2, oParams.end * 3, oParams.end * 4, -1], [oParams.end, oParams.end * 2, oParams.end * 3, oParams.end * 4, aOptions["lengthMenu"]]]; diff --git a/js/pages/backoffice/toolbox.js b/js/pages/backoffice/toolbox.js index 8f72a08f2..3398b5bdf 100644 --- a/js/pages/backoffice/toolbox.js +++ b/js/pages/backoffice/toolbox.js @@ -201,7 +201,10 @@ CombodoModal._InstantiateModal = function(oModalElem, oOptions) { width: 'auto', height: 'auto', modal: oOptions.extra_options.modal ?? true, + close: oOptions.extra_options.callback_on_modal_close, autoOpen: oOptions.auto_open, + title: oOptions.title, + buttons: this._ConvertButtonDefinition(oOptions.buttons) }; // Resize to desired size @@ -245,7 +248,7 @@ CombodoModal._InstantiateModal = function(oModalElem, oOptions) { { case 'string': oModalElem.html(oOptions.content); - this._OnContentLoaded(oModalElem, oOptions.callbackOnContentLoaded); + this._OnContentLoaded(oModalElem, oOptions.callback_on_content_loaded); break; case 'object': @@ -274,7 +277,7 @@ CombodoModal._InstantiateModal = function(oModalElem, oOptions) { me._CenterModalInViewport(oModalElem); }, 500); - me._OnContentLoaded(oModalElem, oOptions.callbackOnContentLoaded); + me._OnContentLoaded(oModalElem, oOptions.callback_on_content_loaded); } ); break; @@ -313,6 +316,28 @@ CombodoModal._InstantiateModal = function(oModalElem, oOptions) { return true; }; + +/** + * Convert generic buttons definitions to jquery ui dialog definitions. + * + * @param aButtonsDefinitions + * @returns {*[]} + * @constructor + */ +CombodoModal._ConvertButtonDefinition = function(aButtonsDefinitions){ + const aConverted = []; + aButtonsDefinitions.forEach(element => { + const aButton = { + text: element.text, + class: element.class, + click: element.callback_on_click + } + aConverted.push(aButton); + } + ); + return aConverted; +} + /** * @override * @inheritDoc @@ -323,6 +348,85 @@ CombodoModal._CenterModalInViewport = function (oModalElem) { }); }; +/** + * Open a standard confirmation modal and put the content into it. + * + * @param oOptions array @see CombodoModal.OpenModal + {do_not_show_again_pref_key: string, callback_on_confirm: function, callback_on_cancel} + * @param aData data passed to callbacks + * @returns object The jQuery object of the modal element + */ +CombodoModal.OpenConfirmationModal = function(oOptions, aData) { + + // Check do not show again preference key + if(oOptions.do_not_show_again_pref_key !== null){ + if(GetUserPreference(oOptions.do_not_show_again_pref_key, false)){ + if(oOptions.callback_on_confirm !== null){ + oOptions.callback_on_confirm(...aData); + } + return; + } + } + // Merge external options with confirmation modal default options + oOptions = $.extend({ + title: Dict.S('UI:Modal:DefaultConfirmationTitle'), + content: '', + do_not_show_again_pref_key: null, + callback_on_confirm: null, + callback_on_cancel: null, + extra_options: { + callback_on_modal_close: function () { + $(this).dialog( "destroy" ); // destroy dialog object + } + }, + buttons: [ + { + text: Dict.S('UI:Button:Cancel'), + class: 'ibo-is-alternative', + callback_on_click: function () { + // call confirm handler and close dialog + let bCanClose = true; + if(oOptions.callback_on_cancel != null){ + bCanClose = oOptions.callback_on_cancel(...aData) !== false; + } + if(bCanClose){ + $(this).dialog('close'); // close dialog + } + } + }, + { + text: Dict.S('UI:Button:Ok'), + class: 'ibo-is-primary', + callback_on_click: function () { + // Call confirm handler and close dialog + let bCanClose = true; + if(oOptions.callback_on_confirm != null){ + bCanClose = oOptions.callback_on_confirm(...aData) !== false; + } + if(bCanClose){ + $(this).dialog('close'); // close dialog + // Handle "do not show again" user preference + let bDoNotShowAgain = oOptions.do_not_show_again_pref_key !== null ? + $('[name="do_not_show_again"]', $(this)).prop('checked') : + false; + if (bDoNotShowAgain) { + SetUserPreference(oOptions.do_not_show_again_pref_key, true, true); + } + } + } + } + ], + callback_on_content_loaded: function(oModalContentElement){ + // Add option do not show again from template + if(oOptions.do_not_show_again_pref_key !== null) { + oModalContentElement.append($('#ibo-modal-option--do-not-show-again-template').html()); + } + } + }, oOptions); + + // Open modal + CombodoModal.OpenModal(oOptions); +} + // Processing on each pages of the backoffice $(document).ready(function(){ // Initialize global keyboard shortcuts diff --git a/js/utils.js b/js/utils.js index 923f63d36..c42ad1a14 100644 --- a/js/utils.js +++ b/js/utils.js @@ -1121,6 +1121,7 @@ let CombodoModal = { * * @param sTargetUrl {String} * @param bCloseOtherModals {String} + * @param callbackOnContentLoaded {function} * @return {Object} The jQuery object representing the modal element * @api */ @@ -1145,7 +1146,7 @@ let CombodoModal = { }; if (callbackOnContentLoaded !== undefined) { - oOptions.callbackOnContentLoaded = callbackOnContentLoaded; + oOptions.callback_on_content_loaded = callbackOnContentLoaded; } // Opening modal @@ -1170,10 +1171,12 @@ let CombodoModal = { usage: 'clone', // Either 'clone' or 'replace' selector: this._GetDefaultBaseModalSelector() // Either a selector of the modal element used to base this one on or the modal element itself }, + title: undefined, // Title of the modal content: undefined, // Either a string, an object containing the endpoint / data or undefined to keep base modal content as-is + buttons: null, size: 'auto', // Either 'auto' / 'xs' / 'sm' / 'md' / 'lg' or specific height & width via {width: '80px', height: '100px'} auto_open: true, // true for the modal to open automatically on instantiation - callbackOnContentLoaded: null, // Callback to call once the content is loaded. Arguments will be oModalElem (the jQuery object representing the modal) + callback_on_content_loaded: null, // Callback to call once the content is loaded. Arguments will be oModalElem (the jQuery object representing the modal) callback_on_content_loaded extra_options: {}, // Extra options to pass to the modal lib directly if they are not handled by the CombodoModal widget yet }, oOptions @@ -1206,7 +1209,7 @@ let CombodoModal = { if (oOptions.base_modal.usage === 'clone') { // Clone modal using a real template if (oSelectorElem[0].tagName === 'TEMPLATE') { - oModalElem = $(oSelectorElem[0].content.firstElementChild.cloneNode(true)); + oModalElem = $(oSelectorElem.html()); } // Clone modal using an existing element else { @@ -1216,8 +1219,7 @@ let CombodoModal = { // Force modal to have an HTML ID, otherwise it can lead to complications, especially with the portal_leave_handle.js // See N°3469 let sModalID = (oOptions.id !== null) ? oOptions.id : 'modal-with-generated-id-'+Date.now(); - oModalElem.attr('id', sModalID) - .appendTo('body'); + oModalElem.attr('id', sModalID); } // - Get an existing modal in the DOM else { @@ -1291,5 +1293,16 @@ let CombodoModal = { callback(oModalElem); } } + }, + + /** + * Open a standard confirmation modal and put the content into it. + * + * @param oOptions + * @returns object The jQuery object of the modal element + */ + OpenConfirmationModal: function(oOptions) { + // Meant for overlaoding + CombodoJSConsole.Debug('CombodoModal.OpenConfirmationModal not implemented'); } }; \ No newline at end of file diff --git a/lib/composer/InstalledVersions.php b/lib/composer/InstalledVersions.php index 7c5502ca4..d50e0c9fc 100644 --- a/lib/composer/InstalledVersions.php +++ b/lib/composer/InstalledVersions.php @@ -24,8 +24,21 @@ use Composer\Semver\VersionParser; */ class InstalledVersions { + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array}|array{}|null + */ private static $installed; + + /** + * @var bool|null + */ private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ private static $installedByVendor = array(); /** diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index c74acc1c2..715cc6475 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -145,10 +145,6 @@ return array( 'CAS_Request_Exception' => $vendorDir . '/apereo/phpcas/source/CAS/Request/Exception.php', 'CAS_Request_MultiRequestInterface' => $vendorDir . '/apereo/phpcas/source/CAS/Request/MultiRequestInterface.php', 'CAS_Request_RequestInterface' => $vendorDir . '/apereo/phpcas/source/CAS/Request/RequestInterface.php', - 'CAS_ServiceBaseUrl_AllowedListDiscovery' => $vendorDir . '/apereo/phpcas/source/CAS/ServiceBaseUrl/AllowedListDiscovery.php', - 'CAS_ServiceBaseUrl_Base' => $vendorDir . '/apereo/phpcas/source/CAS/ServiceBaseUrl/Base.php', - 'CAS_ServiceBaseUrl_Interface' => $vendorDir . '/apereo/phpcas/source/CAS/ServiceBaseUrl/Interface.php', - 'CAS_ServiceBaseUrl_Static' => $vendorDir . '/apereo/phpcas/source/CAS/ServiceBaseUrl/Static.php', 'CAS_Session_PhpSession' => $vendorDir . '/apereo/phpcas/source/CAS/Session/PhpSession.php', 'CAS_TypeMismatchException' => $vendorDir . '/apereo/phpcas/source/CAS/TypeMismatchException.php', 'CLILikeWebPage' => $baseDir . '/sources/Application/WebPage/CLILikeWebPage.php', @@ -233,8 +229,6 @@ return array( 'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\StaticTable\\FormTable\\FormTable' => $baseDir . '/sources/Application/UI/Base/Component/DataTable/StaticTable/FormTable/FormTable.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\StaticTable\\StaticTable' => $baseDir . '/sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\tTableRowActions' => $baseDir . '/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php', - 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Dialog\\Dialog' => $baseDir . '/sources/Application/UI/Base/Component/Dialog/Dialog.php', - 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Dialog\\DialogUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/Dialog/DialogUIBlockFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldBadge\\FieldBadge' => $baseDir . '/sources/Application/UI/Base/Component/FieldBadge/FieldBadge.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldBadge\\FieldBadgeUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/FieldBadge/FieldBadgeUIBlockFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldSet\\FieldSet' => $baseDir . '/sources/Application/UI/Base/Component/FieldSet/FieldSet.php', @@ -262,6 +256,7 @@ return array( 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Input\\TextArea' => $baseDir . '/sources/Application/UI/Base/Component/Input/TextArea.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Input\\tInputLabel' => $baseDir . '/sources/Application/UI/Base/Component/Input/tInputLabel.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\MedallionIcon\\MedallionIcon' => $baseDir . '/sources/Application/UI/Base/Component/MedallionIcon/MedallionIcon.php', + 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Modal\\DoNotShowAgainOptionBlock' => $baseDir . '/sources/Application/UI/Base/Component/Modal/DoNotShowAgainOptionBlock.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Panel\\Panel' => $baseDir . '/sources/Application/UI/Base/Component/Panel/Panel.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Panel\\PanelUIBlockFactory' => $baseDir . '/sources/Application/UI/Base/Component/Panel/PanelUIBlockFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Pill\\Pill' => $baseDir . '/sources/Application/UI/Base/Component/Pill/Pill.php', diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index d89144835..67161ae8a 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -510,10 +510,6 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'CAS_Request_Exception' => __DIR__ . '/..' . '/apereo/phpcas/source/CAS/Request/Exception.php', 'CAS_Request_MultiRequestInterface' => __DIR__ . '/..' . '/apereo/phpcas/source/CAS/Request/MultiRequestInterface.php', 'CAS_Request_RequestInterface' => __DIR__ . '/..' . '/apereo/phpcas/source/CAS/Request/RequestInterface.php', - 'CAS_ServiceBaseUrl_AllowedListDiscovery' => __DIR__ . '/..' . '/apereo/phpcas/source/CAS/ServiceBaseUrl/AllowedListDiscovery.php', - 'CAS_ServiceBaseUrl_Base' => __DIR__ . '/..' . '/apereo/phpcas/source/CAS/ServiceBaseUrl/Base.php', - 'CAS_ServiceBaseUrl_Interface' => __DIR__ . '/..' . '/apereo/phpcas/source/CAS/ServiceBaseUrl/Interface.php', - 'CAS_ServiceBaseUrl_Static' => __DIR__ . '/..' . '/apereo/phpcas/source/CAS/ServiceBaseUrl/Static.php', 'CAS_Session_PhpSession' => __DIR__ . '/..' . '/apereo/phpcas/source/CAS/Session/PhpSession.php', 'CAS_TypeMismatchException' => __DIR__ . '/..' . '/apereo/phpcas/source/CAS/TypeMismatchException.php', 'CLILikeWebPage' => __DIR__ . '/../..' . '/sources/Application/WebPage/CLILikeWebPage.php', @@ -598,8 +594,6 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\StaticTable\\FormTable\\FormTable' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/DataTable/StaticTable/FormTable/FormTable.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\StaticTable\\StaticTable' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/DataTable/StaticTable/StaticTable.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\DataTable\\tTableRowActions' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php', - 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Dialog\\Dialog' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Dialog/Dialog.php', - 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Dialog\\DialogUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Dialog/DialogUIBlockFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldBadge\\FieldBadge' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/FieldBadge/FieldBadge.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldBadge\\FieldBadgeUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/FieldBadge/FieldBadgeUIBlockFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\FieldSet\\FieldSet' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/FieldSet/FieldSet.php', @@ -627,6 +621,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Input\\TextArea' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Input/TextArea.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Input\\tInputLabel' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Input/tInputLabel.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\MedallionIcon\\MedallionIcon' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/MedallionIcon/MedallionIcon.php', + 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Modal\\DoNotShowAgainOptionBlock' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Modal/DoNotShowAgainOptionBlock.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Panel\\Panel' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Panel/Panel.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Panel\\PanelUIBlockFactory' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Panel/PanelUIBlockFactory.php', 'Combodo\\iTop\\Application\\UI\\Base\\Component\\Pill\\Pill' => __DIR__ . '/../..' . '/sources/Application/UI/Base/Component/Pill/Pill.php', diff --git a/lib/composer/installed.php b/lib/composer/installed.php index fff50ab0f..3b6d4406b 100644 --- a/lib/composer/installed.php +++ b/lib/composer/installed.php @@ -5,7 +5,7 @@ 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => 'cdde765a85ee0262181e3c493183b1fb80536e74', + 'reference' => '64d9eef7c926f98aa1aabe61294397be308dd885', 'name' => 'combodo/itop', 'dev' => true, ), @@ -25,7 +25,7 @@ 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), - 'reference' => 'cdde765a85ee0262181e3c493183b1fb80536e74', + 'reference' => '64d9eef7c926f98aa1aabe61294397be308dd885', 'dev_requirement' => false, ), 'combodo/tcpdf' => array( diff --git a/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php b/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php index 289e8711b..9add4be7b 100644 --- a/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php +++ b/sources/Application/UI/Base/Component/DataTable/DataTableUIBlockFactory.php @@ -214,7 +214,7 @@ class DataTableUIBlockFactory extends AbstractUIBlockFactory array_key_exists('tooltip', $aAction) ? Dict::S($aAction['tooltip']) : '', array_key_exists('name', $aAction) ? $aAction['name'] : 'undefined' ); - $oButton->SetDataAttributes(['action-id' => $iKey]); + $oButton->SetDataAttributes(['action-id' => $iKey, 'tooltip-append-to' => 'body']); $oToolbar->AddSubBlock($oButton); } diff --git a/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php b/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php index 632d5b3c4..264763558 100644 --- a/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php +++ b/sources/Application/UI/Base/Component/DataTable/tTableRowActions.php @@ -7,8 +7,6 @@ namespace Combodo\iTop\Application\UI\Base\Component\DataTable; use Combodo\iTop\Application\UI\Base\Component\Dialog\DialogUIBlockFactory; -use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory; -use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory; /** * Trait tTableRowActions @@ -33,11 +31,11 @@ trait tTableRowActions * confirmation => { * message: string, * message_row_data: string, - * remember_choice_pref_key: string + * do_not_show_again_pref_key: string * } * } */ - protected $aRowActions; + protected $aRowActions = []; /** * Set row actions. @@ -82,31 +80,4 @@ trait tTableRowActions { return DataTableUIBlockFactory::MakeActionRowToolbarTemplate($this); } - - /** - * GetRowActionsConfirmDialog. - * - * @return \Combodo\iTop\Application\UI\Base\Component\Html\Html - */ - public function GetRowActionsConfirmDialog() - { - static::$bDialogInitialized = true; - - $oDialog = DialogUIBlockFactory::MakeNeutral('', '
', 'table-row-action-confirmation-dialog'); - - $oContent = UIContentBlockUIBlockFactory::MakeStandard(); - $oContent->AddCSSClass('ibo-row-action--confirmation--do-not-show-again'); - $checkBox = InputUIBlockFactory::MakeStandard('checkbox', 'do_not_show_again', false); - $checkBox->AddCSSClass('ibo-row-action--confirmation--do-not-show-again--checkbox'); - $checkBox->SetLabel(\Dict::S('UI:UserPref:DoNotShowAgain')); - $oContent->AddSubBlock($checkBox); - $oDialog->AddSubBlock($oContent); - - return $oDialog; - } - - public function GetRowActionsConfirmDialogInitializedFlag() - { - return static::$bDialogInitialized; - } } \ No newline at end of file diff --git a/sources/Application/UI/Base/Component/Dialog/Dialog.php b/sources/Application/UI/Base/Component/Dialog/Dialog.php deleted file mode 100644 index 722ba2902..000000000 --- a/sources/Application/UI/Base/Component/Dialog/Dialog.php +++ /dev/null @@ -1,109 +0,0 @@ -sContent = $sContent; - if (!empty($sContent)) { - $this->AddSubBlock(new Html($sContent)); - } - } - - /** - * @return string - */ - public function GetTitle(): string - { - return $this->sTitle; - } - - /** - * @param string $sTitle Title of the alert - * - * @return $this - */ - public function SetTitle(string $sTitle): Dialog - { - $this->sTitle = $sTitle; - - return $this; - } - - /** - * Return the raw HTML content, should be already sanitized. - * - * @return string - */ - public function GetContent(): string - { - return $this->sContent; - } - - /** - * Set the raw HTML content, must be already sanitized. - * - * @param string $sContent The raw HTML content, must be already sanitized - * - * @return $this - */ - public function SetContent(string $sContent): Dialog - { - $this->sContent = $sContent; - - return $this; - } - - - -} \ No newline at end of file diff --git a/sources/Application/UI/Base/Component/Dialog/DialogUIBlockFactory.php b/sources/Application/UI/Base/Component/Dialog/DialogUIBlockFactory.php deleted file mode 100644 index 0fa444794..000000000 --- a/sources/Application/UI/Base/Component/Dialog/DialogUIBlockFactory.php +++ /dev/null @@ -1,54 +0,0 @@ -InitUI(); + } + + /** + * Initialize UI. + * + * @return void + */ + private function InitUI() + { + // Create checkbox + $oCheckBox = InputUIBlockFactory::MakeStandard('checkbox', 'do_not_show_again', false); + $oCheckBox->AddCSSClass('ibo-modal-option--do-not-show-again--checkbox'); + $oCheckBox->SetLabel(Dict::S('UI:UserPref:DoNotShowAgain')); + $this->AddSubBlock($oCheckBox); + } +} \ No newline at end of file diff --git a/sources/Application/UI/Base/Component/Template/TemplateUIBlockFactory.php b/sources/Application/UI/Base/Component/Template/TemplateUIBlockFactory.php index b62220552..8a4c10eb6 100644 --- a/sources/Application/UI/Base/Component/Template/TemplateUIBlockFactory.php +++ b/sources/Application/UI/Base/Component/Template/TemplateUIBlockFactory.php @@ -20,6 +20,7 @@ namespace Combodo\iTop\Application\UI\Base\Component\Template; use Combodo\iTop\Application\UI\Base\AbstractUIBlockFactory; +use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock; /** * Class TemplateUIBlockFactory @@ -47,4 +48,17 @@ class TemplateUIBlockFactory extends AbstractUIBlockFactory { return new Template($sId); } + + /** + * Make a Template component with a block inside. + * + * @return \Combodo\iTop\Application\UI\Base\Component\Template\Template + */ + public static function MakeForBlock(string $sId, UIContentBlock $oContentBlock) + { + $oBlock = TemplateUIBlockFactory::MakeStandard($sId); + $oBlock->AddSubBlock($oContentBlock); + + return $oBlock; + } } \ No newline at end of file diff --git a/sources/Application/UI/Links/Direct/BlockDirectLinksViewTable.php b/sources/Application/UI/Links/Direct/BlockDirectLinksViewTable.php index aed230084..3030a4440 100644 --- a/sources/Application/UI/Links/Direct/BlockDirectLinksViewTable.php +++ b/sources/Application/UI/Links/Direct/BlockDirectLinksViewTable.php @@ -55,9 +55,9 @@ class BlockDirectLinksViewTable extends AbstractBlockLinksViewTable 'icon_classes' => 'fas fa-minus', 'js_row_action' => "LinkSetWorker.DetachLinkedObject('{$this->sTargetClass}', aRowData['{$this->sTargetClass}/_key_/raw'], '{$this->oAttDef->GetExtKeyToMe()}');", 'confirmation' => [ - 'message' => 'UI:Links:ActionRow:detach:confirmation', - 'message_row_data' => "{$this->sTargetClass}/hyperlink", - 'remember_choice_pref_key' => 'LinkSetWorker.DetachLinkedObject', + 'message' => 'UI:Links:ActionRow:detach:confirmation', + 'message_row_data' => "{$this->sTargetClass}/hyperlink", + 'do_not_show_again_pref_key' => 'LinkSetWorker.DetachLinkedObject', ], ); break; @@ -68,9 +68,9 @@ class BlockDirectLinksViewTable extends AbstractBlockLinksViewTable 'icon_classes' => 'fas fa-trash', 'js_row_action' => "LinkSetWorker.DeleteLinkedObject('{$this->oAttDef->GetLinkedClass()}', aRowData['{$this->oAttDef->GetLinkedClass()}/_key_/raw']);", 'confirmation' => [ - 'message' => 'UI:Links:ActionRow:delete:confirmation', - 'message_row_data' => "{$this->sTargetClass}/hyperlink", - 'remember_choice_pref_key' => 'LinkSetWorker.DeleteLinkedObject', + 'message' => 'UI:Links:ActionRow:delete:confirmation', + 'message_row_data' => "{$this->sTargetClass}/hyperlink", + 'do_not_show_again_pref_key' => 'LinkSetWorker.DeleteLinkedObject', ], ); break; diff --git a/sources/Application/UI/Links/Indirect/BlockIndirectLinksViewTable.php b/sources/Application/UI/Links/Indirect/BlockIndirectLinksViewTable.php index db9114990..c0a93c26b 100644 --- a/sources/Application/UI/Links/Indirect/BlockIndirectLinksViewTable.php +++ b/sources/Application/UI/Links/Indirect/BlockIndirectLinksViewTable.php @@ -63,9 +63,9 @@ class BlockIndirectLinksViewTable extends AbstractBlockLinksViewTable 'icon_classes' => 'fas fa-minus', 'js_row_action' => "LinkSetWorker.DeleteLinkedObject('{$this->oAttDef->GetLinkedClass()}', aRowData['Link/_key_/raw']);", 'confirmation' => [ - 'message' => 'UI:Links:ActionRow:detach:confirmation', - 'message_row_data' => "Remote/hyperlink", - 'remember_choice_pref_key' => 'LinkSetWorker.DetachLinkedObject', + 'message' => 'UI:Links:ActionRow:detach:confirmation', + 'message_row_data' => "Remote/hyperlink", + 'do_not_show_again_pref_key' => 'LinkSetWorker.DetachLinkedObject', ], ); diff --git a/sources/Application/WebPage/iTopWebPage.php b/sources/Application/WebPage/iTopWebPage.php index 13e8cda8f..ef3494feb 100644 --- a/sources/Application/WebPage/iTopWebPage.php +++ b/sources/Application/WebPage/iTopWebPage.php @@ -8,7 +8,9 @@ use Combodo\iTop\Application\TwigBase\Twig\TwigHelper; use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Breadcrumbs\Breadcrumbs; +use Combodo\iTop\Application\UI\Base\Component\Modal\DoNotShowAgainOptionBlock; use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory; +use Combodo\iTop\Application\UI\Base\Component\Template\TemplateUIBlockFactory; use Combodo\iTop\Application\UI\Base\iUIBlock; use Combodo\iTop\Application\UI\Base\Layout\iUIContentBlock; use Combodo\iTop\Application\UI\Base\Layout\NavigationMenu\NavigationMenu; @@ -210,6 +212,9 @@ class iTopWebPage extends NiceWebPage implements iTabbedPage $this->add_dict_entry('UI:DisconnectedDlgTitle'); $this->add_dict_entry('UI:LoginAgain'); $this->add_dict_entry('UI:StayOnThePage'); + + // Modals + $this->add_dict_entries('UI:Modal:'); } /** @@ -891,6 +896,9 @@ HTML; // - Prepare content $aData['aLayouts']['oPageContent'] = $this->GetContentLayout(); $aData['aDeferredBlocks']['oPageContent'] = $this->GetDeferredBlocks($this->GetContentLayout()); + // - Prepare generic templates + $aData['aTemplates'] = array(); + $aData['aTemplates'][] = TemplateUIBlockFactory::MakeForBlock('ibo-modal-option--do-not-show-again-template', new DoNotShowAgainOptionBlock()); // - Retrieve layouts linked files // Note: Adding them now instead of in the template allow us to remove duplicates and lower the browser parsing time @@ -1241,4 +1249,5 @@ EOF return parent::SetBlockParam($sKey, $value); } + } diff --git a/templates/base/components/datatable/layout.html.twig b/templates/base/components/datatable/layout.html.twig index bc72bca3c..c8d6d20a2 100644 --- a/templates/base/components/datatable/layout.html.twig +++ b/templates/base/components/datatable/layout.html.twig @@ -28,7 +28,4 @@ {% if oUIBlock.HasRowActions() %} {{ render_block(oUIBlock.GetRowActionsTemplate()) }} - {% if not oUIBlock.GetRowActionsConfirmDialogInitializedFlag() %} - {{ render_block(oUIBlock.GetRowActionsConfirmDialog()) }} - {% endif %} {% endif %} \ No newline at end of file diff --git a/templates/base/components/datatable/layout.ready.js.twig b/templates/base/components/datatable/layout.ready.js.twig index 2588bcc9d..32ffca5a2 100644 --- a/templates/base/components/datatable/layout.ready.js.twig +++ b/templates/base/components/datatable/layout.ready.js.twig @@ -420,6 +420,7 @@ var aOptions{{ sListIDForVarSuffix }} = { oData: {{ oUIBlock.GetJsonAjaxData() |raw }}, oDefaultSettings: {{ oUIBlock.GetOption("oDefaultSettings")|raw }}, oLabels: {moveup: "{{ 'UI:Button:MoveUp'|dict_s }}", movedown: "{{ 'UI:Button:MoveDown'|dict_s }}"}, + bHasRowActions: {{ oUIBlock.HasRowActions()|var_export }}, }; if ($('#datatable_dlg_{{ oUIBlock.GetId() }}').hasClass('itop-datatable')) diff --git a/templates/base/components/datatable/row-actions/handler.js.twig b/templates/base/components/datatable/row-actions/handler.js.twig index f1cfa1bf6..68f69b911 100644 --- a/templates/base/components/datatable/row-actions/handler.js.twig +++ b/templates/base/components/datatable/row-actions/handler.js.twig @@ -15,23 +15,21 @@ {% if aAction.confirmation is defined %} - // Handle action row with confirmation - let sTitle = '{{ 'UI:Datatables:RowActions:ConfirmationDialog'|dict_s }}'; + // Prepare confirmation message let sMessage = '{{ 'UI:Datatables:RowActions:ConfirmationMessage'|dict_s }}'; {% if aAction.confirmation.message is defined %} sMessage = '{{ aAction.confirmation.message|dict_s|raw }}'; - sMessage = sMessage.replaceAll('{item}', aRowData['{{ aAction.confirmation.message_row_data }}']); {% endif %} - let sPrefKey = null; - {% if aAction.confirmation.remember_choice_pref_key is defined %} - sPrefKey = '{{ aAction.confirmation.remember_choice_pref_key }}'; - {% endif %} - HandleActionRowConfirmation (sTitle, sMessage, sPrefKey, ActionRowFunction{{ oUIBlock.GetId() }}{{ loop.index0 }}, { - action_id: iActionId, - datatable: oDatatable, - tr_element: oTrElement, - row_data: aRowData - }); + + // Handle action row with confirmation modal + CombodoModal.OpenConfirmationModal({ + title: '{{ 'UI:Datatables:RowActions:ConfirmationDialog'|dict_s }}', + content: sMessage.replaceAll('{item}', aRowData['{{ aAction.confirmation.message_row_data }}']), + callback_on_confirm: ActionRowFunction{{ oUIBlock.GetId() }}{{ loop.index0 }}, + {% if aAction.confirmation.do_not_show_again_pref_key is defined %} + do_not_show_again_pref_key: '{{ aAction.confirmation.do_not_show_again_pref_key }}', + {% endif %} + }, [oDatatable, oTrElement, iActionId, aRowData]); {% else %} diff --git a/templates/base/components/datatable/static/formtable/layout.html.twig b/templates/base/components/datatable/static/formtable/layout.html.twig index 11a807f62..3da718895 100644 --- a/templates/base/components/datatable/static/formtable/layout.html.twig +++ b/templates/base/components/datatable/static/formtable/layout.html.twig @@ -21,7 +21,4 @@ {% if oUIBlock.HasRowActions() %} {{ render_block(oUIBlock.GetRowActionsTemplate()) }} - {% if not oUIBlock.GetRowActionsConfirmDialogInitializedFlag() %} - {{ render_block(oUIBlock.GetRowActionsConfirmDialog()) }} - {% endif %} {% endif %} \ No newline at end of file diff --git a/templates/base/components/datatable/static/layout.html.twig b/templates/base/components/datatable/static/layout.html.twig index 5e8d9030f..a7b9748b8 100644 --- a/templates/base/components/datatable/static/layout.html.twig +++ b/templates/base/components/datatable/static/layout.html.twig @@ -48,7 +48,4 @@ {% if oUIBlock.HasRowActions() %} {{ render_block(oUIBlock.GetRowActionsTemplate()) }} - {% if not oUIBlock.GetRowActionsConfirmDialogInitializedFlag() %} - {{ render_block(oUIBlock.GetRowActionsConfirmDialog()) }} - {% endif %} {% endif %} \ No newline at end of file diff --git a/templates/base/components/dialog/layout.ready.js.twig b/templates/base/components/dialog/layout.ready.js.twig index d37e9c573..e69de29bb 100644 --- a/templates/base/components/dialog/layout.ready.js.twig +++ b/templates/base/components/dialog/layout.ready.js.twig @@ -1,4 +0,0 @@ -$('#{{ oUIBlock.GetId() }}').alert({ - bOpenedByDefault: {{ oUIBlock.IsOpenedByDefault()|var_export }} - {% if oUIBlock.IsSaveCollapsibleStateEnabled() %}, collapsibleStateStorageKey: '{{ oUIBlock.GetSessionCollapsibleStateStorageKey() }}'{% endif %} -}); \ No newline at end of file diff --git a/templates/pages/backoffice/itopwebpage/layout.html.twig b/templates/pages/backoffice/itopwebpage/layout.html.twig index 0e838c272..9f2992368 100644 --- a/templates/pages/backoffice/itopwebpage/layout.html.twig +++ b/templates/pages/backoffice/itopwebpage/layout.html.twig @@ -45,6 +45,12 @@ + + {# Templates #} + {% for oTemplate in aTemplates %} + {{ render_block(oTemplate, {aPage: aPage}) }} + {% endfor %} + {% endblock %} {% block iboCapturedOutput %}