N°931: TagSet search form integration

This commit is contained in:
Eric
2018-09-12 15:03:35 +02:00
parent b86b24d444
commit 2ba31244c2
11 changed files with 577 additions and 327 deletions

View File

@@ -62,6 +62,7 @@ class NiceWebPage extends WebPage
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_external_field.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_numeric.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_enum.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_tag_set.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_external_key.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_hierarchical_key.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria_date_abstract.js');

File diff suppressed because it is too large Load Diff

View File

@@ -928,6 +928,24 @@ class ScalarExpression extends UnaryExpression
IssueLog::Error($e->getMessage());
}
break;
case ($oAttDef instanceof AttributeTagSet):
try
{
if (!empty($this->GetValue()))
{
/** @var AttributeTagSet $oAttDef */
$aValue['label'] = $oAttDef->GetValueLabel($this->GetValue());
}
else
{
$aValue['label'] = Dict::S('Enum:Undefined');
}
}
catch (Exception $e)
{
IssueLog::Error($e->getMessage());
}
break;
case $oAttDef->IsExternalKey():
try
{
@@ -1575,7 +1593,7 @@ class ListExpression extends Expression
{
if ($oExpr instanceof VariableExpression)
{
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar();
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar($aArgs);
}
else
{
@@ -2119,7 +2137,7 @@ class CharConcatExpression extends Expression
{
if ($oExpr instanceof VariableExpression)
{
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar();
$this->m_aExpressions[$idx] = $oExpr->GetAsScalar($aArgs);
}
else
{

View File

@@ -884,6 +884,9 @@ input.dp-applied {
left: 0px;
margin-top: -1px;
}
.search_form_handler .sf_criterion_area .search_form_criteria .sfc_form_group .sfc_fg_buttons, .search_form_handler .sf_criterion_area .sf_more_criterion .sfc_form_group .sfc_fg_buttons, .search_form_handler .sf_criterion_area .sf_button .sfc_form_group .sfc_fg_buttons, .search_form_handler .sf_criterion_area .search_form_criteria .sfm_content .sfc_fg_buttons, .search_form_handler .sf_criterion_area .sf_more_criterion .sfm_content .sfc_fg_buttons, .search_form_handler .sf_criterion_area .sf_button .sfm_content .sfc_fg_buttons {
white-space: nowrap;
}
.search_form_handler .sf_criterion_area .search_form_criteria {
/* Non editable criteria */
/* Draft criteria (modifications not applied) */
@@ -1127,6 +1130,15 @@ input.dp-applied {
.search_form_handler .sf_criterion_area .search_form_criteria.search_form_criteria_enum .sfc_form_group .sfc_fg_operator_in > label .sfc_op_content {
width: 100%;
}
.search_form_handler .sf_criterion_area .search_form_criteria.search_form_criteria_tag_set .sfc_form_group .sfc_fg_operator_in > label {
display: inline-block;
width: 100%;
line-height: initial;
white-space: nowrap;
}
.search_form_handler .sf_criterion_area .search_form_criteria.search_form_criteria_tag_set .sfc_form_group .sfc_fg_operator_in > label .sfc_op_content {
width: 100%;
}
.search_form_handler .sf_criterion_area .search_form_criteria.search_form_criteria_numeric .sfc_fg_operators .sfc_fg_operator.sfc_fg_operator_between .sfc_op_content_from_outer {
display: inline;
}
@@ -2862,19 +2874,16 @@ table.listResults .originColor {
.menu-icon-select > .ui-menu-item {
padding: 0.3em 3%;
}
.attribute-tagset > span, .attribute-tagset-undefined > span {
display: inline-block;
padding: 3px;
margin-right: 3px;
border-radius: 4px;
}
.attribute-tagset > span {
color: white;
background-color: grey;
}
.attribute-tagset-undefined > span {
color: grey;
background-color: lightgrey;

View File

@@ -991,6 +991,10 @@ input.dp-applied {
min-width: 100%;
left: 0px;
margin-top: -1px;
.sfc_fg_buttons{
white-space: nowrap;
}
}
}
@@ -1304,6 +1308,22 @@ input.dp-applied {
}
}
}
&.search_form_criteria_tag_set{
.sfc_form_group{
.sfc_fg_operator_in{
> label{
display: inline-block;
width: 100%;
line-height: initial;
white-space: nowrap;
.sfc_op_content{
width: 100%;
}
}
}
}
}
&.search_form_criteria_numeric {
//.sfc_form_group.advanced {
// .sfc_fg_operator_between {

View File

@@ -1447,6 +1447,8 @@ When associated with a trigger, each action is given an "order" number, specifyi
'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:All' => '%1$s: Any',
// - TagSet widget
'UI:Search:Criteria:Title:TagSet:Matches' => '%1$s: %2$s',
// - External key widget
'UI:Search:Criteria:Title:ExternalKey:Empty' => '%1$s is defined',
'UI:Search:Criteria:Title:ExternalKey:NotEmpty' => '%1$s is not defined',
@@ -1480,6 +1482,8 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:Search:Criteria:Operator:Numeric:LessThan' => 'Less', // => '<',
'UI:Search:Criteria:Operator:Numeric:LessThanOrEquals' => 'Less / equals', // > '<=',
'UI:Search:Criteria:Operator:Numeric:Different' => 'Different', // => '≠',
// - Tag Set Widget
'UI:Search:Criteria:Operator:TagSet:Matches' => 'Matches',
// - Other translations
'UI:Search:Value:Filter:Placeholder' => 'Filter...',

View File

@@ -1280,6 +1280,8 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'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:All' => '%1$s : Indifférent',
// - TagSet widget
'UI:Search:Criteria:Title:TagSet:Matches' => '%1$s : %2$s',
// - External key widget
'UI:Search:Criteria:Title:ExternalKey:Empty' => '%1$s est renseigné',
'UI:Search:Criteria:Title:ExternalKey:NotEmpty' => '%1$s n\'est pas renseigné',
@@ -1294,6 +1296,8 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'UI:Search:Criteria:Title:HierarchicalKey:In' => '%1$s : %2$s',
'UI:Search:Criteria:Title:HierarchicalKey:In:Many' => '%1$s : %2$s et %3$s autres',
'UI:Search:Criteria:Title:HierarchicalKey:In:All' => '%1$s : Indifférent',
// - Tag Set Widget
'UI:Search:Criteria:Operator:TagSet:Matches' => 'Contient',
/// - Criteria operators
// - Default widget

View File

@@ -0,0 +1,86 @@
//iTop Search form criteria tag_set
;
$(function()
{
// the widget definition, where 'itop' is the namespace,
// 'search_form_criteria_tag_set' the widget name
$.widget( 'itop.search_form_criteria_tag_set', $.itop.search_form_criteria_enum,
{
// default options
options:
{
// Overload default operator
'operator': 'MATCHES',
// Available operators
'available_operators': {
'MATCHES': {
'label': Dict.S('UI:Search:Criteria:Operator:TagSet:MATCHES'),
'code': 'matches',
'rank': 10,
},
'IN': null,
'=': null, // Remove this one from tag_set widget.
'empty': null, // Remove as it will be handle by the "null" value in the "MATCHES" operator
'not_empty': null, // Remove as it will be handle by the "null" value in the "MATCHES" operator
},
},
// the constructor
_create: function()
{
var me = this;
this._super();
this.element.addClass('search_form_criteria_tag_set');
},
// called when created, and later when changing options
_refresh: function()
{
},
// events bound via _bind are removed automatically
// revert other modifications here
_destroy: function()
{
this.element.removeClass('search_form_criteria_tag_set');
this._super();
},
// _setOptions is called with a hash of all options that are changing
// always refresh when changing options
_setOptions: function()
{
this._superApply(arguments);
},
// _setOption is called for each individual option that is changing
_setOption: function( key, value )
{
this._super( key, value );
},
//------------------
// Inherited methods
//------------------
_prepareMatchesOperator: function(oOpElem, sOpIdx, oOp)
{
this._prepareInOperator(oOpElem, sOpIdx, oOp);
},
// Operators helpers
// Reset operator's state
_resetMatchesOperator: function(oOpElem)
{
this._resetInOperator(oOpElem);
},
// Get operator's values
_getMatchesOperatorValues: function(oOpElem)
{
return this._getInOperatorValues(oOpElem);
},
// Set operator's values
_setMatchesOperatorValues: function(oOpElem, aValues)
{
return this._setInOperatorValues(oOpElem, aValues);
},
});
});

View File

@@ -74,6 +74,7 @@ class CriterionToOQL extends CriterionConversionAbstract
self::OP_BETWEEN => 'BetweenToOql',
self::OP_REGEXP => 'RegexpToOql',
self::OP_IN => 'InToOql',
self::OP_MATCHES => 'MatchesToOql',
self::OP_ALL => 'AllToOql',
);
@@ -118,7 +119,10 @@ class CriterionToOQL extends CriterionConversionAbstract
$aValues = self::GetValues($aCriteria);
$sValue = self::GetValue($aValues, 0);
if (empty($sValue)) return "1";
if (empty($sValue))
{
return "1";
}
return "({$sRef} LIKE '%{$sValue}%')";
}
@@ -128,7 +132,10 @@ class CriterionToOQL extends CriterionConversionAbstract
$aValues = self::GetValues($aCriteria);
$sValue = self::GetValue($aValues, 0);
if (empty($sValue)) return "1";
if (empty($sValue))
{
return "1";
}
return "({$sRef} LIKE '{$sValue}%')";
}
@@ -138,7 +145,10 @@ class CriterionToOQL extends CriterionConversionAbstract
$aValues = self::GetValues($aCriteria);
$sValue = self::GetValue($aValues, 0);
if (empty($sValue)) return "1";
if (empty($sValue))
{
return "1";
}
return "({$sRef} LIKE '%{$sValue}')";
}
@@ -148,7 +158,10 @@ class CriterionToOQL extends CriterionConversionAbstract
$aValues = self::GetValues($aCriteria);
$sValue = self::GetValue($aValues, 0);
if (empty($sValue)) return "1";
if (empty($sValue))
{
return "1";
}
return "({$sRef} = '{$sValue}')";
}
@@ -158,11 +171,32 @@ class CriterionToOQL extends CriterionConversionAbstract
$aValues = self::GetValues($aCriteria);
$sValue = self::GetValue($aValues, 0);
if (empty($sValue)) return "1";
if (empty($sValue))
{
return "1";
}
return "({$sRef} REGEXP '{$sValue}')";
}
protected static function MatchesToOql($oSearch, $sRef, $aCriteria)
{
$aValues = self::GetValues($aCriteria);
$aRawValues = array();
for($i = 0; $i < count($aValues); $i++)
{
$aRawValues[] = self::GetValue($aValues, $i);
}
$sValue = implode(' ', $aRawValues);
if (empty($sValue))
{
return "1";
}
return "({$sRef} MATCHES '{$sValue}')";
}
protected static function EmptyToOql($oSearch, $sRef, $aCriteria)
{
if (isset($aCriteria['widget']))
@@ -197,18 +231,18 @@ class CriterionToOQL extends CriterionConversionAbstract
return "({$sRef} != '')";
}
/**
* @param \DBObjectSearch $oSearch
* @param string $sRef
* @param array $aCriteria
*
* @return mixed|string
*
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
/**
* @param \DBObjectSearch $oSearch
* @param string $sRef
* @param array $aCriteria
*
* @return mixed|string
*
* @throws \CoreException
* @throws \MissingQueryArgument
* @throws \MySQLException
* @throws \MySQLHasGoneAwayException
*/
protected static function InToOql($oSearch, $sRef, $aCriteria)
{
$sAttCode = $aCriteria['code'];
@@ -225,8 +259,7 @@ class CriterionToOQL extends CriterionConversionAbstract
try
{
$aAttributeDefs = MetaModel::ListAttributeDefs($sClass);
}
catch (\CoreException $e)
} catch (\CoreException $e)
{
return "1";
}
@@ -254,8 +287,7 @@ class CriterionToOQL extends CriterionConversionAbstract
try
{
$sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sTargetClass);
}
catch (\CoreException $e)
} catch (\CoreException $e)
{
}
}
@@ -371,9 +403,10 @@ class CriterionToOQL extends CriterionConversionAbstract
else
{
// Add 'AND 1' to group the 'OR' inside an AND list for OQL parsing
$sCondition = "(({$sCondition} OR {$sFilterOnUndefined}) AND 1)";
$sCondition = "(({$sCondition} OR {$sFilterOnUndefined}) AND 1)";
}
}
return $sCondition;
}
@@ -406,8 +439,7 @@ class CriterionToOQL extends CriterionConversionAbstract
$oDate = $oFormat->parse($sStartDate);
$sStartDate = $oDate->format($sAttributeClass::GetSQLFormat());
$aOQL[] = "({$sRef} >= '$sStartDate')";
}
catch (Exception $e)
} catch (Exception $e)
{
}
}
@@ -420,8 +452,7 @@ class CriterionToOQL extends CriterionConversionAbstract
$oDate = $oFormat->parse($sEndDate);
$sEndDate = $oDate->format($sAttributeClass::GetSQLFormat());
$aOQL[] = "({$sRef} <= '$sEndDate')";
}
catch (Exception $e)
} catch (Exception $e)
{
}
}

