Feature/modals - generic modal API (#373)

Default modal JS Implementation:

Add title option
Add buttons option
Change template cloning
Confirmation Modal:

Add implementation
Do not show again functionality
Web Page:

Add blocks array with twig loop insertion
This commit is contained in:
bdalsass
2022-12-06 10:18:59 +01:00
committed by GitHub
parent 98a53a46f0
commit dbe7fae82e
29 changed files with 322 additions and 345 deletions

View File

@@ -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;
}
}

View File

@@ -2,3 +2,17 @@
* @copyright Copyright (C) 2010-2021 Combodo SARL
* @license http://opensource.org/licenses/AGPL-3.0
*/
/* 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;
}
}

View File

@@ -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');

View File

@@ -0,0 +1,22 @@
<?php
/**
* Copyright (C) 2013-2021 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
Dict::Add('EN US', 'English', 'English', array(
'UI:Modal:Confirmation:DefaultTitle' => 'Confirmation',
));

View File

@@ -0,0 +1,22 @@
<?php
/**
* Copyright (C) 2013-2021 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
Dict::Add('FR FR', 'French', 'Français', array(
'UI:Modal:DefaultConfirmationTitle' => 'Confirmation',
));

View File

@@ -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.
@@ -31,76 +28,3 @@ 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');
}
}
},
],
});
}

View File

@@ -111,6 +111,13 @@ $(function () {
}
});
// Append row actions column
if (me.options.bHasRowActions) {
sThead += "<th></th>";
let iColumnCount = aOptions['columns'].length;
aOptions["columns"][iColumnCount] = getRowActionsColumnDefinition(oParams.list_id);
}
parentElt.append("<table id=\""+me.options.sListId+"\" width=\"100%\" class=\"ibo-datatable\">"+
"<thead><tr>"+sThead+"</tr></thead></table>");
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"]]];

View File

@@ -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

View File

@@ -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');
}
};

View File

@@ -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<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
*/
private static $installed;
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static $installedByVendor = array();
/**

View File

@@ -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',

View File

@@ -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',

View File

@@ -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(

View File

@@ -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);
}

View File

@@ -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('', '<div class="ibo-row-action--confirmation--explanation"></div>', '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;
}
}

View File

@@ -1,109 +0,0 @@
<?php
/**
* Copyright (C) 2013-2021 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Application\UI\Base\Component\Dialog;
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
/**
*
* @package Combodo\iTop\Application\UI\Base\Component\Dialog
* @since 3.1.0
*/
class Dialog extends UIContentBlock
{
// Overloaded constants
public const BLOCK_CODE = 'ibo-dialog';
public const DEFAULT_HTML_TEMPLATE_REL_PATH = 'base/components/dialog/layout';
public const DEFAULT_JS_ON_READY_TEMPLATE_REL_PATH = 'base/components/dialog/layout';
public const DEFAULT_JS_FILES_REL_PATH = [
'js/components/dialog.js',
];
/** @var string $sTitle */
protected string $sTitle;
/** @var string $sContent */
protected string $sContent;
/**
* Alert constructor.
*
* @param string $sTitle
* @param string $sContent
* @param string|null $sId
*/
public function __construct(string $sTitle = '', string $sContent = '', ?string $sId = null)
{
parent::__construct($sId);
$this->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;
}
}

View File

@@ -1,54 +0,0 @@
<?php
/**
* Copyright (C) 2013-2021 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Application\UI\Base\Component\Dialog;
use Combodo\iTop\Application\UI\Base\AbstractUIBlockFactory;
/**
* Class DialogUIBlockFactory
*
* @api
*
* @since 3.1.0
* @package Combodo\iTop\Application\UI\Base\Component\Dialog
*/
class DialogUIBlockFactory extends AbstractUIBlockFactory
{
/** @inheritDoc */
public const TWIG_TAG_NAME = 'UIDialog';
/** @inheritDoc */
public const UI_BLOCK_CLASS_NAME = Dialog::class;
/**
* Make a basis Dialog component
*
* @param string $sTitle Title of the alert
* @param string $sContent The raw HTML content, must be already sanitized
* @param string|null $sId id of the html block
*
* @return \Combodo\iTop\Application\UI\Base\Component\Alert\Alert
*/
public static function MakeNeutral(string $sTitle = '', string $sContent = '', ?string $sId = null)
{
return new Dialog($sTitle, $sContent, $sId);
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* Copyright (C) 2013-2023 Combodo SARL
*
* This file is part of iTop.
*
* iTop is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* iTop is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
*/
namespace Combodo\iTop\Application\UI\Base\Component\Modal;
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock;
use Dict;
/**
*
* @package Combodo\iTop\Application\UI\Base\Component\Modal
* @since 3.1.0
*/
class DoNotShowAgainOptionBlock extends UIContentBlock
{
/**
* Constructor.
*
* @param string|null $sId
*/
public function __construct(string $sId = null)
{
parent::__construct($sId, ['ibo-modal-option--do-not-show-again']);
// initialize UI
$this->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);
}
}

View File

@@ -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;
}
}

