Prv8 Shell
Server : Apache
System : Linux vps.urbanovitalino.adv.br 3.10.0-1062.12.1.el7.x86_64 #1 SMP Tue Feb 4 23:02:59 UTC 2020 x86_64
User : urbanovitalinoad ( 1001)
PHP Version : 7.3.33
Disable Function : exec,passthru,shell_exec,system
Directory :  /home/urbanovitalinoad/public_html/servicedesk/inc/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/urbanovitalinoad/public_html/servicedesk/inc/itilfollowup.class.php
<?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");
}

/**
 * @since 9.4.0
 */
class ITILFollowup  extends CommonDBChild {

   // From CommonDBTM
   public $auto_message_on_action = false;
   static $rightname              = 'followup';
   private $item                  = null;

   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 SEEPUBLIC       =    1;
   const UPDATEMY        =    2;
   const ADDMYTICKET     =    4;
   const UPDATEALL       = 1024;
   const ADDGROUPTICKET  = 2048;
   const ADDALLTICKET    = 4096;
   const SEEPRIVATE      = 8192;

   static public $itemtype = 'itemtype';
   static public $items_id = 'items_id';


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


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


   /**
    * can read the parent ITIL Object ?
    *
    * @return boolean
    */
   function canReadITILItem() {

      $itemtype = $this->getItilObjectItemType();
      $item     = new $itemtype();
      if (!$item->can($this->getField($item->getForeignKeyField()), READ)) {
         return false;
      }
      return true;
   }


   static function canView() {
      return (Session::haveRightsOr(self::$rightname, [self::SEEPUBLIC, self::SEEPRIVATE])
              || Session::haveRight('ticket', Ticket::OWN))
              || Session::haveRight('ticket', READ)
              || Session::haveRight('change', READ)
              || Session::haveRight('problem', READ);
   }


   static function canCreate() {
      return Session::haveRight('change', UPDATE)
             || Session::haveRight('problem', UPDATE)
             || (Session::haveRightsOr(self::$rightname,
                    [self::ADDALLTICKET, self::ADDMYTICKET, self::ADDGROUPTICKET])
             || Session::haveRight('ticket', Ticket::OWN));
   }


   function canViewItem() {

      $itilobject = new $this->fields['itemtype'];
      if (!$itilobject->can($this->getField('items_id'), READ)) {
         return false;
      }
      if (Session::haveRight(self::$rightname, self::SEEPRIVATE)) {
         return true;
      }
      if (!$this->fields['is_private']
          && Session::haveRight(self::$rightname, self::SEEPUBLIC)) {
         return true;
      }
      if ($itilobject instanceof Ticket) {
         if ($this->fields["users_id"] === Session::getLoginUserID()) {
            return true;
         }
      } else {
         return Session::haveRight($itilobject::$rightname, READ);
      }
      return false;
   }


   function canCreateItem() {
      if (!isset($this->fields['itemtype'])
          || strlen($this->fields['itemtype']) == 0) {
         return false;
      }

      $itilobject = new $this->fields['itemtype'];

      if (!$itilobject->can($this->getField('items_id'), READ)
         // No validation for closed tickets
         || in_array($itilobject->fields['status'], $itilobject->getClosedStatusArray())
         && !$itilobject->canReopen()
      ) {
         return false;
      }
      return $itilobject->canAddFollowups();
   }


   function canPurgeItem() {

      $itilobject = new $this->fields['itemtype'];
      if (!$itilobject->can($this->getField('items_id'), READ)) {
         return false;
      }

      if (Session::haveRight(self::$rightname, PURGE)) {
         return true;
      }

      return false;
   }


