Advanced Search: Auto-complete search on foreign keys

SVN:b1162[5594]
This commit is contained in:
Eric Espié
2018-04-03 13:12:49 +00:00
parent d22d3945ee
commit 013dcdf93e
5 changed files with 106 additions and 26 deletions

View File

@@ -26,7 +26,7 @@ require_once(APPROOT.'/application/displayblock.class.inc.php');
class UISearchFormForeignKeys
{
public function __construct($sTargetClass, $iInputId, $sAttCode, $sSuffix)
public function __construct($sTargetClass, $iInputId = null, $sAttCode = null, $sSuffix = null)
{
$this->m_sRemoteClass = $sTargetClass;
$this->m_iInputId = $iInputId;
@@ -75,6 +75,21 @@ class UISearchFormForeignKeys
$oPage->add_ready_script("$('#SearchFormToAdd_{$this->m_iInputId}').resize(oForeignKeysWidget{$this->m_iInputId}.UpdateSizes);");
}
public function GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter)
{
try
{
$aLinkedObjects = utils::ReadMultipleSelectionWithFriendlyname($oFullSetFilter);
$oPage->add(json_encode($aLinkedObjects));
}
catch (CoreException $e)
{
http_response_code(500);
$oPage->add(json_encode(array('error' => $e->GetMessage())));
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
}
}
/**
* Search for objects to be linked to the current object (i.e "remote" objects)
*

View File

@@ -413,8 +413,10 @@ class utils
/**
* Interprets the results posted by a normal or paginated list (in multiple selection mode)
*
* @param $oFullSetFilter DBSearch The criteria defining the whole sets of objects being selected
* @return Array An arry of object IDs corresponding to the objects selected in the set
*
* @return Array An array of object IDs corresponding to the objects selected in the set
*/
public static function ReadMultipleSelection($oFullSetFilter)
{
@@ -448,6 +450,47 @@ class utils
return $aSelectedObj;
}
/**
* Interprets the results posted by a normal or paginated list (in multiple selection mode)
*
* @param DBSearch $oFullSetFilter The criteria defining the whole sets of objects being selected
*
* @return Array An array of object IDs:friendlyname corresponding to the objects selected in the set
* @throws \CoreException
*/
public static function ReadMultipleSelectionWithFriendlyname($oFullSetFilter)
{
$sSelectionMode = utils::ReadParam('selectionMode', '');
if ($sSelectionMode === '')
{
throw new CoreException('selectionMode is mandatory');
}
// Paginated selection
$aSelectedIds = utils::ReadParam('storedSelection', array());
if ($sSelectionMode == 'positive')
{
// Only the explicitly listed items are selected
$oFullSetFilter->AddCondition('id', $aSelectedIds, 'IN');
}
else
{
// All items of the set are selected, except the one explicitly listed
$oFullSetFilter->AddCondition('id', $aSelectedIds, 'NOTIN');
}
$aSelectedObj = array();
$oFullSet = new DBObjectSet($oFullSetFilter);
$sClassAlias = $oFullSetFilter->GetClassAlias();
$oFullSet->OptimizeColumnLoad(array($sClassAlias => array('friendlyname'))); // We really need only the IDs but it does not work since id is not a real field
while ($oObj = $oFullSet->Fetch())
{
$aSelectedObj[$oObj->GetKey()] = $oObj->Get('friendlyname');
}
return $aSelectedObj;
}
public static function GetNewTransactionId()
{
return privUITransaction::GetNewTransactionId();

View File

@@ -277,6 +277,7 @@ $(function()
oFilterIconElem.attr('id'), // id
me.options.field.target_class, // sTargetClass
me.options.field.code, // sAttCode
me.element, // oSearchWidgetElmt
'', // sFilter //TODO
me.options.field.label // sTitle
);

View File

@@ -20,18 +20,20 @@
* @param id String the dom identifier of the source input
* @param sTargetClass
* @param sAttCode
* @param oSearchWidgetElmt
* @param sFilter
* @param sTitle
* @constructor
*/
function SearchFormForeignKeys(id, sTargetClass, sAttCode, sFilter, sTitle)
function SearchFormForeignKeys(id, sTargetClass, sAttCode, oSearchWidgetElmt, sFilter, sTitle)
{
this.id = id;
this.sOriginalTargetClass = sTargetClass;
//this.sOriginalTargetClass = sTargetClass;
this.sTargetClass = sTargetClass;
this.sFilter = sFilter;
this.sTitle = sTitle;
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;
@@ -46,7 +48,7 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, sFilter, sTitle)
$('#'+this.id+'_btnRemove').prop('disabled', false);
var dialog = $('<div id="dlg_'+me.id+'"></div>').appendTo(document.body);
$('<div id="dlg_'+me.id+'"></div>').appendTo(document.body);
// me.trace(dialog);
@@ -59,18 +61,6 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, sFilter, sTitle)
$('#dlg_'+me.id).remove();
});
// $('#linkedset_'+me.id+' :input').off('change').on('change', function() {
// if (!($(this).hasClass('selection')) && !($(this).hasClass('select_all'))) {
// var oCheckbox = $(this).closest('tr').find('.selection');
// var iLink = oCheckbox.attr('data-link-id');
// var iUniqueId = oCheckbox.attr('data-unique-id');
// var sAttCode = $(this).closest('.attribute-edit').attr('data-attcode');
// var value = $(this).val();
// return me.OnValueChange(iLink, iUniqueId, sAttCode, value);
// }
// return true;
// });
$('#'+this.iInputId).closest('form').submit(function() {
return me.OnFormSubmit();
});
@@ -101,7 +91,6 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, sFilter, sTitle)
sAttCode: me.sAttCode,
iInputId: me.id,
sTitle: me.sTitle,
sAttCode: me.sAttCode,
sTargetClass: me.sTargetClass,
// bSearchMode: me.bSearchMode,
operation: 'ShowModalSearchForeignKeys'
@@ -154,9 +143,9 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, sFilter, sTitle)
oPadding[aKeys[k]] = parseInt(dlg.css('padding-'+aKeys[k]).replace('px', ''));
}
}
width = dlg.innerWidth() - oPadding['right'] - oPadding['left'] - 22; // 5 (margin-left) + 5 (padding-left) + 5 (padding-right) + 5 (margin-right) + 2 for rounding !
height = dlg.innerHeight() - oPadding['top'] - oPadding['bottom'] -22;
form_height = searchForm.outerHeight();
//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
};
@@ -173,7 +162,10 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, sFilter, sTitle)
}
};
this.ListResultsSearchForeignKeys = function(id)
/**
* @return {boolean}
*/
this.ListResultsSearchForeignKeys = function ()
{
var theMap = {
sTargetClass: me.sTargetClass,
@@ -184,7 +176,7 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, sFilter, sTitle)
// Gather the parameters from the search form
$('#fs_'+me.id+' :input').each( function() {
if (this.name != '')
if (this.name !== '')
{
var val = $(this).val(); // supports multiselect as well
if (val !== null)
@@ -199,7 +191,7 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, sFilter, sTitle)
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;
sSearchAreaId = '#SearchResultsToAdd_'+me.id;
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();
@@ -209,7 +201,7 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, sFilter, sTitle)
me.StopPendingRequest();
// Run the query and display the results
me.ajax_request = $.post( AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'), theMap,
me.ajax_request = $.post(AddAppContext(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php'), theMap,
function(data)
{
$(sSearchAreaId).html(data);
@@ -254,7 +246,7 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, sFilter, sTitle)
// Normal table, retrieve all the checked check-boxes
$(':checked[name^=selectObject]', context).each(
function (i) {
function () {
if ((this.name !== '') && ((this.type !== 'checkbox') || (this.checked))) {
var arrayExpr = /\[\]$/;
if (arrayExpr.test(this.name)) {
@@ -271,6 +263,26 @@ function SearchFormForeignKeys(id, sTargetClass, sAttCode, sFilter, sTitle)
$(this).parents('tr:first').remove(); // Remove the whole line, so that, next time the dialog gets displayed it's no longer there
}
);
theMap["class"] = me.sTargetClass;
theMap['operation'] = 'GetFullListForeignKeysFromSelection';
$('#busy_'+me.iInputId).html('&nbsp;<img src="../images/indicator.gif"/>');
// Run the query and display the results
$.ajax(GetAbsoluteUrlAppRoot()+'pages/ajax.render.php', {
"data": theMap,
"method": "POST"
})
.done(function (data) {
if (Object.keys(data).length > 0) {
me.oSearchWidgetElmt.trigger("itop.search.criteria_enum.add_selected_values", data);
}
})
.fail(function (data) {
try {
console.error(data);
} catch (e) {
}
})
;
$('#dlg_'+me.id).dialog('close');

View File

@@ -263,6 +263,14 @@ try
$oWidget->ShowModalSearchForeignKeys($oPage, $sTitle);
break;
// ui.searchformforeignkeys
case 'GetFullListForeignKeysFromSelection':
$oPage->SetContentType('application/json');
$oWidget = new UISearchFormForeignKeys($sClass);
$oFullSetFilter = new DBObjectSearch($sClass);
$oWidget->GetFullListForeignKeysFromSelection($oPage, $oFullSetFilter);
break;
// ui.searchformforeignkeys
case 'ListResultsSearchForeignKeys':
$oPage->SetContentType('text/html');
@@ -275,6 +283,7 @@ try
$oWidget->ListResultsSearchForeignKeys($oPage, $sRemoteClass);
break;
// ui.linkswidget
case 'addObjects':
$oPage->SetContentType('text/html');