diff --git a/.idea/inspectionProfiles/iTop.xml b/.idea/inspectionProfiles/iTop.xml deleted file mode 100644 index b7028562f..000000000 --- a/.idea/inspectionProfiles/iTop.xml +++ /dev/null @@ -1,153 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index ef816746c..000000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - \ No newline at end of file diff --git a/application/nicewebpage.class.inc.php b/application/nicewebpage.class.inc.php index 61c2bdcaf..b3c023aaf 100644 --- a/application/nicewebpage.class.inc.php +++ b/application/nicewebpage.class.inc.php @@ -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'); diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 5b84748eb..fa60d0fe8 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -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) diff --git a/core/config.class.inc.php b/core/config.class.inc.php index 3413f05b7..9ef9935af 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -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) diff --git a/core/oql/expression.class.inc.php b/core/oql/expression.class.inc.php index 0a36c106a..8d49afdfe 100644 --- a/core/oql/expression.class.inc.php +++ b/core/oql/expression.class.inc.php @@ -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) { diff --git a/datamodels/2.x/itop-portal-base/portal/src/views/layout.html.twig b/datamodels/2.x/itop-portal-base/portal/src/views/layout.html.twig index 920c70d54..869dadc32 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/views/layout.html.twig +++ b/datamodels/2.x/itop-portal-base/portal/src/views/layout.html.twig @@ -85,7 +85,7 @@ - + {# Visible.js to check if an element is visible on screen #} {# Base64.js #} diff --git a/dictionaries/en.dictionary.itop.ui.php b/dictionaries/en.dictionary.itop.ui.php index 7aa344ff8..9bfbdbe52 100644 --- a/dictionaries/en.dictionary.itop.ui.php +++ b/dictionaries/en.dictionary.itop.ui.php @@ -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', diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php index d6a852266..362019ea3 100644 --- a/dictionaries/fr.dictionary.itop.ui.php +++ b/dictionaries/fr.dictionary.itop.ui.php @@ -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 diff --git a/datamodels/2.x/itop-portal-base/portal/web/lib/latinise/latinise.min.js b/js/latinise/latinise.min.js similarity index 100% rename from datamodels/2.x/itop-portal-base/portal/web/lib/latinise/latinise.min.js rename to js/latinise/latinise.min.js diff --git a/js/search/search_form_criteria.js b/js/search/search_form_criteria.js index 3fcc07f57..de6875596 100644 --- a/js/search/search_form_criteria.js +++ b/js/search/search_form_criteria.js @@ -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 diff --git a/js/search/search_form_criteria_date_abstract.js b/js/search/search_form_criteria_date_abstract.js index d3bba1cf6..28db826e0 100644 --- a/js/search/search_form_criteria_date_abstract.js +++ b/js/search/search_form_criteria_date_abstract.js @@ -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); diff --git a/js/search/search_form_criteria_enum.js b/js/search/search_form_criteria_enum.js index 79374ac76..baccd0101 100644 --- a/js/search/search_form_criteria_enum.js +++ b/js/search/search_form_criteria_enum.js @@ -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(); } diff --git a/js/search/search_form_criteria_numeric.js b/js/search/search_form_criteria_numeric.js index 364b7a944..e50552134 100644 --- a/js/search/search_form_criteria_numeric.js +++ b/js/search/search_form_criteria_numeric.js @@ -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 diff --git a/js/search/search_form_handler.js b/js/search/search_form_handler.js index 276cc452a..db7ddbdee 100644 --- a/js/search/search_form_handler.js +++ b/js/search/search_form_handler.js @@ -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(); } diff --git a/sources/application/search/criterionconversion/criteriontooql.class.inc.php b/sources/application/search/criterionconversion/criteriontooql.class.inc.php index ebd852b3c..27a8292b3 100644 --- a/sources/application/search/criterionconversion/criteriontooql.class.inc.php +++ b/sources/application/search/criterionconversion/criteriontooql.class.inc.php @@ -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); diff --git a/sources/application/search/criterionconversion/criteriontosearchform.class.inc.php b/sources/application/search/criterionconversion/criteriontosearchform.class.inc.php index f85dcf9c3..6daa18b55 100644 --- a/sources/application/search/criterionconversion/criteriontosearchform.class.inc.php +++ b/sources/application/search/criterionconversion/criteriontosearchform.class.inc.php @@ -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: diff --git a/sources/application/search/criterionconversionabstract.class.inc.php b/sources/application/search/criterionconversionabstract.class.inc.php index 041d840e8..8a5b83284 100644 --- a/sources/application/search/criterionconversionabstract.class.inc.php +++ b/sources/application/search/criterionconversionabstract.class.inc.php @@ -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'; diff --git a/sources/application/search/criterionparser.class.inc.php b/sources/application/search/criterionparser.class.inc.php index 903bb7b71..097e02b5c 100644 --- a/sources/application/search/criterionparser.class.inc.php +++ b/sources/application/search/criterionparser.class.inc.php @@ -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).')'; diff --git a/sources/application/search/searchform.class.inc.php b/sources/application/search/searchform.class.inc.php index f24d61860..9e99fd6b0 100644 --- a/sources/application/search/searchform.class.inc.php +++ b/sources/application/search/searchform.class.inc.php @@ -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 .= "
\n"; // Don't use $_SERVER['SCRIPT_NAME'] since the form may be called asynchronously (from ajax.php) $sHtml .= "

" . Dict::Format('UI:SearchFor_Class_Objects', $sClassesCombo) . "" . Dict::S('UI:SearchToggle') . ""; $sHtml .= ""; @@ -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);