Advanced search improvements (restore 2018-04-10 revisions : r5635..r5641)

* Add 'search_manual_submit' config parameter to manage auto-submit
* bugfixes
** date i18n is now handled (using two new options `datepicker.dateFormat` and `datepicker.timeFormat` computed from `AttributeDateTime::GetFormat()->ToDatePicker()`
** handling of `empty` `not empty` operator titles
*** it led to tohers bugfixes and a redesign of the `_computeTitle` (from overwriting to extension using ie `_computeBetweenDaysOperatorTitle`
*** some bug still remain, because autocomplete needs to been finished before checking on them
* Promote 'friendlyname' in the 'most popular' list
* bugfixes
** filters (criterions, enum and FK without autocomplete) now ignore accent on matching with user input
** this is done by using a pre-existing tool used only by the portal, so it was moved to the core (latinize.min.js)
* Integration with manual submit parameter on client side.
* bugfixes : history/breadcrumb had several c[menu] appended (one for each refresh)
* Fix various sanity bugs

SVN:trunk[5632]
This commit is contained in:
Pierre Goiffon
2018-04-12 08:54:05 +00:00
parent 757130847f
commit efa7a4ee55
20 changed files with 259 additions and 253 deletions

View File

@@ -1,153 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="iTop" />
<inspection_tool class="BladeControlDirectives" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckEmptyScriptTag" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckImageSize" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckNodeTest" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckTagEmptyBody" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckValidXmlInScriptTagBody" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptArgumentsOutsideFunction" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptFunctionSignatures" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptInfiniteLoop" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptLiteralNotFunction" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptSillyAssignment" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptSwitchStatementWithNoDefaultBranch" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CoffeeScriptUnusedLocalSymbols" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ComposeFileStructure" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssFloatPxLength" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="CssInvalidAtRule" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidCharsetRule" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssInvalidElement" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidFunction" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidHtmlTagReference" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssInvalidImport" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssInvalidMediaFeature" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidPropertyValue" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidPseudoSelector" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssMissingComma" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssNegativeValue" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssNoGenericFontName" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssOptimizeSimilarProperties" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="CssOverwrittenProperties" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssRedundantUnit" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssUnitlessNumber" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssUnknownProperty" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myCustomPropertiesEnabled" value="false" />
<option name="myIgnoreVendorSpecificProperties" value="false" />
<option name="myCustomPropertiesList">
<value>
<list size="0" />
</value>
</option>
</inspection_tool>
<inspection_tool class="CssUnknownTarget" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssUnresolvedClass" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssUnresolvedCustomProperty" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssUnresolvedCustomPropertySet" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssUnusedSymbol" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CucumberExamplesColon" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CucumberMissedExamples" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CucumberTableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CucumberUndefinedStep" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="DockerFileArgumentCount" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="DuplicateKeyInSection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="DuplicateSectionInFile" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EmptyEventHandler" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="FileHeaderInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="GherkinBrokenTableInspection" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="GherkinMisplacedBackground" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="HamlNestedTagContent" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HardwiredNamespacePrefix" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlDeprecatedTag" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlExtraClosingTag" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlFormInputWithoutLabel" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlMissingClosingTag" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="HtmlPresentationalElement" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlUnknownAnchorTarget" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlUnknownAttribute" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myValues">
<value>
<list size="0" />
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
<inspection_tool class="HtmlUnknownBooleanAttribute" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlUnknownTag" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myValues">
<value>
<list size="6">
<item index="0" class="java.lang.String" itemvalue="nobr" />
<item index="1" class="java.lang.String" itemvalue="noembed" />
<item index="2" class="java.lang.String" itemvalue="comment" />
<item index="3" class="java.lang.String" itemvalue="noscript" />
<item index="4" class="java.lang.String" itemvalue="embed" />
<item index="5" class="java.lang.String" itemvalue="script" />
</list>
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
<inspection_tool class="HtmlUnknownTarget" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ImplicitTypeConversion" enabled="false" level="WARNING" enabled_by_default="false">
<option name="BITS" value="1720" />
<option name="FLAG_EXPLICIT_CONVERSION" value="true" />
<option name="IGNORE_NODESET_TO_BOOLEAN_VIA_STRING" value="true" />
</inspection_tool>
<inspection_tool class="InconsistentLineSeparators" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="IndexZeroUsage" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnresolvedFunction" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnresolvedVariable" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="LossyEncoding" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MissingSinceTagDocInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="MysqlParsingInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="NonAsciiCharacters" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpDocMissingReturnTagInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpDocMissingThrowsInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpIncludeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpRedundantClosingTagInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUndefinedClassConstantInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUndefinedClassInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUndefinedConstantInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUndefinedFieldInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUndefinedFunctionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUndefinedGotoLabelInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUndefinedMethodInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUndefinedNamespaceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PhpUnusedParameterInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RedundantTypeConversion" enabled="false" level="WARNING" enabled_by_default="false">
<option name="CHECK_ANY" value="false" />
</inspection_tool>
<inspection_tool class="RequiredAttributes" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myAdditionalRequiredHtmlAttributes" value="" />
</inspection_tool>
<inspection_tool class="SqlAddNotNullColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlAmbiguousColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlAutoIncrementDuplicateInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlCheckUsingColumnsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlConstantConditionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDeprecateTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDerivedTableAliasInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDialectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDropIndexedColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlIdentifierInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlInsertValuesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlNullComparisonInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlPostgresqlSelectFromProcedureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlResolveInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlShouldBeInGroupByInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlSignatureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlStorageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlUnusedVariableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TaskProblemsInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptPreferShortImport" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlDefaultAttributeValue" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlUnboundNsPrefix" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlUnusedNamespaceDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XsltUnusedDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XsltVariableShadowing" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View File

@@ -1,7 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="projectProfile" value="iTop" />
<option name="PROJECT_PROFILE" value="iTop" />
<version value="1.0" />
</settings>
</component>

View File

@@ -52,6 +52,7 @@ class NiceWebPage extends WebPage
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.positionBy.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.popupmenu.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/searchformforeignkeys.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/latinise/latinise.min.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_handler.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_handler_history.js');
$this->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/search/search_form_criteria.js');

View File

@@ -3957,7 +3957,11 @@ class AttributeDateTime extends AttributeDBField
const SEARCH_WIDGET_TYPE = self::SEARCH_WIDGET_TYPE_DATE_TIME;
static $oFormat = null;
/**
*
* @return DateTimeFormat
*/
static public function GetFormat()
{
if (self::$oFormat == null)

View File

@@ -1085,6 +1085,14 @@ class Config
'source_of_value' => '',
'show_in_conf_sample' => false,
),
'search_manual_submit' => array(
'type' => 'array',
'description' => 'Force manual submit of search requests (class => true)',
'default' => false,
'value' => true,
'source_of_value' => '',
'show_in_conf_sample' => true,
),
);
public function IsProperty($sPropCode)

View File

@@ -733,12 +733,19 @@ class ScalarExpression extends UnaryExpression
case $oAttDef->IsExternalKey():
try
{
/** @var AttributeExternalKey $oAttDef */
$sTarget = $oAttDef->GetTargetClass();
$oObj = MetaModel::GetObject($sTarget, $this->GetValue());
$aValue['label'] = $oObj->Get("friendlyname");
if ($this->GetValue() == 0)
{
$aValue['label'] = Dict::S('UI:UndefinedObject');
}
else
{
/** @var AttributeExternalKey $oAttDef */
$sTarget = $oAttDef->GetTargetClass();
$oObj = MetaModel::GetObject($sTarget, $this->GetValue());
$aValue['label'] = $oObj->Get("friendlyname");
}
}
catch (Exception $e)
{

View File

@@ -85,7 +85,7 @@
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/jquery.iframe-transport.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/jquery.fileupload.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/bootstrap/js/bootstrap.min.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/latinise/latinise.min.js'|add_itop_version }}"></script>
<script type="text/javascript" src="{{ app['combodo.absolute_url'] ~ 'js/latinise/latinise.min.js'|add_itop_version }}"></script>
{# Visible.js to check if an element is visible on screen #}
<script type="text/javascript" src="{{ app['combodo.portal.base.absolute_url'] ~ 'lib/jquery-visible/js/jquery.visible.min.js'|add_itop_version }}"></script>
{# Base64.js #}

View File

@@ -1430,13 +1430,13 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:Search:Criteria:Title:Default:LessThanOrEquals' => '%1$s <= %2$s',
'UI:Search:Criteria:Title:Default:Different' => '%1$s ≠ %2$s',
'UI:Search:Criteria:Title:Default:Between' => '%1$s between [%2$s]',
'UI:Search:Criteria:Title:Default:BetweenDates' => '%1$s [%2$s]',
'UI:Search:Criteria:Title:Default:BetweenDates:All' => '%1$s: Any',
'UI:Search:Criteria:Title:Default:BetweenDates:From' => '%1$s from %2$s',
'UI:Search:Criteria:Title:Default:BetweenDates:Until' => '%1$s until %2$s',
'UI:Search:Criteria:Title:Default:Between:All' => '%1$s: Any',
'UI:Search:Criteria:Title:Default:Between:From' => '%1$s from %2$s',
'UI:Search:Criteria:Title:Default:Between:Until' => '%1$s until %2$s',
'UI:Search:Criteria:Title:Default:BetweenDays' => '%1$s [%2$s]',
'UI:Search:Criteria:Title:Default:Between:Until' => '%1$s up to %2$s',
// - Numeric widget
// None yet
// - DateTime widget
@@ -1483,7 +1483,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
// - Widget other translations
'UI:Search:Criteria:Numeric:From' => 'From',
'UI:Search:Criteria:Numeric:Until' => 'Until',
'UI:Search:Criteria:Numeric:Until' => 'To',
'UI:Search:Criteria:Numeric:PlaceholderFrom' => 'Any',
'UI:Search:Criteria:Numeric:PlaceholderUntil' => 'Any',
'UI:Search:Criteria:DateTime:From' => 'From',

View File

@@ -1247,13 +1247,13 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'UI:Search:Criteria:Title:Default:LessThanOrEquals' => '%1$s <= %2$s',
'UI:Search:Criteria:Title:Default:Different' => '%1$s ≠ %2$s',
'UI:Search:Criteria:Title:Default:Between' => '%1$s entre [%2$s]',
'UI:Search:Criteria:Title:Default:BetweenDates' => '%1$s [%2$s]',
'UI:Search:Criteria:Title:Default:BetweenDates:All' => '%1$s : Indifférent',
'UI:Search:Criteria:Title:Default:BetweenDates:From' => '%1$s depuis %2$s',
'UI:Search:Criteria:Title:Default:BetweenDates:Until' => '%1$s jusqu\'à %2$s',
'UI:Search:Criteria:Title:Default:Between:All' => '%1$s : Indifférent',
'UI:Search:Criteria:Title:Default:Between:From' => '%1$s à partir de %2$s',
'UI:Search:Criteria:Title:Default:Between:Until' => '%1$s jusqu\'à %2$s',
'UI:Search:Criteria:Title:Default:BetweenDays' => '%1$s [%2$s]',
// - Numeric widget
// None yet
// - DateTime widget

View File

@@ -487,28 +487,64 @@ $(function()
});
},
// - Compute the title string
_computeEmptyOperatorTitle: function(sTitle) {
if (sTitle !== undefined) {
return sTitle;
}
sTitle = Dict.Format('UI:Search:Criteria:Title:Default:Any:Empty', this.options.field.label);
return sTitle;
},
_computeNotEmptyOperatorTitle: function(sTitle) {
if (sTitle !== undefined) {
return sTitle;
}
sTitle = Dict.Format('UI:Search:Criteria:Title:Default:Any:NotEmpty', this.options.field.label);
return sTitle;
},
_computeTitle: function(sTitle)
{
if(sTitle === undefined)
if(sTitle !== undefined)
{
var sValueAsText = this._getValuesAsText();
var sOperator = (sValueAsText !== '') ? this.operators[this.options.operator].code : 'Any';
var sDictEntry = 'UI:Search:Criteria:Title:' + this._toCamelCase(this.options.field.widget) + ':' + this._toCamelCase(sOperator);
return sTitle;
}
// Fallback to default widget dict entry if none exists for the current widget
if(Dict.S(sDictEntry) === sDictEntry)
var sCallback = '_compute' + this._toCamelCase(this.operators[this.options.operator].code) + 'OperatorTitle';
if(this[sCallback] !== undefined)
{
var sCallbackTitle = this[sCallback](sTitle);
if (sCallbackTitle !== undefined)
{
sDictEntry = 'UI:Search:Criteria:Title:Default:' + this._toCamelCase(sOperator);
}
sTitle = Dict.Format(sDictEntry, this.options.field.label, this._getValuesAsText());
// Last chande fallback
if(sTitle === sDictEntry)
{
sTitle = this.options.label;
sTitle = sCallbackTitle;
return sTitle;
}
}
var sValueAsText = this._getValuesAsText();
var sOperator = (sValueAsText !== '') ? this.operators[this.options.operator].code : 'Any';
var sDictEntry = 'UI:Search:Criteria:Title:' + this._toCamelCase(this.options.field.widget) + ':' + this._toCamelCase(sOperator);
// Fallback to default widget dict entry if none exists for the current widget
if(Dict.S(sDictEntry) === sDictEntry)
{
sDictEntry = 'UI:Search:Criteria:Title:Default:' + this._toCamelCase(sOperator);
}
sTitle = Dict.Format(sDictEntry, this.options.field.label, sValueAsText);
// Last chande fallback
if(sTitle === sDictEntry)
{
sTitle = this.options.label;
}
return sTitle;
},
// - Set the title element

View File

@@ -175,8 +175,9 @@ $(function()
};
var odatetimepickerOptionsDefault = {
dateFormat: 'yy-mm-dd',
timeFormat: 'HH:mm:ss',
// dateFormat: 'yy-mm-dd',
// timeFormat: 'HH:mm:ss',
showSecond: true,
buttonImage: GetAbsoluteUrlAppRoot()+"/images/calendar.png",
// buttonImageOnly: true,
buttonText: "",
@@ -294,10 +295,10 @@ $(function()
//------------------
// Inherited methods
//------------------
_computeTitle: function(sTitle)
_computeBetweenDaysOperatorTitle: function(sTitle)
{
var me = this;
if (sTitle === undefined && me.options.operator == 'between_dates')
if (sTitle === undefined)
{
var aValues = me._getValues();
switch (true)
@@ -318,24 +319,23 @@ $(function()
var sDictEntrySuffix = ':From';
break;
default:
var sDictEntrySuffix = undefined;
var sDictEntrySuffix = '';
break;
}
if (sDictEntrySuffix != undefined)
var sDictEntry = 'UI:Search:Criteria:Title:' + this._toCamelCase(this.options.field.widget) + ':' + this._toCamelCase(me.options.operator) + sDictEntrySuffix ;
// Fallback to default widget dict entry if none exists for the current widget
if(Dict.S(sDictEntry) === sDictEntry)
{
var sDictEntry = 'UI:Search:Criteria:Title:' + this._toCamelCase(this.options.field.widget) + ':' + this._toCamelCase(me.options.operator) + sDictEntrySuffix ;
// Fallback to default widget dict entry if none exists for the current widget
if(Dict.S(sDictEntry) === sDictEntry)
{
sDictEntry = 'UI:Search:Criteria:Title:Default:' + this._toCamelCase(me.options.operator) + sDictEntrySuffix;
}
sTitle = Dict.Format(sDictEntry, this.options.field.label, this._getValuesAsText());
sDictEntry = 'UI:Search:Criteria:Title:Default:' + this._toCamelCase(me.options.operator) + sDictEntrySuffix;
}
sTitle = Dict.Format(sDictEntry, this.options.field.label, this._getValuesAsText());
return sTitle;
}
return me._super(sTitle);
return undefined;
},
@@ -358,7 +358,8 @@ $(function()
}
else
{
aRawValues[1].label = aRawValues[1].label.replace(/(\s\d{2}:\d{2}:\d{2})/, '');
// aRawValues[1].label = aRawValues[1].label.replace(/(\s\d{2}:\d{2}:\d{2})/, '');
aRawValues[1].label = aRawValues[1].label.replace('23:59:59', '');
}
if (typeof aRawValues[0] == 'undefined' || typeof aRawValues[0].label == 'undefined' || aRawValues[0].label == '')
{
@@ -366,7 +367,8 @@ $(function()
}
else
{
aRawValues[0].label = aRawValues[0].label.replace(/(\s\d{2}:\d{2}:\d{2})/, '');
// aRawValues[0].label = aRawValues[0].label.replace(/(\s\d{2}:\d{2}:\d{2})/, '');
aRawValues[0].label = aRawValues[0].label.replace('00:00:00', '');
}
}
return me._super(aRawValues);

View File

@@ -238,11 +238,11 @@ $(function()
else
{
oOpContentElem.find('.sfc_opc_mc_item').each(function(){
var oRegExp = new RegExp(sQuery, 'ig');
var oRegExp = new RegExp(sQuery.latinise(), 'ig');
var sValue = $(this).find('input').val();
var sLabel = $(this).text();
if( (sValue.match(oRegExp) !== null) || (sLabel.match(oRegExp) !== null) )
if( (sValue.latinise().match(oRegExp) !== null) || (sLabel.latinise().match(oRegExp) !== null) )
{
$(this).show();
}

View File

@@ -186,7 +186,7 @@ $(function()
//------------------
// Inherited methods
//------------------
_computeTitle: function(sTitle)
_computeBetweenOperatorTitle: function(sTitle)
{
var me = this;
if (sTitle === undefined && me.options.operator == 'between')
@@ -222,7 +222,7 @@ $(function()
var sDictEntrySuffix = ':From';
break;
default:
var sDictEntrySuffix = undefined;
var sDictEntrySuffix = '';
break;
}
@@ -236,11 +236,12 @@ $(function()
}
sTitle = Dict.Format(sDictEntry, this.options.field.label, this._getValuesAsText());
return sTitle;
}
}
return me._super(sTitle);
return undefined;
},
// - Convert values to a standard string

View File

@@ -209,7 +209,7 @@ $(function()
for (var j = 0; j < prefix.length; j++) {
var pos = pars[i].lastIndexOf(prefix[j], 0);
if (pos !== -1) {
return pars[i].substring(pos);
return pars[i].substring(pos + prefix[j].length);
}
}
}
@@ -524,12 +524,12 @@ $(function()
else
{
oListsElem.find('.sfl_items > li:not(.sfl_i_placeholder)').each(function(){
var oRegExp = new RegExp(sFilter, 'ig');
var oRegExp = new RegExp(sFilter.latinize(), 'ig');
var sValue = $(this).find('input').val();
var sLabel = $(this).text();
// We don't check the sValue as it contains the class alias.
if(sLabel.match(oRegExp) !== null)
if(sLabel.latinise().match(oRegExp) !== null)
{
$(this).show();
}

View File

@@ -30,6 +30,8 @@ use AttributeEnum;
use Combodo\iTop\Application\Search\AjaxSearchException;
use Combodo\iTop\Application\Search\CriterionConversionAbstract;
use Combodo\iTop\Application\Search\SearchForm;
use Exception;
use MetaModel;
class CriterionToOQL extends CriterionConversionAbstract
{
@@ -59,6 +61,7 @@ class CriterionToOQL extends CriterionConversionAbstract
$aMappedOperators = array(
self::OP_CONTAINS => 'ContainsToOql',
self::OP_EQUALS => 'EqualsToOql',
self::OP_STARTS_WITH => 'StartsWithToOql',
self::OP_ENDS_WITH => 'EndsWithToOql',
self::OP_EMPTY => 'EmptyToOql',
@@ -110,6 +113,8 @@ class CriterionToOQL extends CriterionConversionAbstract
$aValues = self::GetValues($aCriteria);
$sValue = self::GetValue($aValues, 0);
if (empty($sValue)) return "1";
return "({$sRef} LIKE '%{$sValue}%')";
}
@@ -118,6 +123,8 @@ class CriterionToOQL extends CriterionConversionAbstract
$aValues = self::GetValues($aCriteria);
$sValue = self::GetValue($aValues, 0);
if (empty($sValue)) return "1";
return "({$sRef} LIKE '{$sValue}%')";
}
@@ -126,9 +133,21 @@ class CriterionToOQL extends CriterionConversionAbstract
$aValues = self::GetValues($aCriteria);
$sValue = self::GetValue($aValues, 0);
if (empty($sValue)) return "1";
return "({$sRef} LIKE '%{$sValue}')";
}
protected static function EqualsToOql($sRef, $aCriteria)
{
$aValues = self::GetValues($aCriteria);
$sValue = self::GetValue($aValues, 0);
if (empty($sValue)) return "1";
return "({$sRef} = '{$sValue}')";
}
protected static function EmptyToOql($sRef, $aCriteria)
{
if (isset($aCriteria['widget']))
@@ -164,7 +183,7 @@ class CriterionToOQL extends CriterionConversionAbstract
$bFilterOnUndefined = false;
try
{
$aAttributeDefs = \MetaModel::ListAttributeDefs($sClass);
$aAttributeDefs = MetaModel::ListAttributeDefs($sClass);
if (array_key_exists($sAttCode, $aAttributeDefs))
{
$oAttDef = $aAttributeDefs[$sAttCode];
@@ -272,17 +291,29 @@ class CriterionToOQL extends CriterionConversionAbstract
$sStartDate = $aValues[0]['value'];
if (!empty($sStartDate))
{
$oDate = $oFormat->parse($sStartDate);
$sStartDate = $oDate->format($sAttributeClass::GetSQLFormat());
$aOQL[] = "({$sRef} >= '$sStartDate')";
try
{
$oDate = $oFormat->parse($sStartDate);
$sStartDate = $oDate->format($sAttributeClass::GetSQLFormat());
$aOQL[] = "({$sRef} >= '$sStartDate')";
}
catch (Exception $e)
{
}
}
$sEndDate = $aValues[1]['value'];
if (!empty($sEndDate))
{
$oDate = $oFormat->parse($sEndDate);
$sEndDate = $oDate->format($sAttributeClass::GetSQLFormat());
$aOQL[] = "({$sRef} <= '$sEndDate')";
try
{
$oDate = $oFormat->parse($sEndDate);
$sEndDate = $oDate->format($sAttributeClass::GetSQLFormat());
$aOQL[] = "({$sRef} <= '$sEndDate')";
}
catch (Exception $e)
{
}
}
$sOQL = implode(' AND ', $aOQL);

View File

@@ -197,6 +197,8 @@ class CriterionToSearchForm extends CriterionConversionAbstract
if (($a['widget'] === AttributeDefinition::SEARCH_WIDGET_TYPE_RAW) &&
($b['widget'] === AttributeDefinition::SEARCH_WIDGET_TYPE_RAW))
{
if (!isset($a['label'])) return -1;
if (!isset($b['label'])) return 1;
return strcmp($a['label'], $b['label']);
}
if ($a['widget'] === AttributeDefinition::SEARCH_WIDGET_TYPE_RAW)
@@ -207,6 +209,8 @@ class CriterionToSearchForm extends CriterionConversionAbstract
return 1;
}
if (!isset($a['label'])) return -1;
if (!isset($b['label'])) return 1;
return strcmp($a['label'], $b['label']);
});
@@ -564,7 +568,7 @@ class CriterionToSearchForm extends CriterionConversionAbstract
$aCriteria['values'] = array();
}
// Convention for 'undefined' enums
$aCriteria['values'][] = array('value' => 'null', 'label' => 'null');
$aCriteria['values'][] = array('value' => 'null', 'label' => Dict::S('Enum:Undefined'));
}
break;
default:

View File

@@ -27,6 +27,7 @@ abstract class CriterionConversionAbstract
{
const OP_CONTAINS = 'contains';
const OP_EQUALS = '=';
const OP_STARTS_WITH = 'starts_with';
const OP_ENDS_WITH = 'ends_with';
const OP_EMPTY = 'empty';

View File

@@ -90,12 +90,17 @@ class CriterionParser
$aExpression = array();
foreach($aAnd as $aCriteria)
{
$aExpression[] = CriterionToOQL::Convert($aCriteria);
$sExpression = CriterionToOQL::Convert($aCriteria);
if ($sExpression !== '1')
{
$aExpression[] = $sExpression;
}
}
if (empty($aExpression))
{
return '';
return '1';
}
return '('.implode(" AND ", $aExpression).')';

View File

@@ -131,8 +131,39 @@ class SearchForm
{
$sClassesCombo = MetaModel::GetName($sClassName);
}
$bAutoSubmit = true;
$mSubmitParam = utils::GetConfig()->Get('search_manual_submit');
if (is_array($mSubmitParam))
{
// List of classes
if (isset($mSubmitParam[$sClassName]))
{
$bAutoSubmit = !$mSubmitParam[$sClassName];
}
else
{
// Serach for child classes
foreach($mSubmitParam as $sConfigClass => $bFlag)
{
$aChildClasses = MetaModel::EnumChildClasses($sConfigClass);
if (in_array($sClassName, $aChildClasses))
{
$bAutoSubmit = !$bFlag;
break;
}
}
}
}
else if ($mSubmitParam !== false)
{
$bAutoSubmit = false;
}
$sAction = (isset($aExtraParams['action'])) ? $aExtraParams['action'] : utils::GetAbsoluteUrlAppRoot().'pages/UI.php';
$sStyle = ($bOpen == 'true') ? '' : 'closed';
$sStyle .= ($bAutoSubmit === true) ? '' : ' no_auto_submit';
$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_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>";
@@ -168,7 +199,10 @@ class SearchForm
}
$oBaseSearch = $oSearch->DeepClone();
$oBaseSearch->ResetCondition();
if (method_exists($oSearch, 'GetCriteria'))
{
$oBaseSearch->ResetCondition();
}
$sBaseOQL = str_replace(' WHERE 1', '', $oBaseSearch->ToOQL());
if (isset($aExtraParams['table_inner_id']))
@@ -195,13 +229,25 @@ class SearchForm
$aMonthsShort = array(Dict::S('Month-01-Short'), Dict::S('Month-02-Short'), Dict::S('Month-03-Short'), Dict::S('Month-04-Short'), Dict::S('Month-05-Short'), Dict::S('Month-06-Short'),
Dict::S('Month-07-Short'), Dict::S('Month-08-Short'), Dict::S('Month-09-Short'), Dict::S('Month-10-Short'), Dict::S('Month-11-Short'), Dict::S('Month-12-Short'));
// $sDateTimeFormat = \AttributeDateTime::GetFormat()->ToMomentJS('%s');
// $iDateTimeSeparatorPos = strpos($sDateTimeFormat, ' ');
// $sDateFormat = substr($sDateTimeFormat, 0, $iDateTimeSeparatorPos);
// $sTimeFormat = substr($sDateTimeFormat, $iDateTimeSeparatorPos + 1);
$sDateTimeFormat = \AttributeDateTime::GetFormat()->ToDatePicker();
$iDateTimeSeparatorPos = strpos($sDateTimeFormat, ' ');
$sDateFormat = substr($sDateTimeFormat, 0, $iDateTimeSeparatorPos);
$sTimeFormat = substr($sDateTimeFormat, $iDateTimeSeparatorPos + 1);
$aSearchParams = array(
'criterion_outer_selector' => "#fs_{$sSearchFormId}_criterion_outer",
'result_list_outer_selector' => "#{$aExtraParams['result_list_outer_selector']}",
'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.
'auto_submit' => $bAutoSubmit,
'list_params' => $aListParams,
'search' => array(
'has_hidden_criteria' => (array_key_exists('hidden_criteria', $aListParams) && !empty($aListParams['hidden_criteria'])),
@@ -217,6 +263,9 @@ class SearchForm
'dayNamesMin' => $aDaysMin,
'monthNamesShort' => $aMonthsShort,
'firstDay' => (int) Dict::S('Calendar-FirstDayOfWeek'),
// 'format' => \AttributeDateTime::GetFormat()->ToDatePicker()
'dateFormat' => $sDateFormat,
'timeFormat' => $sTimeFormat,
),
),
);
@@ -286,6 +335,7 @@ class SearchForm
{
$aAttributeDefs = MetaModel::ListAttributeDefs($sClass);
$aList = MetaModel::GetZListItems($sClass, 'standard_search');
$bHasFriendlyname = false;
foreach($aList as $sAttCode)
{
if (array_key_exists($sAttCode, $aAttributeDefs))
@@ -294,6 +344,18 @@ class SearchForm
$aZList = $this->AppendField($sClass, $sAlias, $sAttCode, $oAttDef, $aZList);
unset($aAttributeDefs[$sAttCode]);
}
if ($sAttCode == 'friendlyname')
{
$bHasFriendlyname = true;
}
}
if (!$bHasFriendlyname)
{
// Add friendlyname to the most popular
$sAttCode = 'friendlyname';
$oAttDef = $aAttributeDefs[$sAttCode];
$aZList = $this->AppendField($sClass, $sAlias, $sAttCode, $oAttDef, $aZList);
unset($aAttributeDefs[$sAttCode]);
}
$aZList = $this->AppendId($sClass, $sAlias, $aZList);
uasort($aZList, function ($aItem1, $aItem2) {
@@ -373,40 +435,44 @@ class SearchForm
*/
public function GetCriterion($oSearch, $aFields, $aArgs = array(), $bIsRemovable = true)
{
$oExpression = $oSearch->GetCriteria();
if (!empty($aArgs))
{
$aArgs = MetaModel::PrepareQueryArguments($aArgs);
$sOQL = $oExpression->Render($aArgs);
$oExpression = Expression::FromOQL($sOQL);
}
$aOrCriterion = array();
$aORExpressions = Expression::Split($oExpression, 'OR');
$bIsEmptyExpression = true;
foreach($aORExpressions as $oORSubExpr)
{
$aAndCriterion = array();
$aAndExpressions = Expression::Split($oORSubExpr, 'AND');
foreach($aAndExpressions as $oAndSubExpr)
{
if (($oAndSubExpr instanceof TrueExpression) || ($oAndSubExpr->Render() == 1))
{
continue;
}
$aAndCriterion[] = $oAndSubExpr->GetCriterion($oSearch);
$bIsEmptyExpression = false;
}
$aAndCriterion = CriterionToSearchForm::Convert($aAndCriterion, $aFields, $oSearch->GetJoinedClasses(), $bIsRemovable);
$aOrCriterion[] = array('and' => $aAndCriterion);
}
if ($bIsEmptyExpression)
if (method_exists($oSearch, 'GetCriteria'))
{
// Add default criterion
$aOrCriterion = $this->GetDefaultCriterion($oSearch);
$oExpression = $oSearch->GetCriteria();
if (!empty($aArgs))
{
$aArgs = MetaModel::PrepareQueryArguments($aArgs);
$sOQL = $oExpression->Render($aArgs);
$oExpression = Expression::FromOQL($sOQL);
}
$aORExpressions = Expression::Split($oExpression, 'OR');
$bIsEmptyExpression = true;
foreach($aORExpressions as $oORSubExpr)
{
$aAndCriterion = array();
$aAndExpressions = Expression::Split($oORSubExpr, 'AND');
foreach($aAndExpressions as $oAndSubExpr)
{
if (($oAndSubExpr instanceof TrueExpression) || ($oAndSubExpr->Render() == 1))
{
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);