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


/**
 * ITILSolution Class
**/
class ITILSolution extends CommonDBChild {

   // From CommonDBTM
   public $dohistory                   = true;
   private $item                       = null;

   static public $itemtype = 'itemtype'; // Class name or field name (start with itemtype) for link to Parent
   static public $items_id = 'items_id'; // Field name

   public static function getNameField() {
      return 'id';
   }

   static function getTypeName($nb = 0) {
      return _n('Solution', 'Solutions', $nb);
   }

   function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) {
      if ($item->isNewItem()) {
         return;
      }
      if ($item->maySolve()) {
         $nb    = 0;
         $title = self::getTypeName(Session::getPluralNumber());
         if ($_SESSION['glpishow_count_on_tabs']) {
            $nb = self::countFor($item->getType(), $item->getID());
         }
         return self::createTabEntry($title, $nb);
      }
   }

   static function canView() {
      return Session::haveRight('ticket', READ)
             || Session::haveRight('change', READ)
             || Session::haveRight('problem', READ);
   }

   public static function canUpdate() {
      //always true, will rely on ITILSolution::canUpdateItem
      return true;
   }

   public function canUpdateItem() {
      return $this->item->maySolve();
   }

   public static function canCreate() {
      //always true, will rely on ITILSolution::canCreateItem
      return true;
   }

   public function canCreateItem() {
      $item = new $this->fields['itemtype'];
      $item->getFromDB($this->fields['items_id']);
      return $item->canSolve();
   }

   function canEdit($ID) {
      return $this->item->maySolve();
   }

   function post_getFromDB() {
      $this->item = new $this->fields['itemtype'];
      $this->item->getFromDB($this->fields['items_id']);
   }

   /**
    * Print the phone form
    *
    * @param $ID integer ID of the item
    * @param $options array
    *     - item: CommonITILObject instance
    *     - kb_id_toload: load new item content from KB entry
    *
    * @return boolean item found
   **/
   function showForm($ID, $options = []) {
      global $CFG_GLPI;

      if ($this->isNewItem()) {
         $this->getEmpty();
      }

      if (!isset($options['item']) && isset($options['parent'])) {
         //when we came from aja/viewsubitem.php
         $options['item'] = $options['parent'];
      }
      $options['formoptions'] = ($options['formoptions'] ?? '') . ' data-track-changes=true';

      $item = $options['item'];
      $this->item = $item;
      $item->check($item->getID(), READ);

      $entities_id = isset($options['entities_id']) ? $options['entities_id'] : $item->getEntityID();

      if ($item instanceof Ticket && $this->isNewItem()) {
         $ti = new Ticket_Ticket();
         $open_child = $ti->countOpenChildren($item->getID());
         if ($open_child > 0) {
            echo "<div class='tab_cadre_fixe warning'>" . __('Warning: non closed children tickets depends on current ticket. Are you sure you want to close it?')  . "</div>";
         }
      }

      $canedit = $item->maySolve();

      if (isset($options['kb_id_toload']) && $options['kb_id_toload'] > 0) {
         $kb = new KnowbaseItem();
         if ($kb->getFromDB($options['kb_id_toload'])) {
            $this->fields['content'] = $kb->getField('answer');
         }
      }

      // Alert if validation waiting
      $validationtype = $item->getType().'Validation';
      if (method_exists($validationtype, 'alertValidation') && $this->isNewItem()) {
         $validationtype::alertValidation($item, 'solution');
      }

      if (!isset($options['noform'])) {
         $this->showFormHeader($options);
      }

      $show_template = $canedit;
      $rand_template = mt_rand();
      $rand_text     = $rand_type = 0;
      if ($canedit) {
         $rand_text = mt_rand();
         $rand_type = mt_rand();
      }
      if ($show_template) {
         echo "<tr class='tab_bg_2'>";
         echo "<td>"._n('Solution template', 'Solution templates', 1)."</td><td>";

         SolutionTemplate::dropdown([
            'value'    => 0,
            'entity'   => $entities_id,
            'rand'     => $rand_template,
            // Load type and solution from bookmark
            'toupdate' => [
               'value_fieldname' => 'value',
               'to_update'       => 'solution'.$rand_text,
               'url'             => $CFG_GLPI["root_doc"]. "/ajax/solution.php",
               'moreparams' => [
                  'type_id' => 'dropdown_solutiontypes_id'.$rand_type
               ]
            ]
         ]);

         echo "</td><td colspan='2'>";
         if (Session::haveRightsOr('knowbase', [READ, KnowbaseItem::READFAQ])) {
            echo "<a class='vsubmit' title=\"".__s('Search a solution')."\"
                   href='".$CFG_GLPI['root_doc']."/front/knowbaseitem.php?item_itemtype=".
                   $item->getType()."&amp;item_items_id=".$item->getID().
                   "&amp;forcetab=Knowbase$1'>".__('Search a solution')."</a>";
         }
         echo "</td></tr>";
      }

      echo "<tr class='tab_bg_2'>";
      echo "<td>".SolutionType::getTypeName(1)."</td><td>";

      echo Html::hidden('itemtype', ['value' => $item->getType()]);
      echo Html::hidden('items_id', ['value' => $item->getID()]);
      echo Html::hidden('_no_message_link', ['value' => 1]);

      // Settings a solution will set status to solved
      if ($canedit) {
         SolutionType::dropdown(['value'  => $this->getField('solutiontypes_id'),
                                 'rand'   => $rand_type,
                                 'entity' => $entities_id]);
      } else {
         echo Dropdown::getDropdownName('glpi_solutiontypes',
                                        $this->getField('solutiontypes_id'));
      }
      echo "</td><td colspan='2'>";

      if (Session::haveRightsOr('knowbase', [READ, KnowbaseItem::READFAQ]) && isset($options['kb_id_toload']) && $options['kb_id_toload'] != 0) {
         echo '<br/><input type="checkbox" name="kb_linked_id" id="kb_linked_id" value="' . $kb->getID() . '" checked="checked">';
         echo ' <label for="kb_linked_id">' . str_replace('%id', $kb->getID(), __('Link to knowledge base entry #%id')) . '</label>';
      } else {
         echo '&nbsp;';
      }
      echo "</td></tr>";
      if ($canedit && Session::haveRight('knowbase', UPDATE) && !isset($options['nokb'])) {
         echo "<tr class='tab_bg_2'><td>".__('Save and add to the knowledge base')."</td><td>";
         Dropdown::showYesNo('_sol_to_kb', false);
         echo "</td><td colspan='2'>&nbsp;</td></tr>";
      }
      echo "<tr class='tab_bg_2'>";
      echo "<td>".__('Description')."</td><td colspan='3'>";

      if ($canedit) {
         $rand = mt_rand();
         Html::initEditorSystem("content$rand");

         echo "<div id='solution$rand_text'>";
         echo "<textarea id='content$rand' name='content' rows='12' cols='80'>".
                $this->getField('content')."</textarea></div>";

         // Hide file input to handle only images pasted in text editor
         echo '<div style="display:none;">';
         Html::file(['editor_id' => "content$rand",
                     'filecontainer' => "filecontainer$rand",
                     'onlyimages' => true,
                     'showtitle' => false,
                     'multiple' => true]);
         echo '</div>';
      } else {
         echo Toolbox::unclean_cross_side_scripting_deep($this->getField('content'));
      }
      echo "</td></tr>";

      if (!isset($options['noform'])) {
         $options['candel']   = false;
         $options['canedit']  = $canedit;
         $this->showFormButtons($options);
      }
   }


   /**
    * Count solutions for specific item
    *
    * @param string  $itemtype Item type
    * @param integer $items_id Item ID
    *
    * @return integer
    */
   public static function countFor($itemtype, $items_id) {
      return countElementsInTable(
         self::getTable(), [
            'WHERE' => [
               'itemtype'  => $itemtype,
               'items_id'  => $items_id
            ]
         ]
      );
   }

   function prepareInputForAdd($input) {
      $input['users_id'] = Session::getLoginUserID();

      if ($this->item == null
         || (isset($input['itemtype']) && isset($input['items_id']))
      ) {
         $this->item = new $input['itemtype'];
         $this->item->getFromDB($input['items_id']);
      }

      // check itil object is not already solved
      if (in_array($this->item->fields["status"], $this->item->getSolvedStatusArray())) {
         Session::addMessageAfterRedirect(__("The item is already solved, did anyone pushed a solution before you ?"),
                                          false, ERROR);
         return false;
      }

      //default status for global solutions
      $status = CommonITILValidation::ACCEPTED;

      //handle autoclose, for tickets only
      if ($input['itemtype'] == Ticket::getType()) {
         $autoclosedelay =  Entity::getUsedConfig(
            'autoclose_delay',
            $this->item->getEntityID(),
            '',
            Entity::CONFIG_NEVER
         );

         // 0 = immediatly
         if ($autoclosedelay != 0) {
            $status = CommonITILValidation::WAITING;
         }
      }

      //Accepted; store user and date
      if ($status == CommonITILValidation::ACCEPTED) {
         $input['users_id_approval'] = Session::getLoginUserID();
         $input['date_approval'] = $_SESSION["glpi_currenttime"];
      }

      $input['status'] = $status;

      return $input;
   }

   function post_addItem() {

      //adding a solution mean the ITIL object is now solved
      //and maybe closed (according to entitiy configuration)
      if ($this->item == null) {
         $this->item = new $this->fields['itemtype'];
         $this->item->getFromDB($this->fields['items_id']);
      }

      $item = $this->item;

      // Replace inline pictures
      $this->input["_job"] = $this->item;
      $this->input = $this->addFiles(
         $this->input, [
            'force_update' => true,
            'name' => 'content',
            'content_field' => 'content',
         ]
      );

      // Add solution to duplicates
      if ($this->item->getType() == 'Ticket' && !isset($this->input['_linked_ticket'])) {
         Ticket_Ticket::manageLinkedTicketsOnSolved($this->item->getID(), $this);
      }

      if (!isset($this->input['_linked_ticket'])) {
         $status = $item::SOLVED;

         //handle autoclose, for tickets only
         if ($item->getType() == Ticket::getType()) {
            $autoclosedelay =  Entity::getUsedConfig(
               'autoclose_delay',
               $this->item->getEntityID(),
               '',
               Entity::CONFIG_NEVER
            );

            // 0 = immediatly
            if ($autoclosedelay == 0) {
               $status = $item::CLOSED;
            }
         }

         $this->item->update([
            'id'     => $this->item->getID(),
            'status' => $status
         ]);
      }

      parent::post_addItem();
   }

   function prepareInputForUpdate($input) {

      if (!isset($this->fields['itemtype'])) {
         return false;
      }
      $input["_job"] = new $this->fields['itemtype']();
      if (!$input["_job"]->getFromDB($this->fields["items_id"])) {
         return false;
      }

      return $input;
   }

   function post_updateItem($history = 1) {
      // Replace inline pictures
      $options = [
         'force_update' => true,
         'name' => 'content',
         'content_field' => 'content',
      ];
      $this->input = $this->addFiles($this->input, $options);
   }


   /**
    * {@inheritDoc}
    * @see CommonDBTM::getSpecificValueToDisplay()
    */
   static function getSpecificValueToDisplay($field, $values, array $options = []) {

      if (!is_array($values)) {
         $values = [$field => $values];
      }

      switch ($field) {
         case 'status':
            $value = $values[$field];
            $statuses = self::getStatuses();

            return (isset($statuses[$value]) ? $statuses[$value] : $value);
            break;
      }

      return parent::getSpecificValueToDisplay($field, $values, $options);
   }

   /**
    * {@inheritDoc}
    * @see CommonDBTM::getSpecificValueToSelect()
    */
   static function getSpecificValueToSelect($field, $name = '', $values = '', array $options = []) {

      if (!is_array($values)) {
         $values = [$field => $values];
      }

      switch ($field) {
         case 'status':
            $options['display'] = false;
            $options['value'] = $values[$field];
            return Dropdown::showFromArray($name, self::getStatuses(), $options);
            break;
      }

      return parent::getSpecificValueToSelect($field, $name, $values, $options);
   }

   /**
    * Return list of statuses.
    * Key as status values, values as labels.
    *
    * @return string[]
    */
   static function getStatuses() {
      return [
         CommonITILValidation::WAITING  => __('Waiting for approval'),
         CommonITILValidation::REFUSED  => __('Refused'),
         CommonITILValidation::ACCEPTED => __('Accepted'),
      ];
   }
}
