mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-23 10:38:45 +02:00
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:
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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).')';
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user