<?php

/**
 * ---------------------------------------------------------------------
 *
 * GLPI - Gestionnaire Libre de Parc Informatique
 *
 * http://glpi-project.org
 *
 * @copyright 2015-2025 Teclib' and contributors.
 * @copyright 2003-2014 by the INDEPNET Development Team.
 * @licence   https://www.gnu.org/licenses/gpl-3.0.html
 *
 * ---------------------------------------------------------------------
 *
 * LICENSE
 *
 * This file is part of GLPI.
 *
 * This program 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 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * ---------------------------------------------------------------------
 */

use Glpi\Application\View\TemplateRenderer;
use Glpi\RichText\RichText;

/**
 * CommonITILValidation Class
 *
 * @since 0.85
 **/
abstract class CommonITILValidation extends CommonDBChild
{
    // From CommonDBTM
    public $auto_message_on_action    = false;

    public static $log_history_add    = Log::HISTORY_LOG_SIMPLE_MESSAGE;
    public static $log_history_update = Log::HISTORY_LOG_SIMPLE_MESSAGE;
    public static $log_history_delete = Log::HISTORY_LOG_SIMPLE_MESSAGE;

    public const VALIDATE               = 1024;


    // STATUS
    public const NONE      = 1; // none
    public const WAITING   = 2; // waiting
    public const ACCEPTED  = 3; // accepted
    public const REFUSED   = 4; // rejected



    public function getItilObjectItemType()
    {
        return str_replace('Validation', '', $this->getType());
    }

    public static function getIcon()
    {
        return 'ti ti-thumb-up';
    }

    public static function getCreateRights()
    {
        return [CREATE];
    }


    public static function getPurgeRights()
    {
        return [PURGE];
    }


    public static function getValidateRights()
    {
        return [static::VALIDATE];
    }


    public function getForbiddenStandardMassiveAction()
    {

        $forbidden   = parent::getForbiddenStandardMassiveAction();
        $forbidden[] = 'update';
        return $forbidden;
    }


    public static function getTypeName($nb = 0)
    {
        return _n('Approval', 'Approvals', $nb);
    }


    public static function canCreate()
    {
        return Session::haveRightsOr(static::$rightname, static::getCreateRights());
    }


    /**
     * Is the current user have right to delete the current validation ?
     *
     * @return boolean
     **/
    public function canCreateItem()
    {

        if (
            ($this->fields["users_id"] == Session::getLoginUserID())
            || Session::haveRightsOr(static::$rightname, static::getCreateRights())
        ) {
            return true;
        }
        return false;
    }


    public static function canView()
    {

        return Session::haveRightsOr(
            static::$rightname,
            array_merge(
                static::getCreateRights(),
                static::getValidateRights(),
                static::getPurgeRights()
            )
        );
    }


    public 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
     **/
    public 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
     */
    public 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
     **/
    public static function canValidate($items_id)
    {
        /** @var \DBmysql $DB */
        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;
    }


    public function getTabNameForItem(CommonGLPI $item, $withtemplate = 0)
    {
        /** @var CommonDBTM $item */
        $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 '';
    }


