.
* ---------------------------------------------------------------------
*/
if (!defined('GLPI_ROOT')) {
die("Sorry. You can't access this file directly");
}
/**
* Search Class
*
* Generic class for Search Engine
**/
class Search {
// Default number of items displayed in global search
const GLOBAL_DISPLAY_COUNT = 10;
// EXPORT TYPE
const GLOBAL_SEARCH = -1;
const HTML_OUTPUT = 0;
const SYLK_OUTPUT = 1;
const PDF_OUTPUT_LANDSCAPE = 2;
const CSV_OUTPUT = 3;
const PDF_OUTPUT_PORTRAIT = 4;
const LBBR = '#LBBR#';
const LBHR = '#LBHR#';
const SHORTSEP = '$#$';
const LONGSEP = '$$##$$';
const NULLVALUE = '__NULL__';
static $output_type = self::HTML_OUTPUT;
static $search = [];
/**
* Display search engine for an type
*
* @param string $itemtype Item type to manage
*
* @return void
**/
static function show($itemtype) {
$params = self::manageParams($itemtype, $_GET);
echo "
";
self::showGenericSearch($itemtype, $params);
if ($params['as_map'] == 1) {
self::showMap($itemtype, $params);
} else {
self::showList($itemtype, $params);
}
echo "
";
}
/**
* Display result table for search engine for an type
*
* @param string $itemtype Item type to manage
* @param array $params Search params passed to prepareDatasForSearch function
*
* @return void
**/
static function showList($itemtype, $params) {
self::displayData(self::getDatas($itemtype, $params));
}
/**
* Display result table for search engine for an type as a map
*
* @param string $itemtype Item type to manage
* @param array $params Search params passed to prepareDatasForSearch function
*
* @return void
**/
static function showMap($itemtype, $params) {
global $CFG_GLPI;
if ($itemtype == 'Location') {
$latitude = 21;
$longitude = 20;
} else if ($itemtype == 'Entity') {
$latitude = 67;
$longitude = 68;
} else {
$latitude = 998;
$longitude = 999;
}
$params['criteria'][] = [
'link' => 'AND NOT',
'field' => $latitude,
'searchtype' => 'contains',
'value' => 'NULL'
];
$params['criteria'][] = [
'link' => 'AND NOT',
'field' => $longitude,
'searchtype' => 'contains',
'value' => 'NULL'
];
$data = self::getDatas($itemtype, $params);
self::displayData($data);
if ($data['data']['totalcount'] > 0) {
$target = $data['search']['target'];
$criteria = $data['search']['criteria'];
array_pop($criteria);
array_pop($criteria);
$criteria[] = [
'link' => 'AND',
'field' => ($itemtype == 'Location' || $itemtype == 'Entity') ? 1 : (($itemtype == 'Ticket') ? 83 : 3),
'searchtype' => 'equals',
'value' => 'CURLOCATION'
];
$globallinkto = Toolbox::append_params(
[
'criteria' => Toolbox::stripslashes_deep($criteria),
'metacriteria' => Toolbox::stripslashes_deep($data['search']['metacriteria'])
],
'&'
);
$parameters = "as_map=0&sort=".$data['search']['sort']."&order=".$data['search']['order'].'&'.
$globallinkto;
if (strpos($target, '?') == false) {
$fulltarget = $target."?".$parameters;
} else {
$fulltarget = $target."&".$parameters;
}
$typename = class_exists($itemtype) ? $itemtype::getTypeName($data['data']['totalcount']) :
($itemtype == 'AllAssets' ? __('assets') : $itemtype);
echo "".__('Search results for localized items only')."
";
$js = "$(function() {
var map = initMap($('#page'), 'map', 'full');
_loadMap(map, '$itemtype');
});
var _loadMap = function(map_elt, itemtype) {
L.AwesomeMarkers.Icon.prototype.options.prefix = 'far';
var _micon = 'circle';
var stdMarker = L.AwesomeMarkers.icon({
icon: _micon,
markerColor: 'blue'
});
var aMarker = L.AwesomeMarkers.icon({
icon: _micon,
markerColor: 'cadetblue'
});
var bMarker = L.AwesomeMarkers.icon({
icon: _micon,
markerColor: 'purple'
});
var cMarker = L.AwesomeMarkers.icon({
icon: _micon,
markerColor: 'darkpurple'
});
var dMarker = L.AwesomeMarkers.icon({
icon: _micon,
markerColor: 'red'
});
var eMarker = L.AwesomeMarkers.icon({
icon: _micon,
markerColor: 'darkred'
});
//retrieve geojson data
map_elt.spin(true);
$.ajax({
dataType: 'json',
method: 'POST',
url: '{$CFG_GLPI['root_doc']}/ajax/map.php',
data: {
itemtype: itemtype,
params: ".json_encode($params)."
}
}).done(function(data) {
var _points = data.points;
var _markers = L.markerClusterGroup({
iconCreateFunction: function(cluster) {
var childCount = cluster.getChildCount();
var markers = cluster.getAllChildMarkers();
var n = 0;
for (var i = 0; i < markers.length; i++) {
n += markers[i].count;
}
var c = ' marker-cluster-';
if (n < 10) {
c += 'small';
} else if (n < 100) {
c += 'medium';
} else {
c += 'large';
}
return new L.DivIcon({ html: '
' + n + '
', className: 'marker-cluster' + c, iconSize: new L.Point(40, 40) });
}
});
$.each(_points, function(index, point) {
var _title = '
' + point.title + '".sprintf(__('%1$s %2$s'), 'COUNT', $typename)."'.replace(/COUNT/, point.count)+'';
if (point.types) {
$.each(point.types, function(tindex, type) {
_title += '
".sprintf(__('%1$s %2$s'), 'COUNT', 'TYPE')."'.replace(/COUNT/, type.count).replace(/TYPE/, type.name);
});
}
var _icon = stdMarker;
if (point.count < 10) {
_icon = stdMarker;
} else if (point.count < 100) {
_icon = aMarker;
} else if (point.count < 1000) {
_icon = bMarker;
} else if (point.count < 5000) {
_icon = cMarker;
} else if (point.count < 10000) {
_icon = dMarker;
} else {
_icon = eMarker;
}
var _marker = L.marker([point.lat, point.lng], { icon: _icon, title: point.title });
_marker.count = point.count;
_marker.bindPopup(_title);
_markers.addLayer(_marker);
});
map_elt.addLayer(_markers);
map_elt.fitBounds(
_markers.getBounds(), {
padding: [50, 50],
maxZoom: 12
}
);
}).fail(function (response) {
var _data = response.responseJSON;
var _message = '".__s('An error occured loading data :(')."';
if (_data.message) {
_message = _data.message;
}
var fail_info = L.control();
fail_info.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'fail_info');
this._div.innerHTML = _message + '
".__s('Reload')."';
return this._div;
};
fail_info.addTo(map_elt);
$('#reload_data').on('click', function() {
$('.fail_info').remove();
_loadMap(map_elt);
});
}).always(function() {
//hide spinner
map_elt.spin(false);
});
}
";
echo Html::scriptBlock($js);
echo "
";
}
}
/**
* Get data based on search parameters
*
* @since 0.85
*
* @param string $itemtype Item type to manage
* @param array $params Search params passed to prepareDatasForSearch function
* @param array $forcedisplay Array of columns to display (default empty = empty use display pref and search criteria)
*
* @return array The data
**/
static function getDatas($itemtype, $params, array $forcedisplay = []) {
$data = self::prepareDatasForSearch($itemtype, $params, $forcedisplay);
self::constructSQL($data);
self::constructData($data);
return $data;
}
/**
* Prepare search criteria to be used for a search
*
* @since 0.85
*
* @param string $itemtype Item type
* @param array $params Array of parameters
* may include sort, order, start, list_limit, deleted, criteria, metacriteria
* @param array $forcedisplay Array of columns to display (default empty = empty use display pref and search criterias)
*
* @return array prepare to be used for a search (include criteria and others needed information)
**/
static function prepareDatasForSearch($itemtype, array $params, array $forcedisplay = []) {
global $CFG_GLPI;
// Default values of parameters
$p['criteria'] = [];
$p['metacriteria'] = [];
$p['sort'] = '1'; //
$p['order'] = 'ASC';//
$p['start'] = 0;//
$p['is_deleted'] = 0;
$p['export_all'] = 0;
if (class_exists($itemtype)) {
$p['target'] = $itemtype::getSearchURL();
} else {
$p['target'] = Toolbox::getItemTypeSearchURL($itemtype);
}
$p['display_type'] = self::HTML_OUTPUT;
$p['showmassiveactions'] = true;
$p['dont_flush'] = false;
$p['show_pager'] = true;
$p['show_footer'] = true;
$p['no_sort'] = false;
$p['list_limit'] = $_SESSION['glpilist_limit'];
$p['massiveactionparams'] = [];
foreach ($params as $key => $val) {
switch ($key) {
case 'order':
if (in_array($val, ['ASC', 'DESC'])) {
$p[$key] = $val;
}
break;
case 'sort':
$p[$key] = intval($val);
if ($p[$key] < 0) {
$p[$key] = 1;
}
break;
case 'is_deleted':
if ($val == 1) {
$p[$key] = '1';
}
break;
default:
$p[$key] = $val;
break;
}
}
// Set display type for export if define
if (isset($p['display_type'])) {
// Limit to 10 element
if ($p['display_type'] == self::GLOBAL_SEARCH) {
$p['list_limit'] = self::GLOBAL_DISPLAY_COUNT;
}
}
if ($p['export_all']) {
$p['start'] = 0;
}
$data = [];
$data['search'] = $p;
$data['itemtype'] = $itemtype;
// Instanciate an object to access method
$data['item'] = null;
if ($itemtype != 'AllAssets') {
$data['item'] = getItemForItemtype($itemtype);
}
$data['display_type'] = $data['search']['display_type'];
if (!$CFG_GLPI['allow_search_all']) {
foreach ($p['criteria'] as $val) {
if (isset($val['field']) && $val['field'] == 'all') {
Html::displayRightError();
}
}
}
if (!$CFG_GLPI['allow_search_view']) {
foreach ($p['criteria'] as $val) {
if (isset($val['field']) && $val['field'] == 'view') {
Html::displayRightError();
}
}
}
/// Get the items to display
// Add searched items
$forcetoview = false;
if (is_array($forcedisplay) && count($forcedisplay)) {
$forcetoview = true;
}
$data['search']['all_search'] = false;
$data['search']['view_search'] = false;
// If no research limit research to display item and compute number of item using simple request
$data['search']['no_search'] = true;
$data['toview'] = self::addDefaultToView($itemtype, $params);
$data['meta_toview'] = [];
if (!$forcetoview) {
// Add items to display depending of personal prefs
$displaypref = DisplayPreference::getForTypeUser($itemtype, Session::getLoginUserID());
if (count($displaypref)) {
foreach ($displaypref as $val) {
array_push($data['toview'], $val);
}
}
} else {
$data['toview'] = array_merge($data['toview'], $forcedisplay);
}
if (count($p['criteria']) > 0) {
// use a recursive closure to push searchoption when using nested criteria
$parse_criteria = function($criteria) use (&$parse_criteria, &$data) {
foreach ($criteria as $criterion) {
// recursive call
if (isset($criterion['criteria'])) {
$parse_criteria($criterion['criteria']);
} else {
// normal behavior
if (isset($criterion['field'])
&& !in_array($criterion['field'], $data['toview'])) {
if ($criterion['field'] != 'all'
&& $criterion['field'] != 'view'
&& (!isset($criterion['meta'])
|| !$criterion['meta'])) {
array_push($data['toview'], $criterion['field']);
} else if ($criterion['field'] == 'all') {
$data['search']['all_search'] = true;
} else if ($criterion['field'] == 'view') {
$data['search']['view_search'] = true;
}
}
if (isset($criterion['value'])
&& (strlen($criterion['value']) > 0)) {
$data['search']['no_search'] = false;
}
}
}
};
// call the closure
$parse_criteria($p['criteria']);
}
if (count($p['metacriteria'])) {
$data['search']['no_search'] = false;
}
// Add order item
if (!in_array($p['sort'], $data['toview'])) {
array_push($data['toview'], $p['sort']);
}
// Special case for Ticket : put ID in front
if ($itemtype == 'Ticket') {
array_unshift($data['toview'], 2);
}
$limitsearchopt = self::getCleanedOptions($itemtype);
// Clean and reorder toview
$tmpview = [];
foreach ($data['toview'] as $val) {
if (isset($limitsearchopt[$val]) && !in_array($val, $tmpview)) {
$tmpview[] = $val;
}
}
$data['toview'] = $tmpview;
$data['tocompute'] = $data['toview'];
// Force item to display
if ($forcetoview) {
foreach ($data['toview'] as $val) {
if (!in_array($val, $data['tocompute'])) {
array_push($data['tocompute'], $val);
}
}
}
return $data;
}
/**
* Construct SQL request depending of search parameters
*
* Add to data array a field sql containing an array of requests :
* search : request to get items limited to wanted ones
* count : to count all items based on search criterias
* may be an array a request : need to add counts
* maybe empty : use search one to count
*
* @since 0.85
*
* @param array $data Array of search datas prepared to generate SQL
*
* @return void
**/
static function constructSQL(array &$data) {
global $CFG_GLPI, $DB;
if (!isset($data['itemtype'])) {
return false;
}
$data['sql']['count'] = [];
$data['sql']['search'] = '';
$searchopt = &self::getOptions($data['itemtype']);
$blacklist_tables = [];
$orig_table = self::getOrigTableName($data['itemtype']);
if (isset($CFG_GLPI['union_search_type'][$data['itemtype']])) {
$itemtable = $CFG_GLPI['union_search_type'][$data['itemtype']];
$blacklist_tables[] = $orig_table;
} else {
$itemtable = $orig_table;
}
// hack for AllAssets
if (isset($CFG_GLPI['union_search_type'][$data['itemtype']])) {
$entity_restrict = true;
} else {
$entity_restrict = $data['item']->isEntityAssign() && $data['item']->isField('entities_id');
}
// Construct the request
//// 1 - SELECT
// request currentuser for SQL supervision, not displayed
$SELECT = "SELECT DISTINCT `$itemtable`.`id` AS id, '".Toolbox::addslashes_deep($_SESSION['glpiname'])."' AS currentuser,
".self::addDefaultSelect($data['itemtype']);
// Add select for all toview item
foreach ($data['toview'] as $val) {
$SELECT .= self::addSelect($data['itemtype'], $val);
}
if (isset($data['search']['as_map']) && $data['search']['as_map'] == 1 && $data['itemtype'] != 'Entity') {
$SELECT .= ' `glpi_locations`.`id` AS loc_id, ';
}
//// 2 - FROM AND LEFT JOIN
// Set reference table
$FROM = " FROM `$itemtable`";
// Init already linked tables array in order not to link a table several times
$already_link_tables = [];
// Put reference table
array_push($already_link_tables, $itemtable);
// Add default join
$COMMONLEFTJOIN = self::addDefaultJoin($data['itemtype'], $itemtable, $already_link_tables);
$FROM .= $COMMONLEFTJOIN;
// Add all table for toview items
foreach ($data['tocompute'] as $val) {
if (!in_array($searchopt[$val]["table"], $blacklist_tables)) {
$FROM .= self::addLeftJoin($data['itemtype'], $itemtable, $already_link_tables,
$searchopt[$val]["table"],
$searchopt[$val]["linkfield"], 0, 0,
$searchopt[$val]["joinparams"],
$searchopt[$val]["field"]);
}
}
// Search all case :
if ($data['search']['all_search']) {
foreach ($searchopt as $key => $val) {
// Do not search on Group Name
if (is_array($val) && isset($val['table'])) {
if (!in_array($searchopt[$key]["table"], $blacklist_tables)) {
$FROM .= self::addLeftJoin($data['itemtype'], $itemtable, $already_link_tables,
$searchopt[$key]["table"],
$searchopt[$key]["linkfield"], 0, 0,
$searchopt[$key]["joinparams"],
$searchopt[$key]["field"]);
}
}
}
}
//// 3 - WHERE
// default string
$COMMONWHERE = self::addDefaultWhere($data['itemtype']);
$first = empty($COMMONWHERE);
// Add deleted if item have it
if ($data['item'] && $data['item']->maybeDeleted()) {
$LINK = " AND ";
if ($first) {
$LINK = " ";
$first = false;
}
$COMMONWHERE .= $LINK."`$itemtable`.`is_deleted` = ".(int)$data['search']['is_deleted']." ";
}
// Remove template items
if ($data['item'] && $data['item']->maybeTemplate()) {
$LINK = " AND ";
if ($first) {
$LINK = " ";
$first = false;
}
$COMMONWHERE .= $LINK."`$itemtable`.`is_template` = 0 ";
}
// Add Restrict to current entities
if ($entity_restrict) {
$LINK = " AND ";
if ($first) {
$LINK = " ";
$first = false;
}
if ($data['itemtype'] == 'Entity') {
$COMMONWHERE .= getEntitiesRestrictRequest($LINK, $itemtable, 'id', '', true);
} else if (isset($CFG_GLPI["union_search_type"][$data['itemtype']])) {
// Will be replace below in Union/Recursivity Hack
$COMMONWHERE .= $LINK." ENTITYRESTRICT ";
} else {
$COMMONWHERE .= getEntitiesRestrictRequest($LINK, $itemtable, '', '',
$data['item']->maybeRecursive() && $data['item']->isField('is_recursive'));
}
}
$WHERE = "";
$HAVING = "";
// Add search conditions
// If there is search items
if (count($data['search']['criteria'])) {
$WHERE = self::constructCriteriaSQL($data['search']['criteria'], $data, $searchopt);
$HAVING = self::constructCriteriaSQL($data['search']['criteria'], $data, $searchopt, true);
// if criteria (with meta flag) need additional join/from sql
self::constructAdditionalSqlForMetacriteria($data['search']['criteria'], $SELECT, $FROM, $already_link_tables, $data);
}
//// 4 - ORDER
$ORDER = " ORDER BY `id` ";
foreach ($data['tocompute'] as $val) {
if ($data['search']['sort'] == $val) {
$ORDER = self::addOrderBy(
$data['itemtype'],
$data['search']['sort'],
$data['search']['order']
);
}
}
$SELECT = rtrim(trim($SELECT), ',');
//// 7 - Manage GROUP BY
$GROUPBY = "";
// Meta Search / Search All / Count tickets
$criteria_with_meta = array_filter($data['search']['criteria'], function($criterion) {
return isset($criterion['meta'])
&& $criterion['meta'];
});
if ((count($data['search']['metacriteria']))
|| count($criteria_with_meta)
|| !empty($HAVING)
|| $data['search']['all_search']) {
$GROUPBY = " GROUP BY `$itemtable`.`id`";
}
if (empty($GROUPBY)) {
foreach ($data['toview'] as $val2) {
if (!empty($GROUPBY)) {
break;
}
if (isset($searchopt[$val2]["forcegroupby"])) {
$GROUPBY = " GROUP BY `$itemtable`.`id`";
}
}
}
$LIMIT = "";
$numrows = 0;
//No search : count number of items using a simple count(ID) request and LIMIT search
if ($data['search']['no_search']) {
$LIMIT = " LIMIT ".(int)$data['search']['start'].", ".(int)$data['search']['list_limit'];
// Force group by for all the type -> need to count only on table ID
if (!isset($searchopt[1]['forcegroupby'])) {
$count = "count(*)";
} else {
$count = "count(DISTINCT `$itemtable`.`id`)";
}
// request currentuser for SQL supervision, not displayed
$query_num = "SELECT $count,
'".Toolbox::addslashes_deep($_SESSION['glpiname'])."' AS currentuser
FROM `$itemtable`".
$COMMONLEFTJOIN;
$first = true;
if (!empty($COMMONWHERE)) {
$LINK = " AND ";
if ($first) {
$LINK = " WHERE ";
$first = false;
}
$query_num .= $LINK.$COMMONWHERE;
}
// Union Search :
if (isset($CFG_GLPI["union_search_type"][$data['itemtype']])) {
$tmpquery = $query_num;
foreach ($CFG_GLPI[$CFG_GLPI["union_search_type"][$data['itemtype']]] as $ctype) {
$ctable = $ctype::getTable();
if (($citem = getItemForItemtype($ctype))
&& $citem->canView()) {
// State case
if ($data['itemtype'] == 'AllAssets') {
$query_num = str_replace($CFG_GLPI["union_search_type"][$data['itemtype']],
$ctable, $tmpquery);
$query_num = str_replace($data['itemtype'], $ctype, $query_num);
$query_num .= " AND `$ctable`.`id` IS NOT NULL ";
// Add deleted if item have it
if ($citem && $citem->maybeDeleted()) {
$query_num .= " AND `$ctable`.`is_deleted` = 0 ";
}
// Remove template items
if ($citem && $citem->maybeTemplate()) {
$query_num .= " AND `$ctable`.`is_template` = 0 ";
}
} else {// Ref table case
$reftable = $data['itemtype']::getTable();
if ($data['item'] && $data['item']->maybeDeleted()) {
$tmpquery = str_replace("`".$CFG_GLPI["union_search_type"][$data['itemtype']]."`.
`is_deleted`",
"`$reftable`.`is_deleted`", $tmpquery);
}
$replace = "FROM `$reftable`
INNER JOIN `$ctable`
ON (`$reftable`.`items_id` =`$ctable`.`id`
AND `$reftable`.`itemtype` = '$ctype')";
$query_num = str_replace("FROM `".
$CFG_GLPI["union_search_type"][$data['itemtype']]."`",
$replace, $tmpquery);
$query_num = str_replace($CFG_GLPI["union_search_type"][$data['itemtype']],
$ctable, $query_num);
}
$query_num = str_replace("ENTITYRESTRICT",
getEntitiesRestrictRequest('', $ctable, '', '',
$citem->maybeRecursive()),
$query_num);
$data['sql']['count'][] = $query_num;
}
}
} else {
$data['sql']['count'][] = $query_num;
}
}
// If export_all reset LIMIT condition
if ($data['search']['export_all']) {
$LIMIT = "";
}
if (!empty($WHERE) || !empty($COMMONWHERE)) {
if (!empty($COMMONWHERE)) {
$WHERE = ' WHERE '.$COMMONWHERE.(!empty($WHERE)?' AND ( '.$WHERE.' )':'');
} else {
$WHERE = ' WHERE '.$WHERE.' ';
}
$first = false;
}
if (!empty($HAVING)) {
$HAVING = ' HAVING '.$HAVING;
}
// Create QUERY
if (isset($CFG_GLPI["union_search_type"][$data['itemtype']])) {
$first = true;
$QUERY = "";
foreach ($CFG_GLPI[$CFG_GLPI["union_search_type"][$data['itemtype']]] as $ctype) {
$ctable = $ctype::getTable();
if (($citem = getItemForItemtype($ctype))
&& $citem->canView()) {
if ($first) {
$first = false;
} else {
$QUERY .= " UNION ";
}
$tmpquery = "";
// AllAssets case
if ($data['itemtype'] == 'AllAssets') {
$tmpquery = $SELECT.", '$ctype' AS TYPE ".
$FROM.
$WHERE;
$tmpquery .= " AND `$ctable`.`id` IS NOT NULL ";
// Add deleted if item have it
if ($citem && $citem->maybeDeleted()) {
$tmpquery .= " AND `$ctable`.`is_deleted` = 0 ";
}
// Remove template items
if ($citem && $citem->maybeTemplate()) {
$tmpquery .= " AND `$ctable`.`is_template` = 0 ";
}
$tmpquery.= $GROUPBY.
$HAVING;
// Replace 'asset_types' by itemtype table name
$tmpquery = str_replace(
$CFG_GLPI["union_search_type"][$data['itemtype']],
$ctable,
$tmpquery
);
// Replace 'AllAssets' by itemtype
// Use quoted value to prevent replacement of AllAssets in column identifiers
$tmpquery = str_replace(
$DB->quoteValue('AllAssets'),
$DB->quoteValue($ctype),
$tmpquery
);
} else {// Ref table case
$reftable = $data['itemtype']::getTable();
$tmpquery = $SELECT.", '$ctype' AS TYPE,
`$reftable`.`id` AS refID, "."
`$ctable`.`entities_id` AS ENTITY ".
$FROM.
$WHERE;
if ($data['item']->maybeDeleted()) {
$tmpquery = str_replace("`".$CFG_GLPI["union_search_type"][$data['itemtype']]."`.
`is_deleted`",
"`$reftable`.`is_deleted`", $tmpquery);
}
$replace = "FROM `$reftable`"."
INNER JOIN `$ctable`"."
ON (`$reftable`.`items_id`=`$ctable`.`id`"."
AND `$reftable`.`itemtype` = '$ctype')";
$tmpquery = str_replace("FROM `".
$CFG_GLPI["union_search_type"][$data['itemtype']]."`",
$replace, $tmpquery);
$tmpquery = str_replace($CFG_GLPI["union_search_type"][$data['itemtype']],
$ctable, $tmpquery);
$name_field = $ctype::getNameField();
$tmpquery = str_replace("`$ctable`.`name`", "`$ctable`.`$name_field`", $tmpquery);
}
$tmpquery = str_replace("ENTITYRESTRICT",
getEntitiesRestrictRequest('', $ctable, '', '',
$citem->maybeRecursive()),
$tmpquery);
// SOFTWARE HACK
if ($ctype == 'Software') {
$tmpquery = str_replace("`glpi_softwares`.`serial`", "''", $tmpquery);
$tmpquery = str_replace("`glpi_softwares`.`otherserial`", "''", $tmpquery);
}
$QUERY .= $tmpquery;
}
}
if (empty($QUERY)) {
echo self::showError($data['display_type']);
return;
}
$QUERY .= str_replace($CFG_GLPI["union_search_type"][$data['itemtype']].".", "", $ORDER) .
$LIMIT;
} else {
$QUERY = $SELECT.
$FROM.
$WHERE.
$GROUPBY.
$HAVING.
$ORDER.
$LIMIT;
}
$data['sql']['search'] = $QUERY;
}
/**
* Construct WHERE (or HAVING) part of the sql based on passed criteria
*
* @since 9.4
*
* @param array $criteria list of search criterion, we should have these keys:
* - link (optionnal): AND, OR, NOT AND, NOT OR
* - field: id of the searchoption
* - searchtype: how to match value (contains, equals, etc)
* - value
* @param array $data common array used by search engine,
* contains all the search part (sql, criteria, params, itemtype etc)
* TODO: should be a property of the class
* @param array $searchopt Search options for the current itemtype
* @param boolean $is_having Do we construct sql WHERE or HAVING part
*
* @return string the sql sub string
*/
static function constructCriteriaSQL($criteria = [], $data = [], $searchopt = [], $is_having = false) {
$sql = "";
foreach ($criteria as $criterion) {
if (!isset($criterion['criteria'])
&& (!isset($criterion['value'])
|| strlen($criterion['value']) <= 0)) {
continue;
}
$itemtype = $data['itemtype'];
$meta = false;
if (isset($criterion['meta'])
&& $criterion['meta']
&& isset($criterion['itemtype'])) {
$itemtype = $criterion['itemtype'];
$meta = true;
}
// common search
if (!isset($criterion['field'])
|| ($criterion['field'] != "all"
&& $criterion['field'] != "view")) {
$LINK = " ";
$NOT = 0;
$tmplink = "";
if (isset($criterion['link'])
&& in_array($criterion['link'], array_keys(self::getLogicalOperators()))) {
if (strstr($criterion['link'], "NOT")) {
$tmplink = " ".str_replace(" NOT", "", $criterion['link']);
$NOT = 1;
} else {
$tmplink = " ".$criterion['link'];
}
} else {
$tmplink = " AND ";
}
// Manage Link if not first item
if (!empty($sql)) {
$LINK = $tmplink;
}
if (isset($criterion['criteria']) && count($criterion['criteria'])) {
$sub_sql = self::constructCriteriaSQL($criterion['criteria'], $data, $searchopt, $is_having);
if (strlen($sub_sql)) {
if ($NOT) {
$sql .= "$LINK NOT($sub_sql)";
} else {
$sql .= "$LINK ($sub_sql)";
}
}
} else if (isset($searchopt[$criterion['field']]["usehaving"])
|| ($meta && "AND NOT" === $criterion['link'])) {
if (!$is_having) {
// the having part will be managed in a second pass
continue;
}
$new_having = self::addHaving($LINK, $NOT, $itemtype,
$criterion['field'], $criterion['searchtype'],
$criterion['value']);
if ($new_having !== false) {
$sql .= $new_having;
}
} else {
if ($is_having) {
// the having part has been already managed in the first pass
continue;
}
$new_where = self::addWhere($LINK, $NOT, $itemtype, $criterion['field'],
$criterion['searchtype'], $criterion['value'], $meta);
if ($new_where !== false) {
$sql .= $new_where;
}
}
} else if (isset($criterion['value'])
&& strlen($criterion['value']) > 0) { // view and all search
$LINK = " OR ";
$NOT = 0;
$globallink = " AND ";
if (isset($criterion['link'])) {
switch ($criterion['link']) {
case "AND" :
$LINK = " OR ";
$globallink = " AND ";
break;
case "AND NOT" :
$LINK = " AND ";
$NOT = 1;
$globallink = " AND ";
break;
case "OR" :
$LINK = " OR ";
$globallink = " OR ";
break;
case "OR NOT" :
$LINK = " AND ";
$NOT = 1;
$globallink = " OR ";
break;
}
} else {
$tmplink =" AND ";
}
// Manage Link if not first item
if (!empty($sql)) {
$sql .= $globallink;
}
$first2 = true;
$items = [];
if (isset($criterion['field']) && $criterion['field'] == "all") {
$items = $searchopt;
} else { // toview case : populate toview
foreach ($data['toview'] as $key2 => $val2) {
$items[$val2] = $searchopt[$val2];
}
}
$view_sql = "";
foreach ($items as $key2 => $val2) {
if (isset($val2['nosearch']) && $val2['nosearch']) {
continue;
}
if (is_array($val2)) {
// Add Where clause if not to be done in HAVING CLAUSE
if (!$is_having && !isset($val2["usehaving"])) {
$tmplink = $LINK;
if ($first2) {
$tmplink = " ";
}
$new_where = self::addWhere($tmplink, $NOT, $itemtype, $key2,
$criterion['searchtype'], $criterion['value'], $meta);
if ($new_where !== false) {
$first2 = false;
$view_sql .= $new_where;
}
}
}
}
if (strlen($view_sql)) {
$sql.= " ($view_sql) ";
}
}
}
return $sql;
}
/**
* Construct aditionnal SQL (select, joins, etc) for meta-criteria
*
* @since 9.4
*
* @param array $criteria list of search criterion
* @param string &$SELECT TODO: should be a class property (output parameter)
* @param string &$FROM TODO: should be a class property (output parameter)
* @param array &$already_link_tables TODO: should be a class property (output parameter)
* @param array &$data TODO: should be a class property (output parameter)
*
* @return void
*/
static function constructAdditionalSqlForMetacriteria($criteria = [],
&$SELECT = "",
&$FROM = "",
&$already_link_tables = [],
&$data = []) {
$data['meta_toview'] = [];
foreach ($criteria as $criterion) {
// manage sub criteria
if (isset($criterion['criteria'])) {
self::constructAdditionalSqlForMetacriteria(
$criterion['criteria'],
$SELECT,
$FROM,
$already_link_tables,
$data
);
continue;
}
// parse only criterion with meta flag
if (!isset($criterion['itemtype'])
|| empty($criterion['itemtype'])
|| !isset($criterion['meta'])
|| !$criterion['meta']
|| !isset($criterion['value'])
|| strlen($criterion['value']) <= 0) {
continue;
}
$m_itemtype = $criterion['itemtype'];
$metaopt = &self::getOptions($m_itemtype);
$sopt = $metaopt[$criterion['field']];
//add toview for meta criterion
$data['meta_toview'][$m_itemtype][] = $criterion['field'];
$SELECT .= self::addSelect(
$m_itemtype,
$criterion['field'],
true, // meta-criterion
$m_itemtype
);
if (!in_array($m_itemtype::getTable(), $already_link_tables)) {
$FROM .= self::addMetaLeftJoin($data['itemtype'], $m_itemtype,
$already_link_tables,
$sopt["joinparams"]);
}
if (!in_array($sopt["table"]."_".$criterion['itemtype'], $already_link_tables)) {
$FROM .= self::addLeftJoin($m_itemtype,
$m_itemtype::getTable(),
$already_link_tables, $sopt["table"],
$sopt["linkfield"], 1, $m_itemtype,
$sopt["joinparams"], $sopt["field"]);
}
}
}
/**
* Retrieve datas from DB : construct data array containing columns definitions and rows datas
*
* add to data array a field data containing :
* cols : columns definition
* rows : rows data
*
* @since 0.85
*
* @param array $data array of search data prepared to get data
* @param boolean $onlycount If we just want to count results
*
* @return void
**/
static function constructData(array &$data, $onlycount = false) {
if (!isset($data['sql']) || !isset($data['sql']['search'])) {
return false;
}
$data['data'] = [];
// Use a ReadOnly connection if available and configured to be used
$DBread = DBConnection::getReadConnection();
$DBread->query("SET SESSION group_concat_max_len = 16384;");
// directly increase group_concat_max_len to avoid double query
if (count($data['search']['metacriteria'])) {
foreach ($data['search']['metacriteria'] as $metacriterion) {
if ($metacriterion['link'] == 'AND NOT'
|| $metacriterion['link'] == 'OR NOT') {
$DBread->query("SET SESSION group_concat_max_len = 4194304;");
break;
}
}
}
$DBread->execution_time = true;
$result = $DBread->query($data['sql']['search']);
/// Check group concat limit : if warning : increase limit
if ($result2 = $DBread->query('SHOW WARNINGS')) {
if ($DBread->numrows($result2) > 0) {
$res = $DBread->fetchAssoc($result2);
if ($res['Code'] == 1260) {
$DBread->query("SET SESSION group_concat_max_len = 8194304;");
$DBread->execution_time = true;
$result = $DBread->query($data['sql']['search']);
}
if ($res['Code'] == 1116) { // too many tables
echo self::showError($data['search']['display_type'],
__("'All' criterion is not usable with this object list, ".
"sql query fails (too many tables). ".
"Please use 'Items seen' criterion instead"));
return false;
}
}
}
if ($result) {
$data['data']['execution_time'] = $DBread->execution_time;
if (isset($data['search']['savedsearches_id'])) {
SavedSearch::updateExecutionTime(
(int)$data['search']['savedsearches_id'],
$DBread->execution_time
);
}
$data['data']['totalcount'] = 0;
// if real search or complete export : get numrows from request
if (!$data['search']['no_search']
|| $data['search']['export_all']) {
$data['data']['totalcount'] = $DBread->numrows($result);
} else {
if (!isset($data['sql']['count'])
|| (count($data['sql']['count']) == 0)) {
$data['data']['totalcount'] = $DBread->numrows($result);
} else {
foreach ($data['sql']['count'] as $sqlcount) {
$result_num = $DBread->query($sqlcount);
$data['data']['totalcount'] += $DBread->result($result_num, 0, 0);
}
}
}
if ($onlycount) {
//we just want to coutn results; no need to continue process
return;
}
// Search case
$data['data']['begin'] = $data['search']['start'];
$data['data']['end'] = min($data['data']['totalcount'],
$data['search']['start']+$data['search']['list_limit'])-1;
//map case
if (isset($data['search']['as_map']) && $data['search']['as_map'] == 1) {
$data['data']['end'] = $data['data']['totalcount']-1;
}
// No search Case
if ($data['search']['no_search']) {
$data['data']['begin'] = 0;
$data['data']['end'] = min($data['data']['totalcount']-$data['search']['start'],
$data['search']['list_limit'])-1;
}
// Export All case
if ($data['search']['export_all']) {
$data['data']['begin'] = 0;
$data['data']['end'] = $data['data']['totalcount']-1;
}
// Get columns
$data['data']['cols'] = [];
$searchopt = &self::getOptions($data['itemtype']);
foreach ($data['toview'] as $opt_id) {
$data['data']['cols'][] = [
'itemtype' => $data['itemtype'],
'id' => $opt_id,
'name' => $searchopt[$opt_id]["name"],
'meta' => 0,
'searchopt' => $searchopt[$opt_id],
];
}
// manage toview column for criteria with meta flag
foreach ($data['meta_toview'] as $m_itemtype => $toview) {
$searchopt = &self::getOptions($m_itemtype);
foreach ($toview as $opt_id) {
$data['data']['cols'][] = [
'itemtype' => $m_itemtype,
'id' => $opt_id,
'name' => $searchopt[$opt_id]["name"],
'meta' => 1,
'searchopt' => $searchopt[$opt_id],
];
}
}
// Display columns Headers for meta items
$already_printed = [];
if (count($data['search']['metacriteria'])) {
foreach ($data['search']['metacriteria'] as $metacriteria) {
if (isset($metacriteria['itemtype']) && !empty($metacriteria['itemtype'])
&& isset($metacriteria['value']) && (strlen($metacriteria['value']) > 0)) {
if (!isset($already_printed[$metacriteria['itemtype'].$metacriteria['field']])) {
$searchopt = &self::getOptions($metacriteria['itemtype']);
$data['data']['cols'][] = [
'itemtype' => $metacriteria['itemtype'],
'id' => $metacriteria['field'],
'name' => $searchopt[$metacriteria['field']]["name"],
'meta' => 1,
'searchopt' =>$searchopt[$metacriteria['field']]
];
$already_printed[$metacriteria['itemtype'].$metacriteria['field']] = 1;
}
}
}
}
// search group (corresponding of dropdown optgroup) of current col
foreach ($data['data']['cols'] as $num => $col) {
// search current col in searchoptions ()
while (key($searchopt) !== null
&& key($searchopt) != $col['id']) {
next($searchopt);
}
if (key($searchopt) !== null) {
//search optgroup (non array option)
while (key($searchopt) !== null
&& is_numeric(key($searchopt))
&& is_array(current($searchopt))) {
prev($searchopt);
}
if (key($searchopt) !== null
&& key($searchopt) !== "common") {
$data['data']['cols'][$num]['groupname'] = current($searchopt);
}
}
//reset
reset($searchopt);
}
// Get rows
// if real search seek to begin of items to display (because of complete search)
if (!$data['search']['no_search']) {
$DBread->dataSeek($result, $data['search']['start']);
}
$i = $data['data']['begin'];
$data['data']['warning']
= "For compatibility keep raw data (ITEM_X, META_X) at the top for the moment. Will be drop in next version";
$data['data']['rows'] = [];
$data['data']['items'] = [];
self::$output_type = $data['display_type'];
while (($i < $data['data']['totalcount']) && ($i <= $data['data']['end'])) {
$row = $DBread->fetchAssoc($result);
$newrow = [];
$newrow['raw'] = $row;
// Parse datas
foreach ($newrow['raw'] as $key => $val) {
if (preg_match('/ITEM(_(\w[^\d]+))?_(\d+)(_(.+))?/', $key, $matches)) {
$j = $matches[3];
if (isset($matches[2]) && !empty($matches[2])) {
$j = $matches[2] . '_' . $matches[3];
}
$fieldname = 'name';
if (isset($matches[5])) {
$fieldname = $matches[5];
}
// No Group_concat case
if ($fieldname == 'content' || strpos($val, self::LONGSEP) === false) {
$newrow[$j]['count'] = 1;
$handled = false;
if ($fieldname != 'content' && strpos($val, self::SHORTSEP) !== false) {
$split2 = self::explodeWithID(self::SHORTSEP, $val);
if (is_numeric($split2[1])) {
$newrow[$j][0][$fieldname] = $split2[0];
$newrow[$j][0]['id'] = $split2[1];
$handled = true;
}
}
if (!$handled) {
if ($val === self::NULLVALUE) {
$newrow[$j][0][$fieldname] = null;
} else {
$newrow[$j][0][$fieldname] = $val;
}
}
} else {
if (!isset($newrow[$j])) {
$newrow[$j] = [];
}
$split = explode(self::LONGSEP, $val);
$newrow[$j]['count'] = count($split);
foreach ($split as $key2 => $val2) {
$handled = false;
if (strpos($val2, self::SHORTSEP) !== false) {
$split2 = self::explodeWithID(self::SHORTSEP, $val2);
if (is_numeric($split2[1])) {
$newrow[$j][$key2]['id'] = $split2[1];
if ($split2[0] == self::NULLVALUE) {
$newrow[$j][$key2][$fieldname] = null;
} else {
$newrow[$j][$key2][$fieldname] = $split2[0];
}
$handled = true;
}
}
if (!$handled) {
$newrow[$j][$key2][$fieldname] = $val2;
}
}
}
} else {
if ($key == 'currentuser') {
if (!isset($data['data']['currentuser'])) {
$data['data']['currentuser'] = $val;
}
} else {
$newrow[$key] = $val;
// Add id to items list
if ($key == 'id') {
$data['data']['items'][$val] = $i;
}
}
}
}
foreach ($data['data']['cols'] as $val) {
$newrow[$val['itemtype'] . '_' . $val['id']]['displayname'] = self::giveItem(
$val['itemtype'],
$val['id'],
$newrow
);
}
$data['data']['rows'][$i] = $newrow;
$i++;
}
$data['data']['count'] = count($data['data']['rows']);
} else {
echo $DBread->error();
}
}
/**
* Display datas extracted from DB
*
* @param array $data Array of search datas prepared to get datas
*
* @return void
**/
static function displayData(array $data) {
global $CFG_GLPI;
//sophal debut
$RevueAnnuelle = [ 0 =>'Revue Annuelle Plan Directeur Commercial' ,
1=>'Revue Annuelle Plan Directeur Marketing',
2=>'Revue Annuelle Plan Directeur Production',
3=>'Revue Annuelle Plan Directeur Approvisionnements',
4=>'Revue Annuelle Plan Directeur Ressources Humaines' ];
$roleuser =$_SESSION['glpiactiveprofile']['name'];
if($data['itemtype'] == "PlanningExternalEvent"){
if( $roleuser == "Directeur Commercial" || $roleuser == "Directeur Marketing"
|| $roleuser == "Directeur Production" || $roleuser == "Directeur Approvisionnement"
|| $roleuser == "Directeur RH"){
$countRows = sizeof($data['data']['rows']);
for($SUPP = 0 ;$SUPP < $countRows ; $SUPP++){
$io = $data['data']['rows'][$SUPP]['raw']['ITEM_PlanningExternalEvent_1'];
if($roleuser == "Directeur Commercial"){
if( $io != $RevueAnnuelle[0]){ ;
unset($data['data']['rows'][$SUPP]);
}
}elseif($roleuser == "Directeur Marketing"){
if( $io != $RevueAnnuelle[1]){ ;
unset($data['data']['rows'][$SUPP]);
}
}elseif($roleuser == "Directeur Production"){
if( $io != $RevueAnnuelle[2]){ ;
unset($data['data']['rows'][$SUPP]);
}
}elseif($roleuser == "Directeur Approvisionnement"){
if( $io != $RevueAnnuelle[3]){ ;
unset($data['data']['rows'][$SUPP]);
}
}elseif($roleuser == "Directeur RH"){
if( $io != $RevueAnnuelle[4]){ ;
unset($data['data']['rows'][$SUPP]);
}
}
}
}
}
//sophal fin
$item = null;
if (class_exists($data['itemtype'])) {
$item = new $data['itemtype']();
}
if (!isset($data['data']) || !isset($data['data']['totalcount'])) {
return false;
}
// Contruct Pager parameters
$globallinkto
= Toolbox::append_params(['criteria'
=> Toolbox::stripslashes_deep($data['search']['criteria']),
'metacriteria'
=> Toolbox::stripslashes_deep($data['search']['metacriteria'])],
'&');
$parameters = "sort=".$data['search']['sort']."&order=".$data['search']['order'].'&'.
$globallinkto;
if (isset($_GET['_in_modal'])) {
$parameters .= "&_in_modal=1";
}
// Global search header
if ($data['display_type'] == self::GLOBAL_SEARCH) {
if ($data['item']) {
echo "