mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
Advanced Search
SVN:b1162[5392]
This commit is contained in:
@@ -448,6 +448,7 @@ class BinaryExpression extends Expression
|
||||
|
||||
public function GetCriterion($oSearch, &$aArgs = null, $bRetrofitParams = false, $oAttDef = null)
|
||||
{
|
||||
$bReverseOperator = false;
|
||||
$oLeftExpr = $this->GetLeftExpr();
|
||||
$oRightExpr = $this->GetRightExpr();
|
||||
if ($oLeftExpr instanceof FieldExpression && $oRightExpr instanceof FieldExpression)
|
||||
@@ -462,6 +463,7 @@ class BinaryExpression extends Expression
|
||||
if ($oRightExpr instanceof FieldExpression)
|
||||
{
|
||||
$oAttDef = $oRightExpr->GetAttDef($oSearch->GetJoinedClasses());
|
||||
$bReverseOperator = true;
|
||||
}
|
||||
|
||||
if (is_null($oAttDef))
|
||||
@@ -474,7 +476,33 @@ class BinaryExpression extends Expression
|
||||
|
||||
$aCriteria = array_merge($aCriteriaLeft, $aCriteriaRight);
|
||||
|
||||
$aCriteria['operator'] = $this->GetOperator();
|
||||
if ($bReverseOperator)
|
||||
{
|
||||
// switch left and right expressions so reverse the operator
|
||||
// Note that the operation is the same so < becomes > and not >=
|
||||
switch ($this->GetOperator())
|
||||
{
|
||||
case '>':
|
||||
$aCriteria['operator'] = '<';
|
||||
break;
|
||||
case '<':
|
||||
$aCriteria['operator'] = '>';
|
||||
break;
|
||||
case '>=':
|
||||
$aCriteria['operator'] = '<=';
|
||||
break;
|
||||
case '<=':
|
||||
$aCriteria['operator'] = '>=';
|
||||
break;
|
||||
default:
|
||||
$aCriteria['operator'] = $this->GetOperator();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$aCriteria['operator'] = $this->GetOperator();
|
||||
}
|
||||
$aCriteria['oql'] = $this->Render($aArgs, $bRetrofitParams);
|
||||
|
||||
return $aCriteria;
|
||||
|
||||
81
pages/ajax.searchform.php
Normal file
81
pages/ajax.searchform.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2010-2018 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
use Combodo\iTop\Application\Search\AjaxSearchException;
|
||||
use Combodo\iTop\Application\Search\CriterionParser;
|
||||
|
||||
require_once('../approot.inc.php');
|
||||
require_once(APPROOT.'/application/application.inc.php');
|
||||
require_once(APPROOT.'/application/webpage.class.inc.php');
|
||||
require_once(APPROOT.'/application/ajaxwebpage.class.inc.php');
|
||||
require_once(APPROOT.'/application/startup.inc.php');
|
||||
require_once(APPROOT.'/application/user.preferences.class.inc.php');
|
||||
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
|
||||
require_once(APPROOT.'/sources/application/search/ajaxsearchexception.class.inc.php');
|
||||
require_once(APPROOT.'/sources/application/search/criterionparser.class.inc.php');
|
||||
|
||||
try
|
||||
{
|
||||
if (LoginWebPage::EXIT_CODE_OK != LoginWebPage::DoLoginEx(null /* any portal */, false, LoginWebPage::EXIT_RETURN))
|
||||
{
|
||||
throw new SecurityException('You must be logged in');
|
||||
}
|
||||
|
||||
$sParams = stripslashes(utils::ReadParam('params', '', false, 'raw_data'));
|
||||
if (!$sParams)
|
||||
{
|
||||
throw new AjaxSearchException("Invalid query (empty filter)", 400);
|
||||
}
|
||||
|
||||
$oPage = new ajax_page("");
|
||||
$oPage->no_cache();
|
||||
$oPage->SetContentType('text/html');
|
||||
|
||||
$aParams = json_decode($sParams, true);
|
||||
$sOQL = CriterionParser::Parse($aParams['base_oql'], $aParams['criterion']);
|
||||
$oFilter = DBSearch::FromOQL($sOQL);
|
||||
$oDisplayBlock = new DisplayBlock($oFilter, 'list', false);
|
||||
|
||||
$aExtraParams['display_limit'] = true;
|
||||
$aExtraParams['truncated'] = true;
|
||||
$oDisplayBlock->RenderContent($oPage, $aExtraParams);
|
||||
|
||||
$oPage->output();
|
||||
|
||||
} catch (AjaxSearchException $e)
|
||||
{
|
||||
http_response_code($e->getCode());
|
||||
// note: transform to cope with XSS attacks
|
||||
echo htmlentities($e->GetMessage(), ENT_QUOTES, 'utf-8');
|
||||
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
|
||||
} catch (SecurityException $e)
|
||||
{
|
||||
http_response_code(403);
|
||||
// note: transform to cope with XSS attacks
|
||||
echo htmlentities($e->GetMessage(), ENT_QUOTES, 'utf-8');
|
||||
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
|
||||
} catch (Exception $e)
|
||||
{
|
||||
http_response_code(500);
|
||||
// note: transform to cope with XSS attacks
|
||||
echo htmlentities($e->GetMessage(), ENT_QUOTES, 'utf-8');
|
||||
IssueLog::Error($e->getMessage()."\nDebug trace:\n".$e->getTraceAsString());
|
||||
}
|
||||
35
sources/application/search/ajaxsearchexception.class.inc.php
Normal file
35
sources/application/search/ajaxsearchexception.class.inc.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2010-2018 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Eric
|
||||
* Date: 08/03/2018
|
||||
* Time: 11:18
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Application\Search;
|
||||
|
||||
|
||||
class AjaxSearchException extends \Exception
|
||||
{
|
||||
|
||||
}
|
||||
35
sources/application/search/criterionconversion.class.inc.php
Normal file
35
sources/application/search/criterionconversion.class.inc.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2010-2018 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Eric
|
||||
* Date: 08/03/2018
|
||||
* Time: 14:47
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Application\Search;
|
||||
|
||||
|
||||
class CriterionConversion
|
||||
{
|
||||
|
||||
}
|
||||
114
sources/application/search/criterionparser.class.inc.php
Normal file
114
sources/application/search/criterionparser.class.inc.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2010-2018 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Eric
|
||||
* Date: 08/03/2018
|
||||
* Time: 11:25
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Application\Search;
|
||||
|
||||
|
||||
use DBObjectSearch;
|
||||
use IssueLog;
|
||||
use OQLException;
|
||||
|
||||
class CriterionParser
|
||||
{
|
||||
|
||||
/**
|
||||
* @param $sBaseOql
|
||||
* @param $aCriterion
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function Parse($sBaseOql, $aCriterion)
|
||||
{
|
||||
$aExpression = array();
|
||||
$aOr = $aCriterion['or'];
|
||||
foreach($aOr as $aAndList)
|
||||
{
|
||||
|
||||
$sExpression = self::ParseAndList($aAndList['and']);
|
||||
if (!empty($sExpression))
|
||||
{
|
||||
$aExpression[] = $sExpression;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($aExpression))
|
||||
{
|
||||
return $sBaseOql;
|
||||
}
|
||||
|
||||
// Sanitize the base OQL
|
||||
if (strpos($sBaseOql, ' WHERE '))
|
||||
{
|
||||
try
|
||||
{
|
||||
$oSearch = DBObjectSearch::FromOQL($sBaseOql);
|
||||
$oSearch->ResetCondition();
|
||||
$sBaseOql = $oSearch->ToOQL();
|
||||
} catch (OQLException $e)
|
||||
{
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return $sBaseOql.' WHERE '.implode(" OR ", $aExpression).'';
|
||||
}
|
||||
|
||||
private static function ParseAndList($aAnd)
|
||||
{
|
||||
$aExpression = array();
|
||||
foreach($aAnd as $aCriteria)
|
||||
{
|
||||
$aExpression[] = self::ParseCriteria($aCriteria);
|
||||
}
|
||||
|
||||
if (empty($aExpression))
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
return '('.implode(" AND ", $aExpression).')';
|
||||
}
|
||||
|
||||
private static function ParseCriteria($aCriteria)
|
||||
{
|
||||
|
||||
if (!empty($aCriteria['oql']))
|
||||
{
|
||||
return $aCriteria['oql'];
|
||||
}
|
||||
|
||||
// TODO Manage more complicated case
|
||||
$aRef = explode('.', $aCriteria['ref']);
|
||||
$sRef = '`'.$aRef[0].'`.`'.$aRef[1].'`';
|
||||
|
||||
$sOperator = $aCriteria['operator'];
|
||||
$sValue = $aCriteria['values'][0]['value'];
|
||||
|
||||
return "({$sRef} {$sOperator} '{$sValue}')";
|
||||
}
|
||||
}
|
||||
@@ -112,6 +112,7 @@ class SearchForm
|
||||
|
||||
$aSearchParams = array(
|
||||
'criterion_outer_selector' => "#fs_{$sSearchFormId}_criterion_outer",
|
||||
'endpoint' => utils::GetAbsoluteUrlAppRoot().'pages/ajax.searchform.php',
|
||||
'search' => array(
|
||||
'fields' => $aFields,
|
||||
'criterion' => $aCriterion,
|
||||
|
||||
86
test/application/search/CriterionParserTest.php
Normal file
86
test/application/search/CriterionParserTest.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2010-2018 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Eric
|
||||
* Date: 08/03/2018
|
||||
* Time: 11:28
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Test\UnitTest\Application\Search;
|
||||
|
||||
use Combodo\iTop\Application\Search\CriterionParser;
|
||||
use Combodo\iTop\Test\UnitTest\ItopDataTestCase;
|
||||
|
||||
|
||||
class CriterionParserTest extends ItopDataTestCase
|
||||
{
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
require_once(APPROOT."sources/application/search/criterionparser.class.inc.php");
|
||||
}
|
||||
|
||||
public function testParse()
|
||||
{
|
||||
$sBaseOql = 'SELECT UserRequest';
|
||||
$aCriterion = json_decode('{
|
||||
"or": [
|
||||
{
|
||||
"and": [
|
||||
{
|
||||
"ref": "UserRequest.start_date",
|
||||
"values": [
|
||||
{
|
||||
"value": "2017-01-01",
|
||||
"label": "2017-01-01 00:00:00"
|
||||
}
|
||||
],
|
||||
"operator": ">",
|
||||
"oql": ""
|
||||
},
|
||||
{
|
||||
"ref": "UserRequest.start_date",
|
||||
"values": [
|
||||
{
|
||||
"value": "2018-01-01",
|
||||
"label": "2018-01-01 00:00:00"
|
||||
}
|
||||
],
|
||||
"operator": "<",
|
||||
"oql": "(`UserRequest`.`start_date` < \'2018-01-01\')"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
', true);
|
||||
$sOQL = CriterionParser::Parse($sBaseOql, $aCriterion);
|
||||
|
||||
$this->debug($sOQL);
|
||||
$this->markTestIncomplete();
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,8 @@ class SearchFormTest extends ItopDataTestCase
|
||||
public function testGetCriterion($sOQL, $iOrCount)
|
||||
{
|
||||
$aCriterion = SearchForm::GetCriterion(\DBObjectSearch::FromOQL($sOQL));
|
||||
$aRes = array('base_oql' => $sOQL, 'criterion' => $aCriterion);
|
||||
$this->debug(json_encode($aRes));
|
||||
$this->debug($sOQL);
|
||||
$this->debug(json_encode($aCriterion, JSON_PRETTY_PRINT));
|
||||
$this->assertCount($iOrCount, $aCriterion['or']);
|
||||
@@ -75,7 +77,7 @@ class SearchFormTest extends ItopDataTestCase
|
||||
array('OQL' => "SELECT Contact WHERE status IN ('active', 'inactive')", 1),
|
||||
array('OQL' => "SELECT Contact WHERE status = 'active' OR name LIKE 'toto%'", 2),
|
||||
array('OQL' => "SELECT UserRequest WHERE DATE_SUB(NOW(), INTERVAL 14 DAY) < start_date", 1),
|
||||
array('OQL' => "SELECT UserRequest WHERE start_date > '2017-01-01' AND start_date < '2018-01-01'", 1),
|
||||
array('OQL' => "SELECT UserRequest WHERE start_date > '2017-01-01' AND '2018-01-01' >= start_date", 1),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user