From 2a16143e53b4019415db90b6ac89f16fc3f773ff Mon Sep 17 00:00:00 2001 From: Stephen Abello Date: Wed, 18 Mar 2026 15:23:52 +0100 Subject: [PATCH] =?UTF-8?q?N=C2=B09229=20-=20Modernize=20search=20foreign?= =?UTF-8?q?=20keys=20code=20with=20built=20in=20JS=20tools=20(#847)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- .../ui.searchformforeignkeys.class.inc.php | 67 +---- js/pages/backoffice/toolbox.js | 6 + js/searchformforeignkeys.js | 252 ++++-------------- pages/ajax.render.php | 13 +- 4 files changed, 56 insertions(+), 282 deletions(-) diff --git a/application/ui.searchformforeignkeys.class.inc.php b/application/ui.searchformforeignkeys.class.inc.php index 4be179c3e0..1d858e9983 100644 --- a/application/ui.searchformforeignkeys.class.inc.php +++ b/application/ui.searchformforeignkeys.class.inc.php @@ -27,6 +27,9 @@ require_once(APPROOT.'/application/displayblock.class.inc.php'); class UISearchFormForeignKeys { + private $m_sRemoteClass; + private $m_iInputId; + public function __construct($sTargetClass, $iInputId = null) { $this->m_sRemoteClass = $sTargetClass; @@ -40,7 +43,7 @@ class UISearchFormForeignKeys * * @throws \Exception */ - public function ShowModalSearchForeignKeys($oPage, $sTitle) + public function ShowModalSearchForeignKeys($oPage) { $oFilter = new DBObjectSearch($this->m_sRemoteClass); @@ -60,8 +63,6 @@ class UISearchFormForeignKeys ] )); $sEmptyList = Dict::S('UI:Message:EmptyList:UseSearchForm'); - $sCancel = Dict::S('UI:Button:Cancel'); - $sAdd = Dict::S('UI:Button:Add'); $oPage->add( << HTML ); - - $oPage->add_ready_script( - <<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) @@ -119,31 +87,4 @@ JS 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}"] - ); - } - } diff --git a/js/pages/backoffice/toolbox.js b/js/pages/backoffice/toolbox.js index 293b0f8acb..c30434b20e 100644 --- a/js/pages/backoffice/toolbox.js +++ b/js/pages/backoffice/toolbox.js @@ -332,6 +332,12 @@ CombodoModal._ConvertButtonDefinition = function (aButtonsDefinitions) { class: typeof(element.classes) !== 'undefined' ? element.classes.join(' ') : '', 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); } ); diff --git a/js/searchformforeignkeys.js b/js/searchformforeignkeys.js index dc702b93cf..2aa6058e30 100644 --- a/js/searchformforeignkeys.js +++ b/js/searchformforeignkeys.js @@ -35,116 +35,63 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, oSearchWidgetElmt, sF this.sAttCode = sAttCode; this.oSearchWidgetElmt = oSearchWidgetElmt; 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.bSearchMode = bSearchMode; // true if selecting a value in the context of a search form var me = this; 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); - - $('
').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() { - // // Query the server to get the form to search for target objects - // if (me.bSelectMode) - // { - // $('#fstatus_'+me.id).html(''); - // } - // else - // { - // $('#label_'+me.id).addClass('dlg_loading'); - // } - $('#label_'+me.id).addClass('dlg_loading'); - var theMap = { - sAttCode: me.sAttCode, - iInputId: me.id, - sTitle: me.sTitle, - sTargetClass: me.sTargetClass, - // bSearchMode: me.bSearchMode, - operation: 'ShowModalSearchForeignKeys' - }; + const oModalParams = { + content: { + endpoint: AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'), + data: { + sAttCode: me.sAttCode, + iInputId: me.id, + sTargetClass: me.sTargetClass, + operation: 'ShowModalSearchForeignKeys' + }, + }, + title: me.sTitle, + id: 'dlg_'+me.id, + size: 'lg', + buttons: [ + { + text: Dict.S('UI:Button:Cancel'), + 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); - - // Make sure that we cancel any pending request before issuing another - // since responses may arrive in arbitrary order - 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 + // Bind events + oModal.on('change', '#count_'+me.id, function(){ + me.UpdateButtons(); + }); }; 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('
'); - $(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} */ @@ -286,56 +176,4 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, oSearchWidgetElmt, sF 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(); - } - }; } \ No newline at end of file diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 63e48af19f..2c02f19083 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -173,10 +173,9 @@ try { case 'ShowModalSearchForeignKeys': $oPage->SetContentType('text/html'); $iInputId = utils::ReadParam('iInputId', ''); - $sTitle = utils::ReadParam('sTitle', '', false, 'raw_data'); $sTargetClass = utils::ReadParam('sTargetClass', '', false, 'class'); $oWidget = new UISearchFormForeignKeys($sTargetClass, $iInputId); - $oWidget->ShowModalSearchForeignKeys($oPage, $sTitle); + $oWidget->ShowModalSearchForeignKeys($oPage); break; // ui.searchformforeignkeys @@ -187,16 +186,6 @@ try { $oWidget->GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter); 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 case 'addObjects': $oPage->SetContentType('text/html');