View File

@@ -71,6 +71,7 @@ class CriterionToSearchForm extends CriterionConversionAbstract
AttributeDefinition::SEARCH_WIDGET_TYPE_EXTERNAL_KEY => 'ExternalKeyToSearchForm',
AttributeDefinition::SEARCH_WIDGET_TYPE_HIERARCHICAL_KEY => 'ExternalKeyToSearchForm',
AttributeDefinition::SEARCH_WIDGET_TYPE_ENUM => 'EnumToSearchForm',
AttributeDefinition::SEARCH_WIDGET_TYPE_TAG_SET => 'TagSetToSearchForm',
);
foreach($aAndCriterionRaw as $aCriteria)
@@ -666,6 +667,36 @@ class CriterionToSearchForm extends CriterionConversionAbstract
return $aCriteria;
}
protected static function TagSetToSearchForm($aCriteria, $aFields)
{
$sOperator = $aCriteria['operator'];
switch ($sOperator)
{
case 'MATCHES':
// Nothing special to do
break;
case 'ISNULL':
$aCriteria['operator'] = CriterionConversionAbstract::OP_EQUALS;
if (isset($aCriteria['has_undefined']) && $aCriteria['has_undefined'])
{
if (!isset($aCriteria['values']))
{
$aCriteria['values'] = array();
}
// Convention for 'undefined' enums
$aCriteria['values'][] = array('value' => 'null', 'label' => Dict::S('Enum:Undefined'));
}
break;
default:
// Unknown operator
$aCriteria['widget'] = AttributeDefinition::SEARCH_WIDGET_TYPE_RAW;
break;
}
return $aCriteria;
}
protected static function ExternalKeyToSearchForm($aCriteria, $aFields)
{
$sOperator = $aCriteria['operator'];

View File

@@ -37,6 +37,7 @@ abstract class CriterionConversionAbstract
const OP_BETWEEN = 'between';
const OP_REGEXP = 'REGEXP';
const OP_ALL = 'all';
const OP_MATCHES = 'MATCHES';
}