Просмотр исходного кода

Implemented the "multiple choices" in search forms for Enums and External keys.

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@2305 a333f486-631f-4898-b8df-5754b55c2be0
dflaven 12 лет назад
Родитель
Сommit
08032a685c

+ 14 - 4
application/cmdbabstract.class.inc.php

@@ -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";

+ 50 - 7
application/displayblock.class.inc.php

@@ -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;		
+	}
 }
 
 /**

+ 10 - 1
application/itopwebpage.class.inc.php

@@ -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

+ 17 - 5
application/ui.extkeywidget.class.inc.php

@@ -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 - 0
css/jquery.multiselect.css

@@ -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 }

+ 1 - 0
dictionaries/dictionary.itop.ui.php

@@ -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.',

+ 1 - 0
dictionaries/fr.dictionary.itop.ui.php

@@ -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!',

+ 7 - 6
js/extkeywidget.js

@@ -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'] = '';

Разница между файлами не показана из-за своего большого размера
+ 19 - 0
js/jquery.multiselect.min.js


+ 7 - 6
js/linkswidget.js

@@ -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();

Некоторые файлы не были показаны из-за большого количества измененных файлов