. * --------------------------------------------------------------------- */ if (!defined('GLPI_ROOT')) { die("Sorry. You can't access this file directly"); } /** * CommonITILValidation Class * * @since 0.85 **/ abstract class CommonITILValidation extends CommonDBChild { // From CommonDBTM public $auto_message_on_action = false; static public $log_history_add = Log::HISTORY_LOG_SIMPLE_MESSAGE; static public $log_history_update = Log::HISTORY_LOG_SIMPLE_MESSAGE; static public $log_history_delete = Log::HISTORY_LOG_SIMPLE_MESSAGE; const VALIDATE = 1024; // STATUS const NONE = 1; // none const WAITING = 2; // waiting const ACCEPTED = 3; // accepted const REFUSED = 4; // rejected function getItilObjectItemType() { return str_replace('Validation', '', $this->getType()); } static function getCreateRights() { return [CREATE]; } static function getPurgeRights() { return [PURGE]; } static function getValidateRights() { return [static::VALIDATE]; } function getForbiddenStandardMassiveAction() { $forbidden = parent::getForbiddenStandardMassiveAction(); $forbidden[] = 'update'; return $forbidden; } static function getTypeName($nb = 0) { return _n('Approval', 'Approvals', $nb); } static function canCreate() { return Session::haveRightsOr(static::$rightname, static::getCreateRights()); } /** * Is the current user have right to delete the current validation ? * * @return boolean **/ function canCreateItem() { if (($this->fields["users_id"] == Session::getLoginUserID()) || Session::haveRightsOr(static::$rightname, static::getCreateRights())) { return true; } return false; } static function canView() { return Session::haveRightsOr(static::$rightname, array_merge(static::getCreateRights(), static::getValidateRights(), static::getPurgeRights())); } static function canUpdate() { return Session::haveRightsOr(static::$rightname, array_merge(static::getCreateRights(), static::getValidateRights())); } /** * Is the current user have right to delete the current validation ? * * @return boolean **/ function canDeleteItem() { if (($this->fields["users_id"] == Session::getLoginUserID()) || Session::haveRight(static::$rightname, DELETE)) { return true; } return false; } /** * Is the current user have right to update the current validation ? * * @return boolean */ function canUpdateItem() { if (!Session::haveRightsOr(static::$rightname, static::getCreateRights()) && ($this->fields["users_id_validate"] != Session::getLoginUserID())) { return false; } return true; } /** * @param integer $items_id ID of the item **/ static function canValidate($items_id) { global $DB; $iterator = $DB->request([ 'SELECT' => ['users_id_validate'], 'FROM' => static::getTable(), 'WHERE' => [ static::$items_id => $items_id, 'users_id_validate' => Session::getLoginUserID() ], 'START' => 0, 'LIMIT' => 1 ]); if (count($iterator) > 0) { return true; } return false; } function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) { $hidetab = false; // Hide if no rights on validations if (!static::canView()) { $hidetab = true; } // No right to create and no validation for current object if (!$hidetab && !Session::haveRightsOr(static::$rightname, static::getCreateRights()) && !static::canValidate($item->getID())) { $hidetab = true; } if (!$hidetab) { $nb = 0; if ($_SESSION['glpishow_count_on_tabs']) { $restrict = [static::$items_id => $item->getID()]; // No rights for create only count asign ones if (!Session::haveRightsOr(static::$rightname, static::getCreateRights())) { $restrict['users_id_validate'] = Session::getLoginUserID(); } $nb = countElementsInTable(static::getTable(), $restrict); } return self::createTabEntry(self::getTypeName(Session::getPluralNumber()), $nb); } return ''; } static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) { $validation = new static(); $validation->showSummary($item); return true; } function post_getEmpty() { $this->fields["users_id"] = Session::getLoginUserID(); $this->fields["status"] = self::WAITING; } function prepareInputForAdd($input) { $input["users_id"] = 0; // Only set requester on manual action if (!isset($input['_auto_import']) && !isset($input['_auto_update']) && !Session::isCron()) { $input["users_id"] = Session::getLoginUserID(); } $input["submission_date"] = $_SESSION["glpi_currenttime"]; $input["status"] = self::WAITING; if (!isset($input["users_id_validate"]) || ($input["users_id_validate"] <= 0)) { return false; } $itemtype = static::$itemtype; $input['timeline_position'] = $itemtype::getTimelinePosition($input[static::$items_id], $this->getType(), $input["users_id"]); return parent::prepareInputForAdd($input); } function post_addItem() { global $CFG_GLPI; $item = new static::$itemtype(); $mailsend = false; if ($item->getFromDB($this->fields[static::$items_id])) { // Set global validation to waiting if (($item->fields['global_validation'] == self::ACCEPTED) || ($item->fields['global_validation'] == self::NONE)) { $input = [ 'id' => $this->fields[static::$items_id], 'global_validation' => self::WAITING, ]; // to fix lastupdater if (isset($this->input['_auto_update'])) { $input['_auto_update'] = $this->input['_auto_update']; } // to know update by rules if (isset($this->input["_rule_process"])) { $input['_rule_process'] = $this->input["_rule_process"]; } // No update ticket notif on ticket add if (isset($this->input["_ticket_add"])) { $input['_disablenotif'] = true; } $item->update($input); } if (!isset($this->input['_disablenotif']) && $CFG_GLPI["use_notifications"]) { $options = ['validation_id' => $this->fields["id"], 'validation_status' => $this->fields["status"]]; $mailsend = NotificationEvent::raiseEvent('validation', $item, $options); } if ($mailsend) { $user = new User(); $user->getFromDB($this->fields["users_id_validate"]); $email = $user->getDefaultEmail(); if (!empty($email)) { Session::addMessageAfterRedirect(sprintf(__('Approval request send to %s'), $user->getName())); } else { Session::addMessageAfterRedirect( sprintf( __('The selected user (%s) has no valid email address. The request has been created, without email confirmation.'), $user->getName() ), false, ERROR ); } } } parent::post_addItem(); } function prepareInputForUpdate($input) { $forbid_fields = []; if ($this->fields["users_id_validate"] == Session::getLoginUserID()) { if (($input["status"] == self::REFUSED) && (!isset($input["comment_validation"]) || ($input["comment_validation"] == ''))) { Session::addMessageAfterRedirect(__('If approval is denied, specify a reason.'), false, ERROR); return false; } if ($input["status"] == self::WAITING) { // $input["comment_validation"] = ''; $input["validation_date"] = 'NULL'; } else { $input["validation_date"] = $_SESSION["glpi_currenttime"]; } $forbid_fields = ['entities_id', 'users_id', static::$items_id, 'users_id_validate', 'comment_submission', 'submission_date', 'is_recursive']; } else if (Session::haveRightsOr(static::$rightname, $this->getCreateRights())) { // Update validation request $forbid_fields = ['entities_id', static::$items_id, 'status', 'comment_validation', 'validation_date', 'is_recursive']; } if (count($forbid_fields)) { foreach (array_keys($forbid_fields) as $key) { if (isset($input[$key])) { unset($input[$key]); } } } return parent::prepareInputForUpdate($input); } function post_updateItem($history = 1) { global $CFG_GLPI; $item = new static::$itemtype(); $donotif = $CFG_GLPI["use_notifications"]; if (isset($this->input['_disablenotif'])) { $donotif = false; } if ($item->getFromDB($this->fields[static::$items_id])) { if (count($this->updates) && $donotif) { $options = ['validation_id' => $this->fields["id"], 'validation_status' => $this->fields["status"]]; NotificationEvent::raiseEvent('validation_answer', $item, $options); } //Set global validation to accepted to define one if (($item->fields['global_validation'] == self::WAITING) && in_array("status", $this->updates)) { $input = [ 'id' => $this->fields[static::$items_id], 'global_validation' => self::computeValidationStatus($item), ]; $item->update($input); } } parent::post_updateItem($history); } function pre_deleteItem() { $item = new static::$itemtype(); if ($item->getFromDB($this->fields[static::$items_id])) { if (($item->fields['global_validation'] == self::WAITING)) { $input = [ 'id' => $this->fields[static::$items_id], 'global_validation' => self::NONE, ]; $item->update($input); } } return true; } /** * @see CommonDBConnexity::getHistoryChangeWhenUpdateField **/ function getHistoryChangeWhenUpdateField($field) { if ($field == 'status') { $username = getUserName($this->fields["users_id_validate"]); $result = ['0', '', '']; if ($this->fields["status"] == self::ACCEPTED) { //TRANS: %s is the username $result[2] = sprintf(__('Approval granted by %s'), $username); } else { //TRANS: %s is the username $result[2] = sprintf(__('Update the approval request to %s'), $username); } return $result; } return false; } /** * @see CommonDBChild::getHistoryNameForItem **/ function getHistoryNameForItem(CommonDBTM $item, $case) { $username = getUserName($this->fields["users_id_validate"]); switch ($case) { case 'add' : return sprintf(__('Approval request send to %s'), $username); case 'delete' : return sprintf(__('Cancel the approval request to %s'), $username); } return ''; } /** * get the Ticket validation status list * * @param $withmetaforsearch boolean (false by default) * @param $global boolean (true for global status, with "no validation" option) * (false by default) * * @return array **/ static function getAllStatusArray($withmetaforsearch = false, $global = false) { $tab = [self::WAITING => __('Waiting for approval'), self::REFUSED => __('Refused'), self::ACCEPTED => __('Granted')]; if ($global) { $tab[self::NONE] = __('Not subject to approval'); if ($withmetaforsearch) { $tab['can'] = __('Granted + Not subject to approval'); } } if ($withmetaforsearch) { $tab['all'] = __('All'); } return $tab; } /** * Dropdown of validation status * * @param string $name select name * @param array $options possible options: * - value : default value (default waiting) * - all : boolean display all (default false) * - global : for global validation (default false) * - display : boolean display or get string ? (default true) * * @return string|integer Output string if display option is set to false, * otherwise random part of dropdown id **/ static function dropdownStatus($name, $options = []) { $p = [ 'value' => self::WAITING, 'global' => false, 'all' => false, 'display' => true, ]; if (is_array($options) && count($options)) { foreach ($options as $key => $val) { $p[$key] = $val; } } $tab = self::getAllStatusArray($p['all'], $p['global']); unset($p['all']); unset($p['global']); return Dropdown::showFromArray($name, $tab, $p); } /** * Get Ticket validation status Name * * @param integer $value status ID **/ static function getStatus($value) { $tab = self::getAllStatusArray(true, true); // Return $value if not define return (isset($tab[$value]) ? $tab[$value] : $value); } /** * Get Ticket validation status Color * * @param integer $value status ID **/ static function getStatusColor($value) { switch ($value) { case self::WAITING : $style = "#FFC65D"; break; case self::REFUSED : $style = "#cf9b9b"; break; case self::ACCEPTED : $style = "#9BA563"; break; default : $style = "#cf9b9b"; } return $style; } /** * Get item validation demands count for a user * * @param $users_id integer User ID **/ static function getNumberToValidate($users_id) { global $DB; $row = $DB->request([ 'FROM' => static::getTable(), 'COUNT' => 'cpt', 'WHERE' => [ 'status' => self::WAITING, 'users_id_validate' => $users_id ] ])->next(); return $row['cpt']; } /** * Get the number of validations attached to an item having a specified status * * @param integer $items_id item ID * @param integer $status status **/ static function getTicketStatusNumber($items_id, $status) { global $DB; $row = $DB->request([ 'FROM' => static::getTable(), 'COUNT' => 'cpt', 'WHERE' => [ static::$items_id => $items_id, 'status' => $status ] ])->next(); return $row['cpt']; } /** * Check if validation already exists * * @param $items_id integer item ID * @param $users_id integer user ID * * @since 0.85 * * @return boolean **/ static function alreadyExists($items_id, $users_id) { global $DB; $iterator = $DB->request([ 'FROM' => static::getTable(), 'WHERE' => [ static::$items_id => $items_id, 'users_id_validate' => $users_id ], 'START' => 0, 'LIMIT' => 1 ]); if (count($iterator) > 0) { return true; } return false; } /** * Form for Followup on Massive action **/ static function showFormMassiveAction() { global $CFG_GLPI; $types = ['user' => User::getTypeName(1), 'group' => Group::getTypeName(1)]; $rand = Dropdown::showFromArray("validatortype", $types, ['display_emptychoice' => true]); $paramsmassaction = ['validatortype' => '__VALUE__', 'entity' => $_SESSION['glpiactive_entity'], 'right' => ['validate_request', 'validate_incident']]; Ajax::updateItemOnSelectEvent("dropdown_validatortype$rand", "show_massiveaction_field", $CFG_GLPI["root_doc"]. "/ajax/dropdownMassiveActionAddValidator.php", $paramsmassaction); echo "
 \n"; } /** * @since 0.85 * * @see CommonDBTM::showMassiveActionsSubForm() **/ static function showMassiveActionsSubForm(MassiveAction $ma) { switch ($ma->getAction()) { case 'submit_validation' : static::showFormMassiveAction(); return true; } return parent::showMassiveActionsSubForm($ma); } /** * @since 0.85 * * @see CommonDBTM::processMassiveActionsForOneItemtype() **/ static function processMassiveActionsForOneItemtype(MassiveAction $ma, CommonDBTM $item, array $ids) { switch ($ma->getAction()) { case 'submit_validation' : $input = $ma->getInput(); $valid = new static(); foreach ($ids as $id) { if ($item->getFromDB($id)) { $input2 = [static::$items_id => $id, 'comment_submission' => $input['comment_submission']]; if ($valid->can(-1, CREATE, $input2)) { $users = $input['users_id_validate']; if (!is_array($users)) { $users = [$users]; } $ok = true; foreach ($users as $user) { $input2["users_id_validate"] = $user; if (!$valid->add($input2)) { $ok = false; } } if ($ok) { $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)); } } else { $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO); $ma->addMessage($item->getErrorMessage(ERROR_NOT_FOUND)); } } return; } parent::processMassiveActionsForOneItemtype($ma, $item, $ids); } /** * Print the validation list into item * * @param CommonDBTM $item **/ function showSummary(CommonDBTM $item) { global $DB, $CFG_GLPI; if (!Session::haveRightsOr(static::$rightname, array_merge(static::getCreateRights(), static::getValidateRights(), static::getPurgeRights()))) { return false; } $tID = $item->fields['id']; $tmp = [static::$items_id => $tID]; $canadd = $this->can(-1, CREATE, $tmp); $rand = mt_rand(); if ($canadd) { $itemtype = static::$itemtype; echo "
"; } echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; if ($canadd) { echo ""; echo ""; } else { echo ""; } echo ""; echo "
".self::getTypeName(Session::getPluralNumber())."
".__('Global approval status').""; if (Session::haveRightsOr(static::$rightname, TicketValidation::getValidateRights())) { self::dropdownStatus("global_validation", ['value' => $item->fields["global_validation"]]); } else { echo TicketValidation::getStatus($item->fields["global_validation"]); } echo "
"._x('item', 'State').""; echo self::getValidationStats($tID); echo "
".__('Minimum validation required').""; echo $item->getValueToSelect('validation_percent', 'validation_percent', $item->fields["validation_percent"]); echo ""; if (!empty($tID)) { echo ""; } echo ""; echo Dropdown::getValueWithUnit($item->fields["validation_percent"], "%"); echo "
"; if ($canadd) { Html::closeForm(); } echo "
\n"; if ($canadd) { echo "\n"; } $iterator = $DB->Request([ 'FROM' => $this->getTable(), 'WHERE' => [static::$items_id => $item->getField('id')], 'ORDER' => 'submission_date DESC' ]); $colonnes = [_x('item', 'State'), __('Request date'), __('Approval requester'), __('Request comments'), __('Approval status'), __('Approver'), __('Approval comments')]; $nb_colonnes = count($colonnes); echo ""; echo ""; if ($canadd) { if (!in_array($item->fields['status'], array_merge($item->getSolvedStatusArray(), $item->getClosedStatusArray()))) { echo "\n"; } } if (count($iterator)) { $header = ""; foreach ($colonnes as $colonne) { $header .= ""; } $header .= ""; echo $header; Session::initNavigateListItems($this->getType(), //TRANS : %1$s is the itemtype name, %2$s is the name of the item (used for headings of a list) sprintf(__('%1$s = %2$s'), $item->getTypeName(1), $item->fields["name"])); while ($row = $iterator->next()) { $canedit = $this->canEdit($row["id"]); Session::addToNavigateListItems($this->getType(), $row["id"]); $bgcolor = self::getStatusColor($row['status']); $status = self::getStatus($row['status']); echo "fields['id'].$row["id"]."$rand();\"" : '') . " id='viewvalidation" . $this->fields[static::$items_id] . $row["id"] . "$rand'>"; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; } echo $header; } else { //echo "
".__('No item found')."
"; echo "\n"; } echo "
".__('Approvals for the ticket'). "
"; echo ""; echo __('Send an approval request')."
".$colonne."
"; if ($canedit) { echo "\n\n"; } echo "
".$status."
".Html::convDateTime($row["submission_date"])."".getUserName($row["users_id"])."".$row["comment_submission"]."".Html::convDateTime($row["validation_date"])."".getUserName($row["users_id_validate"])."".$row["comment_validation"]."
"; echo __('No item found')."
"; } /** * Print the validation form * * @param $ID integer ID of the item * @param $options array options used **/ function showForm($ID, $options = []) { if ($ID > 0) { $this->canEdit($ID); } else { $options[static::$items_id] = $options['parent']->fields["id"]; $this->check(-1, CREATE, $options); } // No update validation is answer set $validation_admin = (($this->fields["users_id"] == Session::getLoginUserID()) && static::canCreate() && ($this->fields['status'] == self::WAITING)); $validator = ($this->fields["users_id_validate"] == Session::getLoginUserID()); $options['colspan'] = 1; $this->showFormHeader($options); if ($validation_admin) { if ($this->getType() == 'ChangeValidation') { $validation_right = 'validate'; } else if ($this->getType() == 'TicketValidation') { $ticket = new Ticket(); $ticket->getFromDB($this->fields[static::$items_id]); $validation_right = 'validate_incident'; if ($ticket->fields['type'] == Ticket::DEMAND_TYPE) { $validation_right = 'validate_request'; } } echo ""; echo "".__('Approval requester').""; echo ""; echo ""; echo getUserName($this->fields["users_id"]); echo ""; echo "".__('Approver').""; echo ""; if ($ID > 0) { echo getUserName($this->fields["users_id_validate"]); echo ""; } else { $params = ['id' => $this->fields["id"], 'entity' => $this->getEntityID(), 'right' => $validation_right]; if (!is_null($this->fields['users_id_validate'])) { $params['users_id_validate'] = $this->fields['users_id_validate']; } self::dropdownValidator($params); } echo ""; echo ""; echo "".__('Comments').""; echo ""; } else { echo ""; echo "".__('Approval requester').""; echo "".getUserName($this->fields["users_id"]).""; echo "".__('Approver').""; echo "".getUserName($this->fields["users_id_validate"]).""; echo ""; echo ""; echo "".__('Comments').""; echo ""; echo $this->fields["comment_submission"]; echo ""; } if ($ID > 0) { echo " "; echo ""; echo "".__('Status of the approval request').""; $bgcolor = self::getStatusColor($this->fields['status']); echo "". self::getStatus($this->fields["status"]).""; if ($validator) { echo ""; echo "".__('Status of my validation').""; echo ""; self::dropdownStatus("status", ['value' => $this->fields["status"]]); echo ""; echo ""; echo "".__('Approval comments')."
(".__('Optional when approved').")"; echo ""; echo ""; } else { $status = [self::REFUSED,self::ACCEPTED]; if (in_array($this->fields["status"], $status)) { echo ""; echo "".__('Approval comments').""; echo "".$this->fields["comment_validation"].""; } } } $this->showFormButtons($options); return true; } function rawSearchOptions() { $tab = []; $tab[] = [ 'id' => 'common', 'name' => CommonITILValidation::getTypeName(1) ]; $tab[] = [ 'id' => '1', 'table' => $this->getTable(), 'field' => 'comment_submission', 'name' => __('Request comments'), 'datatype' => 'text' ]; $tab[] = [ 'id' => '2', 'table' => $this->getTable(), 'field' => 'comment_validation', 'name' => __('Approval comments'), 'datatype' => 'text' ]; $tab[] = [ 'id' => '3', 'table' => $this->getTable(), 'field' => 'status', 'name' => __('Status'), 'searchtype' => 'equals', 'datatype' => 'specific' ]; $tab[] = [ 'id' => '4', 'table' => $this->getTable(), 'field' => 'submission_date', 'name' => __('Request date'), 'datatype' => 'datetime' ]; $tab[] = [ 'id' => '5', 'table' => $this->getTable(), 'field' => 'validation_date', 'name' => __('Approval date'), 'datatype' => 'datetime' ]; $tab[] = [ 'id' => '6', 'table' => 'glpi_users', 'field' => 'name', 'name' => __('Approval requester'), 'datatype' => 'itemlink', 'right' => [ 'create_incident_validation', 'create_request_validation' ] ]; $tab[] = [ 'id' => '7', 'table' => 'glpi_users', 'field' => 'name', 'linkfield' => 'users_id_validate', 'name' => __('Approver'), 'datatype' => 'itemlink', 'right' => [ 'validate_request', 'validate_incident' ] ]; return $tab; } static function rawSearchOptionsToAdd() { $tab = []; $tab[] = [ 'id' => 'validation', 'name' => CommonITILValidation::getTypeName(1) ]; $tab[] = [ 'id' => '51', 'table' => getTableForItemType(static::$itemtype), 'field' => 'validation_percent', 'name' => __('Minimum validation required'), 'datatype' => 'number', 'unit' => '%', 'min' => 0, 'max' => 100, 'step' => 50 ]; $tab[] = [ 'id' => '52', 'table' => getTableForItemType(static::$itemtype), 'field' => 'global_validation', 'name' => CommonITILValidation::getTypeName(1), 'searchtype' => 'equals', 'datatype' => 'specific' ]; $tab[] = [ 'id' => '53', 'table' => static::getTable(), 'field' => 'comment_submission', 'name' => __('Request comments'), 'datatype' => 'text', 'forcegroupby' => true, 'massiveaction' => false, 'joinparams' => [ 'jointype' => 'child' ] ]; $tab[] = [ 'id' => '54', 'table' => static::getTable(), 'field' => 'comment_validation', 'name' => __('Approval comments'), 'datatype' => 'text', 'forcegroupby' => true, 'massiveaction' => false, 'joinparams' => [ 'jointype' => 'child' ] ]; $tab[] = [ 'id' => '55', 'table' => static::getTable(), 'field' => 'status', 'datatype' => 'specific', 'name' => __('Approval status'), 'searchtype' => 'equals', 'forcegroupby' => true, 'massiveaction' => false, 'joinparams' => [ 'jointype' => 'child' ] ]; $tab[] = [ 'id' => '56', 'table' => static::getTable(), 'field' => 'submission_date', 'name' => __('Request date'), 'datatype' => 'datetime', 'forcegroupby' => true, 'massiveaction' => false, 'joinparams' => [ 'jointype' => 'child' ] ]; $tab[] = [ 'id' => '57', 'table' => static::getTable(), 'field' => 'validation_date', 'name' => __('Approval date'), 'datatype' => 'datetime', 'forcegroupby' => true, 'massiveaction' => false, 'joinparams' => [ 'jointype' => 'child' ] ]; $tab[] = [ 'id' => '58', 'table' => 'glpi_users', 'field' => 'name', 'name' => _n('Requester', 'Requesters', 1), 'datatype' => 'itemlink', 'right' => (static::$itemtype == 'Ticket' ? 'create_ticket_validate' : 'create_validate'), 'forcegroupby' => true, 'massiveaction' => false, 'joinparams' => [ 'beforejoin' => [ 'table' => static::getTable(), 'joinparams' => [ 'jointype' => 'child' ] ] ] ]; $tab[] = [ 'id' => '59', 'table' => 'glpi_users', 'field' => 'name', 'linkfield' => 'users_id_validate', 'name' => __('Approver'), 'datatype' => 'itemlink', 'right' => (static::$itemtype == 'Ticket' ? ['validate_request', 'validate_incident'] : 'validate' ), 'forcegroupby' => true, 'massiveaction' => false, 'joinparams' => [ 'beforejoin' => [ 'table' => static::getTable(), 'joinparams' => [ 'jointype' => 'child' ] ] ] ]; return $tab; } /** * @param $field * @param $values * @param $options array **/ static function getSpecificValueToDisplay($field, $values, array $options = []) { if (!is_array($values)) { $values = [$field => $values]; } switch ($field) { case 'status': return self::getStatus($values[$field]); } return parent::getSpecificValueToDisplay($field, $values, $options); } /** * @param $field * @param $name (default '') * @param $values (default '') * @param $options array **/ static function getSpecificValueToSelect($field, $name = '', $values = '', array $options = []) { if (!is_array($values)) { $values = [$field => $values]; } $options['display'] = false; switch ($field) { case 'status' : $options['value'] = $values[$field]; return self::dropdownStatus($name, $options); } return parent::getSpecificValueToSelect($field, $name, $values, $options); } /** * @see commonDBTM::getRights() **/ function getRights($interface = 'central') { $values = parent::getRights(); unset($values[UPDATE], $values[READ]); $values[self::VALIDATE] = __('Validate'); return $values; } /** * Dropdown of validator * * @param $options array of options * - name : select name * - id : ID of object > 0 Update, < 0 New * - entity : ID of entity * - right : validation rights * - groups_id : ID of group validator * - users_id_validate : ID of user validator * - applyto * * @return void Output is printed **/ static function dropdownValidator(array $options = []) { global $CFG_GLPI; $params = [ 'name' => '' , 'id' => 0, 'entity' => $_SESSION['glpiactive_entity'], 'right' => ['validate_request', 'validate_incident'], 'groups_id' => 0, 'users_id_validate' => [], 'applyto' => 'show_validator_field', ]; foreach ($options as $key => $val) { $params[$key] = $val; } $types = ['user' => User::getTypeName(1), 'group' => Group::getTypeName(1)]; $type = ''; if (isset($params['users_id_validate']['groups_id'])) { $type = 'group'; } else if (!empty($params['users_id_validate'])) { $type = 'user'; } $rand = Dropdown::showFromArray("validatortype", $types, ['value' => $type, 'display_emptychoice' => true]); if ($type) { $params['validatortype'] = $type; Ajax::updateItem($params['applyto'], $CFG_GLPI["root_doc"]."/ajax/dropdownValidator.php", $params); } $params['validatortype'] = '__VALUE__'; Ajax::updateItemOnSelectEvent("dropdown_validatortype$rand", $params['applyto'], $CFG_GLPI["root_doc"]."/ajax/dropdownValidator.php", $params); if (!isset($options['applyto'])) { echo "
 \n"; } } /** * Get list of users from a group which have validation rights * * @param $options array possible: * groups_id * right * entity * * @return array **/ static function getGroupUserHaveRights(array $options = []) { $params = [ 'entity' => $_SESSION['glpiactive_entity'], ]; if (static::$itemtype == 'Ticket') { $params['right'] = ['validate_request', 'validate_incident']; } else { $params['right'] = ['validate']; } $params['groups_id'] = 0; foreach ($options as $key => $val) { $params[$key] = $val; } $list = []; $restrict = []; $res = User::getSqlSearchResult(false, $params['right'], $params['entity']); while ($data = $res->next()) { $list[] = $data['id']; } if (count($list) > 0) { $restrict = ['glpi_users.id' => $list]; } $users = Group_User::getGroupUsers($params['groups_id'], $restrict); return $users; } /** * Compute the validation status * * @param $item CommonITILObject * * @return integer **/ static function computeValidationStatus(CommonITILObject $item) { // Percent of validation $validation_percent = $item->fields['validation_percent']; $statuses = [self::ACCEPTED => 0, self::WAITING => 0, self::REFUSED => 0]; $validations = getAllDataFromTable( static::getTable(), [ static::$items_id => $item->getID() ] ); if ($total = count($validations)) { foreach ($validations as $validation) { $statuses[$validation['status']] ++; } } return self::computeValidation( $statuses[self::ACCEPTED] * 100 / $total, $statuses[self::REFUSED] * 100 / $total, $validation_percent ); } /** * Compute the validation status from the percentage of acceptation, the * percentage of refusals and the target acceptation threshold * * @param int $accepted 0-100 (percentage of acceptation) * @param int $refused 0-100 (percentage of refusals) * @param int $validation_percent 0-100 (target accepation threshold) * * @return int the validation status : ACCEPTED|REFUSED|WAITING */ public static function computeValidation( int $accepted, int $refused, int $validation_percent ): int { if ($validation_percent > 0) { if ($accepted >= $validation_percent) { // We have reached the acceptation threshold return self::ACCEPTED; } else if ($refused + $validation_percent > 100) { // We can no longer reach the acceptation threshold return self::REFUSED; } } else { // No validation threshold set, one approval or denial is enough if ($accepted > 0) { return self::ACCEPTED; } else if ($refused > 0) { return self::REFUSED; } } return self::WAITING; } /** * Get the validation statistics * * @param integer $tID tickets id * * @return string **/ static function getValidationStats($tID) { $tab = self::getAllStatusArray(); $nb = countElementsInTable(static::getTable(), [static::$items_id => $tID]); $stats = []; foreach (array_keys($tab) as $status) { $validations = countElementsInTable(static::getTable(), [static::$items_id => $tID, 'status' => $status]); if ($validations > 0) { if (!isset($stats[$status])) { $stats[$status] = 0; } $stats[$status] = $validations; } } $list = ""; foreach ($stats as $stat => $val) { $list .= $tab[$stat]; $list .= sprintf(__('%1$s (%2$d%%) '), " ", Html::formatNumber($val*100/$nb)); } return $list; } /** * @param $item CommonITILObject * @param $type */ static function alertValidation(CommonITILObject $item, $type) { global $CFG_GLPI; // No alert for new item if ($item->isNewID($item->getID())) { return; } $status = array_merge($item->getClosedStatusArray(), $item->getSolvedStatusArray()); $message = __s("This item is waiting for approval, do you really want to resolve or close it?"); switch ($type) { case 'status' : $jsScript = " $(document).ready( function() { $('[name=\"status\"]').change(function() { var status_ko = 0; var input_status = $(this).val(); if (input_status != undefined) { if (("; $first = true; foreach ($status as $val) { if (!$first) { $jsScript .= "||"; } $jsScript .= "input_status == $val"; $first = false; } $jsScript .= " ) && input_status != ".$item->fields['status']."){ status_ko = 1; } } if ((status_ko == 1) && ('".($item->fields['global_validation'] ?? '')."' == '".self::WAITING."')) { alert('".$message."'); } }); } );"; echo Html::scriptBlock($jsScript); break; case 'solution' : if (!in_array($item->fields['status'], $status) && isset($item->fields['global_validation']) && $item->fields['global_validation'] == self::WAITING) { Html::displayTitle($CFG_GLPI['root_doc']."/pics/warning.png", $message, $message); } break; } } /** * Get the ITIL object can validation status list * * @since 0.85 * * @return array **/ static function getCanValidationStatusArray() { return [self::NONE, self::ACCEPTED]; } /** * Get the ITIL object all validation status list * * @since 0.85 * * @return array **/ static function getAllValidationStatusArray() { return [self::NONE, self::WAITING, self::REFUSED, self::ACCEPTED]; } }