N°5655 - Refactor portal modal dialog helper to global helper for both GUIs

This commit is contained in:
Molkobain
2022-10-28 10:05:58 +02:00
parent df3fc8be7f
commit 36ec455e6d
10 changed files with 217 additions and 156 deletions

View File

@@ -100,6 +100,6 @@ $(document).ready(function()
};
}
CombodoPortalToolbox.OpenModal(oOptions);
CombodoModal.OpenModal(oOptions);
});
});

View File

@@ -397,7 +397,7 @@ $(function()
if(bRedirectInModal === true)
{
// Creating a new modal
CombodoPortalToolbox.OpenModal({
CombodoModal.OpenModal({
content: {
endpoint: sRedirectUrl,
data: {

View File

@@ -24,158 +24,22 @@
const CombodoPortalToolbox = {
/**
* Close all opened modals on the page
* @deprecated 3.1.0 Use CombodoModal.CloseAllModals() instead
*/
CloseAllModals: function()
{
$('.modal.in').modal('hide');
CloseAllModals: function() {
CombodoModal.CloseAllModals();
},
/**
* Open a standard modal and put the content of the URL in it.
*
* @param sTargetUrl
* @param bCloseOtherModals
* @deprecated 3.1.0 Use CombodoModal.OpenUrlInModal() instead
*/
OpenUrlInModal: function(sTargetUrl, bCloseOtherModals){
// Set default values
if(bCloseOtherModals === undefined)
{
bCloseOtherModals = false;
}
// Close other modals if necessary
if(bCloseOtherModals)
{
CombodoPortalToolbox.CloseAllModals();
}
// Opening modal
CombodoPortalToolbox.OpenModal({
content: {
endpoint: sTargetUrl,
}
});
OpenUrlInModal: function(sTargetUrl, bCloseOtherModals) {
CombodoModal.OpenUrlInModal(sTargetUrl, bCloseOtherModals);
},
/**
* Generic function to create and open a modal, used by high-level functions such as "CombodoPortalToolbox.OpenUrlInModal()".
* When developing extensions, you should use them instead.
*
* @param oOptions
* @returns object The jQuery object of the modal element
* @deprecated 3.1.0 Use CombodoModal.OpenModal() instead
*/
OpenModal: function(oOptions){
// Set default options
oOptions = $.extend(
true,
{
id: null, // ID of the created modal
attributes: {}, // HTML attributes
base_modal: {
usage: 'clone', // Either 'clone' or 'replace'
selector: '#modal-for-all' // Either a selector of the modal element used to base this one on or the modal element itself
},
content: undefined, // Either a string, an object containing the endpoint / data or undefined to keep base modal content as-is
size: 'lg', // Either 'xs' / 'sm' / 'md' / 'lg'
},
oOptions
);
// Compute modal selector
let oSelectorElem = null;
switch(typeof oOptions.base_modal.selector)
{
case 'string':
oSelectorElem = $(oOptions.base_modal.selector);
break;
case 'object':
oSelectorElem = oOptions.base_modal.selector;
break;
default:
if (window.console && window.console.warn)
{
console.warn('Could not open modal dialog as the select option was malformed: ', oOptions.content);
}
return false;
}
// Get modal element by either
let oModalElem = null;
// - Create a new modal from template
// Note : This could be better if we check for an existing modal first instead of always creating a new one
if (oOptions.base_modal.usage === 'clone')
{
oModalElem = oSelectorElem.clone();
// Force modal to have an HTML ID, otherwise it can lead to complications, especially with the portal_leave_handle.js
// See N°3469
var sModalID = (oOptions.id !== null) ? oOptions.id : 'modal-with-generated-id-'+Date.now();
oModalElem.attr('id', sModalID)
.appendTo('body');
}
// - Get an existing modal in the DOM
else
{
oModalElem = oSelectorElem;
}
// Set attributes
for(let sProp in oOptions.attributes)
{
oModalElem.attr(sProp, oOptions.attributes[sProp]);
}
// Resize to small modal
oModalElem.find('.modal-dialog')
.removeClass('modal-lg')
.addClass('modal-' + oOptions.size);
// Load content
switch (typeof oOptions.content)
{
case 'string':
oModalElem.find('.modal-content').html(oOptions.content);
//Manually triggers bootstrap event in order to keep listeners working
oModalElem.trigger('loaded.bs.modal');
break;
case 'object':
// Put loader while fetching content
oModalElem.find('.modal-content').html($('#page_overlay .overlay_content').html());
// Fetch content in background
oModalElem.find('.modal-content').load(
oOptions.content.endpoint,
oOptions.content.data || {},
function (sResponseText, sStatus)
{
// Hiding modal in case of error as the general AJAX error handler will display a message
if (sStatus === 'error')
{
oModalElem.modal('hide');
}
//Manually triggers bootstrap event in order to keep listeners working
oModalElem.trigger('loaded.bs.modal');
}
);
break;
case 'undefined':
// Do nothing, we keep the content as-is
break;
default:
if (window.console && window.console.warn)
{
console.warn('Could not open modal dialog as the content option was malformed: ', oOptions.content);
}
}
// Show modal
oModalElem.modal('show');
return oModalElem;
OpenModal: function(oOptions) {
return CombodoModal.OpenModal(oOptions);
},
/**
* Generic function to call a specific endpoint with callbacks
@@ -185,7 +49,7 @@ const CombodoPortalToolbox = {
* @param callbackOnSuccess
* @param callbackOnPending
*/
CallEndpoint: function(sEndpointUrl, oPostedData, callbackOnSuccess, callbackOnPending){
CallEndpoint: function(sEndpointUrl, oPostedData, callbackOnSuccess, callbackOnPending) {
// Call endpoint
$.post(sEndpointUrl, oPostedData, function(oResponse) {
// Call callback on success
@@ -216,3 +80,130 @@ const CombodoPortalToolbox = {
}
}
};
/**
* @override
* @inheritDoc
*/
CombodoModal.CloseAllModals = function() {
$('.modal.in').modal('hide');
};
/**
* @override
* @inheritDoc
*/
CombodoModal.OpenModal = function(oOptions) {
// Set default options
oOptions = $.extend(
true,
{
id: null, // ID of the created modal
attributes: {}, // HTML attributes
base_modal: {
usage: 'clone', // Either 'clone' or 'replace'
selector: '#modal-for-all' // Either a selector of the modal element used to base this one on or the modal element itself
},
content: undefined, // Either a string, an object containing the endpoint / data or undefined to keep base modal content as-is
size: 'lg', // Either 'xs' / 'sm' / 'md' / 'lg'
},
oOptions
);
// Compute modal selector
let oSelectorElem = null;
switch(typeof oOptions.base_modal.selector)
{
case 'string':
oSelectorElem = $(oOptions.base_modal.selector);
break;
case 'object':
oSelectorElem = oOptions.base_modal.selector;
break;
default:
if (window.console && window.console.warn)
{
console.warn('Could not open modal dialog as the select option was malformed: ', oOptions.content);
}
return false;
}
// Get modal element by either
let oModalElem = null;
// - Create a new modal from template
// Note : This could be better if we check for an existing modal first instead of always creating a new one
if (oOptions.base_modal.usage === 'clone')
{
oModalElem = oSelectorElem.clone();
// Force modal to have an HTML ID, otherwise it can lead to complications, especially with the portal_leave_handle.js
// See N°3469
var sModalID = (oOptions.id !== null) ? oOptions.id : 'modal-with-generated-id-'+Date.now();
oModalElem.attr('id', sModalID)
.appendTo('body');
}
// - Get an existing modal in the DOM
else
{
oModalElem = oSelectorElem;
}
// Set attributes
for(let sProp in oOptions.attributes)
{
oModalElem.attr(sProp, oOptions.attributes[sProp]);
}
// Resize to small modal
oModalElem.find('.modal-dialog')
.removeClass('modal-lg')
.addClass('modal-' + oOptions.size);
// Load content
switch (typeof oOptions.content)
{
case 'string':
oModalElem.find('.modal-content').html(oOptions.content);
//Manually triggers bootstrap event in order to keep listeners working
oModalElem.trigger('loaded.bs.modal');
break;
case 'object':
// Put loader while fetching content
oModalElem.find('.modal-content').html($('#page_overlay .overlay_content').html());
// Fetch content in background
oModalElem.find('.modal-content').load(
oOptions.content.endpoint,
oOptions.content.data || {},
function (sResponseText, sStatus)
{
// Hiding modal in case of error as the general AJAX error handler will display a message
if (sStatus === 'error')
{
oModalElem.modal('hide');
}
//Manually triggers bootstrap event in order to keep listeners working
oModalElem.trigger('loaded.bs.modal');
}
);
break;
case 'undefined':
// Do nothing, we keep the content as-is
break;
default:
if (window.console && window.console.warn)
{
console.warn('Could not open modal dialog as the content option was malformed: ', oOptions.content);
}
}
// Show modal
oModalElem.modal('show');
return oModalElem;
};

View File

@@ -208,7 +208,7 @@ class ObjectController extends BrickController
$oModifyButton = new JSButtonItem(
'modify_object',
Dict::S('UI:Menu:Modify'),
'CombodoPortalToolbox.OpenUrlInModal("'.$sModifyUrl.'", true);'
'CombodoModal.OpenUrlInModal("'.$sModifyUrl.'", true);'
);
// Putting this one first
$aData['form']['buttons']['actions'][] = $oModifyButton->GetMenuItem() + array('js_files' => $oModifyButton->GetLinkedScripts());

View File

@@ -25,7 +25,7 @@
sUrl = CombodoGlobalToolbox.AddParameterToUrl(sUrl, 'ar_token', '{{ ar_token }}');
// Creating a new modal
CombodoPortalToolbox.OpenModal({
CombodoModal.OpenModal({
base_modal: {
usage: 'replace',
selector: $(this).closest('.modal'),

View File

@@ -143,7 +143,7 @@
oEvent.stopPropagation();
// Creating a new modal
CombodoPortalToolbox.OpenModal({
CombodoModal.OpenModal({
content: {
endpoint: $(this).attr('href'),
},

View File

@@ -175,6 +175,23 @@ const CombodoBackofficeToolbox = {
}
};
/**
* @override
* @inheritDoc
*/
CombodoModal.CloseAllModals = function() {
// TODO: Implement
};
/**
* @override
* @inheritDoc
*/
CombodoModal.OpenModal = function(oOptions) {
// TODO: Implement
return null;
};
// Processing on each pages of the backoffice
$(document).ready(function(){
// Initialize global keyboard shortcuts

View File

@@ -1098,4 +1098,57 @@ const CombodoInlineImage = {
$(this).addClass('inline-image').attr('href', $(this).attr('src'));
}).magnificPopup({type: 'image', closeOnContentClick: true });
}
}
};
/**
* Abstract wrapper to manage modal dialogs in iTop.
* Implementations for the various GUIs may vary but APIs are the same.
*
* @since 3.1.0
*/
let CombodoModal = {
/**
* Close all opened modals on the page
*/
CloseAllModals: function() {
// Meant for overlaoding
CombodoJSConsole.Debug('CombodoModal.CloseAllModals not implemented');
},
/**
* Open a standard modal and put the content of the URL in it.
*
* @param sTargetUrl
* @param bCloseOtherModals
*/
OpenUrlInModal: function(sTargetUrl, bCloseOtherModals) {
// Set default values
if(bCloseOtherModals === undefined)
{
bCloseOtherModals = false;
}
// Close other modals if necessary
if(bCloseOtherModals)
{
CombodoModal.CloseAllModals();
}
// Opening modal
CombodoModal.OpenModal({
content: {
endpoint: sTargetUrl,
}
});
},
/**
* Generic function to create and open a modal, used by high-level functions such as "CombodoPortalToolbox.OpenUrlInModal()".
* When developing extensions, you should use them instead.
*
* @param oOptions
* @returns object The jQuery object of the modal element
*/
OpenModal: function(oOptions) {
// Meant for overlaoding
CombodoJSConsole.Debug('CombodoModal.OpenModal not implemented');
}
};

View File

@@ -249,7 +249,7 @@ EOF
oEvent.stopPropagation();
// Note : This could be better if we check for an existing modal first instead of always creating a new one
CombodoPortalToolbox.OpenModal({
CombodoModal.OpenModal({
content: {
endpoint: $(this).attr('href'),
},
@@ -527,7 +527,7 @@ EOF
'selector': '.modal[data-source-element="{$sButtonAddId}"]:first'
};
}
CombodoPortalToolbox.OpenModal(oOptions);
CombodoModal.OpenModal(oOptions);
});
JS
);

View File

@@ -381,7 +381,7 @@ EOF
<<<JS
$('#{$sHierarchicalButtonId}').off('click').on('click', function(){
// Creating a new modal
CombodoPortalToolbox.OpenModal({
CombodoModal.OpenModal({
attributes: {
'data-source-element': '{$sHierarchicalButtonId}',
},
@@ -441,7 +441,7 @@ JS
'selector': '.modal[data-source-element="{$sSearchButtonId}"]:first'
};
}
CombodoPortalToolbox.OpenModal(oOptions);
CombodoModal.OpenModal(oOptions);
});
JS
);