mirror of
https://github.com/Combodo/iTop.git
synced 2026-05-18 23:08:46 +02:00
N°9229 - Modernize search foreign keys code with built in JS tools (#847)
* N°9229 - Modernize search foreign keys code with built in JS tools * N°9229 - Allow modals to have button id specified * N°9229 - Remove the modal instead of only destroying it * N°9229 - Remove dead code * Update js/searchformforeignkeys.js * Add robustness to modals button id
This commit is contained in:
@@ -27,6 +27,9 @@ require_once(APPROOT.'/application/displayblock.class.inc.php');
|
|||||||
|
|
||||||
class UISearchFormForeignKeys
|
class UISearchFormForeignKeys
|
||||||
{
|
{
|
||||||
|
private $m_sRemoteClass;
|
||||||
|
private $m_iInputId;
|
||||||
|
|
||||||
public function __construct($sTargetClass, $iInputId = null)
|
public function __construct($sTargetClass, $iInputId = null)
|
||||||
{
|
{
|
||||||
$this->m_sRemoteClass = $sTargetClass;
|
$this->m_sRemoteClass = $sTargetClass;
|
||||||
@@ -40,7 +43,7 @@ class UISearchFormForeignKeys
|
|||||||
*
|
*
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function ShowModalSearchForeignKeys($oPage, $sTitle)
|
public function ShowModalSearchForeignKeys($oPage)
|
||||||
{
|
{
|
||||||
|
|
||||||
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
|
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
|
||||||
@@ -60,8 +63,6 @@ class UISearchFormForeignKeys
|
|||||||
]
|
]
|
||||||
));
|
));
|
||||||
$sEmptyList = Dict::S('UI:Message:EmptyList:UseSearchForm');
|
$sEmptyList = Dict::S('UI:Message:EmptyList:UseSearchForm');
|
||||||
$sCancel = Dict::S('UI:Button:Cancel');
|
|
||||||
$sAdd = Dict::S('UI:Button:Add');
|
|
||||||
|
|
||||||
$oPage->add(
|
$oPage->add(
|
||||||
<<<HTML
|
<<<HTML
|
||||||
@@ -73,39 +74,6 @@ class UISearchFormForeignKeys
|
|||||||
</form>
|
</form>
|
||||||
HTML
|
HTML
|
||||||
);
|
);
|
||||||
|
|
||||||
$oPage->add_ready_script(
|
|
||||||
<<<JS
|
|
||||||
$('#dlg_{$this->m_iInputId}').dialog({
|
|
||||||
width: $(window).width()*0.8,
|
|
||||||
height: $(window).height()*0.8,
|
|
||||||
autoOpen: false,
|
|
||||||
modal: true,
|
|
||||||
resizeStop: oForeignKeysWidget{$this->m_iInputId}.UpdateSizes,
|
|
||||||
buttons: [
|
|
||||||
{
|
|
||||||
text: Dict.S('UI:Button:Cancel'),
|
|
||||||
class: "cancel ibo-is-alternative ibo-is-neutral",
|
|
||||||
click: function() {
|
|
||||||
$('#dlg_{$this->m_iInputId}').dialog('close');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: Dict.S('UI:Button:Add'),
|
|
||||||
id: 'btn_ok_{$this->m_iInputId}',
|
|
||||||
class: "ok ibo-is-regular ibo-is-primary",
|
|
||||||
click: function() {
|
|
||||||
oForeignKeysWidget{$this->m_iInputId}.DoAddObjects(this.id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
});
|
|
||||||
$('#dlg_{$this->m_iInputId}').dialog('option', {title:'$sTitle'});
|
|
||||||
$('#SearchFormToAdd_{$this->m_iInputId} form').on('submit.uilinksWizard', oForeignKeysWidget{$this->m_iInputId}.SearchObjectsToAdd);
|
|
||||||
$('#SearchFormToAdd_{$this->m_iInputId}').on('resize', oForeignKeysWidget{$this->m_iInputId}.UpdateSizes);
|
|
||||||
JS
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter)
|
public function GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter)
|
||||||
@@ -119,31 +87,4 @@ JS
|
|||||||
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
|
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Search for objects to be linked to the current object (i.e "remote" objects)
|
|
||||||
*
|
|
||||||
* @param WebPage $oP The page used for the output (usually an AjaxWebPage)
|
|
||||||
* @param string $sRemoteClass Name of the "remote" class to perform the search on, must be a derived class of m_sRemoteClass
|
|
||||||
*
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function ListResultsSearchForeignKeys(WebPage $oP, $sRemoteClass = '')
|
|
||||||
{
|
|
||||||
if ($sRemoteClass != '') {
|
|
||||||
// assert(MetaModel::IsParentClass($this->m_sRemoteClass, $sRemoteClass));
|
|
||||||
$oFilter = new DBObjectSearch($sRemoteClass);
|
|
||||||
} else {
|
|
||||||
// No remote class specified use the one defined in the linkedset
|
|
||||||
$oFilter = new DBObjectSearch($this->m_sRemoteClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
$oBlock = new DisplayBlock($oFilter, 'list', false);
|
|
||||||
$oBlock->Display(
|
|
||||||
$oP,
|
|
||||||
"ResultsToAdd_{$this->m_iInputId}",
|
|
||||||
['menu' => false, 'cssCount' => "#count_{$this->m_iInputId}", 'selection_mode' => true, 'table_id' => "add_{$this->m_iInputId}"]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -332,6 +332,12 @@ CombodoModal._ConvertButtonDefinition = function (aButtonsDefinitions) {
|
|||||||
class: typeof(element.classes) !== 'undefined' ? element.classes.join(' ') : '',
|
class: typeof(element.classes) !== 'undefined' ? element.classes.join(' ') : '',
|
||||||
click: element.callback_on_click
|
click: element.callback_on_click
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// id is optional, and we don't want to set it if not defined
|
||||||
|
if (typeof element.id !== 'undefined' && element.id !== null) {
|
||||||
|
aButton.id = element.id;
|
||||||
|
}
|
||||||
|
|
||||||
aConverted.push(aButton);
|
aConverted.push(aButton);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -35,116 +35,63 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, oSearchWidgetElmt, sF
|
|||||||
this.sAttCode = sAttCode;
|
this.sAttCode = sAttCode;
|
||||||
this.oSearchWidgetElmt = oSearchWidgetElmt;
|
this.oSearchWidgetElmt = oSearchWidgetElmt;
|
||||||
this.emptyHtml = ''; // content to be displayed when the search results are empty (when opening the dialog)
|
this.emptyHtml = ''; // content to be displayed when the search results are empty (when opening the dialog)
|
||||||
this.emptyOnClose = true; // Workaround for the JQuery dialog being very slow when opening and closing if the content contains many INPUT tags
|
|
||||||
this.ajax_request = null;
|
|
||||||
// this.bSelectMode = bSelectMode; // true if the edited field is a SELECT, false if it's an autocomplete
|
// this.bSelectMode = bSelectMode; // true if the edited field is a SELECT, false if it's an autocomplete
|
||||||
// this.bSearchMode = bSearchMode; // true if selecting a value in the context of a search form
|
// this.bSearchMode = bSearchMode; // true if selecting a value in the context of a search form
|
||||||
var me = this;
|
var me = this;
|
||||||
|
|
||||||
this.Init = function()
|
this.Init = function()
|
||||||
{
|
{
|
||||||
// make sure that the form is clean
|
|
||||||
$('#linkedset_'+this.id+' .selection').each( function() { this.checked = false; });
|
|
||||||
$('#'+this.id+'_btnRemove').prop('disabled', false);
|
|
||||||
|
|
||||||
$('<div id="dlg_'+me.id+'"></div>').appendTo(document.body);
|
|
||||||
|
|
||||||
// me.trace(dialog);
|
|
||||||
|
|
||||||
//TODO : check and remove all unneded code bellow this line!!
|
|
||||||
|
|
||||||
$('#'+this.id+'_linksToRemove').val('');
|
|
||||||
|
|
||||||
$('#linkedset_'+me.id).on('remove', function() {
|
|
||||||
// prevent having the dlg div twice
|
|
||||||
$('#dlg_'+me.id).remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#'+this.iInputId).closest('form').on('submit', function() {
|
|
||||||
return me.OnFormSubmit();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.StopPendingRequest = function()
|
|
||||||
{
|
|
||||||
if (me.ajax_request)
|
|
||||||
{
|
|
||||||
me.ajax_request.abort();
|
|
||||||
me.ajax_request = null;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.ShowModalSearchForeignKeys = function()
|
this.ShowModalSearchForeignKeys = function()
|
||||||
{
|
{
|
||||||
// // Query the server to get the form to search for target objects
|
const oModalParams = {
|
||||||
// if (me.bSelectMode)
|
content: {
|
||||||
// {
|
endpoint: AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'),
|
||||||
// $('#fstatus_'+me.id).html('<img src="../images/indicator.gif" />');
|
data: {
|
||||||
// }
|
sAttCode: me.sAttCode,
|
||||||
// else
|
iInputId: me.id,
|
||||||
// {
|
sTargetClass: me.sTargetClass,
|
||||||
// $('#label_'+me.id).addClass('dlg_loading');
|
operation: 'ShowModalSearchForeignKeys'
|
||||||
// }
|
},
|
||||||
$('#label_'+me.id).addClass('dlg_loading');
|
},
|
||||||
var theMap = {
|
title: me.sTitle,
|
||||||
sAttCode: me.sAttCode,
|
id: 'dlg_'+me.id,
|
||||||
iInputId: me.id,
|
size: 'lg',
|
||||||
sTitle: me.sTitle,
|
buttons: [
|
||||||
sTargetClass: me.sTargetClass,
|
{
|
||||||
// bSearchMode: me.bSearchMode,
|
text: Dict.S('UI:Button:Cancel'),
|
||||||
operation: 'ShowModalSearchForeignKeys'
|
callback_on_click: function() {
|
||||||
};
|
$(this).dialog("close");
|
||||||
|
},
|
||||||
|
classes: ['cancel', 'ibo-is-alternative', 'ibo-is-neutral'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: Dict.S('UI:Button:Add'),
|
||||||
|
id: "btn_ok_"+me.id,
|
||||||
|
classes: ['ok', 'ibo-is-regular', 'ibo-is-primary'],
|
||||||
|
callback_on_click: function() {
|
||||||
|
me.DoAddObjects();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
callback_on_content_loaded: function(oModalContentElement){
|
||||||
|
// Update initial buttons state
|
||||||
|
me.UpdateButtons();
|
||||||
|
},
|
||||||
|
|
||||||
|
extra_options: {
|
||||||
|
callback_on_modal_close: function () {
|
||||||
|
$(this).remove(); // destroy then remove dialog object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const oModal = CombodoModal.OpenModal(oModalParams);
|
||||||
|
|
||||||
|
// Bind events
|
||||||
// Make sure that we cancel any pending request before issuing another
|
oModal.on('change', '#count_'+me.id, function(){
|
||||||
// since responses may arrive in arbitrary order
|
me.UpdateButtons();
|
||||||
me.StopPendingRequest();
|
});
|
||||||
|
|
||||||
// Run the query and get the result back directly in HTML
|
|
||||||
me.ajax_request = $.post( AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'), theMap,
|
|
||||||
function(data)
|
|
||||||
{
|
|
||||||
// $('#dlg_'+me.id).html(data);
|
|
||||||
$('#dlg_'+me.id).empty().append($(data)); // $(data).filter(':not(script)'));
|
|
||||||
$('#dlg_'+me.id).dialog('open');
|
|
||||||
me.UpdateSizes();
|
|
||||||
me.UpdateButtons();
|
|
||||||
me.ajax_request = null;
|
|
||||||
me.ListResultsSearchForeignKeys();
|
|
||||||
},
|
|
||||||
'html'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.UpdateSizes = function()
|
|
||||||
{
|
|
||||||
var dlg = $('#dlg_'+me.id);
|
|
||||||
// Adjust the dialog's size to fit into the screen
|
|
||||||
if (dlg.width() > ($(window).width()-40))
|
|
||||||
{
|
|
||||||
dlg.width($(window).width()-40);
|
|
||||||
}
|
|
||||||
if (dlg.height() > ($(window).height()-70))
|
|
||||||
{
|
|
||||||
dlg.height($(window).height()-70);
|
|
||||||
}
|
|
||||||
var searchForm = dlg.find('div.display_block:first'); // Top search form, enclosing display_block
|
|
||||||
var results = $('#SearchResultsToAdd_'+me.id);
|
|
||||||
var oPadding = {};
|
|
||||||
var aKeys = ['top', 'right', 'bottom', 'left'];
|
|
||||||
for(k in aKeys)
|
|
||||||
{
|
|
||||||
oPadding[aKeys[k]] = 0;
|
|
||||||
if (dlg.css('padding-'+aKeys[k]))
|
|
||||||
{
|
|
||||||
oPadding[aKeys[k]] = parseInt(dlg.css('padding-'+aKeys[k]).replace('px', ''));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//var width = dlg.innerWidth() - oPadding['right'] - oPadding['left'] - 22; // 5 (margin-left) + 5 (padding-left) + 5 (padding-right) + 5 (margin-right) + 2 for rounding !
|
|
||||||
var height = dlg.innerHeight()-oPadding['top']-oPadding['bottom']-22;
|
|
||||||
var form_height = searchForm.outerHeight();
|
|
||||||
results.height(height - form_height - 40); // Leave some space for the buttons
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.UpdateButtons = function()
|
this.UpdateButtons = function()
|
||||||
@@ -160,63 +107,6 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, oSearchWidgetElmt, sF
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {boolean}
|
|
||||||
*/
|
|
||||||
this.ListResultsSearchForeignKeys = function ()
|
|
||||||
{
|
|
||||||
var theMap = {
|
|
||||||
sTargetClass: me.sTargetClass,
|
|
||||||
iInputId: me.id,
|
|
||||||
sFilter: me.sfilter,
|
|
||||||
// bSearchMode: me.bSearchMode
|
|
||||||
};
|
|
||||||
|
|
||||||
// Gather the parameters from the search form
|
|
||||||
$('#fs_'+me.id+' :input').each( function() {
|
|
||||||
if (this.name !== '')
|
|
||||||
{
|
|
||||||
var val = $(this).val(); // supports multiselect as well
|
|
||||||
if (val !== null)
|
|
||||||
{
|
|
||||||
theMap[this.name] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
theMap['sRemoteClass'] = theMap['class']; // swap 'class' (defined in the form) and 'remoteClass'
|
|
||||||
theMap.operation = 'ListResultsSearchForeignKeys'; // Override what is defined in the form itself
|
|
||||||
theMap.sAttCode = me.sAttCode;
|
|
||||||
var sSearchAreaId = '#SearchResultsToAdd_'+me.id;
|
|
||||||
//$(sSearchAreaId).html('<div style="text-align:center;width:100%;height:24px;vertical-align:middle;"><img src="../images/indicator.gif" /></div>');
|
|
||||||
$(sSearchAreaId).block();
|
|
||||||
me.UpdateButtons();
|
|
||||||
|
|
||||||
// Make sure that we cancel any pending request before issuing another
|
|
||||||
// since responses may arrive in arbitrary order
|
|
||||||
me.StopPendingRequest();
|
|
||||||
|
|
||||||
// Run the query and display the results
|
|
||||||
me.ajax_request = $.post(AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'), theMap,
|
|
||||||
function(data)
|
|
||||||
{
|
|
||||||
$(sSearchAreaId).html(data);
|
|
||||||
$('#fr_'+me.id+' input:radio').on('click', function() { me.UpdateButtons(); });
|
|
||||||
me.UpdateButtons();
|
|
||||||
me.ajax_request = null;
|
|
||||||
$('#count_'+me.id).on('change', function(){
|
|
||||||
me.UpdateButtons();
|
|
||||||
});
|
|
||||||
me.UpdateSizes();
|
|
||||||
},
|
|
||||||
'html'
|
|
||||||
);
|
|
||||||
|
|
||||||
return false; // Don't submit the form, stay in the current page !
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
@@ -286,56 +176,4 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, oSearchWidgetElmt, sF
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Workaround for a ui.jquery limitation: if the content of
|
|
||||||
// the dialog contains many INPUTs, closing and opening the
|
|
||||||
// dialog is very slow. So empty it each time.
|
|
||||||
this.OnClose = function()
|
|
||||||
{
|
|
||||||
me.StopPendingRequest();
|
|
||||||
// called by the dialog, so in the context 'this' points to the jQueryObject
|
|
||||||
if (me.emptyOnClose)
|
|
||||||
{
|
|
||||||
$('#SearchResultsToAdd_'+me.id).html(me.emptyHtml);
|
|
||||||
}
|
|
||||||
$('#label_'+me.id).removeClass('dlg_loading');
|
|
||||||
$('#label_'+me.id).focus();
|
|
||||||
me.ajax_request = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.DoSelectObjectClass = function()
|
|
||||||
{
|
|
||||||
// Retrieving selected value
|
|
||||||
var oSelectedClass = $('#ac_create_'+me.id+' select');
|
|
||||||
if(oSelectedClass.length !== 1) return;
|
|
||||||
|
|
||||||
// Setting new target class
|
|
||||||
me.sTargetClass = oSelectedClass.val();
|
|
||||||
|
|
||||||
// Opening real creation form
|
|
||||||
$('#ac_create_'+me.id).dialog('close');
|
|
||||||
me.CreateObject();
|
|
||||||
};
|
|
||||||
|
|
||||||
this.Update = function()
|
|
||||||
{
|
|
||||||
if ($('#'+me.id).prop('disabled'))
|
|
||||||
{
|
|
||||||
$('#v_'+me.id).html('');
|
|
||||||
$('#label_'+me.id).prop('disabled', true);
|
|
||||||
$('#label_'+me.id).css({'background': 'transparent'});
|
|
||||||
$('#mini_add_'+me.id).hide();
|
|
||||||
$('#mini_tree_'+me.id).hide();
|
|
||||||
$('#mini_search_'+me.id).hide();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
$('#label_'+me.id).prop('disabled', false);
|
|
||||||
$('#label_'+me.id).css({'background': '#fff url(../images/ac-background.gif) no-repeat right'});
|
|
||||||
$('#mini_add_'+me.id).show();
|
|
||||||
$('#mini_tree_'+me.id).show();
|
|
||||||
$('#mini_search_'+me.id).show();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
@@ -173,10 +173,9 @@ try {
|
|||||||
case 'ShowModalSearchForeignKeys':
|
case 'ShowModalSearchForeignKeys':
|
||||||
$oPage->SetContentType('text/html');
|
$oPage->SetContentType('text/html');
|
||||||
$iInputId = utils::ReadParam('iInputId', '');
|
$iInputId = utils::ReadParam('iInputId', '');
|
||||||
$sTitle = utils::ReadParam('sTitle', '', false, 'raw_data');
|
|
||||||
$sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class');
|
$sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class');
|
||||||
$oWidget = new UISearchFormForeignKeys($sTargetClass, $iInputId);
|
$oWidget = new UISearchFormForeignKeys($sTargetClass, $iInputId);
|
||||||
$oWidget->ShowModalSearchForeignKeys($oPage, $sTitle);
|
$oWidget->ShowModalSearchForeignKeys($oPage);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// ui.searchformforeignkeys
|
// ui.searchformforeignkeys
|
||||||
@@ -187,16 +186,6 @@ try {
|
|||||||
$oWidget->GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter);
|
$oWidget->GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// ui.searchformforeignkeys
|
|
||||||
case 'ListResultsSearchForeignKeys':
|
|
||||||
$oPage->SetContentType('text/html');
|
|
||||||
$sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class');
|
|
||||||
$iInputId = utils::ReadParam('iInputId', '');
|
|
||||||
$sRemoteClass = utils::ReadParam('sRemoteClass', '', false, 'class');
|
|
||||||
$oWidget = new UISearchFormForeignKeys($sTargetClass, $iInputId);
|
|
||||||
$oWidget->ListResultsSearchForeignKeys($oPage, $sRemoteClass);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// ui.linkswidget
|
// ui.linkswidget
|
||||||
case 'addObjects':
|
case 'addObjects':
|
||||||
$oPage->SetContentType('text/html');
|
$oPage->SetContentType('text/html');
|
||||||
|
|||||||
Reference in New Issue
Block a user