mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-18 16:18:47 +02:00
Advanced search improvements (restore 2018-04-10 revisions : r5629..r5631)
* Add support for default search criteria * replace friendlyname by the class name for consistency * WIP Client SVN:trunk[5629]
This commit is contained in:
@@ -7012,3 +7012,4 @@ MetaModel::RegisterZList("preview", array("description" => "All attributes visib
|
||||
|
||||
MetaModel::RegisterZList("standard_search", array("description" => "List of criteria for the standard search", "type" => "filters"));
|
||||
MetaModel::RegisterZList("advanced_search", array("description" => "List of criteria for the advanced search", "type" => "filters"));
|
||||
MetaModel::RegisterZList("default_search", array("description" => "List of criteria displayed by default during search", "type" => "filters"));
|
||||
|
||||
2705
css/light-grey.css
Normal file
2705
css/light-grey.css
Normal file
File diff suppressed because it is too large
Load Diff
@@ -785,6 +785,7 @@ input.dp-applied {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
z-index: 1100; /* To be over the table block/unblock UI. Not very sure about this. */
|
||||
text-align: center; /* Used when form is closed */
|
||||
|
||||
/* Sizing reset */
|
||||
*{
|
||||
@@ -794,8 +795,13 @@ input.dp-applied {
|
||||
.search_form_handler{
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
width: 100%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
font-size: 12px;
|
||||
text-align: initial; /* To compensate .search_box:text-align */
|
||||
border: 1px solid #3F7294;
|
||||
//transition: width 0.3s ease-in-out;
|
||||
|
||||
/* Sizing reset */
|
||||
*{
|
||||
@@ -814,29 +820,57 @@ input.dp-applied {
|
||||
|
||||
&:not(.closed){
|
||||
.sf_title{
|
||||
.sft_short{
|
||||
display: none;
|
||||
}
|
||||
.sft_toggler{
|
||||
transform: rotateX(180deg);
|
||||
transition: transform 0.5s linear;
|
||||
}
|
||||
}
|
||||
.sf_criterion_area{
|
||||
transition: opacity 0.3s linear;
|
||||
}
|
||||
//.sf_criterion_area{
|
||||
// transition: opacity 0.3s linear;
|
||||
//}
|
||||
}
|
||||
&.closed{
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.3s linear;
|
||||
|
||||
transition: border 0.3s linear;
|
||||
margin-top: -0.25em; /* To remove top padding from the parent .display_block */
|
||||
margin-bottom: 0.5em;
|
||||
width: 150px;
|
||||
overflow: hidden;
|
||||
border-radius: 0 0 4px 4px;
|
||||
//opacity: 0.7;
|
||||
//transition: opacity 0.3s linear;
|
||||
//transition: border 0.3s linear;
|
||||
.sf_criterion_area{
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.sf_title {
|
||||
transition: background-color 0.3s, color 0.3s linear;
|
||||
background-color: #ffffff;
|
||||
color: #3F7294;
|
||||
padding: 6px 8px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
// transition: background-color 0.3s, color 0.3s linear;
|
||||
// background-color: #ffffff;
|
||||
// color: #3F7294;
|
||||
.sft_long{
|
||||
display: none;
|
||||
}
|
||||
.sft_refresh{
|
||||
display: none;
|
||||
line-height: 10pt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.no_auto_submit){
|
||||
.sfc_fg_apply{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&.no_auto_submit{
|
||||
.sfc_fg_search{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1099,12 +1133,14 @@ input.dp-applied {
|
||||
}
|
||||
}
|
||||
|
||||
.sfc_fg_search,
|
||||
.sfc_fg_apply,
|
||||
.sfc_fg_cancel{
|
||||
margin-top: 8px;
|
||||
padding: 3px 6px;
|
||||
font-size: 11px; /* Not bold, so it looks like 10px/bold of more/less buttons */
|
||||
}
|
||||
.sfc_fg_search,
|
||||
.sfc_fg_apply{
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
@@ -418,6 +418,19 @@
|
||||
</item>
|
||||
</items>
|
||||
</search>
|
||||
<default_search>
|
||||
<items>
|
||||
<item id="friendlyname">
|
||||
<rank>10</rank>
|
||||
</item>
|
||||
<item id="status">
|
||||
<rank>20</rank>
|
||||
</item>
|
||||
<item id="org_id">
|
||||
<rank>30</rank>
|
||||
</item>
|
||||
</items>
|
||||
</default_search>
|
||||
<list>
|
||||
<items>
|
||||
<item id="status">
|
||||
|
||||
@@ -1417,8 +1417,8 @@ When associated with a trigger, each action is given an "order" number, specifyi
|
||||
// - Criteria titles
|
||||
// - Default widget
|
||||
'UI:Search:Criteria:Title:Default:Any' => '%1$s: Any',
|
||||
'UI:Search:Criteria:Title:Default:Any:Empty' => '%1$s is empty',
|
||||
'UI:Search:Criteria:Title:Default:Any:NotEmpty' => '%1$s is not empty',
|
||||
'UI:Search:Criteria:Title:Default:Empty' => '%1$s is empty',
|
||||
'UI:Search:Criteria:Title:Default:NotEmpty' => '%1$s is not empty',
|
||||
'UI:Search:Criteria:Title:Default:Equals' => '%1$s equals %2$s',
|
||||
'UI:Search:Criteria:Title:Default:Contains' => '%1$s contains %2$s',
|
||||
'UI:Search:Criteria:Title:Default:StartsWith' => '%1$s starts with %2$s',
|
||||
@@ -1444,7 +1444,6 @@ When associated with a trigger, each action is given an "order" number, specifyi
|
||||
// - Enum widget
|
||||
'UI:Search:Criteria:Title:Enum:In' => '%1$s: %2$s',
|
||||
'UI:Search:Criteria:Title:Enum:In:Many' => '%1$s: %2$s and %3$s others',
|
||||
'UI:Search:Criteria:Title:Enum:In:Many:Shortened' => '%1$s: %2$s selected',
|
||||
'UI:Search:Criteria:Title:Enum:In:All' => '%1$s: Any',
|
||||
// - External key widget
|
||||
'UI:Search:Criteria:Title:ExternalKey:Empty' => '%1$s is defined',
|
||||
@@ -1480,7 +1479,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
|
||||
'UI:Search:Value:Autocomplete:Wait' => 'Please wait...',
|
||||
'UI:Search:Value:Autocomplete:NoResult' => 'No result.',
|
||||
'UI:Search:Value:Toggler:CheckAllNone' => 'Check all / none',
|
||||
'UI:Search:Value:Toggler:CheckAllNoneFiltered' => 'Check all / none filtered',
|
||||
'UI:Search:Value:Toggler:CheckAllNoneFiltered' => 'Check all / none visibles',
|
||||
|
||||
// - Widget other translations
|
||||
'UI:Search:Criteria:Numeric:From' => 'From',
|
||||
|
||||
@@ -1234,8 +1234,8 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
|
||||
// - Criteria titles
|
||||
// - Default widget
|
||||
'UI:Search:Criteria:Title:Default:Any' => '%1$s: Indifférent',
|
||||
'UI:Search:Criteria:Title:Default:Any:Empty' => '%1$s vide',
|
||||
'UI:Search:Criteria:Title:Default:Any:NotEmpty' => '%1$s non vide',
|
||||
'UI:Search:Criteria:Title:Default:Empty' => '%1$s vide',
|
||||
'UI:Search:Criteria:Title:Default:NotEmpty' => '%1$s non vide',
|
||||
'UI:Search:Criteria:Title:Default:Equals' => '%1$s égal %2$s',
|
||||
'UI:Search:Criteria:Title:Default:Contains' => '%1$s contient %2$s',
|
||||
'UI:Search:Criteria:Title:Default:StartsWith' => '%1$s commence par %2$s',
|
||||
@@ -1261,7 +1261,6 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
|
||||
// - Enum widget
|
||||
'UI:Search:Criteria:Title:Enum:In' => '%1$s : %2$s',
|
||||
'UI:Search:Criteria:Title:Enum:In:Many' => '%1$s : %2$s et %3$s autres',
|
||||
'UI:Search:Criteria:Title:Enum:In:Many:Shortened' => '%1$s: %2$s sélectionnés',
|
||||
'UI:Search:Criteria:Title:Enum:In:All' => '%1$s : Indifférent',
|
||||
// - External key widget
|
||||
'UI:Search:Criteria:Title:ExternalKey:Empty' => '%1$s est renseigné',
|
||||
@@ -1297,7 +1296,7 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
|
||||
'UI:Search:Value:Autocomplete:Wait' => 'Patientez ...',
|
||||
'UI:Search:Value:Autocomplete:NoResult' => 'Aucun résultat.',
|
||||
'UI:Search:Value:Toggler:CheckAllNone' => 'Cocher tout / aucun',
|
||||
'UI:Search:Value:Toggler:CheckAllNoneFiltered' => 'Cocher tout / aucun filtrés',
|
||||
'UI:Search:Value:Toggler:CheckAllNoneFiltered' => 'Cocher tout / aucun visibles',
|
||||
|
||||
// - Widget other translations
|
||||
'UI:Search:Criteria:Numeric:From' => 'Depuis',
|
||||
|
||||
@@ -285,6 +285,11 @@ $(function()
|
||||
|
||||
// Event callbacks
|
||||
// - Internal events
|
||||
_onButtonSearch: function()
|
||||
{
|
||||
// Note: We do exactly as for apply, the form handler will manage the difference.
|
||||
this._onButtonApply();
|
||||
},
|
||||
_onButtonApply: function()
|
||||
{
|
||||
this._apply();
|
||||
@@ -436,8 +441,9 @@ $(function()
|
||||
|
||||
// DOM elements
|
||||
this.element.find('.sfc_fg_buttons')
|
||||
.append('<button type="button" name="search" class="sfc_fg_button sfc_fg_search">' + Dict.S('UI:Button:Search') + '</button>')
|
||||
.append('<button type="button" name="apply" class="sfc_fg_button sfc_fg_apply">' + Dict.S('UI:Button:Apply') + '</button>')
|
||||
.append('<button type="button" name="cancel" class="sfc_fg_button sfc_fg_cancel">' + Dict.S('UI:Button:Close') + '</button>')
|
||||
.append('<button type="button" name="cancel" class="sfc_fg_button sfc_fg_cancel">' + Dict.S('UI:Button:Cancel') + '</button>')
|
||||
.append('<button type="button" name="more" class="sfc_fg_button sfc_fg_more">' + Dict.S('UI:Button:More') + '<span class="fa fa-angle-double-down"></span></button>')
|
||||
.append('<button type="button" name="less" class="sfc_fg_button sfc_fg_less">' + Dict.S('UI:Button:Less') + '<span class="fa fa-angle-double-up"></span></button>');
|
||||
|
||||
|
||||
@@ -493,6 +493,12 @@ $(function()
|
||||
var oItemElem = this._makeListItemElement(aValues[iIdx].label, aValues[iIdx].value, true);
|
||||
oItemElem.appendTo(this._getSelectedValuesWrapperSelector());
|
||||
}
|
||||
|
||||
// If autocomplete, hide them from here and show them in the selected values list
|
||||
if(this._hasAutocompleteAllowedValues() === true)
|
||||
{
|
||||
// TODO.
|
||||
}
|
||||
}
|
||||
|
||||
this._refreshSelectedValues();
|
||||
|
||||
@@ -15,6 +15,7 @@ $(function()
|
||||
'submit_button_selector': null,
|
||||
'endpoint': null,
|
||||
'init_opened': false,
|
||||
'auto_submit': true,
|
||||
'search': {
|
||||
'base_oql': '',
|
||||
'class_name': null,
|
||||
@@ -290,6 +291,11 @@ $(function()
|
||||
var me = this;
|
||||
|
||||
// Build DOM elements
|
||||
// - Autosubmit option
|
||||
if(this.options.auto_submit === false)
|
||||
{
|
||||
this.element.addClass('no_auto_submit');
|
||||
}
|
||||
// - Message area
|
||||
this.elements.message_area = this.element.find('.sf_message');
|
||||
this._cleanMessageArea();
|
||||
@@ -305,8 +311,6 @@ $(function()
|
||||
me._submit();
|
||||
});
|
||||
// - Toggle icon
|
||||
// TODO: UX Improvment
|
||||
// Note: Would be better to toggle by clicking on the whole title, but we have an issue with <select> on abstract classes.
|
||||
this.element.find('.sf_title').on('click', function(oEvent){
|
||||
// Prevent anchors
|
||||
oEvent.preventDefault();
|
||||
@@ -806,7 +810,10 @@ $(function()
|
||||
_onCriteriaValueChanged: function(oData)
|
||||
{
|
||||
this._updateSearch();
|
||||
this._submit();
|
||||
if(this.options.auto_submit === true)
|
||||
{
|
||||
this._submit();
|
||||
}
|
||||
},
|
||||
_onCriteriaRemoved: function(oData)
|
||||
{
|
||||
|
||||
@@ -1624,6 +1624,7 @@ EOF
|
||||
$aListRef = array(
|
||||
'details' => 'details',
|
||||
'standard_search' => 'search',
|
||||
'default_search' => 'default_search',
|
||||
'list' => 'list'
|
||||
);
|
||||
|
||||
|
||||
@@ -48,7 +48,14 @@ class CriterionToOQL extends CriterionConversionAbstract
|
||||
}
|
||||
$sRef = implode('.', $aRef);
|
||||
|
||||
$sOperator = $aCriteria['operator'];
|
||||
if (isset($aCriteria['operator']))
|
||||
{
|
||||
$sOperator = $aCriteria['operator'];
|
||||
}
|
||||
else
|
||||
{
|
||||
$sOperator = self::OP_ALL;
|
||||
}
|
||||
|
||||
$aMappedOperators = array(
|
||||
self::OP_CONTAINS => 'ContainsToOql',
|
||||
|
||||
@@ -112,7 +112,6 @@ class SearchForm
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$aSubClasses = MetaModel::GetSubclasses($sRootClass);
|
||||
if (count($aSubClasses) > 0)
|
||||
{
|
||||
@@ -135,7 +134,10 @@ class SearchForm
|
||||
$sAction = (isset($aExtraParams['action'])) ? $aExtraParams['action'] : utils::GetAbsoluteUrlAppRoot().'pages/UI.php';
|
||||
$sStyle = ($bOpen == 'true') ? '' : 'closed';
|
||||
$sHtml .= "<form id=\"fs_{$sSearchFormId}\" action=\"{$sAction}\" class=\"{$sStyle}\">\n"; // Don't use $_SERVER['SCRIPT_NAME'] since the form may be called asynchronously (from ajax.php)
|
||||
$sHtml .= "<h2 class=\"sf_title\"><span class=\"sft_picto fa fa-search\"></span>" . Dict::Format('UI:SearchFor_Class_Objects', $sClassesCombo) . "<a class=\"sft_toggler fa fa-caret-down pull-right\" href=\"#\" title=\"" . Dict::S('UI:Search:Toggle') . "\"></a><a class=\"sft_refresh fa fa-search pull-right\" href=\"#\" title=\"" . Dict::S('UI:Button:Refresh') . "\"></a></h2>\n";
|
||||
$sHtml .= "<h2 class=\"sf_title\"><span class=\"sft_long\">" . Dict::Format('UI:SearchFor_Class_Objects', $sClassesCombo) . "</span><span class=\"sft_short\">" . Dict::S('UI:SearchToggle') . "</span>";
|
||||
$sHtml .= "<a class=\"sft_toggler fa fa-caret-down pull-right\" href=\"#\" title=\"" . Dict::S('UI:Search:Toggle') . "\"></a>";
|
||||
$sHtml .= "<a class=\"sft_refresh fa fa-search pull-right\" href=\"#\" title=\"" . Dict::S('UI:Button:Refresh') . "\"></a>";
|
||||
$sHtml .= "</h2>\n";
|
||||
$sHtml .= "<div id=\"fs_{$sSearchFormId}_message\" class=\"sf_message header_message\"></div>\n";
|
||||
$sHtml .= "<div id=\"fs_{$sSearchFormId}_criterion_outer\">\n</div>\n";
|
||||
$sHtml .= "</form>\n";
|
||||
@@ -169,7 +171,6 @@ class SearchForm
|
||||
$oBaseSearch->ResetCondition();
|
||||
$sBaseOQL = str_replace(' WHERE 1', '', $oBaseSearch->ToOQL());
|
||||
|
||||
|
||||
if (isset($aExtraParams['table_inner_id']))
|
||||
{
|
||||
$sDataConfigListSelector = $aExtraParams['table_inner_id'];
|
||||
@@ -183,8 +184,6 @@ class SearchForm
|
||||
$aListParams['table_inner_id'] = "table_inner_id_{$sSearchFormId}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
$sDebug = utils::ReadParam('debug', 'false', false, 'parameter');
|
||||
if ($sDebug == 'true')
|
||||
{
|
||||
@@ -202,6 +201,7 @@ class SearchForm
|
||||
'data_config_list_selector' => "#{$sDataConfigListSelector}",
|
||||
'endpoint' => utils::GetAbsoluteUrlAppRoot().'pages/ajax.searchform.php',
|
||||
'init_opened' => $bOpen,
|
||||
'auto_submit' => false, // TODO: Change this so it takes the configuration parameter value for the current class.
|
||||
'list_params' => $aListParams,
|
||||
'search' => array(
|
||||
'has_hidden_criteria' => (array_key_exists('hidden_criteria', $aListParams) && !empty($aListParams['hidden_criteria'])),
|
||||
@@ -385,6 +385,7 @@ class SearchForm
|
||||
|
||||
$aOrCriterion = array();
|
||||
$aORExpressions = Expression::Split($oExpression, 'OR');
|
||||
$bIsEmptyExpression = true;
|
||||
foreach($aORExpressions as $oORSubExpr)
|
||||
{
|
||||
$aAndCriterion = array();
|
||||
@@ -396,11 +397,18 @@ class SearchForm
|
||||
continue;
|
||||
}
|
||||
$aAndCriterion[] = $oAndSubExpr->GetCriterion($oSearch);
|
||||
$bIsEmptyExpression = false;
|
||||
}
|
||||
$aAndCriterion = CriterionToSearchForm::Convert($aAndCriterion, $aFields, $oSearch->GetJoinedClasses(), $bIsRemovable);
|
||||
$aOrCriterion[] = array('and' => $aAndCriterion);
|
||||
}
|
||||
|
||||
if ($bIsEmptyExpression)
|
||||
{
|
||||
// Add default criterion
|
||||
$aOrCriterion = $this->GetDefaultCriterion($oSearch);
|
||||
}
|
||||
|
||||
return array('or' => $aOrCriterion);
|
||||
}
|
||||
|
||||
@@ -427,13 +435,13 @@ class SearchForm
|
||||
/**
|
||||
* @param $sClass
|
||||
* @param $sClassAlias
|
||||
* @param $sFilterCode
|
||||
* @param $sAttCode
|
||||
* @param $oAttDef
|
||||
* @param $aFields
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function AppendField($sClass, $sClassAlias, $sFilterCode, $oAttDef, $aFields)
|
||||
private function AppendField($sClass, $sClassAlias, $sAttCode, $oAttDef, $aFields)
|
||||
{
|
||||
if (!is_null($oAttDef) && ($oAttDef->GetSearchType() != AttributeDefinition::SEARCH_WIDGET_TYPE_RAW))
|
||||
{
|
||||
@@ -443,7 +451,21 @@ class SearchForm
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLabel = $oAttDef->GetLabel();
|
||||
if ($sAttCode == 'friendlyname')
|
||||
{
|
||||
try
|
||||
{
|
||||
$sLabel = MetaModel::GetName($sClass);
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
$sLabel = $oAttDef->GetLabel();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$sLabel = $oAttDef->GetLabel();
|
||||
}
|
||||
}
|
||||
|
||||
if (method_exists($oAttDef, 'GetTargetClass'))
|
||||
@@ -456,7 +478,7 @@ class SearchForm
|
||||
}
|
||||
|
||||
$aField = array();
|
||||
$aField['code'] = $sFilterCode;
|
||||
$aField['code'] = $sAttCode;
|
||||
$aField['class'] = $sClass;
|
||||
$aField['class_alias'] = $sClassAlias;
|
||||
$aField['target_class'] = $sTargetClass;
|
||||
@@ -464,7 +486,7 @@ class SearchForm
|
||||
$aField['widget'] = $oAttDef->GetSearchType();
|
||||
$aField['allowed_values'] = self::GetFieldAllowedValues($oAttDef);
|
||||
$aField['is_null_allowed'] = $oAttDef->IsNullAllowed();
|
||||
$aFields[$sClassAlias.'.'.$sFilterCode] = $aField;
|
||||
$aFields[$sClassAlias.'.'.$sAttCode] = $aField;
|
||||
|
||||
// Sub items
|
||||
//
|
||||
@@ -486,5 +508,36 @@ class SearchForm
|
||||
return $aFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $oSearch
|
||||
* @return array
|
||||
*/
|
||||
protected function GetDefaultCriterion($oSearch)
|
||||
{
|
||||
$aAndCriterion = array();
|
||||
$sClass = $oSearch->GetClass();
|
||||
$aList = MetaModel::GetZListItems($sClass, 'default_search');
|
||||
while (empty($aList))
|
||||
{
|
||||
// search in parent class if default criteria are defined
|
||||
$sClass = MetaModel::GetParentClass($sClass);
|
||||
if (is_null($sClass))
|
||||
{
|
||||
$aOrCriterion = array(array('and' => $aAndCriterion));
|
||||
return $aOrCriterion;
|
||||
}
|
||||
$aList = MetaModel::GetZListItems($sClass, 'default_search');
|
||||
}
|
||||
$sAlias = $oSearch->GetClassAlias();
|
||||
foreach($aList as $sAttCode)
|
||||
{
|
||||
$oFieldExpression = new \FieldExpression($sAttCode, $sAlias);
|
||||
$aAndCriterion[] = $oFieldExpression->GetCriterion($oSearch);
|
||||
}
|
||||
// Overwrite with default criterion
|
||||
$aOrCriterion = array(array('and' => $aAndCriterion));
|
||||
return $aOrCriterion;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user