   function canUpdateItem() {

      if (($this->fields["users_id"] != Session::getLoginUserID())
          && !Session::haveRight(self::$rightname, self::UPDATEALL)) {
         return false;
      }

      $itilobject = new $this->fields['itemtype'];
      if (!$itilobject->can($this->getField('items_id'), READ)) {
         return false;
      }

      if ($this->fields["users_id"] === Session::getLoginUserID()) {
         if (!Session::haveRight(self::$rightname, self::UPDATEMY)) {
            return false;
         }
         return true;
      }

      // Only the technician
      return (Session::haveRight(self::$rightname, self::UPDATEALL)
              || $itilobject->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID())
              || (isset($_SESSION["glpigroups"])
                  && $itilobject->haveAGroup(CommonITILActor::ASSIGN, $_SESSION['glpigroups'])));
   }


   function post_getEmpty() {

      if (isset($_SESSION['glpifollowup_private']) && $_SESSION['glpifollowup_private']) {
         $this->fields['is_private'] = 1;
      }

      if (isset($_SESSION["glpiname"])) {
         $this->fields['requesttypes_id'] = RequestType::getDefault('followup');
      }
   }


   function post_addItem() {

      global $CFG_GLPI;

      // Add screenshots if needed, without notification
      $this->input = $this->addFiles($this->input, [
         'force_update'  => true,
         'name'          => 'content',
         'content_field' => 'content',
      ]);

      // Add documents if needed, without notification
      $this->input = $this->addFiles($this->input, [
         'force_update'  => true,
      ]);

      $donotif = !isset($this->input['_disablenotif']) && $CFG_GLPI["use_notifications"];

      // Check if stats should be computed after this change
      $no_stat = isset($this->input['_do_not_compute_takeintoaccount']);

      $parentitem = $this->input['_job'];
      $parentitem->updateDateMod(
         $this->input["items_id"],
         $no_stat,
         $this->input["users_id"]
      );

      if (isset($this->input["_close"])
          && $this->input["_close"]
          && ($parentitem->fields["status"] == CommonITILObject::SOLVED)) {

         $update = [
            'id'        => $parentitem->fields['id'],
            'status'    => CommonITILObject::CLOSED,
            'closedate' => $_SESSION["glpi_currenttime"],
            '_accepted' => true,
         ];

         // Use update method for history
         $this->input["_job"]->update($update);
         $donotif = false; // Done for ITILObject update (new status)
      }

      //manage reopening of ITILObject
      $reopened = false;
      if (!isset($this->input['_status'])) {
         $this->input['_status'] = $parentitem->fields["status"];
      }
      // if reopen set (from followup form or mailcollector)
      // and status is reopenable and not changed in form
      if (isset($this->input["_reopen"])
          && $this->input["_reopen"]
          && in_array($parentitem->fields["status"], $parentitem::getReopenableStatusArray())
          && $this->input['_status'] == $parentitem->fields["status"]) {

         $needupdateparent = false;
         if (($parentitem->countUsers(CommonITILActor::ASSIGN) > 0)
             || ($parentitem->countGroups(CommonITILActor::ASSIGN) > 0)
             || ($parentitem->countSuppliers(CommonITILActor::ASSIGN) > 0)) {

            //check if lifecycle allowed new status
            if (Session::isCron()
                || Session::getCurrentInterface() == "helpdesk"
                || $parentitem::isAllowedStatus($parentitem->fields["status"], CommonITILObject::ASSIGNED)) {
               $needupdateparent = true;
               $update['status'] = CommonITILObject::ASSIGNED;
            }
         } else {
            //check if lifecycle allowed new status
            if (Session::isCron()
                || Session::getCurrentInterface() == "helpdesk"
                || $parentitem::isAllowedStatus($parentitem->fields["status"], CommonITILObject::INCOMING)) {
               $needupdateparent = true;
               $update['status'] = CommonITILObject::INCOMING;
            }
         }

         if ($needupdateparent) {
            $update['id'] = $parentitem->fields['id'];

            // Use update method for history
            $parentitem->update($update);
            $reopened     = true;
         }

      }

      //change ITILObject status only if imput change
      if (!$reopened
          && $this->input['_status'] != $parentitem->fields['status']) {

         $update['status'] = $this->input['_status'];
         $update['id']     = $parentitem->fields['id'];

         // don't notify on ITILObject - update event
         $update['_disablenotif'] = true;

         // Use update method for history
         $parentitem->update($update);
      }

      if ($donotif) {
         $options = ['followup_id' => $this->fields["id"],
                          'is_private'  => $this->fields['is_private']];
         NotificationEvent::raiseEvent("add_followup", $parentitem, $options);
      }

      // Add log entry in the ITILObject
      $changes = [
         0,
         '',
         $this->fields['id'],
      ];
      Log::history($this->getField('items_id'), get_class($parentitem), $changes, $this->getType(),
                   Log::HISTORY_ADD_SUBITEM);
   }


   function post_deleteFromDB() {
      global $CFG_GLPI;

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

      $job = new $this->fields['itemtype'];
      $job->getFromDB($this->fields[self::$items_id]);
      $job->updateDateMod($this->fields[self::$items_id]);

      // Add log entry in the ITIL Object
      $changes = [
         0,
         '',
         $this->fields['id'],
      ];
      Log::history($this->getField(self::$items_id), $this->fields['itemtype'], $changes, $this->getType(),
                   Log::HISTORY_DELETE_SUBITEM);

      if ($donotif) {
         $options = ['followup_id' => $this->fields["id"],
                           // Force is_private with data / not available
                          'is_private'  => $this->fields['is_private']];
         NotificationEvent::raiseEvent('delete_followup', $job, $options);
      }
   }


   function prepareInputForAdd($input) {

      $input["_job"] = new $input['itemtype']();

      if (empty($input['content'])
          && !isset($input['add_close'])
          && !isset($input['add_reopen'])) {
         Session::addMessageAfterRedirect(__("You can't add a followup without description"),
                                          false, ERROR);
         return false;
      }
      if (!$input["_job"]->getFromDB($input["items_id"])) {
         return false;
      }

      $input['_close'] = 0;

      if (!isset($input["users_id"])) {
         $input["users_id"] = 0;
         if ($uid = Session::getLoginUserID()) {
            $input["users_id"] = $uid;
         }
      }
      // if ($input["_isadmin"] && $input["_type"]!="update") {
      if (isset($input["add_close"])) {
         $input['_close'] = 1;
         if (empty($input['content'])) {
            $input['content'] = __('Solution approved');
         }
      }

      unset($input["add_close"]);

      if (!isset($input["is_private"])) {
         $input['is_private'] = 0;
      }

      if (isset($input["add_reopen"])) {
         if ($input["content"] == '') {
            if (isset($input["_add"])) {
               // Reopen using add form
               Session::addMessageAfterRedirect(__('If you want to reopen this item, you must specify a reason'),
                                                false, ERROR);
            } else {
               // Refuse solution
               Session::addMessageAfterRedirect(__('If you reject the solution, you must specify a reason'),
                                                false, ERROR);
            }
            return false;
         }
         $input['_reopen'] = 1;
      }
      unset($input["add_reopen"]);
      // }
      unset($input["add"]);

      $itemtype = $input['itemtype'];
      $input['timeline_position'] = $itemtype::getTimelinePosition($input["items_id"], $this->getType(), $input["users_id"]);

      if (!isset($input['date'])) {
         $input["date"] = $_SESSION["glpi_currenttime"];
      }
      return $input;
   }


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

      // update last editor if content change
      if (($uid = Session::getLoginUserID())
          && isset($input['content']) && ($input['content'] != $this->fields['content'])) {
         $input["users_id_editor"] = $uid;
      }

      return $input;
   }


   function post_updateItem($history = 1) {
      global $CFG_GLPI;

      $job      = new $this->fields['itemtype']();

      if (!$job->getFromDB($this->fields['items_id'])) {
         return;
      }

      // Add screenshots if needed, without notification
      $this->input = $this->addFiles($this->input, [
         'force_update' => true,
         'name'          => 'content',
         'content_field' => 'content',
      ]);

      // Add documents if needed, without notification
      $this->input = $this->addFiles($this->input, [
         'force_update' => true,
      ]);

      //Get user_id when not logged (from mailgate)
      $uid = Session::getLoginUserID();
      if ($uid === false) {
         if (isset($this->fields['users_id_editor'])) {
            $uid = $this->fields['users_id_editor'];
         } else {
            $uid = $this->fields['users_id'];
         }
      }
      $job->updateDateMod($this->fields['items_id'], false, $uid);

      if (count($this->updates)) {
         if (!isset($this->input['_disablenotif'])
             && $CFG_GLPI["use_notifications"]
             && (in_array("content", $this->updates)
                 || isset($this->input['_need_send_mail']))) {
            //FIXME: _need_send_mail does not seems to be used

            $options = ['followup_id' => $this->fields["id"],
                             'is_private'  => $this->fields['is_private']];

            NotificationEvent::raiseEvent("update_followup", $job, $options);
         }
      }

      // change ITIL Object status (from splitted button)
      if (isset($this->input['_status'])
          && ($this->input['_status'] != $this->input['_job']->fields['status'])) {
          $update = [
             'status'        => $this->input['_status'],
             'id'            => $this->input['_job']->fields['id'],
             '_disablenotif' => true,
          ];
          $this->input['_job']->update($update);
      }

      // Add log entry in the ITIL Object
      $changes = [
         0,
         '',
         $this->fields['id'],
      ];
      Log::history($this->getField('items_id'), $this->fields['itemtype'], $changes, $this->getType(),
                   Log::HISTORY_UPDATE_SUBITEM);
   }


   function post_getFromDB() {

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


   protected function computeFriendlyName() {

      if (isset($this->fields['requesttypes_id'])) {
         if ($this->fields['requesttypes_id']) {
            return Dropdown::getDropdownName('glpi_requesttypes', $this->fields['requesttypes_id']);
         }
         return $this->getTypeName();
      }
      return '';
   }


   function rawSearchOptions() {

      $tab = [];

      $tab[] = [
         'id'                 => 'common',
         'name'               => __('Characteristics')
      ];

      $tab[] = [
         'id'                 => '1',
         'table'              => $this->getTable(),
         'field'              => 'content',
         'name'               => __('Description'),
         'datatype'           => 'text'
      ];

      $tab[] = [
         'id'                 => '2',
         'table'              => 'glpi_requesttypes',
         'field'              => 'name',
         'name'               => RequestType::getTypeName(1),
         'forcegroupby'       => true,
         'datatype'           => 'dropdown'
      ];

      $tab[] = [
         'id'                 => '3',
         'table'              => $this->getTable(),
         'field'              => 'date',
         'name'               => _n('Date', 'Dates', 1),
         'datatype'           => 'datetime'
      ];

      $tab[] = [
         'id'                 => '4',
         'table'              => $this->getTable(),
         'field'              => 'is_private',
         'name'               => __('Private'),
         'datatype'           => 'bool'
      ];

      $tab[] = [
         'id'                 => '5',
         'table'              => 'glpi_users',
         'field'              => 'name',
         'name'               => User::getTypeName(1),
         'datatype'           => 'dropdown',
         'right'              => 'all'
      ];

      $tab[] = [
         'id'                 => '6',
         'table'              => $this->getTable(),
         'field'              => 'itemtype',
         'name'               => RequestType::getTypeName(1),
         'datatype'           => 'dropdown'
      ];

      return $tab;
   }


   static function rawSearchOptionsToAdd($itemtype = null) {

      $tab = [];

      $tab[] = [
         'id'                 => 'followup',
         'name'               => _n('Followup', 'Followups', Session::getPluralNumber())
      ];

      $followup_condition = '';
      if (!Session::haveRight('followup', self::SEEPRIVATE)) {
         $followup_condition = "AND (`NEWTABLE`.`is_private` = 0
                                     OR `NEWTABLE`.`users_id` = '".Session::getLoginUserID()."')";
      }

      $tab[] = [
         'id'                 => '25',
         'table'              => static::getTable(),
         'field'              => 'content',
         'name'               => __('Description'),
         'forcegroupby'       => true,
         'splititems'         => true,
         'massiveaction'      => false,
         'joinparams'         => [
            'jointype'           => 'itemtype_item',
            'condition'          => $followup_condition
         ],
         'datatype'           => 'text',
         'htmltext'           => true
      ];

      $tab[] = [
         'id'                 => '36',
         'table'              => static::getTable(),
         'field'              => 'date',
         'name'               => _n('Date', 'Dates', 1),
         'datatype'           => 'datetime',
         'massiveaction'      => false,
         'forcegroupby'       => true,
         'joinparams'         => [
            'jointype'           => 'itemtype_item',
            'condition'          => $followup_condition
         ]
      ];

      $tab[] = [
         'id'                 => '27',
         'table'              => static::getTable(),
         'field'              => 'id',
         'name'               => _x('quantity', 'Number of followups'),
         'forcegroupby'       => true,
         'usehaving'          => true,
         'datatype'           => 'count',
         'massiveaction'      => false,
         'joinparams'         => [
            'jointype'           => 'itemtype_item',
            'condition'          =>$followup_condition
         ]
      ];

      $tab[] = [
         'id'                 => '29',
         'table'              => 'glpi_requesttypes',
         'field'              => 'name',
         'name'               => RequestType::getTypeName(1),
         'datatype'           => 'dropdown',
         'forcegroupby'       => true,
         'massiveaction'      => false,
         'joinparams'         => [
            'beforejoin'         => [
               'table'              => static::getTable(),
               'joinparams'         => [
                  'jointype'           => 'itemtype_item',
                  'condition'          => $followup_condition
               ]
            ]
         ]
      ];

      $tab[] = [
         'id'                 => '91',
         'table'              => static::getTable(),
         'field'              => 'is_private',
         'name'               => __('Private followup'),
         'datatype'           => 'bool',
         'forcegroupby'       => true,
         'splititems'         => true,
         'massiveaction'      => false,
         'joinparams'         => [
            'jointype'           => 'itemtype_item',
            'condition'          => $followup_condition
         ]
      ];

      $tab[] = [
         'id'                 => '93',
         'table'              => 'glpi_users',
         'field'              => 'name',
         'name'               => __('Writer'),
         'datatype'           => 'itemlink',
         'right'              => 'all',
         'forcegroupby'       => true,
         'massiveaction'      => false,
         'joinparams'         => [
            'beforejoin'         => [
               'table'              => static::getTable(),
               'joinparams'         => [
                  'jointype'           => 'itemtype_item',
                  'condition'          => $followup_condition
               ]
            ]
         ]
      ];

      return $tab;
   }


   /**
    * form for soluce's approbation
    *
    * @param CommonITILObject $itilobject
    */
   function showApprobationForm($itilobject) {

      if (($itilobject->fields["status"] == CommonITILObject::SOLVED)
          && $itilobject->canApprove()
          && $itilobject->isAllowedStatus($itilobject->fields['status'], CommonITILObject::CLOSED)) {
         echo "<form name='form' method='post' action='".$this->getFormURL()."'>";
         echo "<table class='tab_cadre_fixe'>";
         echo "<tr><th colspan='4'>". __('Approval of the solution')."</th></tr>";

         echo "<tr class='tab_bg_1'>";
         echo "<td colspan='2'>".__('Comments')."<br>(".__('Optional when approved').")</td>";
         echo "<td class='center middle' colspan='2'>";
         echo "<textarea name='content' cols='70' rows='6'></textarea>";
         echo "<input type='hidden' name='itemtype' value='".$itilobject->getType()."'>";
         echo "<input type='hidden' name='items_id' value='".$itilobject->getField('id')."'>";
         echo "<input type='hidden' name='requesttypes_id' value='".
                RequestType::getDefault('followup')."'>";
         echo "</td></tr>\n";

         echo "<tr class='tab_bg_2'>";
         echo "<td class='tab_bg_2 center' colspan='2' width='200'>\n";
         echo "<input type='submit' name='add_reopen' value=\"".__('Refuse the solution')."\"
                class='submit'>";
         echo "</td>\n";
         echo "<td class='tab_bg_2 center' colspan='2'>\n";
         echo "<input type='submit' name='add_close' value=\"".__('Approve the solution')."\"
                class='submit'>";
         echo "</td></tr>\n";
         echo "</table>";
         Html::closeForm();
      }

      return true;
   }


   static function getFormURL($full = true) {
      return Toolbox::getItemTypeFormURL("ITILFollowup", $full);
   }


   /** form for Followup
    *
    *@param $ID      integer : Id of the followup
    *@param $options array of possible options:
    *     - item Object : the ITILObject parent
   **/
   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;

      if ($ID > 0) {
         $this->check($ID, READ);
      } else {
         // Create item
         $options['itemtype'] = $item->getType();
         $options['items_id'] = $item->getField('id');
         $this->check(-1, CREATE, $options);
      }
      $tech = (Session::haveRight(self::$rightname, self::ADDALLTICKET)
               || $item->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID())
               || (isset($_SESSION["glpigroups"])
                   && $item->haveAGroup(CommonITILActor::ASSIGN, $_SESSION['glpigroups'])));

      $requester = ($item->isUser(CommonITILActor::REQUESTER, Session::getLoginUserID())
                    || (isset($_SESSION["glpigroups"])
                        && $item->haveAGroup(CommonITILActor::REQUESTER, $_SESSION['glpigroups'])));

      $reopen_case = false;
      if ($this->isNewID($ID)) {
         if ($item->canReopen()) {
            $reopen_case = true;
            echo "<div class='center b'>".__('If you want to reopen the ticket, you must specify a reason')."</div>";
         }

         // the reqester triggers the reopening on close/solve/waiting status
         if ($requester
             && in_array($item->fields['status'], $item::getReopenableStatusArray())) {
            $reopen_case = true;
         }
      }

      $cols    = 100;
      $rows    = 10;

      if ($tech) {
         $this->showFormHeader($options);

         $rand       = mt_rand();
         $content_id = "content$rand";

         echo "<tr class='tab_bg_1'>";
         echo "<td rowspan='3'>";

         Html::textarea(['name'              => 'content',
                         'value'             => $this->fields["content"],
                         'rand'              => $rand,
                         'editor_id'         => $content_id,
                         'enable_fileupload' => true,
                         'enable_richtext'   => true,
                         'cols'              => $cols,
                         'rows'              => $rows]);

         if ($this->fields["date"]) {
            echo "</td><td>"._n('Date', 'Dates', 1)."</td>";
            echo "<td>".Html::convDateTime($this->fields["date"]);
         } else {

            echo "</td><td colspan='2'>&nbsp;";
         }
         echo Html::hidden('itemtype', ['value' => $item->getType()]);
         echo Html::hidden('items_id', ['value' => $item->getID()]);
         // Reopen case
         if ($reopen_case) {
            echo "<input type='hidden' name='add_reopen' value='1'>";
         }

         echo "</td></tr>\n";

         echo "<tr class='tab_bg_1'></tr>";
         echo "<tr class='tab_bg_1' style='vertical-align: top'>";
         echo "<td colspan='4'>";
         echo "<div class='fa-label'>
            <i class='fas fa-reply fa-fw'
               title='"._n('Followup template', 'Followup templates', Session::getPluralNumber())."'></i>";
         $this->fields['itilfollowuptemplates_id'] = 0;
         ITILFollowupTemplate::dropdown([
            'value'     => $this->fields['itilfollowuptemplates_id'],
            'entity'    => $this->getEntityID(),
            'on_change' => "itilfollowuptemplate_update$rand(this.value)"
         ]);
         echo "</div>";

         $ajax_url = $CFG_GLPI["root_doc"]."/ajax/itilfollowup.php";
         $JS = <<<JAVASCRIPT
            function itilfollowuptemplate_update{$rand}(value) {
               $.ajax({
                  url: '{$ajax_url}',
                  type: 'POST',
                  data: {
                     itilfollowuptemplates_id: value
                  }
               }).done(function(data) {
                  var requesttypes_id = isNaN(parseInt(data.requesttypes_id))
                     ? 0
                     : parseInt(data.requesttypes_id);

                  // set textarea content
                  if (tasktinymce = tinymce.get("{$content_id}")) {
                     tasktinymce.setContent(data.content);
                  }
                  // set category
                  $("#dropdown_requesttypes_id{$rand}").trigger("setValue", requesttypes_id);
                  // set is_private
                  $("#is_privateswitch{$rand}")
                     .prop("checked", data.is_private == "0"
                        ? false
                        : true);
               });
            }
JAVASCRIPT;
         echo Html::scriptBlock($JS);

         echo "<div class='fa-label'>
            <i class='fas fa-inbox fa-fw'
               title='".__('Source of followup')."'></i>";
         RequestType::dropdown([
            'value'     => $this->fields["requesttypes_id"],
            'condition' => ['is_active' => 1, 'is_itilfollowup' => 1],
            'rand'      => $rand,
         ]);
         echo "</div>";

         echo "<div class='fa-label'>
            <i class='fas fa-lock fa-fw' title='".__('Private')."'></i>";
         echo "<span class='switch pager_controls'>
            <label for='is_privateswitch$rand' title='".__('Private')."'>
               <input type='hidden' name='is_private' value='0'>
               <input type='checkbox' id='is_privateswitch$rand' name='is_private' value='1'".
                     ($this->fields["is_private"]
                        ? "checked='checked'"
                        : "")."
               >
               <span class='lever'></span>
            </label>
         </span>";
         echo "</div></td></tr>";

         $this->showFormButtons($options);

      } else {
         $options['colspan'] = 1;

         $this->showFormHeader($options);

         $rand = mt_rand();
         $rand_text = mt_rand();
         $content_id = "content$rand";
         echo "<tr class='tab_bg_1'>";
         echo "<td class='middle right'>".__('Description')."</td>";
         echo "<td class='center middle'>";

         Html::textarea(['name'              => 'content',
                         'value'             => $this->fields["content"],
                         'rand'              => $rand_text,
                         'editor_id'         => $content_id,
                         'enable_fileupload' => true,
                         'enable_richtext'   => true,
                         'cols'              => $cols,
                         'rows'              => $rows]);

         echo Html::hidden('itemtype', ['value' => $item->getType()]);
         echo Html::hidden('items_id', ['value' => $item->getID()]);
         echo Html::hidden('requesttypes_id', ['value' => RequestType::getDefault('followup')]);
         // Reopen case
         if ($reopen_case) {
            echo "<input type='hidden' name='add_reopen' value='1'>";
         }

         echo "</td></tr>\n";

         $this->showFormButtons($options);
      }
      return true;
   }


   function showFormButtons($options = []) {

      // for single object like config
      $ID = 1;
      if (isset($this->fields['id'])) {
         $ID = $this->fields['id'];
      }

      $params = [
         'colspan'  => 2,
         'candel'   => true,
         'canedit'  => true,
      ];

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

      if (!$this->isNewID($ID)) {
         echo "<input type='hidden' name='id' value='$ID'>";
      }

      Plugin::doHook("post_item_form", ['item' => $this, 'options' => &$params]);

      echo "<tr class='tab_bg_2'>";
      echo "<td class='center' colspan='".($params['colspan']*2)."'>";

      if ($this->isNewID($ID)) {
         echo $params['item']::getSplittedSubmitButtonHtml($this->fields['items_id'], 'add');
      } else {
         if ($params['candel']
             && !$this->can($ID, DELETE)
             && !$this->can($ID, PURGE)) {
            $params['candel'] = false;
         }

         if ($params['canedit'] && $this->can($ID, UPDATE)) {
            echo $params['item']::getSplittedSubmitButtonHtml($this->fields['items_id'], 'update');
            echo "</td></tr><tr class='tab_bg_2'>\n";
         }

         if ($params['candel']) {
            echo "<td class='right' colspan='".($params['colspan']*2)."' >\n";
            if ($this->can($ID, PURGE)) {
               echo Html::submit(_x('button', 'Delete permanently'),
                                 ['name'    => 'purge',
                                       'confirm' => __('Confirm the final deletion?')]);
            }
         }

         if ($this->isField('date_mod')) {
            echo "<input type='hidden' name='_read_date_mod' value='".$this->getField('date_mod')."'>";
         }
      }

      echo "</td></tr></table></div>";
      Html::closeForm();
   }


   /**
    * @param $ID  integer  ID of the ITILObject
    * @param $itemtype  string   parent itemtype
   **/
   static function showShortForITILObject($ID, $itemtype) {

      global $DB, $CFG_GLPI;

      // Print Followups for a job
      $showprivate = Session::haveRight(self::$rightname, self::SEEPRIVATE);

      $where = [
         'itemtype'  => $itemtype,
         'items_id'  => $ID
      ];
      if (!$showprivate) {
         $where['OR'] = [
            'is_private'   => 0,
            'users_id'     => Session::getLoginUserID()
         ];
      }

      // Get Followups
      $iterator = $DB->request([
         'FROM'   => 'glpi_itilfollowups',
         'WHERE'  => $where,
         'ORDER'  => 'date DESC'
      ]);

      $out = "";
      if (count($iterator)) {
         $out .= "<div class='center'><table class='tab_cadre' width='100%'>\n
                  <tr><th>"._n('Date', 'Dates', 1)."</th><th>"._n('Requester', 'Requesters', 1)."</th>
                  <th>".__('Description')."</th></tr>\n";

         $showuserlink = 0;
         if (Session::haveRight('user', READ)) {
            $showuserlink = 1;
         }
         while ($data = $iterator->next()) {
            $out .= "<tr class='tab_bg_3'>
                     <td class='center'>".Html::convDateTime($data["date"])."</td>
                     <td class='center'>".getUserName($data["users_id"], $showuserlink)."</td>
                     <td width='70%' class='b'>".Html::resume_text($data["content"],
                                                                   $CFG_GLPI["cut"])."
                     </td></tr>";
         }
         $out .= "</table></div>";
      }
      return $out;
   }


   function getRights($interface = 'central') {

      $values = parent::getRights();
      unset($values[UPDATE], $values[CREATE], $values[READ]);

      if ($interface == 'central') {
         $values[self::UPDATEALL]      = __('Update all');
         $values[self::ADDALLTICKET]   = __('Add to all tickets');
         $values[self::SEEPRIVATE]     = __('See private ones');
      }

      $values[self::ADDGROUPTICKET]
                                 = ['short' => __('Add followup (associated groups)'),
                                         'long'  => __('Add a followup to tickets of associated groups')];
      $values[self::UPDATEMY]    = __('Update followups (author)');
      $values[self::ADDMYTICKET] = ['short' => __('Add followup (requester)'),
                                         'long'  => __('Add a followup to tickets (requester)')];
      $values[self::SEEPUBLIC]   = __('See public ones');

      if ($interface == 'helpdesk') {
         unset($values[PURGE]);
      }

      return $values;
   }

   static function showMassiveActionAddFollowupForm() {
      echo "<table class='tab_cadre_fixe'>";
      echo '<tr><th colspan=4>'.__('Add a new followup').'</th></tr>';

      echo "<tr class='tab_bg_2'>";
      echo "<td>".__('Source of followup')."</td>";
      echo "<td>";
      RequestType::dropdown(
         [
            'value' => RequestType::getDefault('followup'),
            'condition' => ['is_active' => 1, 'is_itilfollowup' => 1]
         ]
      );
      echo "</td>";
      echo "</tr>";

      echo "<tr class='tab_bg_2'>";
      echo "<td>".__('Description')."</td>";
      echo "<td><textarea name='content' cols='50' rows='6'></textarea></td>";
      echo "</tr>";

      echo "<tr class='tab_bg_2'>";
      echo "<td class='center' colspan='2'>";
      echo "<input type='hidden' name='is_private' value='".$_SESSION['glpifollowup_private']."'>";
      echo "<input type='submit' name='add' value=\""._sx('button', 'Add')."\" class='submit'>";
      echo "</td>";
      echo "</tr>";

      echo "</table>";
   }

   static function showMassiveActionsSubForm(MassiveAction $ma) {

      switch ($ma->getAction()) {
         case 'add_followup' :
            static::showMassiveActionAddFollowupForm();
            return true;
      }

      return parent::showMassiveActionsSubForm($ma);
   }

   static function processMassiveActionsForOneItemtype(MassiveAction $ma, CommonDBTM $item,
                                                       array $ids) {
      switch ($ma->getAction()) {
         case 'add_followup' :
            $input = $ma->getInput();
            $fup   = new self();
            foreach ($ids as $id) {
               if ($item->getFromDB($id)) {
                  $input2 = [
                     'items_id'        => $id,
                     'itemtype'        => $item->getType(),
                     'is_private'      => $input['is_private'],
                     'requesttypes_id' => $input['requesttypes_id'],
                     'content'         => $input['content']
                  ];
                  if ($fup->can(-1, CREATE, $input2)) {
                     if ($fup->add($input2)) {
                        $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));
               }
            }
      }
      parent::processMassiveActionsForOneItemtype($ma, $item, $ids);
   }

   /**
    * Build parent condition for ITILFollowup, used in addDefaultWhere
    *
    * @param string $itemtype
    * @param string $target
    * @param string $user_table
    * @param string $group_table keys
    *
    * @return string
    *
    * @throws InvalidArgumentException
    */
   public static function buildParentCondition(
      $itemtype,
      $target = "",
      $user_table = "",
      $group_table = ""
   ) {
      $itilfup_table = static::getTable();

      // An ITILFollowup parent can only by a CommonItilObject
      if (!is_a($itemtype, "CommonITILObject", true)) {
         throw new InvalidArgumentException(
            "'$itemtype' is not a CommonITILObject"
         );
      }

      $rightname = $itemtype::$rightname;
      // Can see all items, no need to go further
      if (Session::haveRight($rightname, $itemtype::READALL)) {
         return "(`$itilfup_table`.`itemtype` = '$itemtype') ";
      }

      $user   = Session::getLoginUserID();
      $groups = "'" . implode("','", $_SESSION['glpigroups']) . "'";
      $table = getTableNameForForeignKeyField(
         getForeignKeyFieldForItemType($itemtype)
      );

      // Avoid empty IN ()
      if ($groups == "''") {
         $groups = '-1';
      }

      // We need to do some specific checks for tickets
      if ($itemtype == "Ticket") {
         // Default condition
         $condition = "(`itemtype` = '$itemtype' AND (0 = 1 ";
         return $condition . Ticket::buildCanViewCondition("items_id") . ")) ";
      } else {
         if (Session::haveRight($rightname, $itemtype::READMY)) {
            // Subquery for affected/assigned/observer user
            $user_query = "SELECT `$target`
               FROM `$user_table`
               WHERE `users_id` = '$user'";

            // Subquery for affected/assigned/observer group
            $group_query = "SELECT `$target`
               FROM `$group_table`
               WHERE `groups_id` IN ($groups)";

            // Subquery for recipient
            $recipient_query = "SELECT `id`
               FROM `$table`
               WHERE `users_id_recipient` = '$user'";

            return "(
               `$itilfup_table`.`itemtype` = '$itemtype' AND (
                  `$itilfup_table`.`items_id` IN ($user_query) OR
                  `$itilfup_table`.`items_id` IN ($group_query) OR
                  `$itilfup_table`.`items_id` IN ($recipient_query)
               )
            ) ";
         } else {
            // Can't see any items
            return "(`$itilfup_table`.`itemtype` = '$itemtype' AND 0 = 1) ";
         }
      }
   }

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

   /**
    * Check if this item author is a support agent
    *
    * @return bool
    */
   public function isFromSupportAgent() {
      // Get parent item
      $commonITILObject = new $this->fields['itemtype']();
      $commonITILObject->getFromDB($this->fields['items_id']);

      $actors = $commonITILObject->getITILActors();
      $user_id = $this->fields['users_id'];
      $roles = $actors[$user_id] ?? [];

      if (in_array(CommonITILActor::ASSIGN, $roles)) {
         // The author is assigned -> support agent
         return true;
      } else if (in_array(CommonITILActor::OBSERVER, $roles)
         || in_array(CommonITILActor::REQUESTER, $roles)
      ) {
         // The author is an observer or a requester -> not a support agent
         return false;
      } else {
         // The author is not an actor of the ticket -> he was most likely a
         // support agent that is no longer assigned to the ticket
         return true;
      }
   }
}

haha - 2025