1846 lines
63 KiB
PHP
1846 lines
63 KiB
PHP
<?php
|
|
/**
|
|
* ---------------------------------------------------------------------
|
|
* GLPI - Gestionnaire Libre de Parc Informatique
|
|
* Copyright (C) 2015-2020 Teclib' and contributors.
|
|
*
|
|
* http://glpi-project.org
|
|
*
|
|
* based on GLPI - Gestionnaire Libre de Parc Informatique
|
|
* Copyright (C) 2003-2014 by the INDEPNET Development Team.
|
|
*
|
|
* ---------------------------------------------------------------------
|
|
*
|
|
* LICENSE
|
|
*
|
|
* This file is part of GLPI.
|
|
*
|
|
* GLPI is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GLPI 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with GLPI. If not, see <http://www.gnu.org/licenses/>.
|
|
* ---------------------------------------------------------------------
|
|
*/
|
|
|
|
if (!defined('GLPI_ROOT')) {
|
|
die("Sorry. You can't access this file directly");
|
|
}
|
|
|
|
/// Common DataBase Relation Table Manager Class
|
|
abstract class CommonDBRelation extends CommonDBConnexity {
|
|
|
|
// Item 1 information
|
|
// * definition
|
|
static public $itemtype_1; // Type ref or field name (must start with itemtype)
|
|
static public $items_id_1; // Field name
|
|
// * entity inheritance
|
|
static public $take_entity_1 = true;
|
|
// * rights
|
|
static public $checkItem_1_Rights = self::HAVE_SAME_RIGHT_ON_ITEM;
|
|
static public $mustBeAttached_1 = true;
|
|
// * log
|
|
static public $logs_for_item_1 = true;
|
|
static public $log_history_1_add = Log::HISTORY_ADD_RELATION;
|
|
static public $log_history_1_update = Log::HISTORY_UPDATE_RELATION;
|
|
static public $log_history_1_delete = Log::HISTORY_DEL_RELATION;
|
|
static public $log_history_1_lock = Log::HISTORY_LOCK_RELATION;
|
|
static public $log_history_1_unlock = Log::HISTORY_UNLOCK_RELATION;
|
|
|
|
// Item 2 information
|
|
// * definition
|
|
static public $itemtype_2; // Type ref or field name (must start with itemtype)
|
|
static public $items_id_2; // Field name
|
|
// * entity inheritance
|
|
static public $take_entity_2 = false;
|
|
// * rights
|
|
static public $checkItem_2_Rights = self::HAVE_SAME_RIGHT_ON_ITEM;
|
|
static public $mustBeAttached_2 = true;
|
|
// * log
|
|
static public $logs_for_item_2 = true;
|
|
static public $log_history_2_add = Log::HISTORY_ADD_RELATION;
|
|
static public $log_history_2_update = Log::HISTORY_UPDATE_RELATION;
|
|
static public $log_history_2_delete = Log::HISTORY_DEL_RELATION;
|
|
static public $log_history_2_lock = Log::HISTORY_LOCK_RELATION;
|
|
static public $log_history_2_unlock = Log::HISTORY_UNLOCK_RELATION;
|
|
|
|
// Relation between items to check
|
|
/// If both items must be checked for rights (default is only one)
|
|
static public $checkAlwaysBothItems = false;
|
|
/// If both items must be in viewable each other entities
|
|
static public $check_entity_coherency = true;
|
|
|
|
public $no_form_page = true;
|
|
|
|
|
|
/**
|
|
* Get request cirteria to search for an item
|
|
*
|
|
* @since 9.4
|
|
*
|
|
* @param string $itemtype Item type
|
|
* @param integer $items_id Item ID
|
|
*
|
|
* @return array|null
|
|
**/
|
|
static function getSQLCriteriaToSearchForItem($itemtype, $items_id) {
|
|
global $DB;
|
|
|
|
$conditions = [];
|
|
$fields = [
|
|
static::getIndexName(),
|
|
static::$items_id_1 . ' AS items_id_1',
|
|
static::$items_id_2 . ' AS items_id_2'
|
|
];
|
|
|
|
// Check item 1 type
|
|
$where1 = [
|
|
static::$items_id_1 => $items_id
|
|
];
|
|
|
|
$request = false;
|
|
if (preg_match('/^itemtype/', static::$itemtype_1)) {
|
|
$fields[] = static::$itemtype_1 . ' AS itemtype_1';
|
|
$where1[static::$itemtype_1] = $itemtype;
|
|
$request = true;
|
|
} else {
|
|
$fields[] = new \QueryExpression("'" . static::$itemtype_1 . "' AS itemtype_1");
|
|
if (($itemtype == static::$itemtype_1)
|
|
|| is_subclass_of($itemtype, static::$itemtype_1)) {
|
|
$request = true;
|
|
}
|
|
}
|
|
if ($request === true) {
|
|
$conditions[] = $where1;
|
|
$it = new \DBmysqlIterator($DB);
|
|
$fields[] = new \QueryExpression(
|
|
'IF('.$it->analyseCrit($where1).', 1, 0) AS is_1'
|
|
);
|
|
} else {
|
|
$fields[] = new \QueryExpression('0 AS is_1');
|
|
}
|
|
|
|
// Check item 2 type
|
|
$where2 = [
|
|
static::$items_id_2 => $items_id
|
|
];
|
|
$request = false;
|
|
if (preg_match('/^itemtype/', static::$itemtype_2)) {
|
|
$fields[] = static::$itemtype_2 . ' AS itemtype_2';
|
|
$where2[static::$itemtype_2] = $itemtype;
|
|
$request = true;
|
|
} else {
|
|
$fields[] = new \QueryExpression("'" . static::$itemtype_2 . "' AS itemtype_2");
|
|
if (($itemtype == static::$itemtype_2)
|
|
|| is_subclass_of($itemtype, static::$itemtype_2)) {
|
|
$request = true;
|
|
}
|
|
}
|
|
if ($request === true) {
|
|
$conditions[] = $where2;
|
|
$it = new \DBmysqlIterator($DB);
|
|
$fields[] = new \QueryExpression(
|
|
'IF('.$it->analyseCrit($where2).', 1, 0) AS is_2'
|
|
);
|
|
|
|
} else {
|
|
$fields[] = new \QueryExpression('0 AS is_2');
|
|
}
|
|
|
|
if (count($conditions) != 0) {
|
|
$criteria = [
|
|
'SELECT' => $fields,
|
|
'FROM' => static::getTable(),
|
|
'WHERE' => ['OR' => $conditions]
|
|
];
|
|
return $criteria;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
*
|
|
* @param $item CommonDBTM object
|
|
* @param $relations_id (default NULL)
|
|
**/
|
|
static function getOpposite(CommonDBTM $item, &$relations_id = null) {
|
|
return static::getOppositeByTypeAndID($item->getType(), $item->getID(), $relations_id);
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
*
|
|
* @param string $itemtype Type of the item to search for its opposite
|
|
* @param integer $items_id ID of the item to search for its opposite
|
|
* @param integer|null $relations_id
|
|
**/
|
|
static function getOppositeByTypeAndID($itemtype, $items_id, &$relations_id = null) {
|
|
global $DB;
|
|
|
|
if ($items_id < 0) {
|
|
return false;
|
|
}
|
|
|
|
$criteria = self::getSQLCriteriaToSearchForItem($itemtype, $items_id);
|
|
|
|
if ($criteria !== null) {
|
|
$iterator = $DB->request($criteria);
|
|
if (count($iterator) == 1) {
|
|
$line = $iterator->next();
|
|
if ($line['is_1'] == $line['is_2']) {
|
|
return false;
|
|
}
|
|
if ($line['is_1'] == 0) {
|
|
$opposites_id = $line['items_id_1'];
|
|
$oppositetype = $line['itemtype_1'];
|
|
}
|
|
if ($line['is_2'] == 0) {
|
|
$opposites_id = $line['items_id_2'];
|
|
$oppositetype = $line['itemtype_2'];
|
|
}
|
|
if ((isset($oppositetype)) && (isset($opposites_id))) {
|
|
$opposite = getItemForItemtype($oppositetype);
|
|
if ($opposite !== false) {
|
|
if ($opposite->getFromDB($opposites_id)) {
|
|
if (!is_null($relations_id)) {
|
|
$relations_id = $line[static::getIndexName()];
|
|
}
|
|
return $opposite;
|
|
}
|
|
unset($opposite);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
*
|
|
* @param $number
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function getOnePeer($number) {
|
|
|
|
if ($number == 0) {
|
|
$itemtype = static::$itemtype_1;
|
|
$items_id = static::$items_id_1;
|
|
} else if ($number == 1) {
|
|
$itemtype = static::$itemtype_2;
|
|
$items_id = static::$items_id_2;
|
|
} else {
|
|
return false;
|
|
}
|
|
return $this->getConnexityItem($itemtype, $items_id);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get link object between 2 items
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* @param $item1 object 1
|
|
* @param $item2 object 2
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function getFromDBForItems(CommonDBTM $item1, CommonDBTM $item2) {
|
|
|
|
// Check items ID
|
|
if (($item1->getID() < 0) || ($item2->getID() < 0)) {
|
|
return false;
|
|
}
|
|
|
|
$wheres = [];
|
|
$wheres[static::$items_id_1] = $item1->getID();
|
|
$wheres[static::$items_id_2] = $item2->getID();
|
|
|
|
// Check item 1 type
|
|
if (preg_match('/^itemtype/', static::$itemtype_1)) {
|
|
$wheres[static::$itemtype_1] = $item1->getType();
|
|
} else if (!is_a($item1, static::$itemtype_1)) {
|
|
return false;
|
|
}
|
|
|
|
// Check item 1 type
|
|
if (preg_match('/^itemtype/', static::$itemtype_2)) {
|
|
$wheres[static::$itemtype_2] = $item2->getType();
|
|
} else if (!is_a($item2, static::$itemtype_2)) {
|
|
return false;
|
|
}
|
|
return $this->getFromDBByCrit($wheres);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get search function for the class
|
|
*
|
|
* @return array of search option
|
|
**/
|
|
function rawSearchOptions() {
|
|
$tab = [];
|
|
|
|
$tab[] = [
|
|
'id' => 'common',
|
|
'name' => __('Characteristics')
|
|
];
|
|
|
|
$tab[] = [
|
|
'id' => '2',
|
|
'table' => $this->getTable(),
|
|
'field' => 'id',
|
|
'name' => __('ID'),
|
|
'massiveaction' => false,
|
|
'datatype' => 'number'
|
|
];
|
|
|
|
$itemtype1 = static::$itemtype_1;
|
|
if (!preg_match('/^itemtype/', $itemtype1)) {
|
|
$tab[] = [
|
|
'id' => '3',
|
|
'table' => getTableForItemType($itemtype1),
|
|
'field' => $itemtype1::getIndexName(),
|
|
'linkfield' => static::$items_id_1,
|
|
'name' => call_user_func([$itemtype1, 'getTypeName']),
|
|
'datatype' => 'text',
|
|
'massiveaction' => false
|
|
];
|
|
}
|
|
|
|
$itemtype2 = static::$itemtype_2;
|
|
if (!preg_match('/^itemtype/', $itemtype2)) {
|
|
$tab[] = [
|
|
'id' => '4',
|
|
'table' => getTableForItemType($itemtype2),
|
|
'field' => $itemtype2::getIndexName(),
|
|
'linkfield' => static::$items_id_2,
|
|
'name' => call_user_func([$itemtype2, 'getTypeName']),
|
|
'datatype' => 'text',
|
|
'massiveaction' => false
|
|
];
|
|
}
|
|
|
|
return $tab;
|
|
}
|
|
|
|
|
|
/**
|
|
* Specific check for check attach for relation 2
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* @param $input Array of data to be added
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function isAttach2Valid(Array &$input) {
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Specific check for check attach for relation 1
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* @param $input Array of data to be added
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function isAttach1Valid(Array &$input) {
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
*
|
|
* @param $method
|
|
* @param $forceCheckBoth boolean force check both items(false by default)
|
|
*
|
|
* @return boolean
|
|
**/
|
|
static function canRelation($method, $forceCheckBoth = false) {
|
|
|
|
$can1 = static::canConnexity($method, static::$checkItem_1_Rights, static::$itemtype_1,
|
|
static::$items_id_1);
|
|
$can2 = static::canConnexity($method, static::$checkItem_2_Rights, static::$itemtype_2,
|
|
static::$items_id_2);
|
|
|
|
/// Check only one if SAME RIGHT for both items and not force checkBoth
|
|
if (((static::HAVE_SAME_RIGHT_ON_ITEM == static::$checkItem_1_Rights)
|
|
&& (static::HAVE_SAME_RIGHT_ON_ITEM == static::$checkItem_2_Rights))
|
|
&& !$forceCheckBoth) {
|
|
if ($can1) {
|
|
// Can view the second one ?
|
|
if (!static::canConnexity($method, static::HAVE_VIEW_RIGHT_ON_ITEM, static::$itemtype_2,
|
|
static::$items_id_2)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
} else if ($can2) {
|
|
// Can view the first one ?
|
|
if (!static::canConnexity($method, static::HAVE_VIEW_RIGHT_ON_ITEM, static::$itemtype_1,
|
|
static::$items_id_1)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
} else {
|
|
// No item have right
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return ($can1 && $can2);
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
*
|
|
* @param $method
|
|
* @param $methodNotItem
|
|
* @param $check_entity (true by default)
|
|
* @param $forceCheckBoth boolean force check both items (false by default)
|
|
*
|
|
* @return boolean
|
|
**/
|
|
function canRelationItem($method, $methodNotItem, $check_entity = true, $forceCheckBoth = false) {
|
|
|
|
$OneWriteIsEnough = (!$forceCheckBoth
|
|
&& ((static::HAVE_SAME_RIGHT_ON_ITEM == static::$checkItem_1_Rights)
|
|
|| (static::HAVE_SAME_RIGHT_ON_ITEM == static::$checkItem_2_Rights)));
|
|
|
|
try {
|
|
$item1 = null;
|
|
$can1 = $this->canConnexityItem($method, $methodNotItem, static::$checkItem_1_Rights,
|
|
static::$itemtype_1, static::$items_id_1, $item1);
|
|
if ($OneWriteIsEnough) {
|
|
$view1 = $this->canConnexityItem($method, $methodNotItem,
|
|
static::HAVE_VIEW_RIGHT_ON_ITEM, static::$itemtype_1,
|
|
static::$items_id_1, $item1);
|
|
}
|
|
} catch (CommonDBConnexityItemNotFound $e) {
|
|
if (static::$mustBeAttached_1 && !$this->isAttach1Valid($this->fields)) {
|
|
return false;
|
|
}
|
|
$can1 = true;
|
|
$view1 = true;
|
|
$check_entity = false; // If no item, then, we cannot check entities
|
|
}
|
|
|
|
try {
|
|
$item2 = null;
|
|
$can2 = $this->canConnexityItem($method, $methodNotItem, static::$checkItem_2_Rights,
|
|
static::$itemtype_2, static::$items_id_2, $item2);
|
|
if ($OneWriteIsEnough) {
|
|
$view2 = $this->canConnexityItem($method, $methodNotItem,
|
|
static::HAVE_VIEW_RIGHT_ON_ITEM, static::$itemtype_2,
|
|
static::$items_id_2, $item2);
|
|
}
|
|
} catch (CommonDBConnexityItemNotFound $e) {
|
|
if (static::$mustBeAttached_2 && !$this->isAttach2Valid($this->fields)) {
|
|
return false;
|
|
}
|
|
$can2 = true;
|
|
$view2 = true;
|
|
$check_entity = false; // If no item, then, we cannot check entities
|
|
}
|
|
|
|
if ($OneWriteIsEnough) {
|
|
if ((!$can1 && !$can2)
|
|
|| ($can1 && !$view2)
|
|
|| ($can2 && !$view1)) {
|
|
return false;
|
|
}
|
|
} else {
|
|
if (!$can1 || !$can2) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Check coherency of entities
|
|
if ($check_entity && static::$check_entity_coherency) {
|
|
|
|
// If one of both extremity is not valid => not allowed !
|
|
// (default is only to check on create and update not for view and delete)
|
|
if ((!$item1 instanceof CommonDBTM)
|
|
|| (!$item2 instanceof CommonDBTM)) {
|
|
return false;
|
|
}
|
|
if ($item1->isEntityAssign() && $item2->isEntityAssign()) {
|
|
$entity1 = $item1->getEntityID();
|
|
$entity2 = $item2->getEntityID();
|
|
|
|
if ($entity1 == $entity2) {
|
|
return true;
|
|
}
|
|
if (($item1->isRecursive())
|
|
&& in_array($entity1, getAncestorsOf("glpi_entities", $entity2))) {
|
|
return true;
|
|
}
|
|
if (($item2->isRecursive())
|
|
&& in_array($entity2, getAncestorsOf("glpi_entities", $entity1))) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
**/
|
|
static function canCreate() {
|
|
|
|
if ((static::$rightname) && (!Session::haveRight(static::$rightname, CREATE))) {
|
|
return false;
|
|
}
|
|
return static::canRelation('canUpdate', static::$checkAlwaysBothItems);
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
**/
|
|
static function canView() {
|
|
if ((static::$rightname) && (!Session::haveRight(static::$rightname, READ))) {
|
|
return false;
|
|
}
|
|
// Always both checks for view
|
|
return static::canRelation('canView', true);
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
**/
|
|
static function canUpdate() {
|
|
if ((static::$rightname) && (!Session::haveRight(static::$rightname, UPDATE))) {
|
|
return false;
|
|
}
|
|
return static::canRelation('canUpdate', static::$checkAlwaysBothItems);
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
**/
|
|
static function canDelete() {
|
|
if ((static::$rightname) && (!Session::haveRight(static::$rightname, DELETE))) {
|
|
return false;
|
|
}
|
|
return static::canRelation('canUpdate', static::$checkAlwaysBothItems);
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.85
|
|
**/
|
|
static function canPurge() {
|
|
if ((static::$rightname) && (!Session::haveRight(static::$rightname, PURGE))) {
|
|
return false;
|
|
}
|
|
return static::canRelation('canUpdate', static::$checkAlwaysBothItems);
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
**/
|
|
function canCreateItem() {
|
|
|
|
return $this->canRelationItem('canUpdateItem', 'canUpdate', true,
|
|
static::$checkAlwaysBothItems);
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
**/
|
|
function canViewItem() {
|
|
return $this->canRelationItem('canViewItem', 'canView', false, true);
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
**/
|
|
function canUpdateItem() {
|
|
|
|
return $this->canRelationItem('canUpdateItem', 'canUpdate', true,
|
|
static::$checkAlwaysBothItems);
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
**/
|
|
function canDeleteItem() {
|
|
|
|
return $this->canRelationItem('canUpdateItem', 'canUpdate', false,
|
|
static::$checkAlwaysBothItems);
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 9.3.2
|
|
*/
|
|
function canPurgeItem() {
|
|
|
|
return $this->canRelationItem('canUpdateItem', 'canUpdate', false,
|
|
static::$checkAlwaysBothItems);
|
|
}
|
|
|
|
|
|
function addNeededInfoToInput($input) {
|
|
|
|
// is entity missing and forwarding on ?
|
|
if ($this->tryEntityForwarding() && !isset($input['entities_id'])) {
|
|
// Merge both arrays to ensure all the fields are defined for the following checks
|
|
$completeinput = array_merge($this->fields, $input);
|
|
|
|
$itemToGetEntity = false;
|
|
// Set the item to allow parent::prepareinputforadd to get the right item ...
|
|
if (static::$take_entity_1) {
|
|
$itemToGetEntity = static::getItemFromArray(static::$itemtype_1, static::$items_id_1,
|
|
$completeinput);
|
|
} else if (static::$take_entity_2) {
|
|
$itemToGetEntity = static::getItemFromArray(static::$itemtype_2, static::$items_id_2,
|
|
$completeinput);
|
|
}
|
|
|
|
// Set the item to allow parent::prepareinputforadd to get the right item ...
|
|
if (($itemToGetEntity instanceof CommonDBTM)
|
|
&& $itemToGetEntity->isEntityForwardTo(get_called_class())) {
|
|
|
|
$input['entities_id'] = $itemToGetEntity->getEntityID();
|
|
$input['is_recursive'] = intval($itemToGetEntity->isRecursive());
|
|
|
|
} else {
|
|
// No entity link : set default values
|
|
$input['entities_id'] = 0;
|
|
$input['is_recursive'] = 0;
|
|
}
|
|
}
|
|
return $input;
|
|
}
|
|
|
|
|
|
function prepareInputForAdd($input) {
|
|
|
|
if (!is_array($input)) {
|
|
return false;
|
|
}
|
|
|
|
return $this->addNeededInfoToInput($input);
|
|
}
|
|
|
|
|
|
function prepareInputForUpdate($input) {
|
|
|
|
if (!is_array($input)) {
|
|
return false;
|
|
}
|
|
|
|
// True if item changed
|
|
if (!$this->checkAttachedItemChangesAllowed($input, [static::$itemtype_1,
|
|
static::$items_id_1,
|
|
static::$itemtype_2,
|
|
static::$items_id_2])) {
|
|
return false;
|
|
}
|
|
|
|
return parent::addNeededInfoToInput($input);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the history name of first item
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* @param $item CommonDBTM object the other item (ie. : $item2)
|
|
* @param $case : can be overwrite by object
|
|
* - 'add' when this CommonDBRelation is added (to and item)
|
|
* - 'update item previous' transfert : this is removed from the old item
|
|
* - 'update item next' transfert : this is added to the new item
|
|
* - 'delete' when this CommonDBRelation is remove (from an item)
|
|
*
|
|
* @return (string) the name of the entry for the database (ie. : correctly slashed)
|
|
**/
|
|
function getHistoryNameForItem1(CommonDBTM $item, $case) {
|
|
|
|
return $item->getNameID(['forceid' => true,
|
|
'additional' => true]);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the history name of second item
|
|
*
|
|
* @since 0.84
|
|
*
|
|
* @param CommonDBTM $item the other item (ie. : $item1)
|
|
* @param string $case : can be overwrite by object
|
|
* - 'add' when this CommonDBRelation is added (to and item)
|
|
* - 'update item previous' transfert : this is removed from the old item
|
|
* - 'update item next' transfert : this is added to the new item
|
|
* - 'delete' when this CommonDBRelation is remove (from an item)
|
|
*
|
|
* @return string the name of the entry for the database (ie. : correctly slashed)
|
|
**/
|
|
function getHistoryNameForItem2(CommonDBTM $item, $case) {
|
|
|
|
return $item->getNameID(['forceid' => true,
|
|
'additional' => true]);
|
|
}
|
|
|
|
|
|
function post_addItem() {
|
|
|
|
if ((isset($this->input['_no_history']) && $this->input['_no_history'])
|
|
|| (!static::$logs_for_item_1
|
|
&& !static::$logs_for_item_2)) {
|
|
return;
|
|
}
|
|
|
|
$item1 = $this->getConnexityItem(static::$itemtype_1, static::$items_id_1);
|
|
$item2 = $this->getConnexityItem(static::$itemtype_2, static::$items_id_2);
|
|
|
|
if ($item1 instanceof CommonDBTM && $item2 instanceof CommonDBTM) {
|
|
if ($item1->dohistory
|
|
&& static::$logs_for_item_1) {
|
|
$changes = [
|
|
(isset($this->_force_log_option) ? $this->_force_log_option : 0),
|
|
'',
|
|
addslashes($this->getHistoryNameForItem1($item2, 'add')),
|
|
];
|
|
Log::history($item1->getID(), $item1->getType(), $changes, $item2->getType(),
|
|
static::$log_history_1_add);
|
|
}
|
|
|
|
if ($item2->dohistory && static::$logs_for_item_2) {
|
|
$changes = [
|
|
'0',
|
|
'',
|
|
addslashes($this->getHistoryNameForItem2($item1, 'add')),
|
|
];
|
|
Log::history($item2->getID(), $item2->getType(), $changes, $item1->getType(),
|
|
static::$log_history_2_add);
|
|
}
|
|
}
|
|
}
|
|
|
|
function post_updateItem($history = 1) {
|
|
|
|
if ((isset($this->input['_no_history']) && $this->input['_no_history'])
|
|
|| (!static::$logs_for_item_1
|
|
&& !static::$logs_for_item_2)) {
|
|
return;
|
|
}
|
|
|
|
$items_1 = $this->getItemsForLog(static::$itemtype_1, static::$items_id_1);
|
|
$items_2 = $this->getItemsForLog(static::$itemtype_2, static::$items_id_2);
|
|
|
|
$new1 = $items_1['new'];
|
|
if (isset($items_1['previous'])) {
|
|
$previous1 = $items_1['previous'];
|
|
} else {
|
|
$previous1 = $items_1['new'];
|
|
}
|
|
|
|
$new2 = $items_2['new'];
|
|
if (isset($items_2['previous'])) {
|
|
$previous2 = $items_2['previous'];
|
|
} else {
|
|
$previous2 = $items_2['new'];
|
|
}
|
|
|
|
$oldvalues = $this->oldvalues;
|
|
unset($oldvalues[static::$itemtype_1]);
|
|
unset($oldvalues[static::$items_id_1]);
|
|
unset($oldvalues[static::$itemtype_2]);
|
|
unset($oldvalues[static::$items_id_2]);
|
|
|
|
foreach (array_keys($oldvalues) as $field) {
|
|
$changes = $this->getHistoryChangeWhenUpdateField($field);
|
|
if ((!is_array($changes)) || (count($changes) != 3)) {
|
|
continue;
|
|
}
|
|
/// TODO clean management of it
|
|
if ($new1 && $new1->dohistory
|
|
&& static::$logs_for_item_1) {
|
|
Log::history($new1->getID(), $new1->getType(), $changes,
|
|
get_called_class().'#'.$field, static::$log_history_1_update);
|
|
}
|
|
if ($new2 && $new2->dohistory
|
|
&& static::$logs_for_item_2) {
|
|
Log::history($new2->getID(), $new2->getType(), $changes,
|
|
get_called_class().'#'.$field, static::$log_history_2_update);
|
|
}
|
|
}
|
|
|
|
if (isset($items_1['previous']) || isset($items_2['previous'])) {
|
|
|
|
if ($previous2
|
|
&& $previous1 && $previous1->dohistory
|
|
&& static::$logs_for_item_1) {
|
|
$changes[0] = '0';
|
|
$changes[1] = addslashes($this->getHistoryNameForItem1($previous2,
|
|
'update item previous'));
|
|
$changes[2] = "";
|
|
Log::history($previous1->getID(), $previous1->getType(), $changes,
|
|
$previous2->getType(), static::$log_history_1_delete);
|
|
}
|
|
|
|
if ($previous1
|
|
&& $previous2 && $previous2->dohistory
|
|
&& static::$logs_for_item_2) {
|
|
$changes[0] = '0';
|
|
$changes[1] = addslashes($this->getHistoryNameForItem2($previous1,
|
|
'update item previous'));
|
|
$changes[2] = "";
|
|
Log::history($previous2->getID(), $previous2->getType(), $changes,
|
|
$previous1->getType(), static::$log_history_2_delete);
|
|
}
|
|
|
|
if ($new2
|
|
&& $new1 && $new1->dohistory
|
|
&& static::$logs_for_item_1) {
|
|
$changes[0] = '0';
|
|
$changes[1] = "";
|
|
$changes[2] = addslashes($this->getHistoryNameForItem1($new2, 'update item next'));
|
|
Log::history($new1->getID(), $new1->getType(), $changes, $new2->getType(),
|
|
static::$log_history_1_add);
|
|
}
|
|
|
|
if ($new1
|
|
&& $new2 && $new2->dohistory
|
|
&& static::$logs_for_item_2) {
|
|
$changes[0] = '0';
|
|
$changes[1] = "";
|
|
$changes[2] = addslashes($this->getHistoryNameForItem2($new1, 'update item next'));
|
|
Log::history($new2->getID(), $new2->getType(), $changes, $new1->getType(),
|
|
static::$log_history_2_add);
|
|
}
|
|
}
|
|
}
|
|
|
|
function cleanDBonMarkDeleted() {
|
|
|
|
if ((isset($this->input['_no_history']) && $this->input['_no_history'])
|
|
|| (!static::$logs_for_item_1
|
|
&& !static::$logs_for_item_2)) {
|
|
return;
|
|
}
|
|
|
|
if ($this->useDeletedToLockIfDynamic()
|
|
&& $this->isDynamic()) {
|
|
$item1 = $this->getConnexityItem(static::$itemtype_1, static::$items_id_1);
|
|
$item2 = $this->getConnexityItem(static::$itemtype_2, static::$items_id_2);
|
|
|
|
if ($item1 instanceof CommonDBTM && $item2 instanceof CommonDBTM) {
|
|
if ($item1->dohistory
|
|
&& static::$logs_for_item_1) {
|
|
$changes = [
|
|
'0',
|
|
addslashes($this->getHistoryNameForItem1($item2, 'lock')),
|
|
'',
|
|
];
|
|
|
|
Log::history($item1->getID(), $item1->getType(), $changes, $item2->getType(),
|
|
static::$log_history_1_lock);
|
|
}
|
|
|
|
if ($item2->dohistory
|
|
&& static::$logs_for_item_2) {
|
|
$changes = [
|
|
'0',
|
|
addslashes($this->getHistoryNameForItem2($item1, 'lock')),
|
|
'',
|
|
];
|
|
Log::history($item2->getID(), $item2->getType(), $changes, $item1->getType(),
|
|
static::$log_history_2_lock);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
function post_restoreItem() {
|
|
|
|
if ((isset($this->input['_no_history']) && $this->input['_no_history'])
|
|
|| (!static::$logs_for_item_1
|
|
&& !static::$logs_for_item_2)) {
|
|
return;
|
|
}
|
|
|
|
if ($this->useDeletedToLockIfDynamic()
|
|
&& $this->isDynamic()) {
|
|
$item1 = $this->getConnexityItem(static::$itemtype_1, static::$items_id_1);
|
|
$item2 = $this->getConnexityItem(static::$itemtype_2, static::$items_id_2);
|
|
|
|
if ($item1 instanceof CommonDBTM && $item2 instanceof CommonDBTM) {
|
|
if ($item1->dohistory
|
|
&& static::$logs_for_item_1) {
|
|
$changes = [
|
|
'0',
|
|
'',
|
|
addslashes($this->getHistoryNameForItem1($item2, 'unlock')),
|
|
];
|
|
Log::history($item1->getID(), $item1->getType(), $changes, $item2->getType(),
|
|
static::$log_history_1_unlock);
|
|
}
|
|
|
|
if ($item2->dohistory
|
|
&& static::$logs_for_item_2) {
|
|
$changes = [
|
|
'0',
|
|
'',
|
|
addslashes($this->getHistoryNameForItem2($item1, 'unlock')),
|
|
];
|
|
Log::history($item2->getID(), $item2->getType(), $changes, $item1->getType(),
|
|
static::$log_history_2_unlock);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
function post_deleteFromDB() {
|
|
|
|
if ((isset($this->input['_no_history']) && $this->input['_no_history'])
|
|
|| (!static::$logs_for_item_1
|
|
&& !static::$logs_for_item_2)) {
|
|
return;
|
|
}
|
|
|
|
$item1 = $this->getConnexityItem(static::$itemtype_1, static::$items_id_1);
|
|
$item2 = $this->getConnexityItem(static::$itemtype_2, static::$items_id_2);
|
|
|
|
if ($item1 instanceof CommonDBTM && $item2 instanceof CommonDBTM) {
|
|
|
|
if ($item1->dohistory
|
|
&& static::$logs_for_item_1) {
|
|
$changes = [
|
|
'0',
|
|
addslashes($this->getHistoryNameForItem1($item2, 'delete')),
|
|
'',
|
|
];
|
|
Log::history($item1->getID(), $item1->getType(), $changes, $item2->getType(),
|
|
static::$log_history_1_delete);
|
|
}
|
|
|
|
if ($item2->dohistory
|
|
&& static::$logs_for_item_2) {
|
|
$changes = [
|
|
'0',
|
|
addslashes($this->getHistoryNameForItem2($item1, 'delete')),
|
|
'',
|
|
];
|
|
Log::history($item2->getID(), $item2->getType(), $changes, $item1->getType(),
|
|
static::$log_history_2_delete);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
*
|
|
* @param $itemtype
|
|
* @param $base HTMLTableBase object
|
|
* @param $super HTMLTableSuperHeader object (default NULL)
|
|
* @param $father HTMLTableHeader object (default NULL)
|
|
* @param $options array
|
|
**/
|
|
static function getHTMLTableHeader($itemtype, HTMLTableBase $base,
|
|
HTMLTableSuperHeader $super = null,
|
|
HTMLTableHeader $father = null, array $options = []) {
|
|
|
|
if (isset($options[get_called_class().'_side'])) {
|
|
$side = $options[get_called_class().'_side'];
|
|
} else {
|
|
$side = 0;
|
|
}
|
|
$oppositetype = '';
|
|
if (($side == 1)
|
|
|| ($itemtype == static::$itemtype_1)) {
|
|
$oppositetype = static::$itemtype_2;
|
|
}
|
|
if (($side == 2)
|
|
|| ($itemtype == static::$itemtype_2)) {
|
|
$oppositetype = static::$itemtype_1;
|
|
}
|
|
if (class_exists($oppositetype)
|
|
&& method_exists($oppositetype, 'getHTMLTableHeader')) {
|
|
$oppositetype::getHTMLTableHeader(get_called_class(), $base, $super, $father, $options);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.84
|
|
*
|
|
* @param $row HTMLTableRow object (default NULL)
|
|
* @param $item CommonDBTM object (default NULL)
|
|
* @param $father HTMLTableCell object (default NULL)
|
|
* @param $options array
|
|
**/
|
|
static function getHTMLTableCellsForItem(HTMLTableRow $row = null, CommonDBTM $item = null,
|
|
HTMLTableCell $father = null, array $options = []) {
|
|
global $DB;
|
|
|
|
if (empty($item)) {
|
|
if (empty($father)) {
|
|
return;
|
|
}
|
|
$item = $father->getItem();
|
|
}
|
|
|
|
$criteria = self::getSQLCriteriaToSearchForItem($item->getType(), $item->getID());
|
|
if ($criteria !== null) {
|
|
$relation = new static();
|
|
$iterator = $DB->request($criteria);
|
|
while ($line = $iterator->next()) {
|
|
if ($line['is_1'] != $line['is_2']) {
|
|
if ($line['is_1'] == 0) {
|
|
$options['items_id'] = $line['items_id_1'];
|
|
$oppositetype = $line['itemtype_1'];
|
|
} else {
|
|
$options['items_id'] = $line['items_id_2'];
|
|
$oppositetype = $line['itemtype_2'];
|
|
}
|
|
if (class_exists($oppositetype)
|
|
&& method_exists($oppositetype, 'getHTMLTableCellsForItem')
|
|
&& $relation->getFromDB($line[static::getIndexName()])) {
|
|
$oppositetype::getHTMLTableCellsForItem($row, $relation, $father, $options);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Affect a CommonDBRelation to a given item. By default, unaffect it
|
|
*
|
|
* @param integer $id the id of the CommonDBRelation to affect
|
|
* @param integer $peer the number of the peer (ie.: 0 or 1)
|
|
* @param integer $items_id the id of the new item
|
|
* @param string $itemtype the type of the new item
|
|
*
|
|
* @return boolean : true on success
|
|
**/
|
|
function affectRelation($id, $peer, $items_id = 0, $itemtype = '') {
|
|
|
|
$input = [static::getIndexName() => $id];
|
|
|
|
if ($peer == 0) {
|
|
$input[static::$items_id_1] = $items_id;
|
|
|
|
if (preg_match('/^itemtype/', static::$itemtype_1)) {
|
|
$input[static::$itemtype_1] = $itemtype;
|
|
}
|
|
|
|
} else {
|
|
|
|
$input[static::$items_id_2] = $items_id;
|
|
if (preg_match('/^itemtype/', static::$itemtype_2)) {
|
|
$input[static::$itemtype_2] = $itemtype;
|
|
}
|
|
|
|
}
|
|
|
|
return $this->update($input);
|
|
}
|
|
|
|
|
|
/**
|
|
* Get all specificities of the current itemtype concerning the massive actions
|
|
*
|
|
* @since 0.85
|
|
*
|
|
* @return array of the specificities:
|
|
* 'select_items_options_1' Base options for item_1 select
|
|
* 'select_items_options_2' Base options for item_2 select
|
|
* 'can_remove_all_at_once' Is it possible to remove all links at once ?
|
|
* 'only_remove_all_at_once' Do we only allow to remove all links at once ?
|
|
* (implies 'can_remove_all_at_once')
|
|
* 'itemtypes' array of kind of items in case of itemtype as one item
|
|
* 'button_labels' array of the labels of the button indexed by the action name
|
|
* 'normalized' array('add', 'remove') of arrays containing each action
|
|
* 'check_both_items_if_same_type' to check if the link already exists, also care of both
|
|
* items are of the same type, then switch them
|
|
* 'can_link_several_times' Is it possible to link items several times ?
|
|
* 'update_id_different' Do we update the link if it already exists (not used in case
|
|
* of 'can_link_several_times')
|
|
**/
|
|
static function getRelationMassiveActionsSpecificities() {
|
|
|
|
return ['select_items_options_1' => [],
|
|
'dropdown_method_1' => 'dropdown',
|
|
'select_items_options_2' => [],
|
|
'dropdown_method_2' => 'dropdown',
|
|
'can_remove_all_at_once' => true,
|
|
'only_remove_all_at_once' => false,
|
|
'itemtypes' => [],
|
|
'button_labels' => ['add' => _sx('button', 'Add'),
|
|
'remove' => _sx('button',
|
|
'Delete permanently')],
|
|
'normalized' => ['add' => ['add'],
|
|
'remove' => ['remove']],
|
|
'check_both_items_if_same_type' => false,
|
|
'can_link_several_times' => false,
|
|
'update_if_different' => false];
|
|
}
|
|
|
|
|
|
/**
|
|
* Display subForm of the massive action
|
|
*
|
|
* @param MassiveAction $ma current massive action
|
|
* @param integer $peer_number the number of the concerned peer
|
|
*
|
|
* @return void
|
|
**/
|
|
static function showRelationMassiveActionsSubForm(MassiveAction $ma, $peer_number) {
|
|
}
|
|
|
|
|
|
/**
|
|
* get the type of the item with the name of the action or the types of the input
|
|
*
|
|
* @since 0.85
|
|
*
|
|
* @param MassiveAction $ma current massive action
|
|
*
|
|
* @return number of the peer
|
|
**/
|
|
static function getRelationMassiveActionsPeerForSubForm(MassiveAction $ma) {
|
|
|
|
$items = $ma->getItems();
|
|
// If direct itemtype, then, its easy to find !
|
|
if (isset($items[static::$itemtype_1])) {
|
|
return 2;
|
|
}
|
|
if (isset($items[static::$itemtype_2])) {
|
|
return 1;
|
|
}
|
|
|
|
// Else, check if one of both peer is 'itemtype*'
|
|
if (preg_match('/^itemtype/', static::$itemtype_1)) {
|
|
return 2;
|
|
}
|
|
if (preg_match('/^itemtype/', static::$itemtype_2)) {
|
|
return 1;
|
|
}
|
|
|
|
// Else we cannot define !
|
|
return 0;
|
|
}
|
|
|
|
|
|
static function showMassiveActionsSubForm(MassiveAction $ma) {
|
|
|
|
$specificities = static::getRelationMassiveActionsSpecificities();
|
|
$action = $ma->getAction();
|
|
|
|
// First, get normalized action : add or remove
|
|
if (in_array($action, $specificities['normalized']['add'])) {
|
|
$normalized_action = 'add';
|
|
} else if (in_array($action, $specificities['normalized']['remove'])) {
|
|
$normalized_action = 'remove';
|
|
} else {
|
|
// If we cannot get normalized action, then, its not for this method !
|
|
return parent::showMassiveActionsSubForm($ma);
|
|
}
|
|
|
|
switch ($normalized_action) {
|
|
case 'add' :
|
|
case 'remove' :
|
|
// Get the peer number. For Document_Item, it depends of the action's name
|
|
$peer_number = static::getRelationMassiveActionsPeerForSubForm($ma);
|
|
switch ($peer_number) {
|
|
case 1:
|
|
$peertype = static::$itemtype_1;
|
|
$peers_id = static::$items_id_1;
|
|
break;
|
|
case 2:
|
|
$peertype = static::$itemtype_2;
|
|
$peers_id = static::$items_id_2;
|
|
break;
|
|
default:
|
|
exit();
|
|
}
|
|
if (($normalized_action == 'remove')
|
|
&& ($specificities['only_remove_all_at_once'])) {
|
|
// If we just want to remove all the items, then just set hidden fields
|
|
echo Html::hidden('peer_'.$peertype, ['value' => '']);
|
|
echo Html::hidden('peer_'.$peers_id, ['value' => -1]);
|
|
} else {
|
|
// Else, it depends if the peer is an itemtype or not
|
|
$options = $specificities['select_items_options_'.$peer_number];
|
|
// Do we allow to remove all the items at once ? Then, rename the default value !
|
|
if (($normalized_action == 'remove')
|
|
&& $specificities['can_remove_all_at_once']) {
|
|
$options['emptylabel'] = __('Remove all at once');
|
|
}
|
|
if (preg_match('/^itemtype/', $peertype)) {
|
|
if (count($specificities['itemtypes']) > 0) {
|
|
$options['itemtype_name'] = 'peer_'.$peertype;
|
|
$options['items_id_name'] = 'peer_'.$peers_id;
|
|
$options['itemtypes'] = $specificities['itemtypes'];
|
|
// At least, if not forced by user, 'checkright' == true
|
|
if (!isset($options['checkright'])) {
|
|
$options['checkright'] = true;
|
|
}
|
|
Dropdown::showSelectItemFromItemtypes($options);
|
|
}
|
|
} else {
|
|
$options['name'] = 'peer_'.$peers_id;
|
|
if (isset($_POST['entity_restrict'])) {
|
|
$options['entity'] = $_POST['entity_restrict'];
|
|
}
|
|
if ($normalized_action == 'remove') {
|
|
$options['nochecklimit'] = true;
|
|
}
|
|
$dropdown_method = $specificities['dropdown_method_'.$peer_number];
|
|
$peertype::$dropdown_method($options);
|
|
}
|
|
}
|
|
// Allow any relation to display its own fields (NetworkPort_Vlan for tagged ...)
|
|
static::showRelationMassiveActionsSubForm($ma, $peer_number);
|
|
echo "<br><br>".Html::submit($specificities['button_labels'][$action],
|
|
['name' => 'massiveaction']);
|
|
return true;
|
|
}
|
|
return parent::showMassiveActionsSubForm($ma);
|
|
}
|
|
|
|
|
|
/**
|
|
* @since 0.85
|
|
*
|
|
* Set based array for static::add or static::update in case of massive actions are doing
|
|
* something.
|
|
*
|
|
* @param string $action the name of the action
|
|
* @param CommonDBTM $item the item on which apply the massive action
|
|
* @param integer[] $ids ids of the item on which apply the action
|
|
* @param array $input input provided by the form ($_POST, $_GET ...)
|
|
*
|
|
* @return array containing the elements
|
|
**/
|
|
static function getRelationInputForProcessingOfMassiveActions($action, CommonDBTM $item,
|
|
array $ids, array $input) {
|
|
return [];
|
|
}
|
|
|
|
|
|
/**
|
|
* @warning this is not valid if $itemtype_1 == $itemtype_2 !
|
|
*
|
|
* @since 0.85
|
|
*
|
|
* @see CommonDBTM::processMassiveActionsForOneItemtype()
|
|
**/
|
|
static function processMassiveActionsForOneItemtype(MassiveAction $ma, CommonDBTM $item,
|
|
array $ids) {
|
|
global $DB;
|
|
|
|
$action = $ma->getAction();
|
|
$input = $ma->getInput();
|
|
|
|
$specificities = static::getRelationMassiveActionsSpecificities();
|
|
|
|
// First, get normalized action : add or remove
|
|
if (in_array($action, $specificities['normalized']['add'])) {
|
|
$normalized_action = 'add';
|
|
} else if (in_array($action, $specificities['normalized']['remove'])) {
|
|
$normalized_action = 'remove';
|
|
} else {
|
|
// If we cannot get normalized action, then, its not for this method !
|
|
parent::processMassiveActionsForOneItemtype($ma, $item, $ids);
|
|
return;
|
|
}
|
|
|
|
$link = new static();
|
|
|
|
// Get the default 'input' entries from the relation
|
|
$input2 = static::getRelationInputForProcessingOfMassiveActions($action, $item, $ids,
|
|
$input);
|
|
|
|
// complete input2 with the right fields from input and define the peer with this information
|
|
foreach ([static::$itemtype_1, static::$items_id_1] as $field) {
|
|
if (isset($input['peer_'.$field])) {
|
|
$input2[$field] = $input['peer_'.$field];
|
|
$item_number = 2;
|
|
}
|
|
}
|
|
|
|
foreach ([static::$itemtype_2, static::$items_id_2] as $field) {
|
|
if (isset($input['peer_'.$field])) {
|
|
$input2[$field] = $input['peer_'.$field];
|
|
$item_number = 1;
|
|
}
|
|
}
|
|
|
|
// If the fields provided by showMassiveActionsSubForm are not valid then quit !
|
|
if (!isset($item_number)) {
|
|
$ma->itemDone($item->getType(), $ids, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($link->getErrorMessage(ERROR_NOT_FOUND));
|
|
return;
|
|
}
|
|
|
|
if ($item_number == 1) {
|
|
$itemtype = static::$itemtype_1;
|
|
$items_id = static::$items_id_1;
|
|
$peertype = static::$itemtype_2;
|
|
$peers_id = static::$items_id_2;
|
|
} else {
|
|
$itemtype = static::$itemtype_2;
|
|
$items_id = static::$items_id_2;
|
|
$peertype = static::$itemtype_1;
|
|
$peers_id = static::$items_id_1;
|
|
}
|
|
|
|
if (preg_match('/^itemtype/', $itemtype)) {
|
|
$input2[$itemtype] = $item->getType();
|
|
}
|
|
|
|
// Get the peer from the $input2 and the name of its fields
|
|
$peer = static::getItemFromArray($peertype, $peers_id, $input2, true, true, true);
|
|
|
|
// $peer not valid => not in DB or try to remove all at once !
|
|
if (!($peer instanceof CommonDBTM) || $peer->isNewItem()) {
|
|
if ((isset($input2[$peers_id])) && ($input2[$peers_id] > 0)) {
|
|
$ma->itemDone($item->getType(), $ids, MassiveAction::ACTION_KO);
|
|
if ($peer instanceof CommonDBTM) {
|
|
$ma->addMessage($peer->getErrorMessage(ERROR_NOT_FOUND));
|
|
} else {
|
|
$ma->addMessage($link->getErrorMessage(ERROR_NOT_FOUND));
|
|
}
|
|
return;
|
|
}
|
|
if (!$specificities['can_remove_all_at_once']
|
|
&& !$specificities['only_remove_all_at_once']) {
|
|
return false;
|
|
}
|
|
$peer = false;
|
|
}
|
|
|
|
// Make a link between $item_1, $item_2 and $item and $peer. Thus, we will be able to update
|
|
// $item without having to care about the number of the item
|
|
if ($item_number == 1) {
|
|
$item_1 = &$item;
|
|
$item_2 = &$peer;
|
|
} else {
|
|
$item_1 = &$peer;
|
|
$item_2 = &$item;
|
|
}
|
|
|
|
switch ($normalized_action) {
|
|
case 'add' :
|
|
// remove all at once only available for remove !
|
|
if (!$peer) {
|
|
$ma->itemDone($item->getType(), $ids, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($link->getErrorMessage(ERROR_ON_ACTION));
|
|
return;
|
|
}
|
|
foreach ($ids as $key) {
|
|
if (!$item->getFromDB($key)) {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_NOT_FOUND));
|
|
continue;
|
|
}
|
|
$input2[$items_id] = $item->getID();
|
|
// If 'can_link_several_times', then, we add the elements !
|
|
if ($specificities['can_link_several_times']) {
|
|
if ($link->can(-1, CREATE, $input2)) {
|
|
if ($link->add($input2)) {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_OK);
|
|
} else {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($link->getErrorMessage(ERROR_ON_ACTION));
|
|
}
|
|
} else {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_NORIGHT);
|
|
$ma->addMessage($link->getErrorMessage(ERROR_RIGHT));
|
|
}
|
|
} else {
|
|
$link->getEmpty();
|
|
if (!$link->getFromDBForItems($item_1, $item_2)) {
|
|
if (($specificities['check_both_items_if_same_type'])
|
|
&& ($item_1->getType() == $item_2->getType())) {
|
|
$link->getFromDBForItems($item_2, $item_1);
|
|
}
|
|
}
|
|
if (!$link->isNewItem()) {
|
|
if (!$specificities['update_if_different']) {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($link->getErrorMessage(ERROR_ALREADY_DEFINED));
|
|
continue;
|
|
}
|
|
$input2[static::getIndexName()] = $link->getID();
|
|
if ($link->can($link->getID(), UPDATE, $input2)) {
|
|
if ($link->update($input2)) {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_OK);
|
|
} else {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($link->getErrorMessage(ERROR_ON_ACTION));
|
|
}
|
|
} else {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_NORIGHT);
|
|
$ma->addMessage($link->getErrorMessage(ERROR_RIGHT));
|
|
}
|
|
// if index defined, then cannot not add any other link due to index unicity
|
|
unset($input2[static::getIndexName()]);
|
|
} else {
|
|
if ($link->can(-1, CREATE, $input2)) {
|
|
if ($link->add($input2)) {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_OK);
|
|
} else {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($link->getErrorMessage(ERROR_ON_ACTION));
|
|
}
|
|
} else {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_NORIGHT);
|
|
$ma->addMessage($link->getErrorMessage(ERROR_RIGHT));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
|
|
case 'remove' :
|
|
foreach ($ids as $key) {
|
|
// First, get the query to find all occurences of the link item<=>key
|
|
if (!$peer) {
|
|
$criteria = static::getSQLCriteriaToSearchForItem($item->getType(), $key);
|
|
} else {
|
|
if (!$item->getFromDB($key)) {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_NOT_FOUND));
|
|
continue;
|
|
}
|
|
|
|
$WHERE = [
|
|
static::$items_id_1 => $item_1->getID(),
|
|
static::$items_id_2 => $item_2->getID()
|
|
];
|
|
if (preg_match('/^itemtype/', static::$itemtype_1)) {
|
|
$WHERE[static::$itemtype_1] = $item_1->getType();
|
|
}
|
|
if (preg_match('/^itemtype/', static::$itemtype_2)) {
|
|
$WHERE[static::$itemtype_2] = $item_2->getType();
|
|
}
|
|
|
|
if (($specificities['check_both_items_if_same_type'])
|
|
&& ($item_1->getType() == $item_2->getType())) {
|
|
$ORWHERE = [
|
|
static::$items_id_1 = $item_2->getID(),
|
|
static::$items_id_2 = $item_2->getID()
|
|
];
|
|
if (preg_match('/^itemtype/', static::$itemtype_1)) {
|
|
$ORWHERE[static::$itemtype_1] = $item_2->getType();
|
|
}
|
|
if (preg_match('/^itemtype/', static::$itemtype_2)) {
|
|
$ORWHERE[static::$itemtype_2] = $item_2->getType();
|
|
}
|
|
$WHERE = [
|
|
'OR' => [
|
|
$WHERE,
|
|
$ORWHERE
|
|
]
|
|
];
|
|
}
|
|
|
|
$criteria = [
|
|
'SELECT' => static::getIndexName(),
|
|
'FROM' => static::getTable(),
|
|
'WHERE' => $WHERE
|
|
];
|
|
|
|
}
|
|
$request = $DB->request($criteria);
|
|
$number_results = count($request);
|
|
if ($number_results == 0) {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($link->getErrorMessage(ERROR_NOT_FOUND));
|
|
continue;
|
|
}
|
|
$ok = 0;
|
|
$ko = 0;
|
|
$noright = 0;
|
|
while ($line = $request->next()) {
|
|
if ($link->can($line[static::getIndexName()], DELETE)) {
|
|
if ($link->delete(['id' => $line[static::getIndexName()]])) {
|
|
$ok++;
|
|
} else {
|
|
$ko++;
|
|
$ma->addMessage($link->getErrorMessage(ERROR_ON_ACTION));
|
|
}
|
|
} else {
|
|
$noright++;
|
|
$ma->addMessage($link->getErrorMessage(ERROR_RIGHT));
|
|
}
|
|
}
|
|
if ($ok == $number_results) {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_OK);
|
|
} else {
|
|
if ($noright > 0) {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_NORIGHT);
|
|
} else if ($ko > 0) {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_KO);
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
parent::processMassiveActionsForOneItemtype($ma, $item, $ids);
|
|
}
|
|
|
|
/**
|
|
* Get linked items list for specified item
|
|
*
|
|
* @since 9.3.1
|
|
*
|
|
* @param CommonDBTM $item Item instance
|
|
* @param boolean $noent Flag to not compute entity informations (see Document_Item::getListForItemParams)
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getListForItemParams(CommonDBTM $item, $noent = false) {
|
|
global $DB;
|
|
|
|
if (Session::isCron()) {
|
|
$noent = true;
|
|
}
|
|
|
|
$inverse = $item->getType() == static::$itemtype_1;
|
|
|
|
$link_type = static::$itemtype_1;
|
|
$link_id = static::$items_id_1;
|
|
$where_id = static::$items_id_2;
|
|
|
|
if ($inverse === true) {
|
|
$link_type = static::$itemtype_2;
|
|
if ($link_type == 'itemtype') {
|
|
throw new \RuntimeException(
|
|
sprintf(
|
|
'Cannot use getListForItemParams() for a %s',
|
|
$item->getType()
|
|
)
|
|
);
|
|
}
|
|
$link_id = static::$items_id_2;
|
|
$where_id = static::$items_id_1;
|
|
}
|
|
|
|
$link = new $link_type;
|
|
$link_table = getTableForItemType($link_type);
|
|
|
|
$params = [
|
|
'SELECT' => [static::getTable() . '.id AS linkid', $link_table . '.*'],
|
|
'FROM' => static::getTable(),
|
|
'LEFT JOIN' => [
|
|
$link_table => [
|
|
'FKEY' => [
|
|
static::getTable() => $link_id,
|
|
$link_table => 'id'
|
|
]
|
|
]
|
|
],
|
|
'WHERE' => [
|
|
static::getTable() . '.' . $where_id => $item->fields['id']
|
|
],
|
|
'ORDER' => $link_table . '.name'
|
|
];
|
|
|
|
$rel_class = static::class;
|
|
$rel = new $rel_class();
|
|
if ($rel->maybeDynamic()) {
|
|
$params['SELECT'][] = static::getTable() . '.is_dynamic';
|
|
}
|
|
|
|
if ($rel->maybeRecursive()) {
|
|
$params['SELECT'][] = static::getTable() . '.is_recursive';
|
|
}
|
|
|
|
if ($DB->fieldExists(static::getTable(), 'itemtype')) {
|
|
$params['WHERE'][static::getTable() . '.itemtype'] = $item->getType();
|
|
}
|
|
|
|
if ($noent === false && $link->isEntityAssign() && $link_type != Entity::getType()) {
|
|
$params['SELECT'][] = 'glpi_entities.id AS entity';
|
|
$params['INNER JOIN']['glpi_entities'] = [
|
|
'FKEY' => [
|
|
$link_table => 'entities_id',
|
|
'glpi_entities' => 'id'
|
|
]
|
|
];
|
|
$params['WHERE'] += getEntitiesRestrictCriteria($link_table, '', '', 'auto');
|
|
$params['ORDER'] = ['glpi_entities.completename', $params['ORDER']];
|
|
}
|
|
|
|
return $params;
|
|
}
|
|
|
|
/**
|
|
* Get linked items list for specified item
|
|
*
|
|
* @since 9.3.1
|
|
*
|
|
* @param CommonDBTM $item Item instance
|
|
*
|
|
* @return DBmysqlIterator
|
|
*/
|
|
public static function getListForItem(CommonDBTM $item) {
|
|
global $DB;
|
|
|
|
$params = static::getListForItemParams($item);
|
|
$iterator = $DB->request($params);
|
|
|
|
return $iterator;
|
|
}
|
|
|
|
/**
|
|
* Get distinct item types query parameters
|
|
*
|
|
* @since 9.3.1
|
|
*
|
|
* @param integer $items_id Object id to restrict on
|
|
* @param array $extra_where Extra where clause
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getDistinctTypesParams($items_id, $extra_where = []) {
|
|
$params = [
|
|
'SELECT' => 'itemtype',
|
|
'DISTINCT' => true,
|
|
'FROM' => static::getTable(),
|
|
'WHERE' => [
|
|
static::$items_id_1 => $items_id,
|
|
] + $extra_where,
|
|
'ORDER' => 'itemtype'
|
|
];
|
|
return $params;
|
|
}
|
|
|
|
/**
|
|
* Get distinct item types
|
|
*
|
|
* @since 9.3.1
|
|
*
|
|
* @param integer $items_id Object id to restrict on
|
|
* @param array $extra_where Extra where clause
|
|
*
|
|
* @return DBmysqlIterator
|
|
*/
|
|
public static function getDistinctTypes($items_id, $extra_where = []) {
|
|
global $DB;
|
|
|
|
$params = static::getDistinctTypesParams($items_id, $extra_where);
|
|
$types_iterator = $DB->request($params);
|
|
return $types_iterator;
|
|
}
|
|
|
|
/**
|
|
* Get items for an itemtype
|
|
*
|
|
* @since 9.3.1
|
|
*
|
|
* @param integer $items_id Object id to restrict on
|
|
* @param string $itemtype Type for items to retrieve
|
|
* @param boolean $noent Flag to not compute entity informations (see Document_Item::getTypeItemsQueryParams)
|
|
* @param array $where Inital WHERE clause. Defaults to []
|
|
*
|
|
* @return array
|
|
*/
|
|
protected static function getTypeItemsQueryParams($items_id, $itemtype, $noent = false, $where = []) {
|
|
global $DB;
|
|
|
|
$item = getItemForItemtype($itemtype);
|
|
$order_col = $item->getNameField();
|
|
|
|
if ($item instanceof CommonDevice) {
|
|
$order_col = "designation";
|
|
} else if ($item instanceof Item_Devices) {
|
|
$order_col = "itemtype";
|
|
} else if ($itemtype == 'Ticket') {
|
|
$order_col = 'id';
|
|
}
|
|
|
|
if (!count($where)) {
|
|
$where = [static::getTable() . '.' . static::$items_id_1 => $items_id];
|
|
}
|
|
|
|
$params = [
|
|
'SELECT' => [
|
|
$item->getTable() . '.*',
|
|
static::getTable() . '.id AS linkid'
|
|
],
|
|
'FROM' => $item->getTable(),
|
|
'WHERE' => $where,
|
|
'LEFT JOIN' => [
|
|
static::getTable() => [
|
|
'FKEY' => [
|
|
static::getTable() => 'items_id',
|
|
$item->getTable() => 'id'
|
|
]
|
|
]
|
|
],
|
|
'ORDER' => $item->getTable() . '.' . $order_col
|
|
];
|
|
|
|
if ($DB->fieldExists(static::getTable(), 'is_deleted')) {
|
|
$params['WHERE'][static::getTable() . '.is_deleted'] = 0;
|
|
}
|
|
|
|
if ($DB->fieldExists(static::getTable(), 'itemtype')) {
|
|
$params['WHERE'][static::getTable() . '.itemtype'] = $itemtype;
|
|
}
|
|
|
|
if ($item->maybeTemplate()) {
|
|
$params['WHERE'][$item->getTable() . '.is_template'] = 0;
|
|
}
|
|
|
|
if ($noent === false && $item->isEntityAssign() && $itemtype != Entity::getType()) {
|
|
$params['SELECT'][] = 'glpi_entities.id AS entity';
|
|
$params['LEFT JOIN']['glpi_entities'] = [
|
|
'FKEY' => [
|
|
$item->getTable() => 'entities_id',
|
|
'glpi_entities' => 'id'
|
|
]
|
|
];
|
|
$params['WHERE'] += getEntitiesRestrictCriteria($item->getTable(), '', '', 'auto');
|
|
$params['ORDER'] = ['glpi_entities.completename', $params['ORDER']];
|
|
}
|
|
|
|
return $params;
|
|
}
|
|
|
|
/**
|
|
* Get items for an itemtype
|
|
*
|
|
* @since 9.3.1
|
|
*
|
|
* @param integer $items_id Object id to restrict on
|
|
* @param string $itemtype Type for items to retrieve
|
|
*
|
|
* @return DBmysqlIterator
|
|
*/
|
|
public static function getTypeItems($items_id, $itemtype) {
|
|
global $DB;
|
|
|
|
$params = static::getTypeItemsQueryParams($items_id, $itemtype);
|
|
$iterator = $DB->request($params);
|
|
|
|
return $iterator;
|
|
}
|
|
|
|
/**
|
|
* Count for item
|
|
*
|
|
* @param CommonDBTM $item CommonDBTM object
|
|
*
|
|
* @return integer
|
|
*/
|
|
static function countForItem(CommonDBTM $item) {
|
|
global $DB;
|
|
|
|
$params = static::getListForItemParams($item);
|
|
unset($params['SELECT']);
|
|
$params['COUNT'] = 'cpt';
|
|
$iterator = $DB->request($params);
|
|
|
|
$cpt = 0;
|
|
while ($row = $iterator->next()) {
|
|
$cpt += $row['cpt'];
|
|
}
|
|
|
|
return $cpt;
|
|
}
|
|
|
|
/**
|
|
* Count items for main itemtype
|
|
*
|
|
* @param CommonDBTM $item Item instance
|
|
* @param array $extra_types_where Extra WHERE clause on types
|
|
*
|
|
* @return integer
|
|
**/
|
|
static function countForMainItem(CommonDBTM $item, $extra_types_where = []) {
|
|
global $DB;
|
|
|
|
$nb = 0;
|
|
|
|
$types_iterator = static::getDistinctTypes($item->fields['id'], $extra_types_where);
|
|
while ($data = $types_iterator->next()) {
|
|
if (!getItemForItemtype($data['itemtype'])) {
|
|
continue;
|
|
}
|
|
|
|
$params = static::getTypeItemsQueryParams($item->fields['id'], $data['itemtype']);
|
|
unset($params['SELECT']);
|
|
$params['COUNT'] = 'cpt';
|
|
$iterator = $DB->request($params);
|
|
|
|
while ($row = $iterator->next()) {
|
|
$nb += $row['cpt'];
|
|
}
|
|
}
|
|
return $nb;
|
|
}
|
|
|
|
public static final function getItemField($itemtype): string {
|
|
if (isset(static::$items_id_1) && getItemtypeForForeignKeyField(static::$items_id_1) == $itemtype) {
|
|
return static::$items_id_1;
|
|
}
|
|
if (isset(static::$items_id_2) && getItemtypeForForeignKeyField(static::$items_id_2) == $itemtype) {
|
|
return static::$items_id_2;
|
|
}
|
|
|
|
if (isset(static::$itemtype_1) && isset(static::$itemtype_2) && preg_match('/^itemtype/', static::$itemtype_1) && preg_match('/^itemtype/', static::$itemtype_2)) {
|
|
throw new \RuntimeException('Bad relation (' . $itemtype . ', ' .static::class . ', ' . static::$itemtype_1 . ', ' . static::$itemtype_2 . ')');
|
|
}
|
|
|
|
if (isset(static::$itemtype_1) && preg_match('/^itemtype/', static::$itemtype_1)) {
|
|
return static::$items_id_1;
|
|
}
|
|
if (isset(static::$itemtype_2) && preg_match('/^itemtype/', static::$itemtype_2)) {
|
|
return static::$items_id_2;
|
|
}
|
|
|
|
throw new \RuntimeException('Cannot guess ');
|
|
}
|
|
|
|
function getForbiddenStandardMassiveAction() {
|
|
$forbidden = parent::getForbiddenStandardMassiveAction();
|
|
$forbidden[] = 'clone';
|
|
return $forbidden;
|
|
}
|
|
}
|