<?php
/**
 * ---------------------------------------------------------------------
 * GLPI - Gestionnaire Libre de Parc Informatique
 * Copyright (C) 2015-2021 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");
}

/**
 * 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() && isset($input["status"])) {
         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);
         }

         //if status is updated, update global approval status
         if (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 "<br><span id='show_massiveaction_field'>&nbsp;</span>\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 "<form method='post' name=form action='".$itemtype::getFormURL()."'>";
      }
      echo "<table class='tab_cadre_fixe'>";
      echo "<tr>";
      echo "<th colspan='3'>".self::getTypeName(Session::getPluralNumber())."</th>";
      echo "</tr>";

      echo "<tr class='tab_bg_1'>";
      echo "<td>".__('Global approval status')."</td>";
      echo "<td colspan='2'>";
      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 "</td></tr>";

      echo "<tr>";
      echo "<th colspan='2'>"._x('item', 'State')."</th>";
      echo "<th colspan='2'>";
      echo self::getValidationStats($tID);
      echo "</th>";
      echo "</tr>";

      echo "<tr class='tab_bg_1'>";
      echo "<td>".__('Minimum validation required')."</td>";
      if ($canadd) {
         echo "<td>";
         echo $item->getValueToSelect('validation_percent', 'validation_percent',
                                      $item->fields["validation_percent"]);
         echo "</td>";
         echo "<td><input type='submit' name='update' class='submit' value='".
                    _sx('button', 'Save')."'>";
         if (!empty($tID)) {
            echo "<input type='hidden' name='id' value='$tID'>";
         }
         echo "</td>";
      } else {
         echo "<td colspan='2'>";
         echo Dropdown::getValueWithUnit($item->fields["validation_percent"], "%");
         echo "</td>";
      }
      echo "</tr>";
      echo "</table>";
      if ($canadd) {
         Html::closeForm();
      }

      echo "<div id='viewvalidation" . $tID . "$rand'></div>\n";

      if ($canadd) {
         echo "<script type='text/javascript' >\n";
         echo "function viewAddValidation" . $tID . "$rand() {\n";
         $params = ['type'             => $this->getType(),
                         'parenttype'       => static::$itemtype,
                         static::$items_id  => $tID,
                         'id'               => -1];
         Ajax::updateItemJsCode("viewvalidation" . $tID . "$rand",
                                $CFG_GLPI["root_doc"]."/ajax/viewsubitem.php",
                                $params);
         echo "};";
         echo "</script>\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 "<table class='tab_cadre_fixehov'>";
      echo "<tr class='noHover'><th colspan='".$nb_colonnes."'>".__('Approvals for the ticket').
           "</th></tr>";

      if ($canadd) {
         if (!in_array($item->fields['status'], array_merge($item->getSolvedStatusArray(),
            $item->getClosedStatusArray()))) {
               echo "<tr class='tab_bg_1 noHover'><td class='center' colspan='" . $nb_colonnes . "'>";
               echo "<a class='vsubmit' href='javascript:viewAddValidation".$tID."$rand();'>";
               echo __('Send an approval request')."</a></td></tr>\n";
         }
      }
      if (count($iterator)) {
         $header = "<tr>";
         foreach ($colonnes as $colonne) {
            $header .= "<th>".$colonne."</th>";
         }
         $header .= "</tr>";
         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 "<tr class='tab_bg_1' ".
                   ($canedit ? "style='cursor:pointer' onClick=\"viewEditValidation".
                               $item->fields['id'].$row["id"]."$rand();\""
                             : '') .
                  " id='viewvalidation" . $this->fields[static::$items_id] . $row["id"] . "$rand'>";
            echo "<td>";
            if ($canedit) {
               echo "\n<script type='text/javascript' >\n";
               echo "function viewEditValidation" .$item->fields['id']. $row["id"]. "$rand() {\n";
               $params = ['type'             => $this->getType(),
                               'parenttype'       => static::$itemtype,
                               static::$items_id  => $this->fields[static::$items_id],
                               'id'               => $row["id"]];
               Ajax::updateItemJsCode("viewvalidation" . $item->fields['id'] . "$rand",
                                      $CFG_GLPI["root_doc"]."/ajax/viewsubitem.php",
                                      $params);
               echo "};";
               echo "</script>\n";
            }

            echo "<div style='background-color:".$bgcolor.";'>".$status."</div></td>";

            echo "<td>".Html::convDateTime($row["submission_date"])."</td>";
            echo "<td>".getUserName($row["users_id"])."</td>";
            echo "<td>".$row["comment_submission"]."</td>";
            echo "<td>".Html::convDateTime($row["validation_date"])."</td>";
            echo "<td>".getUserName($row["users_id_validate"])."</td>";
            echo "<td>".$row["comment_validation"]."</td>";
            echo "</tr>";
         }
         echo $header;
      } else {
         //echo "<div class='center b'>".__('No item found')."</div>";
         echo "<tr class='tab_bg_1 noHover'><th colspan='" . $nb_colonnes . "'>";
         echo __('No item found')."</th></tr>\n";
      }
      echo "</table>";
   }


   /**
    * 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 "<tr class='tab_bg_1'>";
         echo "<td>".__('Approval requester')."</td>";
         echo "<td>";
         echo "<input type='hidden' name='".static::$items_id."' value='".
                $this->fields[static::$items_id]."'>";
         echo getUserName($this->fields["users_id"]);
         echo "</td></tr>";

         echo "<tr class='tab_bg_1'><td>".__('Approver')."</td>";
         echo "<td>";

         if ($ID > 0) {
            echo getUserName($this->fields["users_id_validate"]);
            echo "<input type='hidden' name='users_id_validate' value='".
                   $this->fields['users_id_validate']."'>";
         } 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 "</td></tr>";

         echo "<tr class='tab_bg_1'>";
         echo "<td>".__('Comments')."</td>";
         echo "<td><textarea cols='60' rows='3' name='comment_submission'>".
               $this->fields["comment_submission"]."</textarea></td></tr>";

      } else {
         echo "<tr class='tab_bg_1'>";
         echo "<td>".__('Approval requester')."</td>";
         echo "<td>".getUserName($this->fields["users_id"])."</td></tr>";

         echo "<tr class='tab_bg_1'><td>".__('Approver')."</td>";
         echo "<td>".getUserName($this->fields["users_id_validate"])."</td></tr>";
         echo "</td></tr>";

         echo "<tr class='tab_bg_1'>";
         echo "<td>".__('Comments')."</td>";
         echo "<td>";
         echo $this->fields["comment_submission"];
         echo "</td></tr>";
      }

      if ($ID > 0) {
         echo "<tr class='tab_bg_2'><td colspan='2'>&nbsp;</td></tr>";

         echo "<tr class='tab_bg_1'>";
         echo "<td>".__('Status of the approval request')."</td>";
         $bgcolor = self::getStatusColor($this->fields['status']);
         echo "<td><span style='background-color:".$bgcolor.";'>".
               self::getStatus($this->fields["status"])."</span></td></tr>";

         if ($validator) {
            echo "<tr class='tab_bg_1'>";
            echo "<td>".__('Status of my validation')."</td>";
            echo "<td>";
            self::dropdownStatus("status", ['value' => $this->fields["status"]]);
            echo "</td></tr>";

            echo "<tr class='tab_bg_1'>";
            echo "<td>".__('Approval comments')."<br>(".__('Optional when approved').")</td>";
            echo "<td><textarea cols='60' rows='3' name='comment_validation'>".
                       $this->fields["comment_validation"]."</textarea>";
            echo "</td></tr>";

         } else {
            $status = [self::REFUSED,self::ACCEPTED];
            if (in_array($this->fields["status"], $status)) {
               echo "<tr class='tab_bg_1'>";
               echo "<td>".__('Approval comments')."</td>";
               echo "<td>".$this->fields["comment_validation"]."</td></tr>";
            }
         }
      }

      $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 "<br><span id='".$params['applyto']."'>&nbsp;</span>\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];
   }

}
