1507 lines
58 KiB
PHP
1507 lines
58 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");
|
|
}
|
|
|
|
/**
|
|
* Class that manages all the massive actions
|
|
*
|
|
* @todo all documentation !
|
|
*
|
|
* @since 0.85
|
|
**/
|
|
class MassiveAction {
|
|
|
|
const CLASS_ACTION_SEPARATOR = ':';
|
|
|
|
const NO_ACTION = 0;
|
|
const ACTION_OK = 1;
|
|
const ACTION_KO = 2;
|
|
const ACTION_NORIGHT = 3;
|
|
|
|
|
|
/**
|
|
* Constructor of massive actions.
|
|
* There is three stages and each one have its own objectives:
|
|
* - initial: propose the actions and filter the checkboxes (only once)
|
|
* - specialize: add action specific fields and filter items. There can be as many as needed!
|
|
* - process: process the massive action (only once, but can be reload to avoid timeout)
|
|
*
|
|
* We trust all previous stages: we don't redo the checks
|
|
*
|
|
* @param array $POST something like $_POST
|
|
* @param array $GET something like $_GET
|
|
* @param string $stage the current stage
|
|
* @param boolean $single Get actions for a single item
|
|
**/
|
|
function __construct (array $POST, array $GET, $stage, $single = false) {
|
|
global $CFG_GLPI;
|
|
|
|
if (!empty($POST)) {
|
|
|
|
if (!isset($POST['is_deleted'])) {
|
|
$POST['is_deleted'] = 0;
|
|
}
|
|
|
|
$this->nb_items = 0;
|
|
|
|
if ((isset($POST['item'])) || (isset($POST['items']))) {
|
|
|
|
$remove_from_post = [];
|
|
|
|
switch ($stage) {
|
|
case 'initial' :
|
|
$POST['action_filter'] = [];
|
|
// 'specific_actions': restrict all possible actions or introduce new ones
|
|
// thus, don't try to load other actions and don't filter any item
|
|
if (isset($POST['specific_actions'])) {
|
|
$POST['actions'] = $POST['specific_actions'];
|
|
$specific_action = 1;
|
|
$dont_filter_for = array_keys($POST['actions']);
|
|
} else {
|
|
$specific_action = 0;
|
|
if (isset($POST['add_actions'])) {
|
|
$POST['actions'] = $POST['add_actions'];
|
|
$dont_filter_for = array_keys($POST['actions']);
|
|
} else {
|
|
$POST['actions'] = [];
|
|
$dont_filter_for = [];
|
|
}
|
|
}
|
|
if (count($dont_filter_for)) {
|
|
$POST['dont_filter_for'] = array_combine($dont_filter_for, $dont_filter_for);
|
|
} else {
|
|
$POST['dont_filter_for'] = [];
|
|
}
|
|
$remove_from_post[] = 'specific_actions';
|
|
$remove_from_post[] = 'add_actions';
|
|
$POST['items'] = [];
|
|
foreach ($POST['item'] as $itemtype => $ids) {
|
|
// initial are raw checkboxes: 0=unchecked or 1=checked
|
|
$items = [];
|
|
foreach ($ids as $id => $checked) {
|
|
if ($checked == 1) {
|
|
$items[$id] = $id;
|
|
$this->nb_items ++;
|
|
}
|
|
}
|
|
$POST['items'][$itemtype] = $items;
|
|
if (!$specific_action) {
|
|
$actions = self::getAllMassiveActions(
|
|
$itemtype,
|
|
$POST['is_deleted'],
|
|
$this->getCheckItem($POST),
|
|
$single
|
|
);
|
|
$POST['actions'] = array_merge($actions, $POST['actions']);
|
|
foreach ($actions as $action => $label) {
|
|
$POST['action_filter'][$action][] = $itemtype;
|
|
$POST['actions'][$action] = $label;
|
|
}
|
|
}
|
|
}
|
|
if (empty($POST['actions']) && false === $single) {
|
|
throw new Exception(__('No action available'));
|
|
}
|
|
// Initial items is used to define $_SESSION['glpimassiveactionselected']
|
|
$POST['initial_items'] = $POST['items'];
|
|
$remove_from_post[] = 'item';
|
|
break;
|
|
|
|
case 'specialize' :
|
|
if (!isset($POST['action'])) {
|
|
Toolbox::logError('Implementation error !');
|
|
throw new Exception(__('Implementation error !'));
|
|
}
|
|
if ($POST['action'] == -1) {
|
|
// Case when no action is choosen
|
|
exit();
|
|
}
|
|
if (isset($POST['actions'])) {
|
|
// First, get the name of current action !
|
|
if (!isset($POST['actions'][$POST['action']])) {
|
|
Toolbox::logError('Implementation error !');
|
|
throw new Exception(__('Implementation error !'));
|
|
}
|
|
$POST['action_name'] = $POST['actions'][$POST['action']];
|
|
$remove_from_post[] = 'actions';
|
|
|
|
// Then filter the items regarding the action
|
|
if (!isset($POST['dont_filter_for'][$POST['action']])) {
|
|
if (isset($POST['action_filter'][$POST['action']])) {
|
|
$items = [];
|
|
foreach ($POST['action_filter'][$POST['action']] as $itemtype) {
|
|
if (isset($POST['items'][$itemtype])) {
|
|
$items[$itemtype] = $POST['items'][$itemtype];
|
|
}
|
|
}
|
|
$POST['items'] = $items;
|
|
}
|
|
}
|
|
// Don't affect items that forbid the action
|
|
$items = [];
|
|
foreach ($POST['items'] as $itemtype => $ids) {
|
|
if ($item = getItemForItemtype($itemtype)) {
|
|
$forbidden = $item->getForbiddenStandardMassiveAction();
|
|
if (in_array($POST['action'], $forbidden)) {
|
|
continue;
|
|
}
|
|
$items[$itemtype] = $ids;
|
|
}
|
|
}
|
|
$POST['items'] = $items;
|
|
$remove_from_post[] = 'dont_filter_for';
|
|
$remove_from_post[] = 'action_filter';
|
|
}
|
|
// Some action works for only one itemtype. Then, we filter items.
|
|
if (isset($POST['specialize_itemtype'])) {
|
|
$itemtype = $POST['specialize_itemtype'];
|
|
if (isset($POST['items'][$itemtype])) {
|
|
$POST['items'] = [$itemtype => $POST['items'][$itemtype]];
|
|
} else {
|
|
$POST['items'] = [];
|
|
}
|
|
$remove_from_post[] = 'specialize_itemtype';
|
|
}
|
|
// Extract processor of the action
|
|
if (!isset($POST['processor'])) {
|
|
$action = explode(self::CLASS_ACTION_SEPARATOR, $POST['action']);
|
|
if (count($action) == 2) {
|
|
$POST['processor'] = $action[0];
|
|
$POST['action'] = $action[1];
|
|
} else {
|
|
$POST['processor'] = 'MassiveAction';
|
|
}
|
|
}
|
|
// Count number of items !
|
|
foreach ($POST['items'] as $itemtype => $ids) {
|
|
$this->nb_items += count($ids);
|
|
}
|
|
break;
|
|
|
|
case 'process' :
|
|
if (isset($POST['initial_items'])) {
|
|
$_SESSION['glpimassiveactionselected'] = $POST['initial_items'];
|
|
} else {
|
|
$_SESSION['glpimassiveactionselected'] = [];
|
|
}
|
|
|
|
$remove_from_post = ['items', 'action', 'action_name', 'processor',
|
|
'massiveaction', 'is_deleted', 'initial_items'];
|
|
|
|
$this->identifier = mt_rand();
|
|
$this->done = [];
|
|
$this->nb_done = 0;
|
|
$this->action_name = $POST['action_name'];
|
|
$this->results = ['ok' => 0,
|
|
'ko' => 0,
|
|
'noright' => 0,
|
|
'messages' => []];
|
|
foreach ($POST['items'] as $itemtype => $ids) {
|
|
$this->nb_items += count($ids);
|
|
}
|
|
if (isset($_SERVER['HTTP_REFERER'])) {
|
|
$this->redirect = $_SERVER['HTTP_REFERER'];
|
|
} else {
|
|
$this->redirect = $CFG_GLPI['root_doc']."/front/central.php";
|
|
}
|
|
// Don't display progress bars if delay is less than 1 second
|
|
$this->display_progress_bars = false;
|
|
break;
|
|
}
|
|
|
|
$this->POST = $POST;
|
|
foreach (['items', 'action', 'processor'] as $field) {
|
|
if (isset($this->POST[$field])) {
|
|
$this->$field = $this->POST[$field];
|
|
}
|
|
}
|
|
foreach ($remove_from_post as $field) {
|
|
if (isset($this->POST[$field])) {
|
|
unset($this->POST[$field]);
|
|
}
|
|
}
|
|
}
|
|
if ($this->nb_items == 0) {
|
|
throw new Exception(__('No selected items'));
|
|
}
|
|
|
|
} else {
|
|
if (($stage != 'process')
|
|
|| (!isset($_SESSION['current_massive_action'][$GET['identifier']]))) {
|
|
Toolbox::logError('Implementation error !');
|
|
throw new Exception(__('Implementation error !'));
|
|
}
|
|
$identifier = $GET['identifier'];
|
|
foreach ($_SESSION['current_massive_action'][$identifier] as $attribute => $value) {
|
|
$this->$attribute = $value;
|
|
}
|
|
if ($this->identifier != $identifier) {
|
|
$this->error = __('Invalid process');
|
|
return;
|
|
}
|
|
unset($_SESSION['current_massive_action'][$identifier]);
|
|
}
|
|
|
|
// Add process elements
|
|
if ($stage == 'process') {
|
|
|
|
if (!isset($this->remainings)) {
|
|
$this->remainings = $this->items;
|
|
}
|
|
|
|
$this->fields_to_remove_when_reload = ['fields_to_remove_when_reload'];
|
|
|
|
$this->timer = new Timer();
|
|
$this->timer->start();
|
|
$this->fields_to_remove_when_reload[] = 'timer';
|
|
|
|
$max_time = (get_cfg_var("max_execution_time") == 0) ? 60
|
|
: get_cfg_var("max_execution_time");
|
|
|
|
$this->timeout_delay = ($max_time - 3);
|
|
$this->fields_to_remove_when_reload[] = 'timeout_delay';
|
|
|
|
if (isset($_SESSION["MESSAGE_AFTER_REDIRECT"])) {
|
|
$this->messaget_after_redirect = $_SESSION["MESSAGE_AFTER_REDIRECT"];
|
|
unset($_SESSION["MESSAGE_AFTER_REDIRECT"]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the fields provided by previous stage through $_POST.
|
|
* Beware that the fields that are common (items, action ...) are not provided
|
|
*
|
|
* @return array of the elements
|
|
**/
|
|
function getInput() {
|
|
|
|
if (isset($this->POST)) {
|
|
return $this->POST;
|
|
}
|
|
return [];
|
|
}
|
|
|
|
|
|
/**
|
|
* Get current action
|
|
*
|
|
* @return a string with the current action or NULL if we are at initial stage
|
|
**/
|
|
function getAction() {
|
|
|
|
if (isset($this->action)) {
|
|
return $this->action;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get all items on which this action must work
|
|
*
|
|
* @return array of the items (empty if initial state)
|
|
**/
|
|
function getItems() {
|
|
|
|
if (isset($this->items)) {
|
|
return $this->items;
|
|
}
|
|
return [];
|
|
}
|
|
|
|
|
|
/**
|
|
* Get remaining items
|
|
*
|
|
* @return array of the remaining items (empty if not in process state)
|
|
**/
|
|
function getRemainings() {
|
|
|
|
if (isset($this->remainings)) {
|
|
return $this->remainings;
|
|
}
|
|
return [];
|
|
}
|
|
|
|
|
|
/**
|
|
* Destructor of the object
|
|
* It is used when reloading the page during process to store informations in $_SESSION.
|
|
**/
|
|
function __destruct() {
|
|
|
|
if (isset($this->identifier)) {
|
|
// $this->identifier is unset by self::process() when the massive actions are finished
|
|
foreach ($this->fields_to_remove_when_reload as $field) {
|
|
unset($this->$field);
|
|
}
|
|
$_SESSION['current_massive_action'][$this->identifier] = get_object_vars ($this);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* @param $POST
|
|
**/
|
|
function getCheckItem($POST) {
|
|
|
|
if (!isset($this->check_item)) {
|
|
if (isset($POST['check_itemtype'])) {
|
|
if (!($this->check_item = getItemForItemtype($POST['check_itemtype']))) {
|
|
exit();
|
|
}
|
|
if (isset($POST['check_items_id'])) {
|
|
if (!$this->check_item->getFromDB($POST['check_items_id'])) {
|
|
exit();
|
|
} else {
|
|
$this->check_item->getEmpty();
|
|
}
|
|
}
|
|
} else {
|
|
$this->check_item = null;
|
|
}
|
|
}
|
|
return $this->check_item;
|
|
}
|
|
|
|
|
|
/**
|
|
* Add hidden fields containing all the checked items to the current form
|
|
*
|
|
* @return void
|
|
**/
|
|
function addHiddenFields() {
|
|
|
|
if (empty($this->hidden_fields_defined)) {
|
|
$this->hidden_fields_defined = true;
|
|
|
|
$common_fields = ['action', 'processor', 'is_deleted', 'initial_items',
|
|
'item_itemtype', 'item_items_id', 'items', 'action_name'];
|
|
|
|
if (!empty($this->POST['massive_action_fields'])) {
|
|
$common_fields = array_merge($common_fields, $this->POST['massive_action_fields']);
|
|
}
|
|
|
|
foreach ($common_fields as $field) {
|
|
if (isset($this->POST[$field])) {
|
|
echo Html::hidden($field, ['value' => $this->POST[$field]]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Extract itemtype from the input (ie.: $input['itemtype'] is defined or $input['item'] only
|
|
* contains one type of item. If none is available and we can display selector (inside the modal
|
|
* window), then display a dropdown to select the itemtype.
|
|
* This is only usefull in case of itemtype specific massive actions (update, ...)
|
|
*
|
|
* @param boolean $display_selector can we display the itemtype selector ?
|
|
*
|
|
* @return string|boolean the itemtype or false if we cannot define it (and we cannot display the selector)
|
|
**/
|
|
function getItemtype($display_selector) {
|
|
|
|
if (isset($this->items) && is_array($this->items)) {
|
|
$keys = array_keys($this->items);
|
|
if (count($keys) == 1) {
|
|
return $keys[0];
|
|
}
|
|
|
|
if ($display_selector
|
|
&& (count($keys) > 1)) {
|
|
$itemtypes = [-1 => Dropdown::EMPTY_VALUE];
|
|
foreach ($keys as $itemtype) {
|
|
$itemtypes[$itemtype] = $itemtype::getTypeName(Session::getPluralNumber());
|
|
}
|
|
echo __('Select the type of the item on which applying this action')."<br>\n";
|
|
|
|
$rand = Dropdown::showFromArray('specialize_itemtype', $itemtypes);
|
|
echo "<br><br>";
|
|
|
|
$params = $this->POST;
|
|
$params['specialize_itemtype'] = '__VALUE__';
|
|
Ajax::updateItemOnSelectEvent("dropdown_specialize_itemtype$rand", "show_itemtype$rand",
|
|
$_SERVER['REQUEST_URI'], $params);
|
|
|
|
echo "<span id='show_itemtype$rand'> </span>\n";
|
|
exit();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get 'add to transfer list' action when needed
|
|
*
|
|
* @param $actions array
|
|
**/
|
|
static function getAddTransferList(array &$actions) {
|
|
|
|
if (Session::haveRight('transfer', READ)
|
|
&& Session::isMultiEntitiesMode()) {
|
|
$actions[__CLASS__.self::CLASS_ACTION_SEPARATOR.'add_transfer_list']
|
|
= "<i class='ma-icon fas fa-level-up-alt'></i>".
|
|
_x('button', 'Add to transfer list');
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the standard massive actions
|
|
*
|
|
* @param string|CommonDBTM $item the item for which we want the massive actions
|
|
* @param boolean $is_deleted massive action for deleted items ? (default 0)
|
|
* @param CommonDBTM $checkitem link item to check right (default NULL)
|
|
* @param integer|boolean $single Get actions for a single item
|
|
*
|
|
* @return array of massive actions or false if $item is not valid
|
|
**/
|
|
static function getAllMassiveActions($item, $is_deleted = 0, CommonDBTM $checkitem = null, $single = false) {
|
|
global $PLUGIN_HOOKS;
|
|
|
|
if (is_string($item)) {
|
|
$itemtype = $item;
|
|
if (!($item = getItemForItemtype($itemtype))) {
|
|
return false;
|
|
}
|
|
} else if ($item instanceof CommonDBTM) {
|
|
$itemtype = $item->getType();
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
if (!is_null($checkitem)) {
|
|
$canupdate = $checkitem->canUpdate();
|
|
$candelete = $checkitem->canDelete();
|
|
$canpurge = $checkitem->canPurge();
|
|
} else {
|
|
$canupdate = $itemtype::canUpdate();
|
|
$candelete = $itemtype::canDelete();
|
|
$canpurge = $itemtype::canPurge();
|
|
}
|
|
|
|
$actions = [];
|
|
$self_pref = __CLASS__.self::CLASS_ACTION_SEPARATOR;
|
|
|
|
if ($is_deleted) {
|
|
if ($canpurge) {
|
|
if (in_array($itemtype, Item_Devices::getConcernedItems())) {
|
|
$actions[$self_pref.'purge_item_but_devices']
|
|
= _x('button', 'Delete permanently but keep devices');
|
|
$actions[$self_pref.'purge'] = _x('button', 'Delete permanently and remove devices');
|
|
} else {
|
|
$actions[$self_pref.'purge'] = _x('button', 'Delete permanently');
|
|
}
|
|
}
|
|
if ($candelete) {
|
|
$actions[$self_pref.'restore'] = _x('button', 'Restore');
|
|
}
|
|
} else {
|
|
if (Session::getCurrentInterface() == 'central'
|
|
&& ($canupdate
|
|
|| (Infocom::canApplyOn($itemtype)
|
|
&& Infocom::canUpdate()))) {
|
|
|
|
//TRANS: select action 'update' (before doing it)
|
|
$actions[$self_pref.'update'] = _x('button', 'Update');
|
|
|
|
$actions[$self_pref.'clone'] = _x('button', 'Clone');
|
|
}
|
|
|
|
Infocom::getMassiveActionsForItemtype($actions, $itemtype, $is_deleted, $checkitem);
|
|
|
|
CommonDBConnexity::getMassiveActionsForItemtype($actions, $itemtype, $is_deleted,
|
|
$checkitem);
|
|
|
|
// do not take into account is_deleted if items may be dynamic
|
|
if ($item->maybeDeleted()
|
|
&& !$item->useDeletedToLockIfDynamic()) {
|
|
if ($candelete) {
|
|
$actions[$self_pref.'delete'] = _x('button', 'Put in trashbin');
|
|
}
|
|
} else if ($canpurge) {
|
|
if ($item instanceof CommonDBRelation) {
|
|
$actions[$self_pref.'purge'] = _x('button', 'Delete permanently the relation with selected elements');
|
|
} else {
|
|
$actions[$self_pref.'purge'] = _x('button', 'Delete permanently');
|
|
}
|
|
if ($item instanceof CommonDropdown) {
|
|
$actions[$self_pref.'purge_but_item_linked']
|
|
= _x('button', 'Delete permanently even if linked items');
|
|
}
|
|
}
|
|
|
|
// Specific actions
|
|
$actions += $item->getSpecificMassiveActions($checkitem);
|
|
|
|
Document::getMassiveActionsForItemtype($actions, $itemtype, $is_deleted, $checkitem);
|
|
Contract::getMassiveActionsForItemtype($actions, $itemtype, $is_deleted, $checkitem);
|
|
|
|
// Amend comment for objects with a 'comment' field
|
|
$item->getEmpty();
|
|
if ($canupdate && isset($item->fields['comment'])) {
|
|
$actions[$self_pref.'amend_comment'] = __("Amend comment");
|
|
}
|
|
|
|
// Add a note for objects with the UPDATENOTE rights
|
|
if (Session::haveRight($item::$rightname, UPDATENOTE)) {
|
|
$actions[$self_pref.'add_note'] = __("Add note");
|
|
}
|
|
|
|
// Plugin Specific actions
|
|
if (isset($PLUGIN_HOOKS['use_massive_action'])) {
|
|
foreach (array_keys($PLUGIN_HOOKS['use_massive_action']) as $plugin) {
|
|
if (!Plugin::isPluginActive($plugin)) {
|
|
continue;
|
|
}
|
|
$plug_actions = Plugin::doOneHook($plugin, 'MassiveActions', $itemtype);
|
|
|
|
if (is_array($plug_actions) && count($plug_actions)) {
|
|
$actions += $plug_actions;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Lock::getMassiveActionsForItemtype($actions, $itemtype, $is_deleted, $checkitem);
|
|
|
|
// Manage forbidden actions : try complete action name or MassiveAction:action_name
|
|
$forbidden_actions = $item->getForbiddenStandardMassiveAction();
|
|
if (false !== $single) {
|
|
$item->getFromDB($single);
|
|
$forbidden_actions = array_merge(
|
|
$forbidden_actions,
|
|
$item->getForbiddenSingleMassiveActions()
|
|
);
|
|
}
|
|
$whitedlisted_actions = $item->getWhitelistedSingleMassiveActions();
|
|
|
|
if (is_array($forbidden_actions) && count($forbidden_actions)) {
|
|
foreach ($forbidden_actions as $actiontodel) {
|
|
if (isset($actions[$actiontodel])) {
|
|
unset($actions[$actiontodel]);
|
|
} else {
|
|
if (Toolbox::startsWith($actiontodel, '*:')) {
|
|
foreach (array_keys($actions) as $action) {
|
|
if (preg_match('/[^:]+:' . str_replace('*:', '', $actiontodel . '/'), $action)
|
|
&& !in_array($action, $whitedlisted_actions)
|
|
) {
|
|
unset($actions[$action]);
|
|
}
|
|
}
|
|
}
|
|
if (Toolbox::endsWith($actiontodel, ':*')) {
|
|
foreach (array_keys($actions) as $action) {
|
|
if (preg_match('/' . str_replace(':*', '', $actiontodel . ':.+/'), $action)
|
|
&& !in_array($action, $whitedlisted_actions)
|
|
) {
|
|
unset($actions[$action]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Not found search adding MassiveAction prefix
|
|
$actiontodel = $self_pref.$actiontodel;
|
|
if (isset($actions[$actiontodel])) {
|
|
unset($actions[$actiontodel]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return $actions;
|
|
}
|
|
|
|
|
|
/**
|
|
* Main entry of the modal window for massive actions
|
|
*
|
|
* @return void
|
|
**/
|
|
function showSubForm() {
|
|
$processor = $this->processor;
|
|
|
|
if (!$processor::showMassiveActionsSubForm($this)) {
|
|
$this->showDefaultSubForm();
|
|
}
|
|
|
|
$this->addHiddenFields();
|
|
}
|
|
|
|
|
|
/**
|
|
* Class-specific method used to show the fields to specify the massive action
|
|
*
|
|
* @return void
|
|
**/
|
|
function showDefaultSubForm() {
|
|
echo Html::submit(_x('button', 'Post'), ['name' => 'massiveaction']);
|
|
}
|
|
|
|
|
|
static function showMassiveActionsSubForm(MassiveAction $ma) {
|
|
global $CFG_GLPI;
|
|
|
|
switch ($ma->getAction()) {
|
|
case 'update':
|
|
if (!isset($ma->POST['id_field'])) {
|
|
$itemtypes = array_keys($ma->items);
|
|
$options_per_type = [];
|
|
$options_counts = [];
|
|
foreach ($itemtypes as $itemtype) {
|
|
$options_per_type[$itemtype] = [];
|
|
$group = '';
|
|
$show_all = true;
|
|
$show_infocoms = true;
|
|
$itemtable = getTableForItemType($itemtype);
|
|
|
|
if (Infocom::canApplyOn($itemtype)
|
|
&& (!$itemtype::canUpdate()
|
|
|| !Infocom::canUpdate())) {
|
|
$show_all = false;
|
|
$show_infocoms = Infocom::canUpdate();
|
|
}
|
|
foreach (Search::getCleanedOptions($itemtype, UPDATE) as $index => $option) {
|
|
|
|
if (!is_array($option) || count($option) == 1) {
|
|
$group = !is_array($option) ? $option : $option['name'];
|
|
$options_per_type[$itemtype][$group] = [];
|
|
} else {
|
|
if (($option['field'] != 'id')
|
|
&& ($index != 1)
|
|
// Permit entities_id is explicitly activate
|
|
&& (($option["linkfield"] != 'entities_id')
|
|
|| (isset($option['massiveaction']) && $option['massiveaction']))) {
|
|
|
|
if (!isset($option['massiveaction']) || $option['massiveaction']) {
|
|
if (($show_all)
|
|
|| (($show_infocoms
|
|
&& Search::isInfocomOption($itemtype, $index))
|
|
|| (!$show_infocoms
|
|
&& !Search::isInfocomOption($itemtype, $index)))) {
|
|
$options_per_type[$itemtype][$group][$itemtype.':'.$index]
|
|
= $option['name'];
|
|
if ($itemtable == $option['table']) {
|
|
$field_key = 'MAIN:'.$option['field'].':'.$index;
|
|
} else {
|
|
$field_key = $option['table'].':'.$option['field'].':'.$index;
|
|
}
|
|
if (!isset($options_count[$field_key])) {
|
|
$options_count[$field_key] = [];
|
|
}
|
|
$options_count[$field_key][] = $itemtype.':'.$index.':'.$group;
|
|
if (isset($option['MA_common_field'])) {
|
|
if (!isset($options_count[$option['MA_common_field']])) {
|
|
$options_count[$option['MA_common_field']] = [];
|
|
}
|
|
$options_count[$option['MA_common_field']][]
|
|
= $itemtype.':'.$index.':'.$group;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (count($itemtypes) > 1) {
|
|
$common_options = [];
|
|
foreach ($options_count as $field => $users) {
|
|
if (count($users) > 1) {
|
|
$labels = [];
|
|
foreach ($users as $user) {
|
|
$user = explode(':', $user);
|
|
$itemtype = $user[0];
|
|
$index = $itemtype.':'.$user[1];
|
|
$group = implode(':', array_slice($user, 2));
|
|
if (isset($options_per_type[$itemtype][$group][$index])) {
|
|
if (!in_array($options_per_type[$itemtype][$group][$index],
|
|
$labels)) {
|
|
$labels[] = $options_per_type[$itemtype][$group][$index];
|
|
}
|
|
}
|
|
$common_options[$field][] = $index;
|
|
}
|
|
$options[$group][$field] = implode('/', $labels);
|
|
}
|
|
}
|
|
$choose_itemtype = true;
|
|
$itemtype_choices = [-1 => Dropdown::EMPTY_VALUE];
|
|
foreach ($itemtypes as $itemtype) {
|
|
$itemtype_choices[$itemtype] = $itemtype::getTypeName(Session::getPluralNumber());
|
|
}
|
|
} else {
|
|
$options = $options_per_type[$itemtypes[0]];
|
|
$common_options = false;
|
|
$choose_itemtype = false;
|
|
}
|
|
$choose_field = (count($options) >= 1);
|
|
|
|
// Beware: "class='tab_cadre_fixe'" induce side effects ...
|
|
echo "<table width='100%'><tr>";
|
|
|
|
$colspan = 0;
|
|
if ($choose_field) {
|
|
$colspan ++;
|
|
echo "<td>";
|
|
if ($common_options) {
|
|
echo __('Select the common field that you want to update');
|
|
} else {
|
|
echo __('Select the field that you want to update');
|
|
}
|
|
echo "</td>";
|
|
if ($choose_itemtype) {
|
|
$colspan ++;
|
|
echo "<td rowspan='2'>".__('or')."</td>";
|
|
}
|
|
}
|
|
|
|
if ($choose_itemtype) {
|
|
$colspan ++;
|
|
echo "<td>".__('Select the type of the item on which applying this action')."</td>";
|
|
}
|
|
|
|
echo "</tr><tr>";
|
|
if ($choose_field) {
|
|
echo "<td>";
|
|
$field_rand = Dropdown::showFromArray('id_field', $options,
|
|
['display_emptychoice' => true]);
|
|
echo "</td>";
|
|
}
|
|
if ($choose_itemtype) {
|
|
echo "<td>";
|
|
$itemtype_rand = Dropdown::showFromArray('specialize_itemtype',
|
|
$itemtype_choices);
|
|
echo "</td>";
|
|
}
|
|
|
|
$next_step_rand = mt_rand();
|
|
|
|
echo "</tr></table>";
|
|
echo "<span id='update_next_step$next_step_rand'> </span>";
|
|
|
|
if ($choose_field) {
|
|
$params = $ma->POST;
|
|
$params['id_field'] = '__VALUE__';
|
|
$params['common_options'] = $common_options;
|
|
Ajax::updateItemOnSelectEvent("dropdown_id_field$field_rand",
|
|
"update_next_step$next_step_rand",
|
|
$_SERVER['REQUEST_URI'], $params);
|
|
}
|
|
|
|
if ($choose_itemtype) {
|
|
$params = $ma->POST;
|
|
$params['specialize_itemtype'] = '__VALUE__';
|
|
$params['common_options'] = $common_options;
|
|
Ajax::updateItemOnSelectEvent("dropdown_specialize_itemtype$itemtype_rand",
|
|
"update_next_step$next_step_rand",
|
|
$_SERVER['REQUEST_URI'], $params);
|
|
}
|
|
// Only display the form for this stage
|
|
exit();
|
|
|
|
}
|
|
|
|
if (!isset($ma->POST['common_options'])) {
|
|
echo "<div class='center'><img src='".$CFG_GLPI["root_doc"]."/pics/warning.png' alt='".
|
|
__s('Warning')."'><br><br>";
|
|
echo "<span class='b'>".__('Implementation error !')."</span><br>";
|
|
echo "</div>";
|
|
exit();
|
|
}
|
|
|
|
if ($ma->POST['common_options'] == 'false') {
|
|
$search_options = [$ma->POST['id_field']];
|
|
} else if (isset($ma->POST['common_options'][$ma->POST['id_field']])) {
|
|
$search_options = $ma->POST['common_options'][$ma->POST['id_field']];
|
|
} else {
|
|
$search_options = [];
|
|
}
|
|
|
|
$items = [];
|
|
foreach ($search_options as $search_option) {
|
|
$search_option = explode(':', $search_option);
|
|
$itemtype = $search_option[0];
|
|
$index = $search_option[1];
|
|
|
|
if (!$item = getItemForItemtype($itemtype)) {
|
|
continue;
|
|
}
|
|
|
|
if (Infocom::canApplyOn($itemtype)) {
|
|
Session::checkSeveralRightsOr([$itemtype => UPDATE,
|
|
"infocom" => UPDATE]);
|
|
} else {
|
|
$item->checkGlobal(UPDATE);
|
|
}
|
|
|
|
$search = Search::getOptions($itemtype);
|
|
if (!isset($search[$index])) {
|
|
exit();
|
|
}
|
|
$item->search = $search[$index];
|
|
|
|
$items[] = $item;
|
|
}
|
|
|
|
if (count($items) == 0) {
|
|
exit();
|
|
}
|
|
|
|
// TODO: ensure that all items are equivalent ...
|
|
$item = $items[0];
|
|
$search = $item->search;
|
|
|
|
$plugdisplay = false;
|
|
if (($plug = isPluginItemType($item->getType()))
|
|
// Specific for plugin which add link to core object
|
|
|| ($plug = isPluginItemType(getItemTypeForTable($item->search['table'])))) {
|
|
$plugdisplay = Plugin::doOneHook($plug['plugin'], 'MassiveActionsFieldsDisplay',
|
|
['itemtype' => $item->getType(),
|
|
'options' => $item->search]);
|
|
}
|
|
|
|
if (empty($search["linkfield"])
|
|
||($search['table'] == 'glpi_infocoms')) {
|
|
$fieldname = $search["field"];
|
|
} else {
|
|
$fieldname = $search["linkfield"];
|
|
}
|
|
|
|
if (!$plugdisplay) {
|
|
$options = [];
|
|
$values = [];
|
|
// For ticket template or aditional options of massive actions
|
|
if (isset($ma->POST['options'])) {
|
|
$options = $ma->POST['options'];
|
|
}
|
|
if (isset($ma->POST['additionalvalues'])) {
|
|
$values = $ma->POST['additionalvalues'];
|
|
}
|
|
$values[$search["field"]] = '';
|
|
echo $item->getValueToSelect($search, $fieldname, $values, $options);
|
|
}
|
|
|
|
$items_index = [];
|
|
foreach ($search_options as $search_option) {
|
|
$search_option = explode(':', $search_option);
|
|
$items_index[$search_option[0]] = $search_option[1];
|
|
}
|
|
echo Html::hidden('search_options', ['value' => $items_index]);
|
|
echo Html::hidden('field', ['value' => $fieldname]);
|
|
echo "<br>\n";
|
|
|
|
$submitname = _sx('button', 'Post');
|
|
if (isset($ma->POST['submitname']) && $ma->POST['submitname']) {
|
|
$submitname= stripslashes($ma->POST['submitname']);
|
|
}
|
|
echo Html::submit($submitname, ['name' => 'massiveaction']);
|
|
|
|
return true;
|
|
|
|
case 'clone':
|
|
$rand = mt_rand();
|
|
|
|
echo "<table width='100%'><tr>";
|
|
echo "<td>";
|
|
echo __('How many copies do you want to create ?');
|
|
echo "</td><tr>";
|
|
echo "<td>".Html::input("nb_copy", ['id' => "nb_copy$rand", 'value' => 0]);
|
|
echo "</td>";
|
|
echo "</tr></table>";
|
|
|
|
echo "<br>\n";
|
|
|
|
$submitname = _sx('button', 'Post');
|
|
if (isset($ma->POST['submitname']) && $ma->POST['submitname']) {
|
|
$submitname= stripslashes($ma->POST['submitname']);
|
|
}
|
|
echo Html::submit($submitname, ['name' => 'massiveaction']);
|
|
|
|
return true;
|
|
|
|
case 'add_transfer_list':
|
|
echo _n("Are you sure you want to add this item to transfer list?",
|
|
"Are you sure you want to add these items to transfer list?",
|
|
count($ma->items, COUNT_RECURSIVE) - count($ma->items));
|
|
echo "<br><br>";
|
|
echo Html::submit(_x('button', 'Add'), ['name' => 'massiveaction']);
|
|
|
|
return true;
|
|
|
|
case 'amend_comment':
|
|
echo __("Amendment to insert");
|
|
echo ("<br><br>");
|
|
Html::textarea([
|
|
'name' => 'amendment'
|
|
]);
|
|
echo ("<br><br>");
|
|
echo Html::submit(__('Update'), [
|
|
'name' => 'massiveaction'
|
|
]);
|
|
|
|
return true;
|
|
|
|
case 'add_note':
|
|
echo __("New Note");
|
|
echo ("<br><br>");
|
|
Html::textarea([
|
|
'name' => 'add_note'
|
|
]);
|
|
echo ("<br><br>");
|
|
echo Html::submit(_sx('button', 'Add'), [
|
|
'name' => 'massiveaction'
|
|
]);
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Update the progress bar
|
|
*
|
|
* Display and update the progress bar. If the delay is more than 1 second, then activate it
|
|
*
|
|
* @return void
|
|
**/
|
|
function updateProgressBars() {
|
|
|
|
if ($this->timer->getTime() > 1) {
|
|
// If the action's delay is more than one second, the display progress bars
|
|
$this->display_progress_bars = true;
|
|
}
|
|
|
|
if ($this->display_progress_bars) {
|
|
if (!isset($this->progress_bar_displayed)) {
|
|
Html::progressBar('main_'.$this->identifier, ['create' => true,
|
|
'message' => $this->action_name]);
|
|
$this->progress_bar_displayed = true;
|
|
$this->fields_to_remove_when_reload[] = 'progress_bar_displayed';
|
|
if (count($this->items) > 1) {
|
|
Html::progressBar('itemtype_'.$this->identifier, ['create' => true]);
|
|
}
|
|
}
|
|
$percent = 100 * $this->nb_done / $this->nb_items;
|
|
Html::progressBar('main_'.$this->identifier, ['percent' => $percent]);
|
|
if ((count($this->items) > 1) && isset($this->current_itemtype)) {
|
|
$itemtype = $this->current_itemtype;
|
|
if (isset($this->items[$itemtype])) {
|
|
if (isset($this->done[$itemtype])) {
|
|
$nb_done = count($this->done[$itemtype]);
|
|
} else {
|
|
$nb_done = 0;
|
|
}
|
|
$percent = 100 * $nb_done / count($this->items[$itemtype]);
|
|
Html::progressBar('itemtype_'.$this->identifier,
|
|
['message' => $itemtype::getTypeName(Session::getPluralNumber()),
|
|
'percent' => $percent]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Process the massive actions for all passed items. This a switch between different methods:
|
|
* new system, old one and plugins ...
|
|
*
|
|
* @return array of results (ok, ko, noright counts, redirect ...)
|
|
**/
|
|
function process() {
|
|
|
|
if (!empty($this->remainings)) {
|
|
|
|
$this->updateProgressBars();
|
|
|
|
if (isset($this->messaget_after_redirect)) {
|
|
$_SESSION["MESSAGE_AFTER_REDIRECT"] = $this->messaget_after_redirect;
|
|
Html::displayMessageAfterRedirect();
|
|
unset($this->messaget_after_redirect);
|
|
}
|
|
|
|
$processor = $this->processor;
|
|
|
|
$this->processForSeveralItemtypes();
|
|
}
|
|
|
|
$this->results['redirect'] = $this->redirect;
|
|
|
|
// unset $this->identifier to ensure the action won't register in $_SESSION
|
|
unset($this->identifier);
|
|
|
|
return $this->results;
|
|
}
|
|
|
|
|
|
/**
|
|
* Process the specific massive actions for severl itemtypes
|
|
* @return array of the results for the actions
|
|
**/
|
|
function processForSeveralItemtypes() {
|
|
|
|
$processor = $this->processor;
|
|
foreach ($this->remainings as $itemtype => $ids) {
|
|
if ($item = getItemForItemtype($itemtype)) {
|
|
$processor::processMassiveActionsForOneItemtype($this, $item, $ids);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static function processMassiveActionsForOneItemtype(MassiveAction $ma, CommonDBTM $item,
|
|
array $ids) {
|
|
global $CFG_GLPI;
|
|
|
|
$action = $ma->getAction();
|
|
|
|
switch ($action) {
|
|
case 'delete' :
|
|
foreach ($ids as $id) {
|
|
if ($item->can($id, DELETE)) {
|
|
if ($item->delete(["id" => $id])) {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK);
|
|
} else {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION));
|
|
}
|
|
} else {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_NORIGHT);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_RIGHT));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'restore' :
|
|
foreach ($ids as $id) {
|
|
if ($item->can($id, DELETE)) {
|
|
if ($item->restore(["id" => $id])) {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK);
|
|
} else {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION));
|
|
}
|
|
} else {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_NORIGHT);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_RIGHT));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'purge_item_but_devices' :
|
|
case 'purge_but_item_linked' :
|
|
case 'purge' :
|
|
foreach ($ids as $id) {
|
|
if ($item->can($id, PURGE)) {
|
|
$force = 1;
|
|
// Only mark deletion for
|
|
if ($item->maybeDeleted()
|
|
&& $item->useDeletedToLockIfDynamic()
|
|
&& $item->isDynamic()) {
|
|
$force = 0;
|
|
}
|
|
$delete_array = ['id' => $id];
|
|
if ($action == 'purge_item_but_devices') {
|
|
$delete_array['keep_devices'] = true;
|
|
}
|
|
|
|
if ($item instanceof CommonDropdown) {
|
|
if ($item->haveChildren()) {
|
|
if ($action != 'purge_but_item_linked') {
|
|
$force = 0;
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
|
|
$ma->addMessage(__("You can't delete that item by massive actions, because it has sub-items"));
|
|
$ma->addMessage(__("but you can do it by the form of the item"));
|
|
continue;
|
|
}
|
|
}
|
|
if ($item->isUsed()) {
|
|
if ($action != 'purge_but_item_linked') {
|
|
$force = 0;
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
|
|
$ma->addMessage(__("You can't delete that item, because it is used for one or more items"));
|
|
$ma->addMessage(__("but you can do it by the form of the item"));
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if ($item->delete($delete_array, $force)) {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK);
|
|
} else {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION));
|
|
}
|
|
} else {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_NORIGHT);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_RIGHT));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'update' :
|
|
if ((!isset($ma->POST['search_options']))
|
|
|| (!isset($ma->POST['search_options'][$item->getType()]))) {
|
|
return false;
|
|
}
|
|
$index = $ma->POST['search_options'][$item->getType()];
|
|
$searchopt = Search::getCleanedOptions($item->getType(), UPDATE);
|
|
$input = $ma->POST;
|
|
if (isset($searchopt[$index])) {
|
|
/// Infocoms case
|
|
if (Search::isInfocomOption($item->getType(), $index)) {
|
|
|
|
$ic = new Infocom();
|
|
$link_entity_type = -1;
|
|
/// Specific entity item
|
|
if ($searchopt[$index]["table"] == "glpi_suppliers") {
|
|
$ent = new Supplier();
|
|
if ($ent->getFromDB($input[$input["field"]])) {
|
|
$link_entity_type = $ent->fields["entities_id"];
|
|
}
|
|
}
|
|
foreach ($ids as $key) {
|
|
if ($item->getFromDB($key)) {
|
|
if (($link_entity_type < 0)
|
|
|| ($link_entity_type == $item->getEntityID())
|
|
|| ($ent->fields["is_recursive"]
|
|
&& in_array($link_entity_type,
|
|
getAncestorsOf("glpi_entities",
|
|
$item->getEntityID())))) {
|
|
$input2 = [
|
|
'items_id' => $key,
|
|
'itemtype' => $item->getType()
|
|
];
|
|
|
|
if ($ic->can(-1, CREATE, $input2)) {
|
|
// Add infocom if not exists
|
|
if (!$ic->getFromDBforDevice($item->getType(), $key)) {
|
|
$input2["items_id"] = $key;
|
|
$input2["itemtype"] = $item->getType();
|
|
unset($ic->fields);
|
|
$ic->add($input2);
|
|
$ic->getFromDBforDevice($item->getType(), $key);
|
|
}
|
|
$id = $ic->fields["id"];
|
|
unset($ic->fields);
|
|
if ($ic->update(['id' => $id,
|
|
$input["field"] => $input[$input["field"]]])) {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_OK);
|
|
} else {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION));
|
|
}
|
|
} else {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_NORIGHT);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_RIGHT));
|
|
}
|
|
} else {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_COMPAT));
|
|
}
|
|
} else {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_NOT_FOUND));
|
|
}
|
|
}
|
|
|
|
} else { /// Not infocoms
|
|
$link_entity_type = [];
|
|
/// Specific entity item
|
|
$itemtable = getTableForItemType($item->getType());
|
|
$itemtype2 = getItemTypeForTable($searchopt[$index]["table"]);
|
|
if ($item2 = getItemForItemtype($itemtype2)) {
|
|
if (($index != 80) // No entities_id fields
|
|
&& ($searchopt[$index]["table"] != $itemtable)
|
|
&& $item2->isEntityAssign()
|
|
&& $item->isEntityAssign()) {
|
|
if ($item2->getFromDB($input[$input["field"]])) {
|
|
if (isset($item2->fields["entities_id"])
|
|
&& ($item2->fields["entities_id"] >= 0)) {
|
|
|
|
if (isset($item2->fields["is_recursive"])
|
|
&& $item2->fields["is_recursive"]) {
|
|
$link_entity_type = getSonsOf("glpi_entities",
|
|
$item2->fields["entities_id"]);
|
|
} else {
|
|
$link_entity_type[] = $item2->fields["entities_id"];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
foreach ($ids as $key) {
|
|
if ($item->canEdit($key)
|
|
&& $item->canMassiveAction($action, $input['field'],
|
|
$input[$input["field"]])) {
|
|
if ((count($link_entity_type) == 0)
|
|
|| in_array($item->fields["entities_id"], $link_entity_type)) {
|
|
if ($item->update(['id' => $key,
|
|
$input["field"] => $input[$input["field"]]])) {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_OK);
|
|
} else {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION));
|
|
}
|
|
} else {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_COMPAT));
|
|
}
|
|
} else {
|
|
$ma->itemDone($item->getType(), $key, MassiveAction::ACTION_NORIGHT);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_RIGHT));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'clone':
|
|
$input = $ma->POST;
|
|
foreach ($ids as $id) {
|
|
// check rights
|
|
if ($item->can($id, CREATE)) {
|
|
// recovers the item from DB
|
|
if ($item->getFromDB($id)) {
|
|
$succeed = true;
|
|
// clone in a loop
|
|
for ($i = 0; $i < $input["nb_copy"] && $succeed; $i++) {
|
|
if ($item->clone() === false) {
|
|
$succeed = false;
|
|
}
|
|
}
|
|
if ($succeed) {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK);
|
|
} else {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION));
|
|
}
|
|
}
|
|
} else {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_NORIGHT);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_RIGHT));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'add_transfer_list' :
|
|
$itemtype = $item->getType();
|
|
if (!isset($_SESSION['glpitransfer_list'])) {
|
|
$_SESSION['glpitransfer_list'] = [];
|
|
}
|
|
if (!isset($_SESSION['glpitransfer_list'][$itemtype])) {
|
|
$_SESSION['glpitransfer_list'][$itemtype] = [];
|
|
}
|
|
foreach ($ids as $id) {
|
|
$_SESSION['glpitransfer_list'][$itemtype][$id] = $id;
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK);
|
|
}
|
|
$ma->setRedirect($CFG_GLPI['root_doc'].'/front/transfer.action.php');
|
|
break;
|
|
|
|
case 'amend_comment':
|
|
$item->getEmpty();
|
|
|
|
// Check the itemtype is a valid target
|
|
if (!array_key_exists('comment', $item->fields)) {
|
|
$ma->addMessage($item->getErrorMessage(ERROR_COMPAT));
|
|
break;
|
|
}
|
|
|
|
// Load input
|
|
$input = $ma->getInput();
|
|
$amendment = $input['amendment'];
|
|
|
|
foreach ($ids as $id) {
|
|
$item->getFromDB($id);
|
|
|
|
// Check rights
|
|
if (!$item->canUpdateItem()) {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_RIGHT));
|
|
continue;
|
|
}
|
|
|
|
$comment = $item->fields['comment'];
|
|
|
|
if (is_null($comment) || $comment == "") {
|
|
// If the comment was empty, use directly the amendment
|
|
$comment = $amendment;
|
|
} else {
|
|
// If there is already a comment, insert some padding then
|
|
// the amendment
|
|
$comment .= "\n\n$amendment";
|
|
}
|
|
|
|
// Update the comment
|
|
$success = $item->update([
|
|
'id' => $id,
|
|
'comment' => $comment
|
|
]);
|
|
|
|
if (!$success) {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION));
|
|
} else {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK);
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case 'add_note':
|
|
// Check rights
|
|
if (!Session::haveRight($item::$rightname, UPDATENOTE)) {
|
|
$ma->addMessage($item->getErrorMessage(ERROR_RIGHT));
|
|
break;
|
|
}
|
|
|
|
// Load input
|
|
$input = $ma->getInput();
|
|
$content = $input['add_note'];
|
|
|
|
$em = new Notepad();
|
|
|
|
foreach ($ids as $id) {
|
|
$success = $em->add([
|
|
'itemtype' => $item::getType(),
|
|
'items_id' => $id,
|
|
'content' => $content,
|
|
'users_id' => Session::getLoginUserID(),
|
|
'users_id_lastupdater' => Session::getLoginUserID(),
|
|
]);
|
|
|
|
if (!$success) {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
|
|
$ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION));
|
|
} else {
|
|
$ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the page to redirect for specific actions. By default, call previous page.
|
|
* This should be call once for the given action.
|
|
*
|
|
* @param $redirect link to the page
|
|
*
|
|
* @return void
|
|
**/
|
|
function setRedirect($redirect) {
|
|
$this->redirect = $redirect;
|
|
}
|
|
|
|
|
|
/**
|
|
* add a message to display when action is done.
|
|
*
|
|
* @param string $message the message to add
|
|
*
|
|
* @return void
|
|
**/
|
|
function addMessage($message) {
|
|
$this->results['messages'][] = $message;
|
|
}
|
|
|
|
|
|
/**
|
|
* Set an item as done. If the delay is too long, then reload the page to continue the action.
|
|
* Update the progress if necessary.
|
|
*
|
|
* @param string $itemtype the type of the item that has been done
|
|
* @param integer $id id or array of ids of the item(s) that have been done.
|
|
* @param integer $result
|
|
* self::NO_ACTION in case of no specific action (used internally for older actions)
|
|
* MassiveAction::ACTION_OK everything is OK for the action
|
|
* MassiveAction::ACTION_KO something went wrong for the action
|
|
* MassiveAction::ACTION_NORIGHT not anough right for the action
|
|
**/
|
|
function itemDone($itemtype, $id, $result) {
|
|
|
|
$this->current_itemtype = $itemtype;
|
|
|
|
if (!isset($this->done[$itemtype])) {
|
|
$this->done[$itemtype] = [];
|
|
}
|
|
|
|
if (is_array($id)) {
|
|
$number = count($id);
|
|
foreach ($id as $single) {
|
|
unset($this->remainings[$itemtype][$single]);
|
|
$this->done[$itemtype][] = $single;
|
|
}
|
|
} else {
|
|
unset($this->remainings[$itemtype][$id]);
|
|
$this->done[$itemtype][] = $id;
|
|
$number = 1;
|
|
}
|
|
if (count($this->remainings[$itemtype]) == 0) {
|
|
unset($this->remainings[$itemtype]);
|
|
}
|
|
|
|
switch ($result) {
|
|
case MassiveAction::ACTION_OK :
|
|
$this->results['ok'] += $number;
|
|
break;
|
|
|
|
case MassiveAction::ACTION_KO :
|
|
$this->results['ko'] += $number;
|
|
break;
|
|
|
|
case MassiveAction::ACTION_NORIGHT :
|
|
$this->results['noright'] += $number;
|
|
break;
|
|
}
|
|
$this->nb_done += $number;
|
|
|
|
// If delay is to big, then reload !
|
|
if ($this->timer->getTime() > $this->timeout_delay) {
|
|
Html::redirect($_SERVER['PHP_SELF'].'?identifier='.$this->identifier);
|
|
}
|
|
|
|
$this->updateProgressBars();
|
|
}
|
|
}
|