    public static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0)
    {

        $validation = new static();
        $validation->showSummary($item);
        return true;
    }


    public function post_getEmpty()
    {

        $this->fields["users_id"] = Session::getLoginUserID();
        $this->fields["status"]   = self::WAITING;
    }


    public 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);
    }


    public function post_addItem()
    {
        /** @var array $CFG_GLPI */
        global $CFG_GLPI;

        // Handle rich-text images
        foreach (['comment_submission', 'comment_validation'] as $content_field) {
            $this->input = $this->addFiles($this->input, [
                'force_update'  => true,
                'name'          => $content_field,
                'content_field' => $content_field,
            ]);
        }

        // Handle uploaded documents
        $this->input = $this->addFiles($this->input);

        $item     = new static::$itemtype();
        $mailsend = false;
        if ($item->getFromDB($this->fields[static::$items_id])) {
            // Set global validation to waiting
            if (((int) $item->fields['global_validation']) !== self::WAITING) {
                $input = [
                    'id'                    => $this->fields[static::$items_id],
                    'global_validation'     => self::WAITING,
                    '_from_itilvalidation'  => true,
                ];

                // 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 sent 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();
    }


    public 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',
            ];
        } elseif (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);
    }


    public function post_updateItem($history = true)
    {
        /** @var array $CFG_GLPI */
        global $CFG_GLPI;

        $item    = new static::$itemtype();
        $donotif = $CFG_GLPI["use_notifications"];
        if (isset($this->input['_disablenotif'])) {
            $donotif = false;
        }

        // Handle rich-text images
        foreach (['comment_submission', 'comment_validation'] as $content_field) {
            $this->input = $this->addFiles($this->input, [
                'force_update'  => true,
                'name'          => $content_field,
                'content_field' => $content_field,
            ]);
        }

        // Handle uploaded documents
        $this->input = $this->addFiles($this->input);

        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),
                    '_from_itilvalidation'  => true,
                ];
                $item->update($input);
            }
        }
        parent::post_updateItem($history);
    }

    public 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,
                    '_from_itilvalidation'  => true,
                ];
                $item->update($input);
            }
        }
        return true;
    }


    /**
     * @see CommonDBConnexity::getHistoryChangeWhenUpdateField
     **/
    public function getHistoryChangeWhenUpdateField($field)
    {
        $result = [];
        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;
    }


    /**
     * @see CommonDBChild::getHistoryNameForItem
     **/
    public function getHistoryNameForItem(CommonDBTM $item, $case)
    {

        $username = getUserName($this->fields["users_id_validate"]);

        switch ($case) {
            case 'add':
                return sprintf(__('Approval request sent 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
     **/
    public static function getAllStatusArray($withmetaforsearch = false, $global = false)
    {

        $tab = [
            self::WAITING  => __('Waiting for approval'),
            self::REFUSED  => _x('validation', '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
     **/
    public static function dropdownStatus($name, $options = [])
    {

        $p = [
            'value'             => self::WAITING,
            'global'            => false,
            'all'               => false,
            'display'           => true,
            'disabled'          => false,
            'templateResult'    => "templateValidation",
            'templateSelection' => "templateValidation",
            'width'             => '100%',
            'required'          => false,
        ];

        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
     * @param bool      $decorated
     **/
    public static function getStatus($value, bool $decorated = false)
    {
        $statuses = self::getAllStatusArray(true, true);

        $label = $statuses[$value] ?? $value;

        if ($decorated) {
            $color   = self::getStatusColor($value);
            $classes = null;
            switch ($value) {
                case self::WAITING:
                    $classes = 'waiting far fa-clock';
                    break;
                case self::ACCEPTED:
                    $classes = 'accepted fas fa-check';
                    break;
                case self::REFUSED:
                    $classes = 'refused fas fa-times';
                    break;
            }

            return sprintf('<span><i class="validationstatus %s"></i> %s</span>', $classes, $label);
        }

        return $label;
    }


    /**
     * Get Ticket validation status Color
     *
     * @param integer $value status ID
     **/
    public 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
     **/
    public static function getNumberToValidate($users_id)
    {
        /** @var \DBmysql $DB */
        global $DB;

        $row = $DB->request([
            'FROM'   => static::$itemtype::getTable(),
            'COUNT'  => 'cpt',
            'WHERE'  => [
                [
                    'id' => new QuerySubQuery([
                        'SELECT' => static::$items_id,
                        'FROM'   => self::getTable(),
                        'WHERE'  => [
                            'status' => self::WAITING,
                            'users_id_validate'  => $users_id,
                        ],
                    ]),
                ],
                'NOT' => [
                    'status' => static::$itemtype::getClosedStatusArray(),
                ],
            ],
        ])->current();

        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
     **/
    public static function getTicketStatusNumber($items_id, $status)
    {
        /** @var \DBmysql $DB */
        global $DB;

        $row = $DB->request([
            'FROM'   => static::getTable(),
            'COUNT'  => 'cpt',
            'WHERE'  => [
                static::$items_id => $items_id,
                'status'          => $status,
            ],
        ])->current();

        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
     **/
    public static function alreadyExists($items_id, $users_id)
    {
        /** @var \DBmysql $DB */
        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
     **/
    public static function showFormMassiveAction()
    {

        /** @var array $CFG_GLPI */
        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()
     **/
    public 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()
     **/
    public 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
     **/
    public function showSummary(CommonDBTM $item)
    {
        /**
         * @var array $CFG_GLPI
         * @var \DBmysql $DB
         */
        global $CFG_GLPI, $DB;

        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'>";
        echo TicketValidation::getStatus($item->fields["global_validation"], true);
        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='btn btn-outline-secondary' 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();
        }

        $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'), __('Documents'),
        ];
        $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) {
            /** @var CommonITILObject $item */
            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='btn btn-outline-secondary' 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"]
                )
            );

            foreach ($iterator as $row) {
                $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'>";

                echo "<td>";
                if ($canedit) {
                    echo "<span class='far fa-edit' style='cursor:pointer' title='" . __('Edit') . "' ";
                    echo "onClick=\"viewEditValidation" . $item->fields['id'] . $row["id"] . "$rand();\"";
                    echo " id='viewvalidation" . $this->fields[static::$items_id] . $row["id"] . "$rand'";
                    echo "></span>";
                    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 "</td>";

                echo "<td><div style='background-color:" . $bgcolor . ";'>" . $status . "</div></td>";
                echo "<td>" . Html::convDateTime($row["submission_date"]) . "</td>";
                echo "<td>" . getUserName($row["users_id"]) . "</td>";
                $comment_submission = RichText::getEnhancedHtml($this->fields['comment_submission'], ['images_gallery' => true]);
                echo "<td><div class='rich_text_container'>" . $comment_submission . "</div></td>";
                echo "<td>" . Html::convDateTime($row["validation_date"]) . "</td>";
                echo "<td>" . getUserName($row["users_id_validate"]) . "</td>";
                $comment_validation = RichText::getEnhancedHtml($this->fields['comment_validation'] ?? '', ['images_gallery' => true]);
                echo "<td><div class='rich_text_container'>" . $comment_validation . "</div></td>";

                $doc_item = new Document_Item();
                $docs = $doc_item->find(["itemtype"          => $this->getType(),
                    "items_id"           => $this->getID(),
                    "timeline_position"  => ['>', CommonITILObject::NO_TIMELINE],
                ]);
                $out = "";
                foreach ($docs as $docs_values) {
                    $doc = new Document();
                    $doc->getFromDB($docs_values['documents_id']);
                    $out  .= "<a ";
                    $out .= "href=\"" . Document::getFormURLWithID($docs_values['documents_id']) . "\">";
                    $out .= $doc->getField('name') . "</a><br>";
                }
                echo "<td>" . $out . "</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>";

        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>";
        }
    }


    /**
     * Print the validation form
     *
     * @param $ID        integer  ID of the item
     * @param $options   array    options used
     **/
    public function showForm($ID, array $options = [])
    {
        if ($ID > 0) {
            $this->canEdit($ID);
        } else {
            $options[static::$items_id] = $options['parent']->fields["id"];
            $this->check(-1, CREATE, $options);
        }

        TemplateRenderer::getInstance()->display('components/itilobject/timeline/form_validation.html.twig', [
            'item'      => $options['parent'],
            'subitem'   => $this,
            'scroll'    => true,
        ]);

        return true;
    }


    public function rawSearchOptions()
    {
        $tab = [];

        $tab[] = [
            'id'                 => 'common',
            'name'               => CommonITILValidation::getTypeName(1),
        ];

        $tab[] = [
            'id'                 => '1',
            'table'              => $this->getTable(),
            'field'              => 'comment_submission',
            'name'               => __('Request comments'),
            'datatype'           => 'text',
            'htmltext'           => true,
        ];

        $tab[] = [
            'id'                 => '2',
            'table'              => $this->getTable(),
            'field'              => 'comment_validation',
            'name'               => __('Approval comments'),
            'datatype'           => 'text',
            'htmltext'           => true,
        ];

        $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;
    }


    public 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',
            'htmltext'           => true,
            'forcegroupby'       => true,
            'massiveaction'      => false,
            'joinparams'         => [
                'jointype'           => 'child',
            ],
        ];

        $tab[] = [
            'id'                 => '54',
            'table'              => static::getTable(),
            'field'              => 'comment_validation',
            'name'               => __('Approval comments'),
            'datatype'           => 'text',
            'htmltext'           => true,
            '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
     **/
    public 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
     **/
    public 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()
     **/
    public 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
     **/
    public static function dropdownValidator(array $options = [])
    {
        /** @var array $CFG_GLPI */
        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',
            'display'           => true,
            'disabled'          => false,
            'width'             => '100%',
            'required'          => false,
            'rand'              => mt_rand(),
        ];

        foreach ($options as $key => $val) {
            $params[$key] = $val;
        }

        $type  = '';
        if (isset($params['users_id_validate']['groups_id'])) {
            $type = 'group';
        } elseif (!empty($params['users_id_validate'])) {
            $type = 'user';
        }

        $out = Dropdown::showFromArray("validatortype", [
            'user'  => User::getTypeName(1),
            'group' => Group::getTypeName(1),
        ], [
            'value'               => $type,
            'display_emptychoice' => true,
            'display'             => false,
            'disabled'            => $params['disabled'],
            'rand'                => $params['rand'],
            'width'               => $params['width'],
            'required'            => $params['required'],
        ]);

        if ($type) {
            $params['validatortype'] = $type;
            $out .= Ajax::updateItem(
                $params['applyto'],
                $CFG_GLPI["root_doc"] . "/ajax/dropdownValidator.php",
                $params,
                "",
                false
            );
        }
        $params['validatortype'] = '__VALUE__';
        $out .= Ajax::updateItemOnSelectEvent(
            "dropdown_validatortype{$params['rand']}",
            $params['applyto'],
            $CFG_GLPI["root_doc"] . "/ajax/dropdownValidator.php",
            $params,
            false
        );

        if (!isset($options['applyto'])) {
            $out .= "<br><span id='" . $params['applyto'] . "'>&nbsp;</span>\n";
        }

        if ($params['display']) {
            echo $out;
            return $params['rand'];
        } else {
            return $out;
        }
    }


    /**
     * Get list of users from a group which have validation rights
     *
     * @param $options   array   possible:
     *       groups_id
     *       right
     *       entity
     *
     * @return array
     **/
    public 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']);
        foreach ($res as $data) {
            $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
     **/
    public 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']]++;
            }
        }

        $accepted = 0;
        $refused  = 0;
        if ($total) {
            $accepted = round($statuses[self::ACCEPTED] * 100 / $total);
            $refused  = round($statuses[self::REFUSED]  * 100 / $total);
        }

        return self::computeValidation(
            $accepted,
            $refused,
            $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;
            } elseif ($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;
            } elseif ($refused > 0) {
                return self::REFUSED;
            }
        }

        return self::WAITING;
    }


    /**
     * Get the validation statistics
     *
     * @param integer $tID tickets id
     *
     * @return string
     **/
    public 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
     */
    public static function alertValidation(CommonITILObject $item, $type)
    {
        /** @var array $CFG_GLPI */
        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
                ) {
                    $title   = __s("This item is waiting for approval.");
                    $message = __s("Do you really want to resolve or close it?");
                    ;
                    $html = <<<HTML
                  <div class="alert alert-warning" role="alert">
                     <div class="d-flex">
                        <div class="me-2">
                           <i class="fas fa-2x fa-exclamation-triangle"></i>
                        </div>
                        <div>
                           <h4 class="alert-title">$title</h4>
                           <div class="text-muted">$message</div>
                        </div>
                     </div>
                  </div>
HTML;
                    echo $html;
                }
                break;
        }
    }


    /**
     * Get the ITIL object can validation status list
     *
     * @since 0.85
     *
     * @return array
     **/
    public static function getCanValidationStatusArray()
    {
        return [self::NONE, self::ACCEPTED];
    }


    /**
     * Get the ITIL object all validation status list
     *
     * @since 0.85
     *
     * @return array
     **/
    public static function getAllValidationStatusArray()
    {
        return [self::NONE, self::WAITING, self::REFUSED, self::ACCEPTED];
    }
}
