mirror of
https://github.com/Combodo/iTop.git
synced 2026-05-17 22:39:03 +02:00
Implemented the "multiple choices" in search forms for Enums and External keys.
SVN:trunk[2305]
This commit is contained in:
@@ -1234,6 +1234,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
public static function GetSearchForm(WebPage $oPage, CMDBObjectSet $oSet, $aExtraParams = array())
|
||||
{
|
||||
static $iSearchFormId = 0;
|
||||
$bMultiSelect = false;
|
||||
$oAppContext = new ApplicationContext();
|
||||
$sHtml = '';
|
||||
$numCols=4;
|
||||
@@ -1353,12 +1354,17 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
}
|
||||
else
|
||||
{
|
||||
//Enum field, display a combo
|
||||
$sValue = "<select name=\"$sFilterCode\">\n";
|
||||
$sValue .= "<option value=\"\">".Dict::S('UI:SearchValue:Any')."</option>\n";
|
||||
//Enum field, display a multi-select combo
|
||||
$sValue = "<select class=\"multiselect\" size=\"1\" name=\"{$sFilterCode}[]\" multiple>\n";
|
||||
$bMultiSelect = true;
|
||||
//$sValue .= "<option value=\"\">".Dict::S('UI:SearchValue:Any')."</option>\n";
|
||||
foreach($aAllowedValues as $key => $value)
|
||||
{
|
||||
if ($sFilterValue == $key)
|
||||
if (is_array($sFilterValue) && in_array($key, $sFilterValue))
|
||||
{
|
||||
$sSelected = ' selected';
|
||||
}
|
||||
else if ($sFilterValue == $key)
|
||||
{
|
||||
$sSelected = ' selected';
|
||||
}
|
||||
@@ -1407,6 +1413,10 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
|
||||
{
|
||||
$sHtml .= "</div><!-- Simple search form -->\n";
|
||||
}
|
||||
if ($bMultiSelect)
|
||||
{
|
||||
$oPage->add_ready_script("$('.multiselect').multiselect({header: false, noneSelectedText: '".addslashes(Dict::S('UI:SearchValue:Any'))."', selectedList: 1, selectedText:'".addslashes(Dict::S('UI:SearchValue:NbSelected'))."'});");
|
||||
}
|
||||
/*
|
||||
// OQL query builder
|
||||
$sHtml .= "<div id=\"OQLQuery{$iSearchFormId}\" style=\"display:none\" class=\"mini_tab{$iSearchFormId}\">\n";
|
||||
|
||||
@@ -300,23 +300,40 @@ class DisplayBlock
|
||||
}
|
||||
foreach($aFilterCodes as $sFilterCode)
|
||||
{
|
||||
$sExternalFilterValue = utils::ReadParam($sFilterCode, '', false, 'raw_data');
|
||||
$externalFilterValue = utils::ReadParam($sFilterCode, '', false, 'raw_data');
|
||||
$condition = null;
|
||||
if (isset($aExtraParams[$sFilterCode]))
|
||||
{
|
||||
$condition = $aExtraParams[$sFilterCode];
|
||||
}
|
||||
// else if ($bDoSearch && $sExternalFilterValue != "")
|
||||
if ($bDoSearch && $sExternalFilterValue != "")
|
||||
if ($bDoSearch && $externalFilterValue != "")
|
||||
{
|
||||
// Search takes precedence over context params...
|
||||
unset($aExtraParams[$sFilterCode]);
|
||||
$condition = trim($sExternalFilterValue);
|
||||
if (!is_array($externalFilterValue))
|
||||
{
|
||||
$condition = trim($externalFilterValue);
|
||||
}
|
||||
else if (count($externalFilterValue) == 1)
|
||||
{
|
||||
$condition = trim($externalFilterValue[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$condition = $externalFilterValue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_null($condition))
|
||||
{
|
||||
$this->AddCondition($sFilterCode, $condition);
|
||||
$sOpCode = null; // default operator
|
||||
if (is_array($condition))
|
||||
{
|
||||
// Multiple values, add them as AND X IN (v1, v2, v3...)
|
||||
$sOpCode = 'IN';
|
||||
}
|
||||
|
||||
$this->AddCondition($sFilterCode, $condition, $sOpCode);
|
||||
}
|
||||
}
|
||||
if ($bDoSearch)
|
||||
@@ -1089,7 +1106,7 @@ EOF
|
||||
* Add a condition (restriction) to the current DBObjectSearch on which the display block is based
|
||||
* taking into account the hierarchical keys for which the condition is based on the 'below' operator
|
||||
*/
|
||||
protected function AddCondition($sFilterCode, $condition)
|
||||
protected function AddCondition($sFilterCode, $condition, $sOpCode = null)
|
||||
{
|
||||
// Workaround to an issue revealed whenever a condition on org_id is applied twice (with a hierarchy of organizations)
|
||||
// Moreover, it keeps the query as simple as possible
|
||||
@@ -1115,12 +1132,29 @@ EOF
|
||||
if ($sHierarchicalKeyCode !== false)
|
||||
{
|
||||
$oFilter = new DBObjectSearch($oAttDef->GetTargetClass());
|
||||
$oFilter->AddCondition('id', $condition);
|
||||
if (($sOpCode == 'IN') && is_array($condition))
|
||||
{
|
||||
$oFilter->AddConditionExpression(self::GetConditionIN($oFilter, 'id', $condition));
|
||||
}
|
||||
else
|
||||
{
|
||||
$oFilter->AddCondition('id', $condition);
|
||||
}
|
||||
$oHKFilter = new DBObjectSearch($oAttDef->GetTargetClass());
|
||||
$oHKFilter->AddCondition_PointingTo($oFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW); // Use the 'below' operator by default
|
||||
$this->m_oFilter->AddCondition_PointingTo($oHKFilter, $sFilterCode);
|
||||
$bConditionAdded = true;
|
||||
}
|
||||
else if (($sOpCode == 'IN') && is_array($condition))
|
||||
{
|
||||
$this->m_oFilter->AddConditionExpression(self::GetConditionIN($this->m_oFilter, $sFilterCode, $condition));
|
||||
$bConditionAdded = true;
|
||||
}
|
||||
}
|
||||
else if (($sOpCode == 'IN') && is_array($condition))
|
||||
{
|
||||
$this->m_oFilter->AddConditionExpression(self::GetConditionIN($this->m_oFilter, $sFilterCode, $condition));
|
||||
$bConditionAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1130,6 +1164,15 @@ EOF
|
||||
$this->m_oFilter->AddCondition($sFilterCode, $condition); // Use the default 'loose' operator
|
||||
}
|
||||
}
|
||||
|
||||
static protected function GetConditionIN($oFilter, $sFilterCode, $condition)
|
||||
{
|
||||
$oField = new FieldExpression($sFilterCode, $oFilter->GetClassAlias());
|
||||
$sListExpr = '('.implode(', ', CMDBSource::Quote($condition)).')';
|
||||
$sOQLCondition = $oField->Render()." IN $sListExpr";
|
||||
$oNewCondition = Expression::FromOQL($sOQLCondition);
|
||||
return $oNewCondition;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -56,6 +56,7 @@ class iTopWebPage extends NiceWebPage
|
||||
$this->add_linked_stylesheet("../css/jquery.treeview.css");
|
||||
$this->add_linked_stylesheet("../css/jquery.autocomplete.css");
|
||||
$this->add_linked_stylesheet("../css/fg.menu.css");
|
||||
$this->add_linked_stylesheet("../css/jquery.multiselect.css");
|
||||
$this->add_linked_script('../js/jquery.layout.min.js');
|
||||
$this->add_linked_script('../js/jquery.ba-bbq.min.js');
|
||||
$this->add_linked_script("../js/jquery.treeview.js");
|
||||
@@ -75,6 +76,10 @@ class iTopWebPage extends NiceWebPage
|
||||
$this->add_linked_script('../js/g.pie.js');
|
||||
$this->add_linked_script('../js/g.dot.js');
|
||||
$this->add_linked_script('../js/charts.js');
|
||||
$this->add_linked_script('../js/jquery.multiselect.min.js');
|
||||
|
||||
$sSearchAny = addslashes(Dict::S('UI:SearchValue:Any'));
|
||||
$sSearchNbSelected = addslashes(Dict::S('UI:SearchValue:NbSelected'));
|
||||
|
||||
$this->m_sInitScript =
|
||||
<<< EOF
|
||||
@@ -155,6 +160,8 @@ class iTopWebPage extends NiceWebPage
|
||||
}
|
||||
});
|
||||
|
||||
$('.multiselect').multiselect({header: false, noneSelectedText: '$sSearchAny', selectedList: 1, selectedText:'$sSearchNbSelected'});
|
||||
|
||||
$('.resizable').filter(':visible').resizable();
|
||||
}
|
||||
catch(err)
|
||||
@@ -429,7 +436,9 @@ EOF
|
||||
|
||||
$sFavoriteOrgs = '';
|
||||
$oWidget = new UIExtKeyWidget('Organization', 'org_id', '', true /* search mode */);
|
||||
$sHtml .= $oWidget->Display($this, 50, false, '', $oSet, $iCurrentOrganization, 'org_id', false, 'c[org_id]', '', array('iFieldSize' => 20, 'iMinChars' => MetaModel::GetConfig()->Get('min_autocomplete_chars'), 'sDefaultValue' => Dict::S('UI:AllOrganizations')));
|
||||
$sHtml .= $oWidget->Display($this, 50, false, '', $oSet, $iCurrentOrganization, 'org_id', false, 'c[org_id]', '',
|
||||
array('iFieldSize' => 20, 'iMinChars' => MetaModel::GetConfig()->Get('min_autocomplete_chars'), 'sDefaultValue' => Dict::S('UI:AllOrganizations')),
|
||||
null, 'select', false /* bSearchMultiple */);
|
||||
$this->add_ready_script('$("#org_id").bind("extkeychange", function() { $("#SiloSelection form").submit(); } )');
|
||||
$this->add_ready_script("$('#label_org_id').click( function() { $(this).val(''); $('#org_id').val(''); return true; } );\n");
|
||||
// Add other dimensions/context information to this form
|
||||
|
||||
@@ -101,7 +101,7 @@ class UIExtKeyWidget
|
||||
* @param Hash $aArgs Extra context arguments
|
||||
* @return string The HTML fragment to be inserted into the page
|
||||
*/
|
||||
public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = null, $sDisplayStyle = 'select')
|
||||
public function Display(WebPage $oPage, $iMaxComboLength, $bAllowTargetCreation, $sTitle, $oAllowedValues, $value, $iInputId, $bMandatory, $sFieldName, $sFormPrefix = '', $aArgs = array(), $bSearchMode = null, $sDisplayStyle = 'select', $bSearchMultiple = true)
|
||||
{
|
||||
if (!is_null($bSearchMode))
|
||||
{
|
||||
@@ -169,14 +169,22 @@ class UIExtKeyWidget
|
||||
|
||||
$sHelpText = ''; //$this->oAttDef->GetHelpOnEdition();
|
||||
|
||||
$sHTMLValue = "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
|
||||
if ($this->bSearchMode)
|
||||
{
|
||||
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : Dict::S('UI:SearchValue:Any');
|
||||
$sHTMLValue .= "<option value=\"\">$sDisplayValue</option>\n";
|
||||
if ($bSearchMultiple)
|
||||
{
|
||||
$sHTMLValue = "<select class=\"multiselect\" multiple title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}[]\" id=\"$this->iId\">\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHTMLValue = "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
|
||||
$sDisplayValue = isset($aArgs['sDefaultValue']) ? $aArgs['sDefaultValue'] : Dict::S('UI:SearchValue:Any');
|
||||
$sHTMLValue .= "<option value=\"\">$sDisplayValue</option>\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sHTMLValue = "<select title=\"$sHelpText\" name=\"{$sAttrFieldPrefix}{$sFieldName}\" id=\"$this->iId\">\n";
|
||||
$sHTMLValue .= "<option value=\"\">".Dict::S('UI:SelectOne')."</option>\n";
|
||||
}
|
||||
$oAllowedValues->Rewind();
|
||||
@@ -192,11 +200,15 @@ class UIExtKeyWidget
|
||||
}
|
||||
else
|
||||
{
|
||||
$sSelected = ($value == $key) ? ' selected' : '';
|
||||
$sSelected = (is_array($value) && in_array($key, $value)) || ($value == $key) ? ' selected' : '';
|
||||
}
|
||||
$sHTMLValue .= "<option value=\"$key\"$sSelected>$display_value</option>\n";
|
||||
}
|
||||
$sHTMLValue .= "</select>\n";
|
||||
if (($this->bSearchMode) && $bSearchMultiple)
|
||||
{
|
||||
$oPage->add_ready_script("$('.multiselect').multiselect({header: false, noneSelectedText: '".addslashes(Dict::S('UI:SearchValue:Any'))."', selectedList: 1, selectedText:'".addslashes(Dict::S('UI:SearchValue:NbSelected'))."'});");
|
||||
}
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
oACWidget_{$this->iId} = new ExtKeyWidget('{$this->iId}', '{$this->sTargetClass}', '$sFilter', '$sTitle', true, $sWizHelper, '{$this->sAttCode}', $sJSSearchMode);
|
||||
|
||||
23
css/jquery.multiselect.css
Normal file
23
css/jquery.multiselect.css
Normal file
@@ -0,0 +1,23 @@
|
||||
.ui-multiselect { padding:2px 0 2px 4px; text-align:left }
|
||||
.ui-multiselect span.ui-icon { float:right }
|
||||
.ui-multiselect-single .ui-multiselect-checkboxes input { position:absolute !important; top: auto !important; left:-9999px; }
|
||||
.ui-multiselect-single .ui-multiselect-checkboxes label { padding:5px !important }
|
||||
|
||||
.ui-multiselect-header { margin-bottom:3px; padding:3px 0 3px 4px }
|
||||
.ui-multiselect-header ul { font-size:0.9em }
|
||||
.ui-multiselect-header ul li { float:left; padding:0 10px 0 0 }
|
||||
.ui-multiselect-header a { text-decoration:none }
|
||||
.ui-multiselect-header a:hover { text-decoration:underline }
|
||||
.ui-multiselect-header span.ui-icon { float:left }
|
||||
.ui-multiselect-header li.ui-multiselect-close { float:right; text-align:right; padding-right:0 }
|
||||
|
||||
.ui-multiselect-menu { display:none; padding:3px; position:absolute; z-index:10000; text-align: left }
|
||||
.ui-multiselect-checkboxes { position:relative /* fixes bug in IE6/7 */; overflow-y:scroll }
|
||||
.ui-multiselect-checkboxes label { cursor:default; display:block; border:1px solid transparent; padding:3px 1px }
|
||||
.ui-multiselect-checkboxes label input { position:relative; top:1px }
|
||||
.ui-multiselect-checkboxes li { clear:both; font-size:0.9em; padding-right:3px }
|
||||
.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label { text-align:center; font-weight:bold; border-bottom:1px solid }
|
||||
.ui-multiselect-checkboxes li.ui-multiselect-optgroup-label a { display:block; padding:3px; margin:1px 0; text-decoration:none }
|
||||
|
||||
/* remove label borders in IE6 because IE6 does not support transparency */
|
||||
* html .ui-multiselect-checkboxes label { border:none }
|
||||
@@ -465,6 +465,7 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'UI:Details+' => 'Details',
|
||||
'UI:SearchValue:Any' => '* Any *',
|
||||
'UI:SearchValue:Mixed' => '* mixed *',
|
||||
'UI:SearchValue:NbSelected' => '# selected',
|
||||
'UI:SelectOne' => '-- select one --',
|
||||
'UI:Login:Welcome' => 'Welcome to iTop!',
|
||||
'UI:Login:IncorrectLoginPassword' => 'Incorrect login/password, please try again.',
|
||||
|
||||
@@ -342,6 +342,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'UI:SimpleSearchTab' => 'Recherche simple',
|
||||
'UI:Details+' => 'Détails',
|
||||
'UI:SearchValue:Any' => '* Indifférent *',
|
||||
'UI:SearchValue:NbSelected' => '# sélectionné(e)s',
|
||||
'UI:SearchValue:Mixed' => '* Plusieurs *',
|
||||
'UI:SelectOne' => '-- choisir une valeur --',
|
||||
'UI:Login:Welcome' => 'Bienvenue dans iTop!',
|
||||
|
||||
@@ -150,16 +150,17 @@ function ExtKeyWidget(id, sTargetClass, sFilter, sTitle, bSelectMode, oWizHelper
|
||||
};
|
||||
|
||||
// Gather the parameters from the search form
|
||||
$('#fs_'+me.id+' :input').each(
|
||||
function(i)
|
||||
$('#fs_'+me.id+' :input').each( function() {
|
||||
if (this.name != '')
|
||||
{
|
||||
if (this.name != '')
|
||||
var val = $(this).val(); // supports multiselect as well
|
||||
if (val !== null)
|
||||
{
|
||||
theMap[this.name] = this.value;
|
||||
theMap[this.name] = val;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
if (me.oWizardHelper == null)
|
||||
{
|
||||
theMap['json'] = '';
|
||||
|
||||
20
js/jquery.multiselect.min.js
vendored
Normal file
20
js/jquery.multiselect.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -97,15 +97,16 @@ function LinksWidget(id, sClass, sAttCode, iInputId, sSuffix, bDuplicates, oWizH
|
||||
|
||||
me.UpdateButtons(0);
|
||||
// Gather the parameters from the search form
|
||||
$('#SearchFormToAdd_'+me.id+' :input').each(
|
||||
function(i)
|
||||
{
|
||||
$('#SearchFormToAdd_'+me.id+' :input').each( function() {
|
||||
if (this.name != '')
|
||||
{
|
||||
theMap[this.name] = this.value;
|
||||
var val = $(this).val(); // supports multiselect as well
|
||||
if (val !== null)
|
||||
{
|
||||
theMap[this.name] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Gather the already linked target objects
|
||||
theMap.aAlreadyLinked = new Array();
|
||||
|
||||
Reference in New Issue
Block a user