View File

@@ -57,7 +57,7 @@ class BlockDirectLinksViewTable extends AbstractBlockLinksViewTable
'confirmation' => [
'message' => 'UI:Links:ActionRow:detach:confirmation',
'message_row_data' => "{$this->sTargetClass}/hyperlink",
'remember_choice_pref_key' => 'LinkSetWorker.DetachLinkedObject',
'do_not_show_again_pref_key' => 'LinkSetWorker.DetachLinkedObject',
],
);
break;
@@ -70,7 +70,7 @@ class BlockDirectLinksViewTable extends AbstractBlockLinksViewTable
'confirmation' => [
'message' => 'UI:Links:ActionRow:delete:confirmation',
'message_row_data' => "{$this->sTargetClass}/hyperlink",
'remember_choice_pref_key' => 'LinkSetWorker.DeleteLinkedObject',
'do_not_show_again_pref_key' => 'LinkSetWorker.DeleteLinkedObject',
],
);
break;

View File

@@ -65,7 +65,7 @@ class BlockIndirectLinksViewTable extends AbstractBlockLinksViewTable
'confirmation' => [
'message' => 'UI:Links:ActionRow:detach:confirmation',
'message_row_data' => "Remote/hyperlink",
'remember_choice_pref_key' => 'LinkSetWorker.DetachLinkedObject',
'do_not_show_again_pref_key' => 'LinkSetWorker.DetachLinkedObject',
],
);

View File

@@ -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);
}
}

View File

@@ -28,7 +28,4 @@
{% if oUIBlock.HasRowActions() %}
{{ render_block(oUIBlock.GetRowActionsTemplate()) }}
{% if not oUIBlock.GetRowActionsConfirmDialogInitializedFlag() %}
{{ render_block(oUIBlock.GetRowActionsConfirmDialog()) }}
{% endif %}
{% endif %}

View File

@@ -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'))

View File

@@ -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 }}';
// 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 %}
HandleActionRowConfirmation (sTitle, sMessage, sPrefKey, ActionRowFunction{{ oUIBlock.GetId() }}{{ loop.index0 }}, {
action_id: iActionId,
datatable: oDatatable,
tr_element: oTrElement,
row_data: aRowData
});
}, [oDatatable, oTrElement, iActionId, aRowData]);
{% else %}

View File

@@ -21,7 +21,4 @@
{% if oUIBlock.HasRowActions() %}
{{ render_block(oUIBlock.GetRowActionsTemplate()) }}
{% if not oUIBlock.GetRowActionsConfirmDialogInitializedFlag() %}
{{ render_block(oUIBlock.GetRowActionsConfirmDialog()) }}
{% endif %}
{% endif %}

View File

@@ -48,7 +48,4 @@
{% if oUIBlock.HasRowActions() %}
{{ render_block(oUIBlock.GetRowActionsTemplate()) }}
{% if not oUIBlock.GetRowActionsConfirmDialogInitializedFlag() %}
{{ render_block(oUIBlock.GetRowActionsConfirmDialog()) }}
{% endif %}
{% endif %}

View File

@@ -1,4 +0,0 @@
$('#{{ oUIBlock.GetId() }}').alert({
bOpenedByDefault: {{ oUIBlock.IsOpenedByDefault()|var_export }}
{% if oUIBlock.IsSaveCollapsibleStateEnabled() %}, collapsibleStateStorageKey: '{{ oUIBlock.GetSessionCollapsibleStateStorageKey() }}'{% endif %}
});

View File

@@ -45,6 +45,12 @@
<template id="ibo-modal-template">
<div class="ibo-modal" data-role="ibo-modal">TODO 3.1: Please wait</div>
</template>
{# Templates #}
{% for oTemplate in aTemplates %}
{{ render_block(oTemplate, {aPage: aPage}) }}
{% endfor %}
{% endblock %}
{% block iboCapturedOutput %}