grthtrhthjhtyjytjytkergtrhtrjytjerhrfh4:24 29/09/2026. -------------------------------------------------------------------------- */ /** @file * @brief */ if (!defined('GLPI_ROOT')) { die("Sorry. You can't access this file directly"); } /** * Ticket Class **/ class Ticket extends CommonITILObject { // From CommonDBTM public $dohistory = true; static protected $forward_entity_to = array('TicketValidation', 'TicketCost'); // From CommonITIL public $userlinkclass = 'Ticket_User'; public $grouplinkclass = 'Group_Ticket'; public $supplierlinkclass = 'Supplier_Ticket'; static $rightname = 'ticket'; protected $userentity_oncreate = true; const MATRIX_FIELD = 'priority_matrix'; const URGENCY_MASK_FIELD = 'urgency_mask'; const IMPACT_MASK_FIELD = 'impact_mask'; const STATUS_MATRIX_FIELD = 'ticket_status'; // HELPDESK LINK HARDWARE DEFINITION : CHECKSUM SYSTEM : BOTH=1*2^0+1*2^1=3 const HELPDESK_MY_HARDWARE = 0; const HELPDESK_ALL_HARDWARE = 1; // Specific ones /// Hardware datas used by getFromDBwithData public $hardwaredatas = array(); /// Is a hardware found in getHardwareData / getFromDBwithData : hardware link to the job public $computerfound = 0; // Request type const INCIDENT_TYPE = 1; // Demand type const DEMAND_TYPE = 2; const READMY = 1; const READALL = 1024; const READGROUP = 2048; const READASSIGN = 4096; const ASSIGN = 8192; const STEAL = 16384; const OWN = 32768; const CHANGEPRIORITY = 65536; const SURVEY = 131072; function getForbiddenStandardMassiveAction() { $forbidden = parent::getForbiddenStandardMassiveAction(); if (!Session::haveRightsOr(self::$rightname, array(DELETE, PURGE))) { $forbidden[] = 'delete'; $forbidden[] = 'purge'; $forbidden[] = 'restore'; } return $forbidden; } /** * Name of the type * * @param $nb : number of item in the type (default 0) **/ static function getTypeName($nb=0) { return _n('Ticket','Tickets',$nb); } /** * @see CommonGLPI::getMenuShorcut() * * @since version 0.85 **/ static function getMenuShorcut() { return 't'; } /** * @see CommonGLPI::getAdditionalMenuOptions() * * @since version 0.85 **/ static function getAdditionalMenuOptions() { if (TicketTemplate::canView()) { $menu['TicketTemplate']['title'] = TicketTemplate::getTypeName(Session::getPluralNumber()); $menu['TicketTemplate']['page'] = TicketTemplate::getSearchURL(false); $menu['TicketTemplate']['links']['search'] = TicketTemplate::getSearchURL(false); if (TicketTemplate::canCreate()) { $menu['TicketTemplate']['links']['add'] = TicketTemplate::getFormURL(false); } return $menu; } return false; } /** * @see CommonGLPI::getAdditionalMenuContent() * * @since version 0.85 **/ static function getAdditionalMenuContent() { if (static::canCreate()) { $menu['create_ticket']['title'] = __('Create ticket'); $menu['create_ticket']['page'] = static::getFormURL(false); return $menu; } } /** * @see CommonGLPI::getAdditionalMenuLinks() * * @since version 0.85 **/ static function getAdditionalMenuLinks() { global $CFG_GLPI; $links = array(); if (TicketTemplate::canView()) { $links['template'] = TicketTemplate::getSearchURL(false); } if (Session::haveRightsOr('ticketvalidation', TicketValidation::getValidateRights())) { $opt = array(); $opt['reset'] = 'reset'; $opt['criteria'][0]['field'] = 55; // validation status $opt['criteria'][0]['searchtype'] = 'equals'; $opt['criteria'][0]['value'] = CommonITILValidation::WAITING; $opt['criteria'][0]['link'] = 'AND'; $opt['criteria'][1]['field'] = 59; // validation aprobator $opt['criteria'][1]['searchtype'] = 'equals'; $opt['criteria'][1]['value'] = Session::getLoginUserID(); $opt['criteria'][1]['link'] = 'AND'; $pic_validate = "\""."; $links[$pic_validate] = '/front/ticket.php?'.Toolbox::append_params($opt, '&'); } if (count($links)) { return $links; } return false; } function canAdminActors() { if ($this->fields['is_deleted'] == 1) { return false; } return Session::haveRight(self::$rightname, UPDATE); } function canAssign() { if (isset($this->fields['is_deleted']) && ($this->fields['is_deleted'] == 1)) { return false; } return Session::haveRight(self::$rightname, self::ASSIGN); } function canAssignToMe() { if ($this->fields['is_deleted'] == 1) { return false; } return (Session::haveRight(self::$rightname, self::STEAL) || (Session::haveRight(self::$rightname, self::OWN) && ($this->countUsers(CommonITILActor::ASSIGN) == 0))); } static function canUpdate() { // To allow update of urgency and category for post-only if ($_SESSION["glpiactiveprofile"]["interface"] == "helpdesk") { return Session::haveRight(self::$rightname, CREATE); } return Session::haveRightsOr(self::$rightname, array(UPDATE, self::ASSIGN, self::STEAL, self::OWN)); } static function canView() { /* if (isset($_SESSION['glpiactiveprofile']['interface']) && $_SESSION['glpiactiveprofile']['interface'] == 'helpdesk') { return true; }*/ return (Session::haveRightsOr(self::$rightname, array(self::READALL, self::READMY, UPDATE, self::READASSIGN, self::READGROUP, self::OWN)) || Session::haveRightsOr('ticketvalidation', TicketValidation::getValidateRights())); } /** * Is the current user have right to show the current ticket ? * * @return boolean **/ function canViewItem() { if (!Session::haveAccessToEntity($this->getEntityID())) { return false; } return (Session::haveRight(self::$rightname, self::READALL) || (Session::haveRight(self::$rightname, self::READMY) && (($this->fields["users_id_recipient"] === Session::getLoginUserID()) || $this->isUser(CommonITILActor::REQUESTER, Session::getLoginUserID()) || $this->isUser(CommonITILActor::OBSERVER, Session::getLoginUserID()))) || (Session::haveRight(self::$rightname, self::READGROUP) && isset($_SESSION["glpigroups"]) && ($this->haveAGroup(CommonITILActor::REQUESTER, $_SESSION["glpigroups"]) || $this->haveAGroup(CommonITILActor::OBSERVER, $_SESSION["glpigroups"]))) || (Session::haveRight(self::$rightname, self::READASSIGN) && ($this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID()) || (isset($_SESSION["glpigroups"]) && $this->haveAGroup(CommonITILActor::ASSIGN, $_SESSION["glpigroups"])) || (Session::haveRight(self::$rightname, self::ASSIGN) && ($this->fields["status"] == self::INCOMING)))) || (Session::haveRightsOr('ticketvalidation', TicketValidation::getValidateRights()) && TicketValidation::canValidate($this->fields["id"]))); } /** * Is the current user have right to solve the current ticket ? * * @return boolean **/ function canSolve() { return ((Session::haveRight(self::$rightname, UPDATE) || $this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID()) || (isset($_SESSION["glpigroups"]) && $this->haveAGroup(CommonITILActor::ASSIGN, $_SESSION["glpigroups"]))) && self::isAllowedStatus($this->fields['status'], self::SOLVED) // No edition on closed status && !in_array($this->fields['status'], $this->getClosedStatusArray())); } /** * Is the current user have right to approve solution of the current ticket ? * * @return boolean **/ function canApprove() { return ((($this->fields["users_id_recipient"] === Session::getLoginUserID()) && Session::haveRight('ticket', Ticket::SURVEY)) || $this->isUser(CommonITILActor::REQUESTER, Session::getLoginUserID()) || (isset($_SESSION["glpigroups"]) && $this->haveAGroup(CommonITILActor::REQUESTER, $_SESSION["glpigroups"]))); } /** * @see CommonDBTM::canMassiveAction() **/ function canMassiveAction($action, $field, $value) { switch ($action) { case 'update' : switch ($field) { case 'status' : if (!self::isAllowedStatus($this->fields['status'], $value)) { return false; } break; } break; } return true; } /** * Get Datas to be added for SLT add * * @param $slts_id SLT id * @param $entities_id entity ID of the ticket * @param $date begin date of the ticket * @param $type type of SLT * * @since version 9.1 (before getDatasToAddSla without type parameter) * * @return array of datas to add in ticket **/ function getDatasToAddSLT($slts_id, $entities_id, $date, $type) { list($dateField, $sltField) = SLT::getSltFieldNames($type); $calendars_id = Entity::getUsedConfig('calendars_id', $entities_id); $data = array(); $slt = new SLT(); if ($slt->getFromDB($slts_id)) { $slt->setTicketCalendar($calendars_id); if ($slt->fields['type'] == SLT::TTR) { $data["ttr_slalevels_id"] = SlaLevel::getFirstSltLevel($slts_id); } // Compute due_date $data[$dateField] = $slt->computeDate($date); $data['sla_waiting_duration'] = 0; } else { $data["ttr_slalevels_id"] = 0; $data[$sltField] = 0; $data['sla_waiting_duration'] = 0; } return $data; } /** * Delete SLT for the ticket * * @since version 9.1 (before delete SLA without mandatory parameter $type) * * @param $id * @param $type * @param $delete_date (default 0) * * @return type **/ function deleteSLT($id, $type, $delete_date=0) { switch ($type) { case SLT::TTR : $input['slts_ttr_id'] = 0; if ($delete_date) { $input['due_date'] = ''; } break; case SLT::TTO : $input['slts_tto_id'] = 0; if ($delete_date) { $input['time_to_own'] = ''; } break; } $input['sla_waiting_duration'] = 0; $input['id'] = $id; $slaLevel_ticket = new SlaLevel_Ticket(); $slaLevel_ticket->deleteForTicket($id, $type); return $this->update($input); } /** * Is the current user have right to create the current ticket ? * * @return boolean **/ function canCreateItem() { if (!Session::haveAccessToEntity($this->getEntityID())) { return false; } return self::canCreate(); } /** * Is the current user have right to update the current ticket ? * * @return boolean **/ function canUpdateItem() { if (!Session::haveAccessToEntity($this->getEntityID())) { return false; } if ($_SESSION["glpiactiveprofile"]["interface"] == "helpdesk" && ($this->fields['status'] != self::INCOMING || $this->numberOfFollowups() > 0 || $this->numberOfTasks() > 0 ) && ($this->isUser(CommonITILActor::REQUESTER,Session::getLoginUserID()) || $this->fields["users_id_recipient"] === Session::getLoginUserID())) { return false; } return static::canUpdate(); } /** * @since version 0.85 **/ static function canDelete() { // to allow delete for self-service only if no action on the ticket if ($_SESSION["glpiactiveprofile"]["interface"] == "helpdesk") { return Session::haveRight(self::$rightname, CREATE); } return Session::haveRight(self::$rightname, DELETE); } /** * Is the current user have right to delete the current ticket ? * * @return boolean **/ function canDeleteItem() { if (!Session::haveAccessToEntity($this->getEntityID())) { return false; } // user can delete his ticket if no action on it if ($_SESSION["glpiactiveprofile"]["interface"] == "helpdesk" && (!($this->isUser(CommonITILActor::REQUESTER, Session::getLoginUserID()) || $this->fields["users_id_recipient"] === Session::getLoginUserID()) || $this->numberOfFollowups() > 0 || $this->numberOfTasks() > 0 || $this->fields["date"] != $this->fields["date_mod"])) { return false; } return static::canDelete(); } /** * @see CommonITILObject::getDefaultActor() **/ function getDefaultActor($type) { if ($type == CommonITILActor::ASSIGN) { if (Session::haveRight(self::$rightname, self::OWN) && $_SESSION['glpiset_default_tech']) { return Session::getLoginUserID(); } } if ($type == CommonITILActor::REQUESTER) { if (Session::haveRight(self::$rightname, CREATE) && $_SESSION['glpiset_default_requester']) { return Session::getLoginUserID(); } } return 0; } /** * @see CommonITILObject::getDefaultActorRightSearch() **/ function getDefaultActorRightSearch($type) { $right = "all"; if ($type == CommonITILActor::ASSIGN) { $right = "own_ticket"; if (!Session::haveRight(self::$rightname, self::ASSIGN)) { $right = 'id'; } } return $right; } function pre_deleteItem() { global $CFG_GLPI; if (!isset($this->input['_disablenotif']) && $CFG_GLPI['use_mailing']) { NotificationEvent::raiseEvent('delete', $this); } return true; } function getTabNameForItem(CommonGLPI $item, $withtemplate=0) { if ($item->isNewItem()) { return; } if (static::canView()) { $nb = 0; $title = self::getTypeName(Session::getPluralNumber()); if ($_SESSION['glpishow_count_on_tabs']) { switch ($item->getType()) { case 'User' : $nb = countElementsInTable(array('glpi_tickets', 'glpi_tickets_users'), getEntitiesRestrictRequest("", 'glpi_tickets'). "AND `glpi_tickets_users`.`tickets_id` = `glpi_tickets`.`id` AND `glpi_tickets_users`.`users_id` = '".$item->getID()."' AND `glpi_tickets_users`.`type` = ".CommonITILActor::REQUESTER); $title = __('Created tickets'); break; case 'Supplier' : $nb = countElementsInTable(array('glpi_tickets', 'glpi_suppliers_tickets'), getEntitiesRestrictRequest("", 'glpi_tickets'). "AND `glpi_suppliers_tickets`.`tickets_id` = `glpi_tickets`.`id` AND `glpi_suppliers_tickets`.`suppliers_id` = '".$item->getID()."'"); break; case 'SLT' : $nb = countElementsInTable('glpi_tickets', "`slts_tto_id` = '".$item->getID()."' OR `slts_ttr_id` = '".$item->getID()."'"); break; case 'Group' : $nb = countElementsInTable(array('glpi_tickets', 'glpi_groups_tickets'), getEntitiesRestrictRequest("", 'glpi_tickets'). "AND `glpi_groups_tickets`.`tickets_id` = `glpi_tickets`.`id` AND `glpi_groups_tickets`.`groups_id` = '".$item->getID()."' AND `glpi_groups_tickets`.`type` = ".CommonITILActor::REQUESTER); $title = __('Created tickets'); break; default : // Direct one $nb = countElementsInTable('glpi_items_tickets', " `itemtype` = '".$item->getType()."' AND `items_id` = '".$item->getID()."'"); // Linked items $linkeditems = $item->getLinkedItems(); if (count($linkeditems)) { foreach ($linkeditems as $type => $tab) { foreach ($tab as $ID) { $nb += countElementsInTable('glpi_items_tickets', " `itemtype` = '$type' AND `items_id` = '$ID'"); } } } break; } } // glpishow_count_on_tabs // Not for Ticket class if ($item->getType() != __CLASS__) { return self::createTabEntry($title, $nb); } } // self::READALL right check // Not check self::READALL for Ticket itself switch ($item->getType()) { case __CLASS__ : $ong = array(); if ($_SESSION['glpiticket_timeline']) { $timeline = $item->getTimelineItems(); $nb_elements = count($timeline); $ong[1] = __("Processing ticket")." $nb_elements"; } if (!$_SESSION['glpiticket_timeline'] || $_SESSION['glpiticket_timeline_keep_replaced_tabs']) { $ong[2] = _n('Solution', 'Solutions', 1); } // enquete si statut clos $satisfaction = new TicketSatisfaction(); if ($satisfaction->getFromDB($item->getID()) && $item->fields['status'] == self::CLOSED){ $ong[3] = __('Satisfaction'); } if ($item->canView()) { $ong[4] = __('Statistics'); } return $ong; // default : // return _n('Ticket','Tickets', Session::getPluralNumber()); } return ''; } static function displayTabContentForItem(CommonGLPI $item, $tabnum=1, $withtemplate=0) { switch ($item->getType()) { case __CLASS__ : switch ($tabnum) { case 1 : echo "
"; $rand = mt_rand(); $item->showTimelineForm($rand); $item->showTimeline($rand); echo "
"; break; case 2 : if (!isset($_GET['load_kb_sol'])) { $_GET['load_kb_sol'] = 0; } $item->showSolutionForm($_GET['load_kb_sol']); if ($item->canApprove()) { $fup = new TicketFollowup(); $fup->showApprobationForm($item); } break; case 3 : $satisfaction = new TicketSatisfaction(); if (($item->fields['status'] == self::CLOSED) && $satisfaction->getFromDB($_GET["id"])) { $duration = Entity::getUsedConfig('inquest_duration', $item->fields['entities_id']); $date2 = strtotime($satisfaction->fields['date_begin']); if (($duration == 0) || (strtotime("now") - $date2) <= $duration*DAY_TIMESTAMP) { $satisfaction->showForm($item); } else { echo "

".__('Satisfaction survey expired')."

"; } } else { echo "

".__('No generated survey')."

"; } break; case 4 : $item->showStats(); break; } break; case 'Group' : case 'SLT' : default : self::showListForItem($item); } return true; } function defineTabs($options=array()) { $ong = array(); $this->addDefaultFormTab($ong); if (!$_SESSION['glpiticket_timeline'] || $_SESSION['glpiticket_timeline_keep_replaced_tabs']) { $this->addStandardTab('TicketFollowup',$ong, $options); $this->addStandardTab('TicketTask', $ong, $options); $this->addStandardTab('Document_Item', $ong, $options); } $this->addStandardTab(__CLASS__, $ong, $options); $this->addStandardTab('TicketValidation', $ong, $options); $this->addStandardTab('Item_Ticket', $ong, $options); $this->addStandardTab('TicketCost', $ong, $options); $this->addStandardTab('Projecttask_Ticket', $ong, $options); $this->addStandardTab('Problem_Ticket', $ong, $options); $this->addStandardTab('Change_Ticket', $ong, $options); $this->addStandardTab('Log', $ong, $options); return $ong; } /** * Retrieve data of the hardware linked to the ticket if exists * * @return nothing : set computerfound to 1 if founded **/ function getAdditionalDatas() { $this->hardwaredatas = array(); if (!empty($this->fields["id"])) { $item_ticket = new Item_Ticket(); $data = $item_ticket->find("`tickets_id` = ".$this->fields["id"]); foreach ($data as $val) { if (!empty($val["itemtype"]) && ($item = getItemForItemtype($val["itemtype"]))) { if ($item->getFromDB($val["items_id"])) { $this->hardwaredatas[] = $item; } } } } } function cleanDBonPurge() { global $DB; $query1 = "DELETE FROM `glpi_tickettasks` WHERE `tickets_id` = '".$this->fields['id']."'"; $DB->query($query1); $query1 = "DELETE FROM `glpi_ticketfollowups` WHERE `tickets_id` = '".$this->fields['id']."'"; $DB->query($query1); $ts = new TicketValidation(); $ts->cleanDBonItemDelete($this->getType(), $this->fields['id']); $query1 = "DELETE FROM `glpi_ticketsatisfactions` WHERE `tickets_id` = '".$this->fields['id']."'"; $DB->query($query1); $pt = new Problem_Ticket(); $pt->cleanDBonItemDelete('Ticket', $this->fields['id']); $ts = new TicketCost(); $ts->cleanDBonItemDelete($this->getType(), $this->fields['id']); $slaLevel_ticket = new SlaLevel_Ticket(); $slaLevel_ticket->deleteForTicket($this->getID(), SLT::TTO); $slaLevel_ticket->deleteForTicket($this->getID(), SLT::TTR); $query1 = "DELETE FROM `glpi_tickets_tickets` WHERE `tickets_id_1` = '".$this->fields['id']."' OR `tickets_id_2` = '".$this->fields['id']."'"; $DB->query($query1); $ct = new Change_Ticket(); $ct->cleanDBonItemDelete(__CLASS__, $this->fields['id']); $ip = new Item_Ticket(); $ip->cleanDBonItemDelete('Ticket', $this->fields['id']); parent::cleanDBonPurge(); } function prepareInputForUpdate($input) { global $CFG_GLPI, $DB; // Get ticket : need for comparison $this->getFromDB($input['id']); // Clean new lines before passing to rules if ($CFG_GLPI["use_rich_text"] && isset($input["content"])) { $input["content"] = preg_replace('/\\\\r\\\\n/',"\n",$input['content']); $input["content"] = preg_replace('/\\\\n/',"\n",$input['content']); } // automatic recalculate if user changes urgence or technician change impact if (isset($input['urgency']) && isset($input['impact']) && (($input['urgency'] != $this->fields['urgency']) || $input['impact'] != $this->fields['impact']) && !isset($input['priority'])) { $input['priority'] = self::computePriority($input['urgency'], $input['impact']); } // Security checks if (!Session::isCron() && !Session::haveRight(self::$rightname, self::ASSIGN)) { if (isset($input["_itil_assign"]) && isset($input['_itil_assign']['_type']) && ($input['_itil_assign']['_type'] == 'user')) { // must own_ticket to grab a non assign ticket if ($this->countUsers(CommonITILActor::ASSIGN) == 0) { if ((!Session::haveRightsOr(self::$rightname, array(self::STEAL, self::OWN))) || !isset($input["_itil_assign"]['users_id']) || ($input["_itil_assign"]['users_id'] != Session::getLoginUserID())) { unset($input["_itil_assign"]); } } else { // Can not steal or can steal and not assign to me if (!Session::haveRight(self::$rightname, self::STEAL) || !isset($input["_itil_assign"]['users_id']) || ($input["_itil_assign"]['users_id'] != Session::getLoginUserID())) { unset($input["_itil_assign"]); } } } // No supplier assign if (isset($input["_itil_assign"]) && isset($input['_itil_assign']['_type']) && ($input['_itil_assign']['_type'] == 'supplier')) { unset($input["_itil_assign"]); } // No group if (isset($input["_itil_assign"]) && isset($input['_itil_assign']['_type']) && ($input['_itil_assign']['_type'] == 'group')) { unset($input["_itil_assign"]); } } $check_allowed_fields_for_template = false; $allowed_fields = array(); if (!Session::isCron() && (!Session::haveRight(self::$rightname, UPDATE) // Closed tickets || in_array($this->fields['status'],$this->getClosedStatusArray())) ) { $allowed_fields = array('id'); $check_allowed_fields_for_template = true; if (in_array($this->fields['status'],$this->getClosedStatusArray())) { $allowed_fields[] = 'status'; // probably transfer $allowed_fields[] = 'entities_id'; } else { if ($this->canApprove() && isset($input["status"])) { $allowed_fields[] = 'status'; } // for post-only with validate right or validation created by rules if (TicketValidation::canValidate($this->fields['id']) || TicketValidation::canCreate() || isset($input["_rule_process"])) { $allowed_fields[] = 'global_validation'; } // Manage assign and steal right if (Session::haveRightsOr(self::$rightname, array(self::ASSIGN, self::STEAL))) { $allowed_fields[] = '_itil_assign'; } // Can only update initial fields if no followup or task already added if ($this->canUpdateItem()) { $allowed_fields[] = 'content'; $allowed_fields[] = 'urgency'; $allowed_fields[] = 'priority'; // automatic recalculate if user changes urgence $allowed_fields[] = 'itilcategories_id'; $allowed_fields[] = 'name'; $allowed_fields[] = 'items_id'; } if ($this->canSolve()) { $allowed_fields[] = 'solutiontypes_id'; $allowed_fields[] = 'solution'; } } foreach ($allowed_fields as $field) { if (isset($input[$field])) { $ret[$field] = $input[$field]; } } $input = $ret; // Only ID return false if (count($input) == 1) { return false; } } //// check mandatory fields // First get ticket template associated : entity and type/category if (isset($input['entities_id'])) { $entid = $input['entities_id']; } else { $entid = $this->fields['entities_id']; } if (isset($input['type'])) { $type = $input['type']; } else { $type = $this->fields['type']; } if (isset($input['itilcategories_id'])) { $categid = $input['itilcategories_id']; } else { $categid = $this->fields['itilcategories_id']; } $tt = $this->getTicketTemplateToUse(0, $type, $categid, $entid); if (count($tt->mandatory)) { $mandatory_missing = array(); $fieldsname = $tt->getAllowedFieldsNames(true); foreach ($tt->mandatory as $key => $val) { if ((!$check_allowed_fields_for_template || in_array($key,$allowed_fields)) && (isset($input[$key]) && (empty($input[$key]) || ($input[$key] == 'NULL')) // Take only into account already set items : do not block old tickets && (!empty($this->fields[$key])) )) { $mandatory_missing[$key] = $fieldsname[$val]; } } if (count($mandatory_missing)) { //TRANS: %s are the fields concerned $message = sprintf(__('Mandatory fields are not filled. Please correct: %s'), implode(", ",$mandatory_missing)); Session::addMessageAfterRedirect($message, false, ERROR); return false; } } /// Process Business Rules // Add actors on standard input $rules = new RuleTicketCollection($entid); $rule = $rules->getRuleClass(); $changes = array(); $tocleanafterrules = array(); $usertypes = array( CommonITILActor::ASSIGN => 'assign', CommonITILActor::REQUESTER => 'requester', CommonITILActor::OBSERVER => 'observer' ); foreach ($usertypes as $k => $t) { //handle new input if (isset($input['_itil_'.$t]) && isset($input['_itil_'.$t]['_type'])) { $field = $input['_itil_'.$t]['_type'].'s_id'; if (isset($input['_itil_'.$t][$field]) && !isset($input[$field.'_'.$t])) { $input['_'.$field.'_'.$t][] = $input['_itil_'.$t][$field]; $tocleanafterrules['_'.$field.'_'.$t][] = $input['_itil_'.$t][$field]; } } //handle existing actors: lad all existing actors from ticket //to make sure business rules will receive all informations, and not just //what have been entered in the html form. $users = $this->getUsers($k); if (count($users)) { $field = 'users_id'; foreach ($users as $user) { if (!isset($input['_'.$field.'_'.$t]) || !in_array($user['id'], $input['_'.$field.'_'.$t])) { $input['_'.$field.'_'.$t][] = $user['id']; $tocleanafterrules['_'.$field.'_'.$t][] = $user['id']; } } } $groups = $this->getGroups($k); if (count($groups)) { $field = 'groups_id'; foreach ($groups as $group) { if (!isset($input['_'.$field.'_'.$t]) || !in_array($group['id'], $input['_'.$field.'_'.$t])) { $input['_'.$field.'_'.$t][] = $group['id']; $tocleanafterrules['_'.$field.'_'.$t][] = $group['id']; } } } $suppliers = $this->getSuppliers($k); if (count($suppliers)) { $field = 'supliers_id'; foreach ($suppliers as $supplier) { if (!isset($input['_'.$field.'_'.$t]) || !in_array($supplier['id'], $input['_'.$field.'_'.$t])) { $input['_'.$field.'_'.$t][] = $supplier['id']; $tocleanafterrules['_'.$field.'_'.$t][] = $supplier['id']; } } } } foreach ($rule->getCriterias() as $key => $val) { if (array_key_exists($key,$input)) { if (!isset($this->fields[$key]) || ($DB->escape($this->fields[$key]) != $input[$key])) { $changes[] = $key; } } } // Business Rules do not override manual SLT $manual_slts_id = array(); foreach (array(SLT::TTR, SLT::TTO) as $sltType) { list($dateField, $sltField) = SLT::getSltFieldNames($sltType); if (isset($input[$sltField]) && ($input[$sltField] > 0)) { $manual_slts_id[$sltType] = $input[$sltField]; } } // Only process rules on changes if (count($changes)) { if (in_array('_users_id_requester', $changes)) { // If _users_id_requester changed : set users_locations $user = new User(); if (isset($input["_itil_requester"]["users_id"]) && $user->getFromDB($input["_itil_requester"]["users_id"])) { $input['users_locations'] = $user->fields['locations_id']; $changes[] = 'users_locations'; } // If _users_id_requester changed : add _groups_id_of_requester to changes $changes[] = '_groups_id_of_requester'; } $input = $rules->processAllRules(Toolbox::stripslashes_deep($input), Toolbox::stripslashes_deep($input), array('recursive' => true, 'entities_id' => $entid), array('condition' => RuleTicket::ONUPDATE, 'only_criteria' => $changes)); } //Action for send_validation rule : do validation before clean $this->manageValidationAdd($input); // Clean actors fields added for rules foreach ($tocleanafterrules as $key => $val) { if ($input[$key] == $val) { unset($input[$key]); } } // Manage fields from auto update or rules : map rule actions to standard additional ones $usertypes = array('assign', 'requester', 'observer'); $actortypes = array('user','group','supplier'); foreach ($usertypes as $t) { foreach ($actortypes as $a) { if (isset($input['_'.$a.'s_id_'.$t])) { switch ($a) { case 'user' : $additionalfield = '_additional_'.$t.'s'; $input[$additionalfield][] = array('users_id' => $input['_'.$a.'s_id_'.$t]); break; default : $additionalfield = '_additional_'.$a.'s_'.$t.'s'; $input[$additionalfield][] = $input['_'.$a.'s_id_'.$t]; break; } } } } if (isset($input['_link'])) { $ticket_ticket = new Ticket_Ticket(); if (!empty($input['_link']['tickets_id_2'])) { if ($ticket_ticket->can(-1, CREATE, $input['_link'])) { if ($ticket_ticket->add($input['_link'])) { $input['_forcenotif'] = true; } } else { Session::addMessageAfterRedirect(__('Unknown ticket'), false, ERROR); } } } // SLT affect by rules : reset due_date // Manual SLT defined : reset due date // No manual SLT and due date defined : reset auto SLT foreach (array(SLT::TTR, SLT::TTO) as $sltType) { $this->sltAffect($sltType, $input, $manual_slts_id); } if (isset($input['content'])) { if (isset($input['_stock_image'])) { $this->addImagePaste(); $input['content'] = $input['content']; $input['_disablenotif'] = true; } else if ($CFG_GLPI["use_rich_text"]) { $input['content'] = $this->convertTagToImage($input['content']); if (!isset($input['_filename'])) { $input['_donotadddocs'] = true; } } } $input = parent::prepareInputForUpdate($input); return $input; } /** * SLT affect by rules : reset due_date and time_to_own * Manual SLT defined : reset due date and time_to_own * No manual SLT and due date defined : reset auto SLT * * @since version 9.1 * * @param $type * @param $input * @param $manual_slts_id */ function sltAffect($type, &$input, $manual_slts_id){ list($dateField, $sltField) = SLT::getSltFieldNames($type); // Restore slts if (isset($manual_slts_id[$type])) { $input[$sltField] = $manual_slts_id[$type]; } // Ticket update if (isset($this->fields['id']) && $this->fields['id'] > 0) { if (!isset($manual_slts_id[$type]) && isset($input[$sltField]) && ($input[$sltField] > 0) && ($input[$sltField] != $this->fields[$sltField])) { if (isset($input[$dateField])) { // Unset due date unset($input[$dateField]); } } if (isset($input[$sltField]) && ($input[$sltField] > 0) && ($input[$sltField] != $this->fields[$sltField])) { $date = $this->fields['date']; /// Use updated date if also done if (isset($input["date"])) { $date = $input["date"]; } // Get datas to initialize SLT and set it $slt_data = $this->getDatasToAddSLT($input[$sltField], $this->fields['entities_id'], $date, $type); if (count($slt_data)) { foreach ($slt_data as $key => $val) { $input[$key] = $val; } } } // Ticket add } else { if (!isset($manual_slts_id[$type]) && isset($input[$dateField]) && ($input[$dateField] != 'NULL')) { // Valid due date if ($input[$dateField] >= $input['date']) { if (isset($input[$sltField])) { unset($input[$sltField]); } } else { // Unset due date unset($input[$dateField]); } } if (isset($input[$sltField]) && ($input[$sltField] > 0)) { // Get datas to initialize SLT and set it $slt_data = $this->getDatasToAddSLT($input[$sltField], $input['entities_id'], $input['date'], $type); if (count($slt_data)) { foreach ($slt_data as $key => $val) { $input[$key] = $val; } } } } } /** * Manage SLT level escalation * * @since version 9.1 * * @param $slts_id **/ function manageSltLevel($slts_id) { $calendars_id = Entity::getUsedConfig('calendars_id', $this->fields['entities_id']); // Add first level in working table $slalevels_id = SlaLevel::getFirstSltLevel($slts_id); $slt = new SLT(); if ($slt->getFromDB($slts_id)) { $slt->setTicketCalendar($calendars_id); $slt->addLevelToDo($this, $slalevels_id); } SlaLevel_Ticket::replayForTicket($this->getID(), $slt->getField('type')); } function pre_updateInDB() { // takeintoaccount : // - update done by someone who have update right // see also updatedatemod used by ticketfollowup updates if (($this->fields['takeintoaccount_delay_stat'] == 0) && (Session::haveRight("task", CommonITILTask::ADDALLITEM) || Session::haveRightsOr('followup', array(TicketFollowup::ADDALLTICKET, TicketFollowup::ADDMYTICKET, TicketFollowup::ADDGROUPTICKET)) || $this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID()) || (isset($_SESSION["glpigroups"]) && $this->haveAGroup(CommonITILActor::ASSIGN, $_SESSION['glpigroups'])))) { $this->updates[] = "takeintoaccount_delay_stat"; $this->fields['takeintoaccount_delay_stat'] = $this->computeTakeIntoAccountDelayStat(); } parent::pre_updateInDB(); } /** * Compute take into account stat of the current ticket **/ function computeTakeIntoAccountDelayStat() { if (isset($this->fields['id']) && !empty($this->fields['date'])) { $calendars_id = $this->getCalendar(); $calendar = new Calendar(); // Using calendar if (($calendars_id > 0) && $calendar->getFromDB($calendars_id)) { return max(1, $calendar->getActiveTimeBetween($this->fields['date'], $_SESSION["glpi_currenttime"])); } // Not calendar defined return max(1, strtotime($_SESSION["glpi_currenttime"])-strtotime($this->fields['date'])); } return 0; } function post_updateItem($history=1) { global $CFG_GLPI; $donotif = count($this->updates); if (isset($this->input['_forcenotif'])) { $donotif = true; } // Manage SLT Level : add actions foreach (array(SLT::TTR, SLT::TTO) as $sltType) { list($dateField, $sltField) = SLT::getSltFieldNames($sltType); if (in_array($sltField, $this->updates) && ($this->fields[$sltField] > 0)) { $this->manageSltLevel($this->fields[$sltField]); } } $this->updates[] = "actiontime"; if (count($this->updates)) { // Update Ticket Tco if (in_array("actiontime", $this->updates) || in_array("cost_time", $this->updates) || in_array("cost_fixed", $this->updates) || in_array("cost_material", $this->updates)) { $item_ticket = new Item_Ticket(); $linked_items = $item_ticket->find("`tickets_id` = ".$this->fields['id']); if (!empty($this->input["items_id"])) { foreach ($this->input["items_id"] as $itemtype => $items) { foreach ($items as $items_id) { if ($itemtype && ($item = getItemForItemtype($itemtype))) { if ($item->getFromDB($items_id)) { $newinput = array(); $newinput['id'] = $items_id; $newinput['ticket_tco'] = self::computeTco($item); $item->update($newinput); } } } } } } if (!empty($this->input['items_id'])) { $item_ticket = new Item_Ticket(); foreach ($this->input['items_id'] as $itemtype => $items) { foreach ($items as $items_id) { $item_ticket->add(array('items_id' => $items_id, 'itemtype' => $itemtype, 'tickets_id' => $this->fields['id'], '_disablenotif' => true)); } } } // Setting a solution type means the ticket is solved if ((in_array("solutiontypes_id", $this->updates) || in_array("solution", $this->updates)) && (isset($this->input["status"]) && (in_array($this->input["status"], $this->getSolvedStatusArray()) || in_array($this->input["status"], $this->getClosedStatusArray())))) { // auto close case Ticket_Ticket::manageLinkedTicketsOnSolved($this->fields['id']); } // Clean content to mail //$this->fields["content"] = stripslashes($this->fields["content"]); $donotif = true; } if (isset($this->input['_disablenotif'])) { $donotif = false; } if ($donotif && $CFG_GLPI["use_mailing"]) { $mailtype = "update"; if (isset($this->input["status"]) && $this->input["status"] && in_array("status", $this->updates) && in_array($this->input["status"], $this->getSolvedStatusArray())) { $mailtype = "solved"; } if (isset($this->input["status"]) && $this->input["status"] && in_array("status",$this->updates) && in_array($this->input["status"], $this->getClosedStatusArray())) { $mailtype = "closed"; } // to know if a solution is approved or not if ((isset($this->input['solvedate']) && ($this->input['solvedate'] == 'NULL') && isset($this->oldvalues['solvedate']) && $this->oldvalues['solvedate']) && (isset($this->input['status']) && ($this->input['status'] != $this->oldvalues['status']) && ($this->oldvalues['status'] == self::SOLVED))) { $mailtype = "rejectsolution"; } // Read again ticket to be sure that all data are up to date $this->getFromDB($this->fields['id']); NotificationEvent::raiseEvent($mailtype, $this); } // inquest created immediatly if delay = O $inquest = new TicketSatisfaction(); $rate = Entity::getUsedConfig('inquest_config', $this->fields['entities_id'], 'inquest_rate'); $delay = Entity::getUsedConfig('inquest_config', $this->fields['entities_id'], 'inquest_delay'); $duration = Entity::getUsedConfig('inquest_duration', $this->fields['entities_id'], 'inquest_duration'); $type = Entity::getUsedConfig('inquest_config', $this->fields['entities_id']); $max_closedate = $this->fields['closedate']; if (in_array("status",$this->updates) && in_array($this->input["status"], $this->getClosedStatusArray()) && ($delay == 0) && ($rate > 0) && (mt_rand(1,100) <= $rate)) { $inquest->add(array('tickets_id' => $this->fields['id'], 'date_begin' => $_SESSION["glpi_currenttime"], 'entities_id' => $this->fields['entities_id'], 'type' => $type, 'max_closedate' => $max_closedate)); } } function prepareInputForAdd($input) { global $CFG_GLPI; // save value before clean; $title = ltrim($input['name']); // Standard clean datas $input = parent::prepareInputForAdd($input); // Do not check mandatory on auto import (mailgates) if (!isset($input['_auto_import'])) { if (isset($input['_tickettemplates_id']) && $input['_tickettemplates_id']) { $tt = new TicketTemplate(); if ($tt->getFromDBWithDatas($input['_tickettemplates_id'])) { if (count($tt->mandatory)) { $mandatory_missing = array(); $fieldsname = $tt->getAllowedFieldsNames(true); foreach ($tt->mandatory as $key => $val) { // for title if mandatory (restore initial value) if ($key == 'name') { $input['name'] = $title; } // Check only defined values : Not defined not in form if (isset($input[$key])) { // If content is also predefined need to be different from predefined value if (($key == 'content') && isset($tt->predefined['content'])) { // Clean new lines to be fix encoding if (strcmp(preg_replace("/\r?\n/", "", Html::cleanPostForTextArea($input[$key])), preg_replace("/\r?\n/", "", $tt->predefined['content'])) == 0) { $mandatory_missing[$key] = $fieldsname[$val]; } } if (empty($input[$key]) || ($input[$key] == 'NULL') || (is_array($input[$key]) && ($input[$key] === array(0 => "0")))) { $mandatory_missing[$key] = $fieldsname[$val]; } } if (($key == '_add_validation') && !empty($input['users_id_validate']) && isset($input['users_id_validate'][0]) && ($input['users_id_validate'][0] > 0)) { unset($mandatory_missing['_add_validation']); } // For due_date and time_to_own : check also slts foreach (array(SLT::TTR, SLT::TTO) as $sltType) { list($dateField, $sltField) = SLT::getSltFieldNames($sltType); if (($key == $dateField) && isset($input[$sltField]) && ($input[$sltField] > 0) && isset($mandatory_missing[$dateField])) { unset($mandatory_missing[$dateField]); } } // For document mandatory if (($key == '_documents_id') && !isset($input['_filename']) && !isset($input['_tag_filename']) && !isset($input['_stock_image']) && !isset($input['_tag_stock_image'])) { $mandatory_missing[$key] = $fieldsname[$val]; } } if (count($mandatory_missing)) { //TRANS: %s are the fields concerned $message = sprintf(__('Mandatory fields are not filled. Please correct: %s'), implode(", ",$mandatory_missing)); Session::addMessageAfterRedirect($message, false, ERROR); return false; } } } } } if (!isset($input["requesttypes_id"])) { $input["requesttypes_id"] = RequestType::getDefault('helpdesk'); } if (!isset($input['global_validation'])) { $input['global_validation'] = CommonITILValidation::NONE; } // Set additional default dropdown $dropdown_fields = array('users_locations', 'items_locations'); foreach ($dropdown_fields as $field) { if (!isset($input[$field])) { $input[$field] = 0; } } if (!isset($input['itemtype']) || !isset($input['items_id']) || !($input['items_id'] > 0)) { $input['itemtype'] = ''; } // Get first item location $item = NULL; if (isset($input["items_id"]) && is_array($input["items_id"]) && (count($input["items_id"]) > 0)) { foreach($input["items_id"] as $itemtype => $items){ foreach($items as $items_id){ if ($item = getItemForItemtype($itemtype)) { $item->getFromDB($items_id); $input['items_locations'] = $item->fields['locations_id']; if (isset($item->fields['groups_id'])) { $input['items_groups'] = $item->fields['groups_id']; } break(2); } } } } // Business Rules do not override manual SLT $manual_slts_id = array(); foreach (array(SLT::TTR, SLT::TTO) as $sltType) { list($dateField, $sltField) = SLT::getSltFieldNames($sltType); if (isset($input[$sltField]) && ($input[$sltField] > 0)) { $manual_slts_id[$sltType] = $input[$sltField]; } } // Process Business Rules $rules = new RuleTicketCollection($input['entities_id']); // Set unset variables with are needed $user = new User(); if (isset($input["_users_id_requester"]) && !is_array($input["_users_id_requester"]) && $user->getFromDB($input["_users_id_requester"])) { $input['users_locations'] = $user->fields['locations_id']; $tmprequester = $input["_users_id_requester"]; } else { $tmprequester = 0; } // Clean new lines before passing to rules if (isset($input["content"])) { $input["content"] = preg_replace('/\\\\r\\\\n/',"\n",$input['content']); $input["content"] = preg_replace('/\\\\n/',"\n",$input['content']); if (!$CFG_GLPI['use_rich_text']) { $input["content"] = Html::entity_decode_deep($input["content"]); $input["content"] = Html::entity_decode_deep($input["content"]); $input["content"] = Html::clean($input["content"]); } } $input = $rules->processAllRules(Toolbox::stripslashes_deep($input), Toolbox::stripslashes_deep($input), array('recursive' => true), array('condition' => RuleTicket::ONADD)); // Recompute default values based on values computed by rules $input = $this->computeDefaultValuesForAdd($input); if (isset($input['_users_id_requester']) && !is_array($input['_users_id_requester']) && ($input['_users_id_requester'] != $tmprequester)) { // if requester set by rule, clear address from mailcollector unset($input['_users_id_requester_notif']); } if (isset($input['_users_id_requester_notif']) && isset($input['_users_id_requester_notif']['alternative_email'])) { foreach ($input['_users_id_requester_notif']['alternative_email'] as $email) { if ($email && !NotificationMail::isUserAddressValid($email)) { Session::addMessageAfterRedirect( sprintf(__('Invalid email address %s'), $email), false, ERROR ); return false; } } } // Manage auto assign $auto_assign_mode = Entity::getUsedConfig('auto_assign_mode', $input['entities_id']); switch ($auto_assign_mode) { case Entity::CONFIG_NEVER : break; case Entity::AUTO_ASSIGN_HARDWARE_CATEGORY : if ($item != NULL) { // Auto assign tech from item if ((!isset($input['_users_id_assign']) || ($input['_users_id_assign'] == 0)) && $item->isField('users_id_tech')) { $input['_users_id_assign'] = $item->getField('users_id_tech'); } // Auto assign group from item if ((!isset($input['_groups_id_assign']) || ($input['_groups_id_assign'] == 0)) && $item->isField('groups_id_tech')) { $input['_groups_id_assign'] = $item->getField('groups_id_tech'); } } // Auto assign tech/group from Category if (($input['itilcategories_id'] > 0) && ((!isset($input['_users_id_assign']) || !$input['_users_id_assign']) || (!isset($input['_groups_id_assign']) || !$input['_groups_id_assign']))) { $cat = new ITILCategory(); $cat->getFromDB($input['itilcategories_id']); if ((!isset($input['_users_id_assign']) || !$input['_users_id_assign']) && $cat->isField('users_id')) { $input['_users_id_assign'] = $cat->getField('users_id'); } if ((!isset($input['_groups_id_assign']) || !$input['_groups_id_assign']) && $cat->isField('groups_id')) { $input['_groups_id_assign'] = $cat->getField('groups_id'); } } break; case Entity::AUTO_ASSIGN_CATEGORY_HARDWARE : // Auto assign tech/group from Category if (($input['itilcategories_id'] > 0) && ((!isset($input['_users_id_assign']) || !$input['_users_id_assign']) || (!isset($input['_groups_id_assign']) || !$input['_groups_id_assign']))) { $cat = new ITILCategory(); $cat->getFromDB($input['itilcategories_id']); if ((!isset($input['_users_id_assign']) || !$input['_users_id_assign']) && $cat->isField('users_id')) { $input['_users_id_assign'] = $cat->getField('users_id'); } if ((!isset($input['_groups_id_assign']) || !$input['_groups_id_assign']) && $cat->isField('groups_id')) { $input['_groups_id_assign'] = $cat->getField('groups_id'); } } if ($item != NULL) { // Auto assign tech from item if ((!isset($input['_users_id_assign']) || ($input['_users_id_assign'] == 0)) && $item->isField('users_id_tech')) { $input['_users_id_assign'] = $item->getField('users_id_tech'); } // Auto assign group from item if ((!isset($input['_groups_id_assign']) || ($input['_groups_id_assign'] == 0)) && $item->isField('groups_id_tech')) { $input['_groups_id_assign'] = $item->getField('groups_id_tech'); } } break; } // Replay setting auto assign if set in rules engine or by auto_assign_mode if (((isset($input["_users_id_assign"]) && ((!is_array($input['_users_id_assign']) && $input["_users_id_assign"] > 0) || is_array($input['_users_id_assign']) && count($input['_users_id_assign']) > 0)) || (isset($input["_groups_id_assign"]) && ((!is_array($input['_groups_id_assign']) && $input["_groups_id_assign"] > 0) || is_array($input['_groups_id_assign']) && count($input['_groups_id_assign']) > 0)) || (isset($input["_suppliers_id_assign"]) && ((!is_array($input['_suppliers_id_assign']) && $input["_suppliers_id_assign"] > 0) || is_array($input['_suppliers_id_assign']) && count($input['_suppliers_id_assign']) > 0))) && (in_array($input['status'], $this->getNewStatusArray()))) { $input["status"] = self::ASSIGNED; } // Manage SLT signment // Manual SLT defined : reset due date // No manual SLT and due date defined : reset auto SLT foreach (array(SLT::TTR, SLT::TTO) as $sltType) { $this->sltAffect($sltType, $input, $manual_slts_id); } // auto set type if not set if (!isset($input["type"])) { $input['type'] = Entity::getUsedConfig('tickettype', $input['entities_id'], '', Ticket::INCIDENT_TYPE); } return $input; } function post_addItem() { global $CFG_GLPI; if (isset($this->input['content'])) { if (isset($this->input['_stock_image'])) { $this->addImagePaste(); } } // Log this event $username = 'anonymous'; if (isset($_SESSION["glpiname"])) { $username = $_SESSION["glpiname"]; } Event::log($this->fields['id'], "ticket", 4, "tracking", sprintf(__('%1$s adds the item %2$s'), $username, $this->fields['id'])); if (isset($this->input["_followup"]) && is_array($this->input["_followup"]) && (strlen($this->input["_followup"]['content']) > 0)) { $fup = new TicketFollowup(); $type = "new"; if (isset($this->fields["status"]) && ($this->fields["status"] == self::SOLVED)) { $type = "solved"; } $toadd = array("type" => $type, "tickets_id" => $this->fields['id']); if (isset($this->input["_followup"]['content']) && (strlen($this->input["_followup"]['content']) > 0)) { $toadd["content"] = $this->input["_followup"]['content']; } if (isset($this->input["_followup"]['is_private'])) { $toadd["is_private"] = $this->input["_followup"]['is_private']; } // $toadd['_no_notif'] = true; $fup->add($toadd); } if ((isset($this->input["plan"]) && count($this->input["plan"])) || (isset($this->input["actiontime"]) && ($this->input["actiontime"] > 0))) { $task = new TicketTask(); $type = "new"; if (isset($this->fields["status"]) && ($this->fields["status"] == self::SOLVED)) { $type = "solved"; } $toadd = array("type" => $type, "tickets_id" => $this->fields['id'], "actiontime" => $this->input["actiontime"]); if (isset($this->input["plan"]) && count($this->input["plan"])) { $toadd["plan"] = $this->input["plan"]; } if (isset($_SESSION['glpitask_private'])) { $toadd['is_private'] = $_SESSION['glpitask_private']; } // $toadd['_no_notif'] = true; $task->add($toadd); } $ticket_ticket = new Ticket_Ticket(); // From interface if (isset($this->input['_link'])) { $this->input['_link']['tickets_id_1'] = $this->fields['id']; // message if ticket's ID doesn't exist if (!empty($this->input['_link']['tickets_id_2'])) { if ($ticket_ticket->can(-1, CREATE, $this->input['_link'])) { $ticket_ticket->add($this->input['_link']); } else { Session::addMessageAfterRedirect(__('Unknown ticket'), false, ERROR); } } } // From mailcollector : do not check rights if (isset($this->input["_linkedto"])) { $input2['tickets_id_1'] = $this->fields['id']; $input2['tickets_id_2'] = $this->input["_linkedto"]; $input2['link'] = Ticket_Ticket::LINK_TO; $ticket_ticket->add($input2); } // Manage SLT Level : add actions foreach (array(SLT::TTR, SLT::TTO) as $sltType) { list($dateField, $sltField) = SLT::getSltFieldNames($sltType); if (isset($this->input[$sltField]) && ($this->input[$sltField] > 0)) { $this->manageSltLevel($this->input[$sltField]); } } // Add project task link if needed if (isset($this->input['_projecttasks_id'])) { $projecttask = new ProjectTask(); if ($projecttask->getFromDB($this->input['_projecttasks_id'])) { $pt = new ProjectTask_Ticket(); $pt->add(array('projecttasks_id' => $this->input['_projecttasks_id'], 'tickets_id' => $this->fields['id'], /*'_no_notif' => true*/)); } } if (!empty($this->input['items_id'])) { $item_ticket = new Item_Ticket(); foreach ($this->input['items_id'] as $itemtype => $items) { foreach ($items as $items_id) { $item_ticket->add(array('items_id' => $items_id, 'itemtype' => $itemtype, 'tickets_id' => $this->fields['id'], '_disablenotif' => true)); } } } parent::post_addItem(); $this->manageValidationAdd($this->input); // Processing Email if (!isset($this->input['_disablenotif']) && $CFG_GLPI["use_mailing"]) { // Clean reload of the ticket $this->getFromDB($this->fields['id']); $type = "new"; if (isset($this->fields["status"]) && ($this->fields["status"] == self::SOLVED)) { $type = "solved"; } NotificationEvent::raiseEvent($type, $this); } if (isset($_SESSION['glpiis_ids_visible']) && !$_SESSION['glpiis_ids_visible']) { Session::addMessageAfterRedirect(sprintf(__('%1$s (%2$s)'), __('Your ticket has been registered, its treatment is in progress.'), sprintf(__('%1$s: %2$s'), __('Ticket'), "". $this->fields['id'].""))); } } /** * Manage Validation add from input * * @since version 0.85 * * @param $input array : input array * * @return nothing **/ function manageValidationAdd($input) { //Action for send_validation rule if (isset($input["_add_validation"])) { if (isset($input['entities_id'])) { $entid = $input['entities_id']; } else if (isset($this->fields['entities_id'])){ $entid = $this->fields['entities_id']; } else { return false; } $validations_to_send = array(); if (!is_array($input["_add_validation"])) { $input["_add_validation"] = array($input["_add_validation"]); } foreach ($input["_add_validation"] as $key => $validation) { switch ($validation) { case 'requester_supervisor' : if (isset($input['_groups_id_requester']) && $input['_groups_id_requester']) { $users = Group_User::getGroupUsers($input['_groups_id_requester'], "is_manager='1'"); foreach ($users as $data) { $validations_to_send[] = $data['id']; } } // Add to already set groups foreach ($this->getGroups(CommonITILActor::REQUESTER) as $d) { $users = Group_User::getGroupUsers($d['groups_id'], "is_manager='1'"); foreach ($users as $data) { $validations_to_send[] = $data['id']; } } break; case 'assign_supervisor' : if (isset($input['_groups_id_assign']) && $input['_groups_id_assign']) { $users = Group_User::getGroupUsers($input['_groups_id_assign'], "is_manager='1'"); foreach ($users as $data) { $validations_to_send[] = $data['id']; } } foreach ($this->getGroups(CommonITILActor::ASSIGN) as $d) { $users = Group_User::getGroupUsers($d['groups_id'], "is_manager='1'"); foreach ($users as $data) { $validations_to_send[] = $data['id']; } } break; default : // Group case from rules if ($key === 'group') { foreach ($validation as $groups_id) { $validation_right = 'validate_incident'; if (isset($input['type']) && ($input['type'] == Ticket::DEMAND_TYPE)) { $validation_right = 'validate_request'; } $opt = array('groups_id' => $groups_id, 'right' => $validation_right, 'entity' => $entid); $data_users = TicketValidation::getGroupUserHaveRights($opt); foreach ($data_users as $user) { $validations_to_send[] = $user['id']; } } } else { $validations_to_send[] = $validation; } } } // Validation user added on ticket form if (isset($input['users_id_validate'])) { if (array_key_exists('groups_id', $input['users_id_validate'])) { foreach ($input['users_id_validate'] as $key => $validation_to_add){ if (is_numeric($key)) { $validations_to_send[] = $validation_to_add; } } } else { foreach ($input['users_id_validate'] as $key => $validation_to_add) { if (is_numeric($key)) { $validations_to_send[] = $validation_to_add; } } } } // Keep only one $validations_to_send = array_unique($validations_to_send); $validation = new TicketValidation(); if (count($validations_to_send)) { $values = array(); $values['tickets_id'] = $this->fields['id']; if (isset($input['id']) && $input['id'] != $this->fields['id']) { $values['_ticket_add'] = true; } // to know update by rules if (isset($input["_rule_process"])) { $values['_rule_process'] = $input["_rule_process"]; } // if auto_import, tranfert it for validation if (isset($input['_auto_import'])) { $values['_auto_import'] = $input['_auto_import']; } // Cron or rule process of hability to do if (Session::isCron() || isset($input["_auto_import"]) || isset($input["_rule_process"]) || $validation->can(-1, CREATE, $values)) { // cron or allowed user $add_done = false; foreach ($validations_to_send as $user) { // Do not auto add twice same validation if (!TicketValidation::alreadyExists($values['tickets_id'], $user)) { $values["users_id_validate"] = $user; if ($validation->add($values)) { $add_done = true; } } } if ($add_done) { Event::log($this->fields['id'], "ticket", 4, "tracking", sprintf(__('%1$s updates the item %2$s'), $_SESSION["glpiname"], $this->fields['id'])); } } } } return true; } /** * Number of followups of the ticket * * @param $with_private boolean : true : all followups / false : only public ones (default 1) * * @return followup count **/ function numberOfFollowups($with_private=1) { global $DB; $RESTRICT = ""; if ($with_private!=1) { $RESTRICT = " AND `is_private` = '0'"; } // Set number of followups $query = "SELECT COUNT(*) FROM `glpi_ticketfollowups` WHERE `tickets_id` = '".$this->fields["id"]."' $RESTRICT"; $result = $DB->query($query); return $DB->result($result, 0, 0); } /** * Number of tasks of the ticket * * @param $with_private boolean : true : all ticket / false : only public ones (default 1) * * @return followup count **/ function numberOfTasks($with_private=1) { global $DB; $RESTRICT = ""; if ($with_private!=1) { $RESTRICT = " AND `is_private` = '0'"; } // Set number of followups $query = "SELECT COUNT(*) FROM `glpi_tickettasks` WHERE `tickets_id` = '".$this->fields["id"]."' $RESTRICT"; $result = $DB->query($query); return $DB->result($result, 0, 0); } /** * Get active or solved tickets for an hardware last X days * * @since version 0.83 * * @param $itemtype string Item type * @param $items_id integer ID of the Item * @param $days integer day number * * @return integer **/ function getActiveOrSolvedLastDaysTicketsForItem($itemtype, $items_id, $days) { global $DB; $result = array(); $query = "SELECT * FROM `".$this->getTable()."` LEFT JOIN `glpi_items_tickets` ON (`".$this->getTable()."`.`id` = `glpi_items_tickets`.`tickets_id`) WHERE `glpi_items_tickets`.`itemtype` = '$itemtype' AND `glpi_items_tickets`.`items_id` = '$items_id' AND (`".$this->getTable()."`.`status` NOT IN ('".implode("', '", array_merge($this->getSolvedStatusArray(), $this->getClosedStatusArray()) )."') OR (`".$this->getTable()."`.`solvedate` IS NOT NULL AND ADDDATE(`".$this->getTable()."`.`solvedate`, INTERVAL $days DAY) > NOW()))"; foreach ($DB->request($query) as $tick) { $result[$tick['id']] = $tick['name']; } return $result; } /** * Count active tickets for an hardware * * @since version 0.83 * * @param $itemtype string Item type * @param $items_id integer ID of the Item * * @return integer **/ function countActiveTicketsForItem($itemtype, $items_id) { global $DB; $query = "SELECT COUNT(*) AS cpt FROM `".$this->getTable()."` LEFT JOIN `glpi_items_tickets` ON (`".$this->getTable()."`.`id` = `glpi_items_tickets`.`tickets_id`) WHERE `glpi_items_tickets`.`itemtype` = '$itemtype' AND `glpi_items_tickets`.`items_id` = '$items_id' AND `".$this->getTable()."`.`status` NOT IN ('".implode("', '", array_merge($this->getSolvedStatusArray(), $this->getClosedStatusArray()) )."')"; $result = $DB->query($query); $ligne = $DB->fetch_assoc($result); return $ligne['cpt']; } /** * Count solved tickets for an hardware last X days * * @since version 0.83 * * @param $itemtype string Item type * @param $items_id integer ID of the Item * @param $days integer day number * * @return integer **/ function countSolvedTicketsForItemLastDays($itemtype, $items_id, $days) { global $DB; $query = "SELECT COUNT(*) AS cpt FROM `".$this->getTable()."` LEFT JOIN `glpi_items_tickets` ON (`".$this->getTable()."`.`id` = `glpi_items_tickets`.`tickets_id`) WHERE `glpi_items_tickets`.`itemtype` = '$itemtype' AND `glpi_items_tickets`.`items_id` = '$items_id' AND `".$this->getTable()."`.`solvedate` IS NOT NULL AND ADDDATE(`".$this->getTable()."`.`solvedate`, INTERVAL $days DAY) > NOW() AND `".$this->getTable()."`.`status` IN ('".implode("', '", array_merge($this->getSolvedStatusArray(), $this->getClosedStatusArray()) )."')"; $result = $DB->query($query); $ligne = $DB->fetch_assoc($result); return $ligne['cpt']; } /** * Update date mod of the ticket * * @since version 0.83.3 new proto * * @param $ID ID of the ticket * @param $no_stat_computation boolean do not cumpute take into account stat (false by default) * @param $users_id_lastupdater integer to force last_update id (default 0 = not used) **/ function updateDateMod($ID, $no_stat_computation=false, $users_id_lastupdater=0) { global $DB; if ($this->getFromDB($ID)) { if (!$no_stat_computation && (Session::haveRight('task', CommonITILTask::ADDALLITEM) || Session::haveRightsOr('followup', array(TicketFollowup::ADDALLTICKET, TicketFollowup::ADDMYTICKET, TicketFollowup::ADDGROUPTICKET)) || $this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID()) || (isset($_SESSION["glpigroups"]) && $this->haveAGroup(CommonITILActor::ASSIGN, $_SESSION['glpigroups'])) || isCommandLine())) { if ($this->fields['takeintoaccount_delay_stat'] == 0) { return $this->update(array('id' => $ID, 'takeintoaccount_delay_stat' => $this->computeTakeIntoAccountDelayStat(), '_disablenotif' => true)); } } parent::updateDateMod($ID, $no_stat_computation, $users_id_lastupdater); } } /** * Overloaded from commonDBTM * * @since version 0.83 * * @param $type itemtype of object to add * * @return rights **/ function canAddItem($type) { if (($type == 'Document') && ($this->getField('status') == self::CLOSED)) { return false; } return parent::canAddItem($type); } /** * Is the current user have right to add followups to the current ticket ? * * @return boolean **/ function canAddFollowups() { return ((Session::haveRight("followup", TicketFollowup::ADDMYTICKET) && ($this->isUser(CommonITILActor::REQUESTER, Session::getLoginUserID()) || ($this->fields["users_id_recipient"] === Session::getLoginUserID()))) || Session::haveRight('followup', TicketFollowup::ADDALLTICKET) || (Session::haveRight('followup', TicketFollowup::ADDGROUPTICKET) && isset($_SESSION["glpigroups"]) && $this->haveAGroup(CommonITILActor::REQUESTER, $_SESSION['glpigroups'])) || $this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID()) || (isset($_SESSION["glpigroups"]) && $this->haveAGroup(CommonITILActor::ASSIGN, $_SESSION['glpigroups']))); } /** * Get default values to search engine to override **/ static function getDefaultSearchRequest() { $search = array('criteria' => array(0 => array('field' => 12, 'searchtype' => 'equals', 'value' => 'notclosed')), 'sort' => 19, 'order' => 'DESC'); if (Session::haveRight(self::$rightname, self::READALL)) { $search['criteria'][0]['value'] = 'notold'; } return $search; } /** * @see CommonDBTM::getSpecificMassiveActions() **/ function getSpecificMassiveActions($checkitem=NULL) { $isadmin = static::canUpdate(); $actions = parent::getSpecificMassiveActions($checkitem); if ($_SESSION['glpiactiveprofile']['interface'] == 'central') { if (TicketFollowup::canCreate()) { $actions['TicketFollowup'.MassiveAction::CLASS_ACTION_SEPARATOR.'add_followup'] = __('Add a new followup'); } if (TicketTask::canCreate()) { $actions[__CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'add_task'] = __('Add a new task'); } if (TicketValidation::canCreate()) { $actions['TicketValidation'.MassiveAction::CLASS_ACTION_SEPARATOR.'submit_validation'] = __('Approval request'); } if (Item_Ticket::canCreate()) { $actions['Item_Ticket'.MassiveAction::CLASS_ACTION_SEPARATOR.'add_item'] = _x('button', 'Add an item'); } if (Item_Ticket::canDelete()) { $actions['Item_Ticket'.MassiveAction::CLASS_ACTION_SEPARATOR.'delete_item'] = _x('button', 'Remove an item'); } if (Session::haveRight(self::$rightname, UPDATE)) { $actions[__CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'add_actor'] = __('Add an actor'); $actions[__CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'enable_notif'] = __('Set notifications for all actors'); $actions['Ticket_Ticket'.MassiveAction::CLASS_ACTION_SEPARATOR.'add'] = _x('button', 'Link tickets'); } if (Session::haveRight(self::$rightname, UPDATE)) { MassiveAction::getAddTransferList($actions); } } return $actions; } function getSearchOptions() { $tab = array(); $tab += $this->getSearchOptionsMain(); $tab[155]['table'] = $this->getTable(); $tab[155]['field'] = 'time_to_own'; $tab[155]['name'] = __('Time to own'); $tab[155]['datatype'] = 'datetime'; $tab[155]['maybefuture'] = true; $tab[155]['massiveaction'] = false; $tab[155]['additionalfields'] = array('status'); $tab[158]['table'] = $this->getTable(); $tab[158]['field'] = 'time_to_own'; $tab[158]['name'] = __('Time to own + Progress'); $tab[158]['massiveaction'] = false; $tab[158]['nosearch'] = true; $tab[158]['additionalfields'] = array('status'); $tab[159]['table'] = $this->getTable(); $tab[159]['field'] = 'is_late'; $tab[159]['name'] = __('Time to own exceedeed'); $tab[159]['datatype'] = 'bool'; $tab[159]['massiveaction'] = false; $tab[159]['computation'] = "IF(TABLE.`time_to_own` IS NOT NULL AND TABLE.`status` <> ".self::WAITING." AND (TABLE.`takeintoaccount_delay_stat` > TIME_TO_SEC(TIMEDIFF(TABLE.`time_to_own`, TABLE.`date`)) OR (TABLE.`takeintoaccount_delay_stat` = 0 AND TABLE.`time_to_own` < NOW())), 1, 0)"; $tab[14]['table'] = $this->getTable(); $tab[14]['field'] = 'type'; $tab[14]['name'] = __('Type'); $tab[14]['searchtype'] = 'equals'; $tab[14]['datatype'] = 'specific'; $tab[13]['table'] = 'glpi_items_tickets'; $tab[13]['field'] = 'items_id'; $tab[13]['name'] = _n('Associated element', 'Associated elements', Session::getPluralNumber()); $tab[13]['datatype'] = 'specific'; $tab[13]['comments'] = true; $tab[13]['nosort'] = true; $tab[13]['nosearch'] = true; $tab[13]['additionalfields'] = array('itemtype'); $tab[13]['joinparams'] = array('jointype' => 'child'); $tab[13]['forcegroupby'] = true; $tab[13]['massiveaction'] = false; $tab[131]['table'] = 'glpi_items_tickets'; $tab[131]['field'] = 'itemtype'; $tab[131]['name'] = _n('Associated item type', 'Associated item types', Session::getPluralNumber()); $tab[131]['datatype'] = 'itemtypename'; $tab[131]['itemtype_list'] = 'ticket_types'; $tab[131]['nosort'] = true; $tab[131]['additionalfields'] = array('itemtype'); $tab[131]['joinparams'] = array('jointype' => 'child'); $tab[131]['forcegroupby'] = true; $tab[131]['massiveaction'] = false; $tab[9]['table'] = 'glpi_requesttypes'; $tab[9]['field'] = 'name'; $tab[9]['name'] = __('Request source'); $tab[9]['datatype'] = 'dropdown'; // Can't use Location::getSearchOptionsToAdd because id conflicts $tab[83]['table'] = 'glpi_locations'; $tab[83]['field'] = 'completename'; $tab[83]['name'] = __('Location'); $tab[83]['datatype'] = 'dropdown'; $tab[80]['table'] = 'glpi_entities'; $tab[80]['field'] = 'completename'; $tab[80]['name'] = __('Entity'); $tab[80]['massiveaction'] = false; $tab[80]['datatype'] = 'dropdown'; // For ticket template $tab[142]['table'] = 'glpi_documents'; $tab[142]['field'] = 'name'; $tab[142]['name'] = _n('Document', 'Documents', Session::getPluralNumber()); $tab[142]['forcegroupby'] = true; $tab[142]['usehaving'] = true; $tab[142]['nosearch'] = true; $tab[142]['nodisplay'] = true; $tab[142]['datatype'] = 'dropdown'; $tab[142]['massiveaction'] = false; $tab[142]['joinparams'] = array('jointype' => 'items_id', 'beforejoin' => array('table' => 'glpi_documents_items', 'joinparams' => array('jointype' => 'itemtype_item'))); $tab += $this->getSearchOptionsActors(); $tab['slt'] = __('SLT'); $tab[37]['table'] = 'glpi_slts'; $tab[37]['field'] = 'name'; $tab[37]['linkfield'] = 'slts_tto_id'; $tab[37]['name'] = __('SLT')." ".__('Time to own'); $tab[37]['massiveaction'] = false; $tab[37]['datatype'] = 'dropdown'; $tab[37]['joinparams'] = array('condition' => "AND NEWTABLE.`type` = '".SLT::TTO."'"); $tab[37]['condition'] = "`glpi_slts`.`type` = '".SLT::TTO."'"; $tab[30]['table'] = 'glpi_slts'; $tab[30]['field'] = 'name'; $tab[30]['linkfield'] = 'slts_ttr_id'; $tab[30]['name'] = __('SLT')." ".__('Time to resolve'); $tab[30]['massiveaction'] = false; $tab[30]['datatype'] = 'dropdown'; $tab[30]['joinparams'] = array('condition' => "AND NEWTABLE.`type` = '".SLT::TTR."'"); $tab[30]['condition'] = "`glpi_slts`.`type` = '".SLT::TTR."'"; $tab[32]['table'] = 'glpi_slalevels'; $tab[32]['field'] = 'name'; $tab[32]['name'] = __('Escalation level'); $tab[32]['massiveaction'] = false; $tab[32]['datatype'] = 'dropdown'; $tab[32]['joinparams'] = array('beforejoin' => array('table' => 'glpi_slalevels_tickets', 'joinparams' => array('jointype' => 'child'))); $tab[32]['forcegroupby'] = true; $validation_options = TicketValidation::getSearchOptionsToAdd(); if (!Session::haveRightsOr( 'ticketvalidation', [ TicketValidation::CREATEINCIDENT, TicketValidation::CREATEREQUEST ] )) { foreach ($validation_options as &$validation_option) { if (is_array($validation_option)) { $validation_option['massiveaction'] = false; } } } $tab += $validation_options; $tab['satisfaction'] = __('Satisfaction survey'); $tab[31]['table'] = 'glpi_ticketsatisfactions'; $tab[31]['field'] = 'type'; $tab[31]['name'] = __('Type'); $tab[31]['massiveaction'] = false; $tab[31]['searchtype'] = array('equals', 'notequals'); $tab[31]['searchequalsonfield'] = true; $tab[31]['joinparams'] = array('jointype' => 'child'); $tab[31]['datatype'] = 'specific'; $tab[60]['table'] = 'glpi_ticketsatisfactions'; $tab[60]['field'] = 'date_begin'; $tab[60]['name'] = __('Creation date'); $tab[60]['datatype'] = 'datetime'; $tab[60]['massiveaction'] = false; $tab[60]['joinparams'] = array('jointype' => 'child'); $tab[61]['table'] = 'glpi_ticketsatisfactions'; $tab[61]['field'] = 'date_answered'; $tab[61]['name'] = __('Response date'); $tab[61]['datatype'] = 'datetime'; $tab[61]['massiveaction'] = false; $tab[61]['joinparams'] = array('jointype' => 'child'); $tab[62]['table'] = 'glpi_ticketsatisfactions'; $tab[62]['field'] = 'satisfaction'; $tab[62]['name'] = __('Satisfaction'); $tab[62]['datatype'] = 'number'; $tab[62]['massiveaction'] = false; $tab[62]['joinparams'] = array('jointype' => 'child'); $tab[63]['table'] = 'glpi_ticketsatisfactions'; $tab[63]['field'] = 'comment'; $tab[63]['name'] = __('Comments'); $tab[63]['datatype'] = 'text'; $tab[63]['massiveaction'] = false; $tab[63]['joinparams'] = array('jointype' => 'child'); $tab['followup'] = _n('Followup', 'Followups', Session::getPluralNumber()); $followup_condition = ''; if (!Session::haveRight('followup', TicketFollowup::SEEPRIVATE)) { $followup_condition = "AND (`NEWTABLE`.`is_private` = '0' OR `NEWTABLE`.`users_id` = '".Session::getLoginUserID()."')"; } $tab[25]['table'] = 'glpi_ticketfollowups'; $tab[25]['field'] = 'content'; $tab[25]['name'] = __('Description'); $tab[25]['forcegroupby'] = true; $tab[25]['splititems'] = true; $tab[25]['massiveaction'] = false; $tab[25]['joinparams'] = array('jointype' => 'child', 'condition' => $followup_condition); $tab[25]['datatype'] = 'text'; $tab[36]['table'] = 'glpi_ticketfollowups'; $tab[36]['field'] = 'date'; $tab[36]['name'] = __('Date'); $tab[36]['datatype'] = 'datetime'; $tab[36]['massiveaction'] = false; $tab[36]['forcegroupby'] = true; $tab[36]['joinparams'] = array('jointype' => 'child', 'condition' => $followup_condition); $tab[27]['table'] = 'glpi_ticketfollowups'; $tab[27]['field'] = 'id'; $tab[27]['name'] = _x('quantity', 'Number of followups'); $tab[27]['forcegroupby'] = true; $tab[27]['usehaving'] = true; $tab[27]['datatype'] = 'count'; $tab[27]['massiveaction'] = false; $tab[27]['joinparams'] = array('jointype' => 'child', 'condition' => $followup_condition); $tab[29]['table'] = 'glpi_requesttypes'; $tab[29]['field'] = 'name'; $tab[29]['name'] = __('Request source'); $tab[29]['datatype'] = 'dropdown'; $tab[29]['forcegroupby'] = true; $tab[29]['massiveaction'] = false; $tab[29]['joinparams'] = array('beforejoin' => array('table' => 'glpi_ticketfollowups', 'joinparams' => array('jointype' => 'child', 'condition' => $followup_condition))); $tab[91]['table'] = 'glpi_ticketfollowups'; $tab[91]['field'] = 'is_private'; $tab[91]['name'] = __('Private followup'); $tab[91]['datatype'] = 'bool'; $tab[91]['forcegroupby'] = true; $tab[91]['splititems'] = true; $tab[91]['massiveaction'] = false; $tab[91]['joinparams'] = array('jointype' => 'child', 'condition' => $followup_condition); $tab[93]['table'] = 'glpi_users'; $tab[93]['field'] = 'name'; $tab[93]['name'] = __('Writer'); $tab[93]['datatype'] = 'itemlink'; $tab[93]['right'] = 'all'; $tab[93]['forcegroupby'] = true; $tab[93]['massiveaction'] = false; $tab[93]['joinparams'] = array('beforejoin' => array('table' => 'glpi_ticketfollowups', 'joinparams' => array('jointype' => 'child', 'condition' => $followup_condition))); $tab += $this->getSearchOptionsStats(); $tab[150]['table'] = $this->getTable(); $tab[150]['field'] = 'takeintoaccount_delay_stat'; $tab[150]['name'] = __('Take into account time'); $tab[150]['datatype'] = 'timestamp'; $tab[150]['forcegroupby'] = true; $tab[150]['massiveaction'] = false; if (Session::haveRightsOr(self::$rightname, array(self::READALL, self::READASSIGN, self::OWN))) { $tab['linktickets'] = _n('Linked ticket', 'Linked tickets', Session::getPluralNumber()); $tab[40]['table'] = 'glpi_tickets_tickets'; $tab[40]['field'] = 'tickets_id_1'; $tab[40]['name'] = __('All linked tickets'); $tab[40]['massiveaction'] = false; $tab[40]['forcegroupby'] = true; $tab[40]['searchtype'] = 'equals'; $tab[40]['joinparams'] = array('jointype' => 'item_item'); $tab[40]['additionalfields'] = array('tickets_id_2'); $tab[47]['table'] = 'glpi_tickets_tickets'; $tab[47]['field'] = 'tickets_id_1'; $tab[47]['name'] = __('Duplicated tickets'); $tab[47]['massiveaction'] = false; $tab[47]['searchtype'] = 'equals'; $tab[47]['joinparams'] = array('jointype' => 'item_item', 'condition' => "AND NEWTABLE.`link` = ". Ticket_Ticket::DUPLICATE_WITH); $tab[47]['additionalfields'] = array('tickets_id_2'); $tab[47]['forcegroupby'] = true; $tab[41]['table'] = 'glpi_tickets_tickets'; $tab[41]['field'] = 'id'; $tab[41]['name'] = __('Number of all linked tickets'); $tab[41]['massiveaction'] = false; $tab[41]['datatype'] = 'count'; $tab[41]['usehaving'] = true; $tab[41]['joinparams'] = array('jointype' => 'item_item'); $tab[46]['table'] = 'glpi_tickets_tickets'; $tab[46]['field'] = 'id'; $tab[46]['name'] = __('Number of duplicated tickets'); $tab[46]['massiveaction'] = false; $tab[46]['datatype'] = 'count'; $tab[46]['usehaving'] = true; $tab[46]['joinparams'] = array('jointype' => 'item_item', 'condition' => "AND NEWTABLE.`link` = ". Ticket_Ticket::DUPLICATE_WITH); $tab += TicketTask::getSearchOptionsToAdd(); $tab += $this->getSearchOptionsSolution(); if (Session::haveRight('ticketcost', READ)) { $tab += TicketCost::getSearchOptionsToAdd(); } $tab['problem'] = Problem::getTypeName(Session::getPluralNumber()); $tab[141]['table'] = 'glpi_problems_tickets'; $tab[141]['field'] = 'id'; $tab[141]['name'] = _x('quantity', 'Number of problems'); $tab[141]['forcegroupby'] = true; $tab[141]['usehaving'] = true; $tab[141]['datatype'] = 'count'; $tab[141]['massiveaction'] = false; $tab[141]['joinparams'] = array('jointype' => 'child'); } // Filter search fields for helpdesk if (!Session::isCron() // no filter for cron && (!isset($_SESSION['glpiactiveprofile']['interface']) || ($_SESSION['glpiactiveprofile']['interface'] == 'helpdesk'))) { $tokeep = array('common', 'requester','satisfaction'); if (Session::haveRightsOr('ticketvalidation', array_merge(TicketValidation::getValidateRights(), TicketValidation::getCreateRights()))) { $tokeep[] = 'validation'; } $keep = false; foreach ($tab as $key => $val) { if (!is_array($val)) { $keep = in_array($key, $tokeep); } if (!$keep) { if (is_array($val)) { $tab[$key]['nosearch'] = true; } } } // last updater no search $tab[64]['nosearch'] = true; } return $tab; } /** * @since version 0.84 * * @param $field * @param $values * @param $options array **/ static function getSpecificValueToDisplay($field, $values, array $options=array()) { if (!is_array($values)) { $values = array($field => $values); } switch ($field) { case 'content' : $content = Toolbox::unclean_cross_side_scripting_deep(Html::entity_decode_deep($values[$field])); $content = Html::clean($content); if (empty($content)) { $content = ' '; } return nl2br($content); case 'type': return self::getTicketTypeName($values[$field]); } return parent::getSpecificValueToDisplay($field, $values, $options); } /** * @since version 0.84 * * @param $field * @param $name (default '') * @param $values (default '') * @param $options array * * @return string **/ static function getSpecificValueToSelect($field, $name='', $values='', array $options=array()) { if (!is_array($values)) { $values = array($field => $values); } $options['display'] = false; switch ($field) { case 'content' : return ""; case 'type': $options['value'] = $values[$field]; return self::dropdownType($name, $options); } return parent::getSpecificValueToSelect($field, $name, $values, $options); } /** * Dropdown of ticket type * * @param $name select name * @param $options array of options: * - value : integer / preselected value (default 0) * - toadd : array / array of specific values to add at the begining * - on_change : string / value to transmit to "onChange" * - display : boolean / display or get string (default true) * * @return string id of the select **/ static function dropdownType($name, $options=array()) { $params['value'] = 0; $params['toadd'] = array(); $params['on_change'] = ''; $params['display'] = true; if (is_array($options) && count($options)) { foreach ($options as $key => $val) { $params[$key] = $val; } } $items = array(); if (count($params['toadd']) > 0) { $items = $params['toadd']; } $items += self::getTypes(); return Dropdown::showFromArray($name, $items, $params); } /** * Get ticket types * * @return array of types **/ static function getTypes() { $options[self::INCIDENT_TYPE] = __('Incident'); $options[self::DEMAND_TYPE] = __('Request'); return $options; } /** * Get ticket type Name * * @param $value type ID **/ static function getTicketTypeName($value) { switch ($value) { case self::INCIDENT_TYPE : return __('Incident'); case self::DEMAND_TYPE : return __('Request'); default : // Return $value if not defined return $value; } } /** * get the Ticket status list * * @param $withmetaforsearch boolean (false by default) * * @return an array **/ static function getAllStatusArray($withmetaforsearch=false) { // To be overridden by class $tab = array(self::INCOMING => _x('status', 'New'), self::ASSIGNED => _x('status', 'Processing (assigned)'), self::PLANNED => _x('status', 'Processing (planned)'), self::WAITING => __('Pending'), self::SOLVED => _x('status', 'Solved'), self::CLOSED => _x('status', 'Closed')); if ($withmetaforsearch) { $tab['notold'] = _x('status', 'Not solved'); $tab['notclosed'] = _x('status', 'Not closed'); $tab['process'] = __('Processing'); $tab['old'] = _x('status', 'Solved + Closed'); $tab['all'] = __('All'); } return $tab; } /** * Get the ITIL object closed status list * * @since version 0.83 * * @return an array **/ static function getClosedStatusArray() { return array(self::CLOSED); } /** * Get the ITIL object solved status list * * @since version 0.83 * * @return an array **/ static function getSolvedStatusArray() { return array(self::SOLVED); } /** * Get the ITIL object new status list * * @since version 0.83.8 * * @return an array **/ static function getNewStatusArray() { return array(self::INCOMING); } /** * Get the ITIL object assign or plan status list * * @since version 0.83 * * @return an array **/ static function getProcessStatusArray() { return array(self::ASSIGNED, self::PLANNED); } /** * Get the ITIL object closed, solved or waiting status list * * @since version 0.90.1 * * @return an array **/ static function getReopenableStatusArray() { return array(self::CLOSED, self::SOLVED, self::WAITING); } /** * Calculate Ticket TCO for an item * *@param $item CommonDBTM object of the item * *@return float **/ static function computeTco(CommonDBTM $item) { global $DB; $totalcost = 0; $query = "SELECT `glpi_ticketcosts`.* FROM `glpi_items_tickets`, `glpi_ticketcosts` WHERE `glpi_ticketcosts`.`tickets_id` = `glpi_items_tickets`.`tickets_id` AND `glpi_items_tickets`.`itemtype` = '".get_class($item)."' AND `glpi_items_tickets`.`items_id` = '".$item->getField('id')."' AND (`glpi_ticketcosts`.`cost_time` > '0' OR `glpi_ticketcosts`.`cost_fixed` > '0' OR `glpi_ticketcosts`.`cost_material` > '0')"; $result = $DB->query($query); $i = 0; if ($DB->numrows($result)) { while ($data = $DB->fetch_assoc($result)) { $totalcost += TicketCost::computeTotalCost($data["actiontime"], $data["cost_time"], $data["cost_fixed"], $data["cost_material"]); } } return $totalcost; } /** * Print the helpdesk form * * @param $ID integer ID of the user who want to display the Helpdesk * @param $ticket_template boolean ticket template for preview : false if not used for preview * (false by default) * * @return nothing (print the helpdesk) **/ function showFormHelpdesk($ID, $ticket_template=false) { global $DB, $CFG_GLPI; if (!self::canCreate()) { return false; } if (!$ticket_template && Session::haveRightsOr('ticketvalidation', TicketValidation::getValidateRights())) { $opt = array(); $opt['reset'] = 'reset'; $opt['criteria'][0]['field'] = 55; // validation status $opt['criteria'][0]['searchtype'] = 'equals'; $opt['criteria'][0]['value'] = CommonITILValidation::WAITING; $opt['criteria'][0]['link'] = 'AND'; $opt['criteria'][1]['field'] = 59; // validation aprobator $opt['criteria'][1]['searchtype'] = 'equals'; $opt['criteria'][1]['value'] = Session::getLoginUserID(); $opt['criteria'][1]['link'] = 'AND'; $url_validate = $CFG_GLPI["root_doc"]."/front/ticket.php?".Toolbox::append_params($opt, '&'); if (TicketValidation::getNumberToValidate(Session::getLoginUserID()) > 0) { echo "". __('Tickets awaiting approval')."

"; } } $email = UserEmail::getDefaultForUser($ID); $default_use_notif = Entity::getUsedConfig('is_notif_enable_default', $_SESSION['glpiactive_entity'], '', 1); // Set default values... $default_values = array('_users_id_requester_notif' => array('use_notification' => (($email == "")?0:$default_use_notif)), 'nodelegate' => 1, '_users_id_requester' => 0, '_users_id_observer' => array(0), '_users_id_observer_notif' => array('use_notification' => $default_use_notif), 'name' => '', 'content' => '', 'itilcategories_id' => 0, 'locations_id' => 0, 'urgency' => 3, 'items_id' => 0, 'entities_id' => $_SESSION['glpiactive_entity'], 'plan' => array(), 'global_validation' => CommonITILValidation::NONE, '_add_validation' => 0, 'type' => Entity::getUsedConfig('tickettype', $_SESSION['glpiactive_entity'], '', Ticket::INCIDENT_TYPE), '_right' => "id", '_filename' => array(), '_tag_filename' => array()); // Get default values from posted values on reload form if (!$ticket_template) { if (isset($_POST)) { $values = Html::cleanPostForTextArea($_POST); } } // Restore saved value or override with page parameter $saved = $this->restoreInput(); foreach ($default_values as $name => $value) { if (!isset($values[$name])) { if (isset($saved[$name])) { $values[$name] = $saved[$name]; } else { $values[$name] = $value; } } } // Check category / type validity if ($values['itilcategories_id']) { $cat = new ITILCategory(); if ($cat->getFromDB($values['itilcategories_id'])) { switch ($values['type']) { case self::INCIDENT_TYPE : if (!$cat->getField('is_incident')) { $values['itilcategories_id'] = 0; } break; case self::DEMAND_TYPE : if (!$cat->getField('is_request')) { $values['itilcategories_id'] = 0; } break; default : break; } } } if (!$ticket_template) { echo "
"; } $delegating = User::getDelegateGroupsForUser($values['entities_id']); if (count($delegating) || $CFG_GLPI['use_check_pref']) { echo "
"; } if (count($delegating)) { echo ""; echo ""; echo "
".__('This ticket concerns me')." "; $rand = Dropdown::showYesNo("nodelegate", $values['nodelegate']); $params = array('nodelegate' => '__VALUE__', 'rand' => $rand, 'right' => "delegate", '_users_id_requester' => $values['_users_id_requester'], '_users_id_requester_notif' => $values['_users_id_requester_notif'], 'use_notification' => $values['_users_id_requester_notif']['use_notification'], 'entity_restrict' => $_SESSION["glpiactive_entity"]); Ajax::UpdateItemOnSelectEvent("dropdown_nodelegate".$rand, "show_result".$rand, $CFG_GLPI["root_doc"]."/ajax/dropdownDelegationUsers.php", $params); $class = 'right'; if ($CFG_GLPI['use_check_pref'] && $values['nodelegate']) { echo "".__('Check your personnal information'); $class = 'center'; } echo "
"; echo "
"; $self = new self(); if ($values["_users_id_requester"] == 0) { $values['_users_id_requester'] = Session::getLoginUserID(); } else { $values['_right'] = "delegate"; } $self->showActorAddFormOnCreate(CommonITILActor::REQUESTER, $values); echo "
"; if ($CFG_GLPI['use_check_pref'] && $values['nodelegate']) { echo "
"; User::showPersonalInformation(Session::getLoginUserID()); } echo "
"; echo ""; } else { // User as requester $values['_users_id_requester'] = Session::getLoginUserID(); if ($CFG_GLPI['use_check_pref']) { echo "".__('Check your personnal information').""; echo ""; User::showPersonalInformation(Session::getLoginUserID()); echo ""; echo ""; } } echo ""; echo ""; // Load ticket template if available : $tt = $this->getTicketTemplateToUse($ticket_template, $values['type'], $values['itilcategories_id'], $_SESSION["glpiactive_entity"]); // Predefined fields from template : reset them if (isset($values['_predefined_fields'])) { $values['_predefined_fields'] = Toolbox::decodeArrayFromInput($values['_predefined_fields']); } else { $values['_predefined_fields'] = array(); } // Store predefined fields to be able not to take into account on change template $predefined_fields = array(); if (isset($tt->predefined) && count($tt->predefined)) { foreach ($tt->predefined as $predeffield => $predefvalue) { if (isset($values[$predeffield]) && isset($default_values[$predeffield])) { // Is always default value : not set // Set if already predefined field // Set if ticket template change if (((count($values['_predefined_fields']) == 0) && ($values[$predeffield] == $default_values[$predeffield])) || (isset($values['_predefined_fields'][$predeffield]) && ($values[$predeffield] == $values['_predefined_fields'][$predeffield])) || (isset($values['_tickettemplates_id']) && ($values['_tickettemplates_id'] != $tt->getID()))) { $values[$predeffield] = $predefvalue; $predefined_fields[$predeffield] = $predefvalue; } } else { // Not defined options set as hidden field echo ""; } } // All predefined override : add option to say predifined exists if (count($predefined_fields) == 0) { $predefined_fields['_all_predefined_override'] = 1; } } else { // No template load : reset predefined values if (count($values['_predefined_fields'])) { foreach ($values['_predefined_fields'] as $predeffield => $predefvalue) { if ($values[$predeffield] == $predefvalue) { $values[$predeffield] = $default_values[$predeffield]; } } } } if (($CFG_GLPI['urgency_mask'] == (1<<3)) || $tt->isHiddenField('urgency')) { // Dont show dropdown if only 1 value enabled or field is hidden echo ""; } // Display predefined fields if hidden if ($tt->isHiddenField('items_id')) { if (!empty($values['items_id'])) { foreach ($values['items_id'] as $itemtype => $items) { foreach ($items as $items_id) { echo ""; } } } } if ($tt->isHiddenField('locations_id')) { echo ""; } echo ""; echo "
"; Plugin::doHook("pre_item_form", ['item' => $this, 'options' => []]); echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; if ($CFG_GLPI['urgency_mask'] != (1<<3)) { if (!$tt->isHiddenField('urgency')) { echo ""; echo ""; echo ""; } } if (empty($delegating) && NotificationTargetTicket::isAuthorMailingActivatedForHelpdesk()) { echo ""; echo ""; echo ""; } if (($_SESSION["glpiactiveprofile"]["helpdesk_hardware"] != 0) && (count($_SESSION["glpiactiveprofile"]["helpdesk_item_type"]))) { if (!$tt->isHiddenField('itemtype')) { echo ""; echo ""; echo ""; } } if (!$tt->isHiddenField('locations_id')) { echo ""; } if (!$tt->isHiddenField('_users_id_observer') || $tt->isPredefinedField('_users_id_observer')) { echo ""; echo ""; echo ""; } if (!$tt->isHiddenField('name') || $tt->isPredefinedField('name')) { echo ""; echo ""; } if (!$tt->isHiddenField('content') || $tt->isPredefinedField('content')) { echo ""; echo ""; } // File upload system $width = '100%'; if ($CFG_GLPI['use_rich_text']) { $width = '50%'; } echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; Plugin::doHook("post_item_form", ['item' => $this, 'options' => []]); if (!$ticket_template) { echo ""; echo ""; } echo "
".__('Describe the incident or request').""; if (Session::isMultiEntitiesMode()) { echo "(".Dropdown::getDropdownName("glpi_entities", $_SESSION["glpiactive_entity"]).")"; } echo "
".sprintf(__('%1$s%2$s'), __('Type'), $tt->getMandatoryMark('type')).""; self::dropdownType('type', array('value' => $values['type'], 'on_change' => 'this.form.submit()')); echo "
".sprintf(__('%1$s%2$s'), __('Category'), $tt->getMandatoryMark('itilcategories_id')).""; $condition = "`is_helpdeskvisible`='1'"; switch ($values['type']) { case self::DEMAND_TYPE : $condition .= " AND `is_request`='1'"; break; default: // self::INCIDENT_TYPE : $condition .= " AND `is_incident`='1'"; } $opt = array('value' => $values['itilcategories_id'], 'condition' => $condition, 'entity' => $_SESSION["glpiactive_entity"], 'on_change' => 'this.form.submit()'); if ($values['itilcategories_id'] && $tt->isMandatoryField("itilcategories_id")) { $opt['display_emptychoice'] = false; } ITILCategory::dropdown($opt); echo "
".sprintf(__('%1$s%2$s'), __('Urgency'), $tt->getMandatoryMark('urgency')). ""; self::dropdownUrgency(array('value' => $values["urgency"])); echo "
".__('Inform me about the actions taken').""; if ($values["_users_id_requester"] == 0) { $values['_users_id_requester'] = Session::getLoginUserID(); } $_POST['value'] = $values['_users_id_requester']; $_POST['field'] = '_users_id_requester_notif'; $_POST['use_notification'] = $values['_users_id_requester_notif']['use_notification']; include (GLPI_ROOT."/ajax/uemailUpdate.php"); echo "
".sprintf(__('%1$s%2$s'), __('Hardware type'), $tt->getMandatoryMark('items_id')).""; $values['_canupdate'] = Session::haveRight('ticket', CREATE); Item_Ticket::itemAddForm($this, $values); echo "
"; printf(__('%1$s%2$s'), __('Location'), $tt->getMandatoryMark('locations_id')); echo ""; Location::dropdown(array('value' => $values["locations_id"])); echo "
".sprintf(__('%1$s%2$s'), _n('Watcher', 'Watchers', 2), $tt->getMandatoryMark('_users_id_observer')).""; $values['_right'] = "all"; if (!$tt->isHiddenField('_users_id_observer')) { // Observer if($tt->isPredefinedField('_users_id_observer') && !is_array($values['_users_id_observer'])) { //convert predefined value to array $values['_users_id_observer'] = array($values['_users_id_observer']); $values['_users_id_observer_notif']['use_notification'] = array($values['_users_id_observer_notif']['use_notification']); // add new line to permit adding more observers $values['_users_id_observer'][1] = 0; $values['_users_id_observer_notif']['use_notification'][1] = 1; } echo "
"; if (isset($values['_users_id_observer'])) { $observers = $values['_users_id_observer']; foreach($observers as $index_observer => $observer) { $options = array_merge($values, array('_user_index' => $index_observer)); self::showFormHelpdeskObserver($options); } } echo "
"; } else { // predefined value if (isset($values["_users_id_observer"]) && $values["_users_id_observer"]) { echo self::getActorIcon('user', CommonITILActor::OBSERVER)." "; echo Dropdown::getDropdownName("glpi_users", $values["_users_id_observer"]); echo ""; } } echo "
".sprintf(__('%1$s%2$s'), __('Title'), $tt->getMandatoryMark('name')).""; if (!$tt->isHiddenField('name')) { echo ""; } else { echo $values['name']; echo ""; } echo "
".sprintf(__('%1$s%2$s'), __('Description'), $tt->getMandatoryMark('content')). ""; $rand = mt_rand(); $rand_text = mt_rand(); $cols = 90; $rows = 6; $content_id = "content$rand"; if ($CFG_GLPI["use_rich_text"]) { $values["content"] = $this->setRichTextContent($content_id, $values["content"], $rand); $cols = 100; $rows = 10; } else { $values["content"] = $this->setSimpleTextContent($values["content"]); } echo "
"; echo "
"; echo "
".sprintf(__('%1$s (%2$s)'), __('File'), Document::getMaxUploadSize()); DocumentType::showAvailableTypesLink(); echo ""; echo "
"; echo "
"; echo ""; echo ""; if ($CFG_GLPI['use_rich_text']) { echo ""; } echo "
"; echo Html::file(array('multiple' => true, 'values' => array('filename' => $values['_filename'], 'tag' => $values['_tag_filename']) )); // "
"; echo "
"; if (!isset($rand)) { $rand = mt_rand(); } echo Html::initImagePasteSystem($content_id, $rand); echo "
"; echo "
"; if ($tt->isField('id') && ($tt->fields['id'] > 0)) { echo ""; echo ""; } echo ""; echo "
"; if (!$ticket_template) { Html::closeForm(); } } /** * Display a single oberver selector * * * @param $options array options for default values ($options of showActorAddFormOnCreate) **/ static function showFormHelpdeskObserver($options = array()) { global $CFG_GLPI; //default values $ticket = new Ticket(); $params['_users_id_observer_notif']['use_notification'] = true; $params['_users_id_observer'] = 0; $params['entities_id'] = $_SESSION["glpiactive_entity"]; $params['_right'] = "all"; // overide default value by function parameters if (is_array($options) && count($options)) { foreach ($options as $key => $val) { $params[$key] = $val; } } // add a user selector $rand_observer = $ticket->showActorAddFormOnCreate(CommonITILActor::OBSERVER, $params); // add an additionnal observer on user selection Ajax::updateItemOnSelectEvent("dropdown__users_id_observer[]$rand_observer", "observer_$rand_observer", $CFG_GLPI["root_doc"]."/ajax/helpdesk_observer.php", $params); //remove 'new observer' anchor on user selection echo Html::scriptBlock(" $('#dropdown__users_id_observer__$rand_observer').on('change', function(event) { $('#addObserver$rand_observer').remove(); });"); // add "new observer" anchor echo ""; echo Html::image($CFG_GLPI['root_doc']."/pics/meta_plus.png", array('alt' => __('Add'))); echo ""; // add an additionnal observer on anchor click Ajax::updateItemOnEvent("addObserver$rand_observer", "observer_$rand_observer", $CFG_GLPI["root_doc"]."/ajax/helpdesk_observer.php", $params, array('click')); // div for an additionnal observer echo "
"; } /** * @since version 0.83 * * @param $entity integer entities_id usefull is function called by cron (default 0) **/ static function getDefaultValues($entity=0) { global $CFG_GLPI; if (is_numeric(Session::getLoginUserID(false))) { $users_id_requester = Session::getLoginUserID(); // No default requester if own ticket right = tech and update_ticket right to update requester if (Session::haveRightsOr(self::$rightname, array(UPDATE, self::OWN)) && !$_SESSION['glpiset_default_requester']) { $users_id_requester = 0; } $entity = $_SESSION['glpiactive_entity']; $requesttype = $_SESSION['glpidefault_requesttypes_id']; } else { $users_id_requester = 0; $requesttype = $CFG_GLPI['default_requesttypes_id']; } $type = Entity::getUsedConfig('tickettype', $entity, '', Ticket::INCIDENT_TYPE); $default_use_notif = Entity::getUsedConfig('is_notif_enable_default', $entity, '', 1); // Set default values... return array('_users_id_requester' => $users_id_requester, '_users_id_requester_notif' => array('use_notification' => $default_use_notif, 'alternative_email' => ''), '_groups_id_requester' => 0, '_users_id_assign' => 0, '_users_id_assign_notif' => array('use_notification' => $default_use_notif, 'alternative_email' => ''), '_groups_id_assign' => 0, '_users_id_observer' => 0, '_users_id_observer_notif' => array('use_notification' => $default_use_notif, 'alternative_email' => ''), '_groups_id_observer' => 0, '_link' => array('tickets_id_2' => '', 'link' => ''), '_suppliers_id_assign' => 0, '_suppliers_id_assign_notif' => array('use_notification' => $default_use_notif, 'alternative_email' => ''), 'name' => '', 'content' => '', 'itilcategories_id' => 0, 'urgency' => 3, 'impact' => 3, 'priority' => self::computePriority(3, 3), 'requesttypes_id' => $requesttype, 'actiontime' => 0, 'date' => $_SESSION["glpi_currenttime"], 'entities_id' => $entity, 'status' => self::INCOMING, 'followup' => array(), 'itemtype' => '', 'items_id' => 0, 'locations_id' => 0, 'plan' => array(), 'global_validation' => CommonITILValidation::NONE, 'due_date' => 'NULL', 'time_to_own' => 'NULL', 'slts_tto_id' => 0, 'slts_ttr_id' => 0, '_add_validation' => 0, 'users_id_validate' => array(), 'type' => $type, '_documents_id' => array(), '_filename' => array(), '_tag_filename' => array()); } /** * Get ticket template to use * Use force_template first, then try on template define for type and category * then use default template of active profile of connected user and then use default entity one * * @param $force_template integer tickettemplate_id to used (case of preview for example) * (default 0) * @param $type integer type of the ticket (default 0) * @param $itilcategories_id integer ticket category (default 0) * @param $entities_id integer (default -1) * * @since version 0.84 * * @return ticket template object **/ function getTicketTemplateToUse($force_template=0, $type=0, $itilcategories_id=0, $entities_id=-1) { // Load ticket template if available : $tt = new TicketTemplate(); $template_loaded = false; if ($force_template) { // with type and categ if ($tt->getFromDBWithDatas($force_template, true)) { $template_loaded = true; } } if (!$template_loaded && $type && $itilcategories_id) { $categ = new ITILCategory(); if ($categ->getFromDB($itilcategories_id)) { $field = ''; switch ($type) { case self::INCIDENT_TYPE : $field = 'tickettemplates_id_incident'; break; case self::DEMAND_TYPE : $field = 'tickettemplates_id_demand'; break; } if (!empty($field) && $categ->fields[$field]) { // without type and categ if ($tt->getFromDBWithDatas($categ->fields[$field], false)) { $template_loaded = true; } } } } // If template loaded from type and category do not check after if ($template_loaded) { return $tt; } if (!$template_loaded) { // load default profile one if not already loaded if (isset($_SESSION['glpiactiveprofile']['tickettemplates_id']) && $_SESSION['glpiactiveprofile']['tickettemplates_id']) { // with type and categ if ($tt->getFromDBWithDatas($_SESSION['glpiactiveprofile']['tickettemplates_id'], true)) { $template_loaded = true; } } } if (!$template_loaded && ($entities_id >= 0)) { // load default entity one if not already loaded if ($template_id = Entity::getUsedConfig('tickettemplates_id', $entities_id)) { // with type and categ if ($tt->getFromDBWithDatas($template_id, true)) { $template_loaded = true; } } } // Check if profile / entity set type and category and try to load template for these values if ($template_loaded) { // template loaded for profile or entity $newtype = $type; $newitilcategories_id = $itilcategories_id; // Get predefined values for ticket template if (isset($tt->predefined['itilcategories_id']) && $tt->predefined['itilcategories_id']) { $newitilcategories_id = $tt->predefined['itilcategories_id']; } if (isset($tt->predefined['type']) && $tt->predefined['type']) { $newtype = $tt->predefined['type']; } if ($newtype && $newitilcategories_id) { $categ = new ITILCategory(); if ($categ->getFromDB($newitilcategories_id)) { $field = ''; switch ($newtype) { case self::INCIDENT_TYPE : $field = 'tickettemplates_id_incident'; break; case self::DEMAND_TYPE : $field = 'tickettemplates_id_demand'; break; } if (!empty($field) && $categ->fields[$field]) { // without type and categ if ($tt->getFromDBWithDatas($categ->fields[$field], false)) { $template_loaded = true; } } } } } return $tt; } function showForm($ID, $options=array()) { global $DB, $CFG_GLPI; $default_values = self::getDefaultValues(); // Get default values from posted values on reload form // On get because of tabs // we use REQUEST because method differ with layout (lefttab : GET, vsplit: POST) if (!isset($options['template_preview'])) { if (isset($_REQUEST)) { $values = Html::cleanPostForTextArea($_REQUEST); } } // Restore saved value or override with page parameter $saved = $this->restoreInput(); foreach ($default_values as $name => $value) { if (!isset($values[$name])) { if (isset($saved[$name])) { $values[$name] = $saved[$name]; } else { $values[$name] = $value; } } } if (isset($values['content'])) { // Clean new lines to be fix encoding $order = array('\\r', '\\n', "\\"); $replace = array("", "", ""); $values['content'] = str_replace($order,$replace,$values['content']); } if (isset($values['name'])) { $values['name'] = str_replace("\\", "", $values['name']); } if (!$ID) { // Override defaut values from projecttask if needed if (isset($options['_projecttasks_id'])) { $pt = new ProjectTask(); if ($pt->getFromDB($options['_projecttasks_id'])) { $values['name'] = $pt->getField('name'); $values['content'] = $pt->getField('name'); } } } // Check category / type validity if ($values['itilcategories_id']) { $cat = new ITILCategory(); if ($cat->getFromDB($values['itilcategories_id'])) { switch ($values['type']) { case self::INCIDENT_TYPE : if (!$cat->getField('is_incident')) { $values['itilcategories_id'] = 0; } break; case self::DEMAND_TYPE : if (!$cat->getField('is_request')) { $values['itilcategories_id'] = 0; } break; default : break; } } } // Default check if ($ID > 0) { $this->check($ID, READ); } else { // Create item $this->check(-1, CREATE, $values); } if (!$ID) { $this->userentities = array(); if ($values["_users_id_requester"]) { //Get all the user's entities $all_entities = Profile_User::getUserEntities($values["_users_id_requester"], true, true); //For each user's entity, check if the technician which creates the ticket have access to it foreach ($all_entities as $tmp => $ID_entity) { if (Session::haveAccessToEntity($ID_entity)) { $this->userentities[] = $ID_entity; } } } $this->countentitiesforuser = count($this->userentities); if (($this->countentitiesforuser > 0) && !in_array($this->fields["entities_id"], $this->userentities)) { // If entity is not in the list of user's entities, // then use as default value the first value of the user's entites list $this->fields["entities_id"] = $this->userentities[0]; // Pass to values $values['entities_id'] = $this->userentities[0]; } } if ($values['type'] <= 0) { $values['type'] = Entity::getUsedConfig('tickettype', $values['entities_id'], '', Ticket::INCIDENT_TYPE); } if (!isset($options['template_preview'])) { $options['template_preview'] = 0; } // Load ticket template if available : if ($ID) { $tt = $this->getTicketTemplateToUse($options['template_preview'], $this->fields['type'], $this->fields['itilcategories_id'], $this->fields['entities_id']); } else { $tt = $this->getTicketTemplateToUse($options['template_preview'], $values['type'], $values['itilcategories_id'], $values['entities_id']); } // Predefined fields from template : reset them if (isset($values['_predefined_fields'])) { $values['_predefined_fields'] = Toolbox::decodeArrayFromInput($values['_predefined_fields']); } else { $values['_predefined_fields'] = array(); } // Store predefined fields to be able not to take into account on change template // Only manage predefined values on ticket creation $predefined_fields = array(); if (!$ID) { if (isset($tt->predefined) && count($tt->predefined)) { foreach ($tt->predefined as $predeffield => $predefvalue) { if (isset($default_values[$predeffield])) { // Is always default value : not set // Set if already predefined field // Set if ticket template change if (((count($values['_predefined_fields']) == 0) && ($values[$predeffield] == $default_values[$predeffield])) || (isset($values['_predefined_fields'][$predeffield]) && ($values[$predeffield] == $values['_predefined_fields'][$predeffield])) || (isset($values['_tickettemplates_id']) && ($values['_tickettemplates_id'] != $tt->getID())) // user pref for requestype can't overwrite requestype from template // when change category || (($predeffield == 'requesttypes_id') && empty($saved))) { // Load template data $values[$predeffield] = $predefvalue; $this->fields[$predeffield] = $predefvalue; $predefined_fields[$predeffield] = $predefvalue; } } } // All predefined override : add option to say predifined exists if (count($predefined_fields) == 0) { $predefined_fields['_all_predefined_override'] = 1; } } else { // No template load : reset predefined values if (count($values['_predefined_fields'])) { foreach ($values['_predefined_fields'] as $predeffield => $predefvalue) { if ($values[$predeffield] == $predefvalue) { $values[$predeffield] = $default_values[$predeffield]; } } } } } // Put ticket template on $values for actors $values['_tickettemplate'] = $tt; $canupdate = Session::haveRight(self::$rightname, UPDATE); $canpriority = Session::haveRight(self::$rightname, self::CHANGEPRIORITY); $canstatus = $canupdate; if ($ID && in_array($this->fields['status'], $this->getClosedStatusArray())) { $canupdate = false; // No update for actors $values['_noupdate'] = true; } $showuserlink = 0; if (Session::haveRight('user', READ)) { $showuserlink = 1; } if ($options['template_preview']) { // Add all values to fields of tickets for template preview foreach ($values as $key => $val) { if (!isset($this->fields[$key])) { $this->fields[$key] = $val; } } } // In percent $colsize1 = '13'; $colsize2 = '29'; $colsize3 = '13'; $colsize4 = '45'; $canupdate_descr = $canupdate || $this->canUpdateItem(); if (!$options['template_preview']) { echo ""; if (isset($options['_projecttasks_id'])) { echo ""; } } echo "
"; echo ""; // Optional line $ismultientities = Session::isMultiEntitiesMode(); echo ""; echo ""; Plugin::doHook("pre_item_form", ['item' => $this, 'options' => &$options]); echo ""; echo ""; echo ""; // SLTs echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; if ($ID) { echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; } if ($ID && (in_array($this->fields["status"], $this->getSolvedStatusArray()) || in_array($this->fields["status"], $this->getClosedStatusArray()))) { echo ""; echo ""; echo ""; if (in_array($this->fields["status"], $this->getClosedStatusArray())) { echo ""; echo ""; } else { echo ""; } echo ""; } if ($ID) { echo "
"; if ($ID) { $text = sprintf(__('%1$s - %2$s'), $this->getTypeName(1), sprintf(__('%1$s: %2$s'), __('ID'), $ID)); if ($ismultientities) { $text = sprintf(__('%1$s (%2$s)'), $text, Dropdown::getDropdownName('glpi_entities', $this->fields['entities_id'])); } echo $text; } else { if ($ismultientities) { printf(__('The ticket will be added in the entity %s'), Dropdown::getDropdownName("glpi_entities", $this->fields['entities_id'])); } else { _e('New ticket'); } } echo "
"; echo $tt->getBeginHiddenFieldText('date'); if (!$ID) { printf(__('%1$s%2$s'), __('Opening date'), $tt->getMandatoryMark('date')); } else { _e('Opening date'); } echo $tt->getEndHiddenFieldText('date'); echo ""; echo $tt->getBeginHiddenFieldValue('date'); $date = $this->fields["date"]; if ($canupdate) { Html::showDateTimeField("date", array('value' => $date, 'timestep' => 1, 'maybeempty' => false)); } else { echo Html::convDateTime($date); } echo $tt->getEndHiddenFieldValue('date', $this); echo "
".$tt->getBeginHiddenFieldText('time_to_own'); if (!$ID) { printf(__('%1$s%2$s'), __('Time to own'), $tt->getMandatoryMark('time_to_own')); } else { _e('Time to own'); } echo $tt->getEndHiddenFieldText('time_to_own'); echo ""; $slt = new SLT(); $slt->showSltForTicket($this, SLT::TTO, $tt, $canupdate); echo "".$tt->getBeginHiddenFieldText('due_date'); if (!$ID) { printf(__('%1$s%2$s'), __('Time to resolve'), $tt->getMandatoryMark('due_date')); } else { _e('Time to resolve'); } echo $tt->getEndHiddenFieldText('due_date'); echo ""; $slt->showSltForTicket($this, SLT::TTR, $tt, $canupdate); echo "
".__('By').""; if ($canupdate) { User::dropdown(array('name' => 'users_id_recipient', 'value' => $this->fields["users_id_recipient"], 'entity' => $this->fields["entities_id"], 'right' => 'all')); } else { echo getUserName($this->fields["users_id_recipient"], $showuserlink); } echo "".__('Last update').""; if ($this->fields['users_id_lastupdater'] > 0) { //TRANS: %1$s is the update date, %2$s is the last updater name printf(__('%1$s by %2$s'), Html::convDateTime($this->fields["date_mod"]), getUserName($this->fields["users_id_lastupdater"], $showuserlink)); } echo "
".__('Resolution date').""; Html::showDateTimeField("solvedate", array('value' => $this->fields["solvedate"], 'timestep' => 1, 'maybeempty' => false, 'canedit' => $canupdate)); echo "".__('Close date').""; Html::showDateTimeField("closedate", array('value' => $this->fields["closedate"], 'timestep' => 1, 'maybeempty' => false, 'canedit' => $canupdate)); echo " 
"; echo ""; } echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; if (!$ID) { echo "
".sprintf(__('%1$s%2$s'), __('Type'), $tt->getMandatoryMark('type')).""; // Permit to set type when creating ticket without update right if ($canupdate || !$ID) { $opt = array('value' => $this->fields["type"]); /// Auto submit to load template if (!$ID) { $opt['on_change'] = 'this.form.submit()'; } $rand = self::dropdownType('type', $opt); if ($ID) { $params = array('type' => '__VALUE__', 'entity_restrict' => $this->fields['entities_id'], 'value' => $this->fields['itilcategories_id'], 'currenttype' => $this->fields['type']); Ajax::updateItemOnSelectEvent("dropdown_type$rand", "show_category_by_type", $CFG_GLPI["root_doc"]."/ajax/dropdownTicketCategories.php", $params); } } else { echo self::getTicketTypeName($this->fields["type"]); } echo "".sprintf(__('%1$s%2$s'), __('Category'), $tt->getMandatoryMark('itilcategories_id')).""; // Permit to set category when creating ticket without update right if ($canupdate || !$ID || $canupdate_descr) { $opt = array('value' => $this->fields["itilcategories_id"], 'entity' => $this->fields["entities_id"]); if ($_SESSION["glpiactiveprofile"]["interface"] == "helpdesk") { $opt['condition'] = "`is_helpdeskvisible`='1' AND "; } else { $opt['condition'] = ''; } /// Auto submit to load template if (!$ID) { $opt['on_change'] = 'this.form.submit()'; } /// if category mandatory, no empty choice /// no empty choice is default value set on ticket creation, else yes if (($ID || $values['itilcategories_id']) && $tt->isMandatoryField("itilcategories_id") && ($this->fields["itilcategories_id"] > 0)) { $opt['display_emptychoice'] = false; } switch ($this->fields["type"]) { case self::INCIDENT_TYPE : $opt['condition'] .= "`is_incident`='1'"; break; case self::DEMAND_TYPE : $opt['condition'] .= "`is_request`='1'"; break; default : break; } echo ""; ITILCategory::dropdown($opt); echo ""; } else { echo Dropdown::getDropdownName("glpi_itilcategories", $this->fields["itilcategories_id"]); } echo "
"; $this->showActorsPartForm($ID, $values); echo ""; } echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; // Display validation state echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; if (!$ID) { echo ""; } else { echo ""; } echo ""; echo ""; // Need comment right to add a followup with the actiontime if (!$ID && Session::haveRight('followup', TicketFollowup::ADDALLTICKET)) { echo ""; echo ""; } echo ""; echo "
".$tt->getBeginHiddenFieldText('status'); printf(__('%1$s%2$s'), __('Status'), $tt->getMandatoryMark('status')); echo $tt->getEndHiddenFieldText('status').""; echo $tt->getBeginHiddenFieldValue('status'); if ($canstatus) { self::dropdownStatus(array('value' => $this->fields["status"], 'showtype' => 'allowed')); TicketValidation::alertValidation($this, 'status'); } else { echo self::getStatus($this->fields["status"]); if (in_array($this->fields["status"], $this->getClosedStatusArray()) && $this->isAllowedStatus($this->fields['status'], Ticket::INCOMING)) { echo " ". __('Reopen').""; } } echo $tt->getEndHiddenFieldValue('status',$this); echo "".$tt->getBeginHiddenFieldText('requesttypes_id'); printf(__('%1$s%2$s'), __('Request source'), $tt->getMandatoryMark('requesttypes_id')); echo $tt->getEndHiddenFieldText('requesttypes_id').""; echo $tt->getBeginHiddenFieldValue('requesttypes_id'); if ($canupdate) { RequestType::dropdown(array('value' => $this->fields["requesttypes_id"], 'condition' => 'is_active = 1 AND is_ticketheader = 1')); } else { echo Dropdown::getDropdownName('glpi_requesttypes', $this->fields["requesttypes_id"]); echo Html::hidden('requesttypes_id', array('value' => $this->fields["requesttypes_id"])); } echo $tt->getEndHiddenFieldValue('requesttypes_id',$this); echo "
".$tt->getBeginHiddenFieldText('urgency'); printf(__('%1$s%2$s'), __('Urgency'), $tt->getMandatoryMark('urgency')); echo $tt->getEndHiddenFieldText('urgency').""; if (($canupdate && $canpriority) || !$ID || $canupdate_descr) { // Only change during creation OR when allowed to change priority OR when user is the creator echo $tt->getBeginHiddenFieldValue('urgency'); $idurgency = self::dropdownUrgency(array('value' => $this->fields["urgency"])); echo $tt->getEndHiddenFieldValue('urgency', $this); } else { $idurgency = "value_urgency".mt_rand(); echo ""; echo $tt->getBeginHiddenFieldValue('urgency'); echo parent::getUrgencyName($this->fields["urgency"]); echo $tt->getEndHiddenFieldValue('urgency', $this); } echo ""; if (!$ID) { echo $tt->getBeginHiddenFieldText('_add_validation'); printf(__('%1$s%2$s'), __('Approval request'), $tt->getMandatoryMark('_add_validation')); echo $tt->getEndHiddenFieldText('_add_validation'); } else { echo $tt->getBeginHiddenFieldText('global_validation'); _e('Approval'); echo $tt->getEndHiddenFieldText('global_validation'); } echo ""; if (!$ID) { echo $tt->getBeginHiddenFieldValue('_add_validation'); $validation_right = ''; if (($values['type'] == self::INCIDENT_TYPE) && Session::haveRight('ticketvalidation', TicketValidation::CREATEINCIDENT)) { $validation_right = 'validate_incident'; } if (($values['type'] == self::DEMAND_TYPE) && Session::haveRight('ticketvalidation', TicketValidation::CREATEREQUEST)) { $validation_right = 'validate_request'; } if (!empty($validation_right)) { echo ""; $params = array('name' => "users_id_validate", 'entity' => $this->fields['entities_id'], 'right' => $validation_right, 'users_id_validate' => $values['users_id_validate']); TicketValidation::dropdownValidator($params); } echo $tt->getEndHiddenFieldValue('_add_validation',$this); if ($tt->isPredefinedField('global_validation')) { echo ""; } } else { echo $tt->getBeginHiddenFieldValue('global_validation'); if (Session::haveRightsOr('ticketvalidation', TicketValidation::getCreateRights()) && $canupdate) { TicketValidation::dropdownStatus('global_validation', array('global' => true, 'value' => $this->fields['global_validation'])); } else { echo TicketValidation::getStatus($this->fields['global_validation']); } echo $tt->getEndHiddenFieldValue('global_validation',$this); } echo "
".$tt->getBeginHiddenFieldText('impact'); printf(__('%1$s%2$s'), __('Impact'), $tt->getMandatoryMark('impact')); echo $tt->getEndHiddenFieldText('impact').""; echo $tt->getBeginHiddenFieldValue('impact'); if ($canupdate) { $idimpact = self::dropdownImpact(array('value' => $this->fields["impact"])); } else { $idimpact = "value_impact".mt_rand(); echo ""; echo parent::getImpactName($this->fields["impact"]); } echo $tt->getEndHiddenFieldValue('impact',$this); echo "".$tt->getBeginHiddenFieldText('locations_id'); printf(__('%1$s%2$s'), __('Location'), $tt->getMandatoryMark('locations_id')); echo $tt->getEndHiddenFieldText('locations_id').""; echo $tt->getBeginHiddenFieldValue('locations_id'); if ($canupdate || !$ID) { Location::dropdown(array('value' => $this->fields['locations_id'], 'entity' => $this->fields['entities_id'])); } else { echo Dropdown::getDropdownName('glpi_locations', $this->fields["locations_id"]); } echo $tt->getEndHiddenFieldValue('locations_id', $this); echo "
".$tt->getBeginHiddenFieldText('priority'); printf(__('%1$s%2$s'), __('Priority'), $tt->getMandatoryMark('priority')); echo $tt->getEndHiddenFieldText('priority').""; $idajax = 'change_priority_' . mt_rand(); if ($canpriority && !$tt->isHiddenField('priority')) { $idpriority = parent::dropdownPriority(array('value' => $this->fields["priority"], 'withmajor' => true)); $idpriority = 'dropdown_priority'.$idpriority; echo " "; } else { $idpriority = 0; echo $tt->getBeginHiddenFieldValue('priority'); echo "".parent::getPriorityName($this->fields["priority"]).""; echo $tt->getEndHiddenFieldValue('priority', $this); } if ($canupdate || $canupdate_descr) { $params = array('urgency' => '__VALUE0__', 'impact' => '__VALUE1__', 'priority' => $idpriority); Ajax::updateItemOnSelectEvent(array('dropdown_urgency'.$idurgency, 'dropdown_impact'.$idimpact), $idajax, $CFG_GLPI["root_doc"]."/ajax/priority.php", $params); } echo "".$tt->getBeginHiddenFieldText('items_id'); printf(__('%1$s%2$s'), _n('Associated element', 'Associated elements', Session::getPluralNumber()), $tt->getMandatoryMark('items_id')); if ($ID && $canupdate) { echo " ".__s("; } echo $tt->getEndHiddenFieldText('items_id'); echo ""; echo $tt->getBeginHiddenFieldValue('items_id'); $values['_canupdate'] = Session::haveRight('ticket', CREATE); if ($values['_canupdate']) { Item_Ticket::itemAddForm($this, $values); } echo $tt->getEndHiddenFieldValue('items_id', $this); echo ""; echo $tt->getBeginHiddenFieldValue('items_id'); $values['_canupdate'] = $canupdate || $canupdate_descr; Item_Ticket::itemAddForm($this, $values); echo $tt->getEndHiddenFieldValue('items_id', $this); echo "
".$tt->getBeginHiddenFieldText('actiontime'); printf(__('%1$s%2$s'), __('Total duration'), $tt->getMandatoryMark('actiontime')); echo $tt->getEndHiddenFieldText('actiontime').""; echo $tt->getBeginHiddenFieldValue('actiontime'); Dropdown::showTimeStamp('actiontime', array('value' => $values['actiontime'], 'addfirstminutes' => true)); echo $tt->getEndHiddenFieldValue('actiontime',$this); echo "
"; if ($ID) { $this->showActorsPartForm($ID, $values); } $view_linked_tickets = ($ID || $canupdate); echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; if ($view_linked_tickets) { echo "'; echo ""; } echo ""; // View files added echo ""; // Permit to add doc when creating a ticket echo ""; echo ""; echo ""; Plugin::doHook("post_item_form", ['item' => $this, 'options' => &$options]); echo "
".$tt->getBeginHiddenFieldText('name'); printf(__('%1$s%2$s'), __('Title'), $tt->getMandatoryMark('name')); echo $tt->getEndHiddenFieldText('name').""; if (!$ID || $canupdate_descr) { echo $tt->getBeginHiddenFieldValue('name'); echo "fields["name"])."\">"; echo $tt->getEndHiddenFieldValue('name', $this); } else { if (empty($this->fields["name"])) { _e('Without title'); } else { echo $this->fields["name"]; } } echo "
".$tt->getBeginHiddenFieldText('content'); printf(__('%1$s%2$s'), __('Description'), $tt->getMandatoryMark('content')); if (!$ID || $canupdate_descr) { $content = Toolbox::unclean_cross_side_scripting_deep(Html::entity_decode_deep($this->fields['content'])); Html::showTooltip(nl2br(Html::Clean($content))); } echo $tt->getEndHiddenFieldText('content').""; echo $tt->getBeginHiddenFieldValue('content'); $rand = mt_rand(); $rand_text = mt_rand(); $rows = 6; $content_id = "content$rand"; if ($CFG_GLPI["use_rich_text"]) { $this->fields["content"] = $this->setRichTextContent($content_id, $this->fields["content"], $rand, !$canupdate_descr); $rows = 10; } else { $this->fields["content"] = $this->setSimpleTextContent($this->fields["content"]); } echo "
"; echo "
"; if (!$CFG_GLPI["use_rich_text"]) { echo Html::scriptBlock("$(document).ready(function() { $('#$content_id').autogrow(); });"); } echo $tt->getEndHiddenFieldValue('content', $this); echo "
". _n('Linked ticket', 'Linked tickets', Session::getPluralNumber()); $rand_linked_ticket = mt_rand(); if ($canupdate) { echo " "; echo "\"".__s('Add')."\""; } echo '"; if ($canupdate) { echo ""; if (isset($values["_link"]) && !empty($values["_link"]['tickets_id_2'])) { echo ""; } } Ticket_Ticket::displayLinkedTicketsTo($ID); echo "
"; echo $tt->getBeginHiddenFieldText('_documents_id'); $doctitle = sprintf(__('File (%s)'), Document::getMaxUploadSize()); printf(__('%1$s%2$s'), $doctitle, $tt->getMandatoryMark('_documents_id')); // Do not show if hidden. if (!$tt->isHiddenField('_documents_id')) { DocumentType::showAvailableTypesLink(); } echo $tt->getEndHiddenFieldText('_documents_id'); echo ""; // Do not set values echo $tt->getEndHiddenFieldValue('_documents_id'); if ($tt->isPredefinedField('_documents_id')) { if (isset($values['_documents_id']) && is_array($values['_documents_id']) && count($values['_documents_id'])) { echo "".__('Default documents:').''; echo "
"; $doc = new Document(); foreach ($values['_documents_id'] as $key => $val) { if ($doc->getFromDB($val)) { echo ""; echo "- ".$doc->getNameID()."
"; } } } } echo "
"; echo "
"; if ((!$ID || $canupdate || $canupdate_descr || Session::haveRightsOr(self::$rightname, array(self::ASSIGN, self::STEAL, DELETE, PURGE))) && !$options['template_preview']) { if ($ID) { if (Session::haveRightsOr(self::$rightname, array(UPDATE, DELETE, PURGE)) || $this->canDeleteItem() || $this->canUpdateItem()) { echo "
"; if ($this->fields["is_deleted"] == 1) { if (self::canPurge()) { echo "      "; } } else { if (self::canUpdate() ) { echo "      "; } } if ($this->fields["is_deleted"] == 1) { if (self::canPurge()) { echo ""; } } else { if ($this->canDeleteItem()) { echo ""; } } echo ""; echo "
"; } } else { echo "
"; echo ""; if ($tt->isField('id') && ($tt->fields['id'] > 0)) { echo ""; echo ""; } echo '
'; } } // File upload system echo "
"; echo $tt->getBeginHiddenFieldValue('_documents_id'); echo Html::file(array('multiple' => true, 'showfilecontainer' => 'fileupload_info', 'values' => array('filename' => $values['_filename'], 'tag' => $values['_tag_filename']) )); if ($CFG_GLPI['use_rich_text']) { echo "
"; echo "
"; if (!isset($rand)) { $rand = mt_rand(); } if ($canupdate_descr) { echo Html::initImagePasteSystem($content_id, $rand); } } echo "
"; echo ""; echo "
"; if (!$options['template_preview']) { Html::closeForm(); } return true; } /** * @param $size (default 25) **/ static function showDocumentAddButton($size=25) { global $CFG_GLPI; echo ""; echo "\"".'); nbfiles++; if (nbfiles==maxfiles) { ".Html::jsHide('addfilebutton')." } }\" class='pointer' src='".$CFG_GLPI["root_doc"]."/pics/add_dropdown.png'>"; } /** * @param $start * @param $status (default ''process) * @param $showgrouptickets (true by default) */ static function showCentralList($start, $status="process", $showgrouptickets=true) { global $DB, $CFG_GLPI; if (!Session::haveRightsOr(self::$rightname, array(CREATE, self::READALL, self::READASSIGN)) && !Session::haveRightsOr('ticketvalidation', TicketValidation::getValidateRights())) { return false; } $search_users_id = " (`glpi_tickets_users`.`users_id` = '".Session::getLoginUserID()."' AND `glpi_tickets_users`.`type` = '".CommonITILActor::REQUESTER."') "; $search_assign = " (`glpi_tickets_users`.`users_id` = '".Session::getLoginUserID()."' AND `glpi_tickets_users`.`type` = '".CommonITILActor::ASSIGN."')"; $search_observer = " (`glpi_tickets_users`.`users_id` = '".Session::getLoginUserID()."' AND `glpi_tickets_users`.`type` = '".CommonITILActor::OBSERVER."')"; $is_deleted = " `glpi_tickets`.`is_deleted` = 0 "; if ($showgrouptickets) { $search_users_id = " 0 = 1 "; $search_assign = " 0 = 1 "; if (count($_SESSION['glpigroups'])) { $groups = implode("','",$_SESSION['glpigroups']); $search_assign = " (`glpi_groups_tickets`.`groups_id` IN ('".$groups."') AND `glpi_groups_tickets`.`type` = '".CommonITILActor::ASSIGN."')"; if (Session::haveRight(self::$rightname, self::READGROUP)) { $search_users_id = " (`glpi_groups_tickets`.`groups_id` IN ('".$groups."') AND `glpi_groups_tickets`.`type` = '".CommonITILActor::REQUESTER."') "; $search_observer = " (`glpi_groups_tickets`.`groups_id` IN ('".$groups."') AND `glpi_groups_tickets`.`type` = '".CommonITILActor::OBSERVER."') "; } } } $query = "SELECT DISTINCT `glpi_tickets`.`id` FROM `glpi_tickets` LEFT JOIN `glpi_tickets_users` ON (`glpi_tickets`.`id` = `glpi_tickets_users`.`tickets_id`) LEFT JOIN `glpi_groups_tickets` ON (`glpi_tickets`.`id` = `glpi_groups_tickets`.`tickets_id`)"; switch ($status) { case "waiting" : // on affiche les tickets en attente $query .= "WHERE $is_deleted AND ($search_assign) AND `status` = '".self::WAITING."' ". getEntitiesRestrictRequest("AND", "glpi_tickets"); break; case "process" : // on affiche les tickets planifiés ou assignés au user $query .= "WHERE $is_deleted AND ( $search_assign ) AND (`status` IN ('".implode("','", self::getProcessStatusArray())."')) ". getEntitiesRestrictRequest("AND", "glpi_tickets"); break; case "toapprove" : // on affiche les tickets planifiés ou assignés au user $query .= "WHERE $is_deleted AND (`status` = '".self::SOLVED."') AND ($search_users_id"; if (!$showgrouptickets && Session::haveRight('ticket', Ticket::SURVEY)) { $query .= " OR `glpi_tickets`.users_id_recipient = '".Session::getLoginUserID()."' "; } $query .= ")". getEntitiesRestrictRequest("AND", "glpi_tickets"); break; case "tovalidate" : // on affiche les tickets à valider $query .= " LEFT JOIN `glpi_ticketvalidations` ON (`glpi_tickets`.`id` = `glpi_ticketvalidations`.`tickets_id`) WHERE $is_deleted AND `users_id_validate` = '".Session::getLoginUserID()."' AND `glpi_ticketvalidations`.`status` = '".CommonITILValidation::WAITING."' AND (`glpi_tickets`.`status` NOT IN ('".self::CLOSED."', '".self::SOLVED."')) ". getEntitiesRestrictRequest("AND", "glpi_tickets"); break; case "rejected" : // on affiche les tickets rejetés $query .= "WHERE $is_deleted AND ($search_assign) AND `status` <> '".self::CLOSED."' AND `global_validation` = '".CommonITILValidation::REFUSED."' ". getEntitiesRestrictRequest("AND", "glpi_tickets"); break; case "observed" : $query .= "WHERE $is_deleted AND ($search_observer) AND (`status` IN ('".self::INCOMING."', '".self::PLANNED."', '".self::ASSIGNED."', '".self::WAITING."')) AND NOT ( $search_assign ) AND NOT ( $search_users_id ) ". getEntitiesRestrictRequest("AND","glpi_tickets"); break; case "survey" : // tickets dont l'enquête de satisfaction n'est pas remplie et encore valide $query .= " INNER JOIN `glpi_ticketsatisfactions` ON (`glpi_tickets`.`id` = `glpi_ticketsatisfactions`.`tickets_id`) INNER JOIN `glpi_entities` ON (`glpi_entities`.`id` = `glpi_tickets`.`entities_id`) WHERE $is_deleted AND ($search_users_id"; if (Session::haveRight('ticket', Ticket::SURVEY)) { $query .= " OR `glpi_tickets`.`users_id_recipient` = '" . Session::getLoginUserID() . "'"; } $query .= ") AND `glpi_tickets`.`status` = '".self::CLOSED."' AND (`glpi_entities`.`inquest_duration` = 0 OR DATEDIFF(ADDDATE(`glpi_ticketsatisfactions`.`date_begin`, INTERVAL `glpi_entities`.`inquest_duration` DAY), CURDATE()) > 0) AND `glpi_ticketsatisfactions`.`date_answered` IS NULL ". getEntitiesRestrictRequest("AND", "glpi_tickets"); break; case "requestbyself" : // on affiche les tickets demandés le user qui sont planifiés ou assignés // à quelqu'un d'autre (exclut les self-tickets) default : $query .= "WHERE $is_deleted AND ($search_users_id) AND (`status` IN ('".self::INCOMING."', '".self::PLANNED."', '".self::ASSIGNED."', '".self::WAITING."')) AND NOT ( $search_assign ) ". getEntitiesRestrictRequest("AND","glpi_tickets"); } $query .= " ORDER BY `glpi_tickets`.`date_mod` DESC"; $result = $DB->query($query); $numrows = $DB->numrows($result); if ($_SESSION['glpidisplay_count_on_home'] > 0) { $query .= " LIMIT ".intval($start).','.intval($_SESSION['glpidisplay_count_on_home']); $result = $DB->query($query); $number = $DB->numrows($result); } else { $number = 0; } if ($numrows > 0) { echo ""; echo ""; if ($number) { echo ""; echo ""; echo ""; echo ""; for ($i = 0 ; $i < $number ; $i++) { $ID = $DB->result($result, $i, "id"); self::showVeryShort($ID, $forcetab); } } echo "
"; $options['reset'] = 'reset'; $forcetab = ''; $num = 0; if ($showgrouptickets) { switch ($status) { case "toapprove" : $options['criteria'][0]['field'] = 12; // status $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = self::SOLVED; $options['criteria'][0]['link'] = 'AND'; $options['criteria'][1]['field'] = 71; // groups_id $options['criteria'][1]['searchtype'] = 'equals'; $options['criteria'][1]['value'] = 'mygroups'; $options['criteria'][1]['link'] = 'AND'; $forcetab = 'Ticket$2'; echo "". Html::makeTitle(__('Your tickets to close'), $number, $numrows).""; break; case "waiting" : $options['criteria'][0]['field'] = 12; // status $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = self::WAITING; $options['criteria'][0]['link'] = 'AND'; $options['criteria'][1]['field'] = 8; // groups_id_assign $options['criteria'][1]['searchtype'] = 'equals'; $options['criteria'][1]['value'] = 'mygroups'; $options['criteria'][1]['link'] = 'AND'; echo "". Html::makeTitle(__('Tickets on pending status'), $number, $numrows).""; break; case "process" : $options['criteria'][0]['field'] = 12; // status $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = 'process'; $options['criteria'][0]['link'] = 'AND'; $options['criteria'][1]['field'] = 8; // groups_id_assign $options['criteria'][1]['searchtype'] = 'equals'; $options['criteria'][1]['value'] = 'mygroups'; $options['criteria'][1]['link'] = 'AND'; echo "". Html::makeTitle(__('Tickets to be processed'), $number, $numrows).""; break; case "observed": $options['criteria'][0]['field'] = 12; // status $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = 'notold'; $options['criteria'][0]['link'] = 'AND'; $options['criteria'][1]['field'] = 65; // groups_id $options['criteria'][1]['searchtype'] = 'equals'; $options['criteria'][1]['value'] = 'mygroups'; $options['criteria'][1]['link'] = 'AND'; echo "". Html::makeTitle(__('Your observed tickets'), $number, $numrows).""; break; case "requestbyself" : default : $options['criteria'][0]['field'] = 12; // status $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = 'notold'; $options['criteria'][0]['link'] = 'AND'; $options['criteria'][1]['field'] = 71; // groups_id $options['criteria'][1]['searchtype'] = 'equals'; $options['criteria'][1]['value'] = 'mygroups'; $options['criteria'][1]['link'] = 'AND'; echo "". Html::makeTitle(__('Your tickets in progress'), $number, $numrows).""; } } else { switch ($status) { case "waiting" : $options['criteria'][0]['field'] = 12; // status $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = self::WAITING; $options['criteria'][0]['link'] = 'AND'; $options['criteria'][1]['field'] = 5; // users_id_assign $options['criteria'][1]['searchtype'] = 'equals'; $options['criteria'][1]['value'] = Session::getLoginUserID(); $options['criteria'][1]['link'] = 'AND'; echo "". Html::makeTitle(__('Tickets on pending status'), $number, $numrows).""; break; case "process" : $options['criteria'][0]['field'] = 5; // users_id_assign $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = Session::getLoginUserID(); $options['criteria'][0]['link'] = 'AND'; $options['criteria'][1]['field'] = 12; // status $options['criteria'][1]['searchtype'] = 'equals'; $options['criteria'][1]['value'] = 'process'; $options['criteria'][1]['link'] = 'AND'; echo "". Html::makeTitle(__('Tickets to be processed'), $number, $numrows).""; break; case "tovalidate" : $options['criteria'][0]['field'] = 55; // validation status $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = CommonITILValidation::WAITING; $options['criteria'][0]['link'] = 'AND'; $options['criteria'][1]['field'] = 59; // validation aprobator $options['criteria'][1]['searchtype'] = 'equals'; $options['criteria'][1]['value'] = Session::getLoginUserID(); $options['criteria'][1]['link'] = 'AND'; $options['criteria'][2]['field'] = 12; // validation aprobator $options['criteria'][2]['searchtype'] = 'equals'; $options['criteria'][2]['value'] = 'old'; $options['criteria'][2]['link'] = 'AND NOT'; $forcetab = 'TicketValidation$1'; echo "". Html::makeTitle(__('Your tickets to validate'), $number, $numrows).""; break; case "rejected" : $options['criteria'][0]['field'] = 52; // validation status $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = CommonITILValidation::REFUSED; $options['criteria'][0]['link'] = 'AND'; $options['criteria'][1]['field'] = 5; // assign user $options['criteria'][1]['searchtype'] = 'equals'; $options['criteria'][1]['value'] = Session::getLoginUserID(); $options['criteria'][1]['link'] = 'AND'; echo "". Html::makeTitle(__('Your rejected tickets'), $number, $numrows).""; break; case "toapprove" : $options['criteria'][0]['field'] = 12; // status $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = self::SOLVED; $options['criteria'][0]['link'] = 'AND'; $options['criteria'][1]['field'] = 4; // users_id_assign $options['criteria'][1]['searchtype'] = 'equals'; $options['criteria'][1]['value'] = Session::getLoginUserID(); $options['criteria'][1]['link'] = 'AND'; $options['criteria'][2]['field'] = 22; // users_id_recipient $options['criteria'][2]['searchtype'] = 'equals'; $options['criteria'][2]['value'] = Session::getLoginUserID(); $options['criteria'][2]['link'] = 'OR'; $options['criteria'][3]['field'] = 12; // status $options['criteria'][3]['searchtype'] = 'equals'; $options['criteria'][3]['value'] = self::SOLVED; $options['criteria'][3]['link'] = 'AND'; $forcetab = 'Ticket$2'; echo "". Html::makeTitle(__('Your tickets to close'), $number, $numrows).""; break; case "observed" : $options['criteria'][0]['field'] = 66; // users_id $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = Session::getLoginUserID(); $options['criteria'][0]['link'] = 'AND'; $options['criteria'][1]['field'] = 12; // status $options['criteria'][1]['searchtype'] = 'equals'; $options['criteria'][1]['value'] = 'notold'; $options['criteria'][1]['link'] = 'AND'; echo "". Html::makeTitle(__('Your observed tickets'), $number, $numrows).""; break; case "survey" : $options['criteria'][0]['field'] = 12; // status $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = self::CLOSED; $options['criteria'][0]['link'] = 'AND'; $options['criteria'][1]['field'] = 60; // enquete generee $options['criteria'][1]['searchtype'] = 'contains'; $options['criteria'][1]['value'] = '^'; $options['criteria'][1]['link'] = 'AND'; $options['criteria'][2]['field'] = 61; // date_answered $options['criteria'][2]['searchtype'] = 'contains'; $options['criteria'][2]['value'] = 'NULL'; $options['criteria'][2]['link'] = 'AND'; if (Session::haveRight('ticket', Ticket::SURVEY)) { $options['criteria'][3]['field'] = 22; // author $options['criteria'][3]['searchtype'] = 'equals'; $options['criteria'][3]['value'] = Session::getLoginUserID(); $options['criteria'][3]['link'] = 'AND'; } else { $options['criteria'][3]['field'] = 4; // requester $options['criteria'][3]['searchtype'] = 'equals'; $options['criteria'][3]['value'] = Session::getLoginUserID(); $options['criteria'][3]['link'] = 'AND'; } $forcetab = 'Ticket$3'; echo "". Html::makeTitle(__('Satisfaction survey'), $number, $numrows).""; break; case "requestbyself" : default : $options['criteria'][0]['field'] = 4; // users_id $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = Session::getLoginUserID(); $options['criteria'][0]['link'] = 'AND'; $options['criteria'][1]['field'] = 12; // status $options['criteria'][1]['searchtype'] = 'equals'; $options['criteria'][1]['value'] = 'notold'; $options['criteria'][1]['link'] = 'AND'; echo "". Html::makeTitle(__('Your tickets in progress'), $number, $numrows).""; } } echo "
".__('Requester').""._n('Associated element', 'Associated elements', Session::getPluralNumber())."".__('Description')."
"; } } /** * Get tickets count * * @param $foruser boolean : only for current login user as requester (false by default) **/ static function showCentralCount($foruser=false) { global $DB, $CFG_GLPI; // show a tab with count of jobs in the central and give link if (!Session::haveRight(self::$rightname, self::READALL) && !self::canCreate()) { return false; } if (!Session::haveRight(self::$rightname, self::READALL)) { $foruser = true; } $query = "SELECT `glpi_tickets`.`status`, COUNT(DISTINCT `glpi_tickets`.`id`) AS COUNT FROM `glpi_tickets` "; if ($foruser) { $query .= " LEFT JOIN `glpi_tickets_users` ON (`glpi_tickets`.`id` = `glpi_tickets_users`.`tickets_id` AND `glpi_tickets_users`.`type` = '".CommonITILActor::REQUESTER."') LEFT JOIN `glpi_ticketvalidations` ON (`glpi_tickets`.`id` = `glpi_ticketvalidations`.`tickets_id`)"; if (Session::haveRight(self::$rightname, self::READGROUP) && isset($_SESSION["glpigroups"]) && count($_SESSION["glpigroups"])) { $query .= " LEFT JOIN `glpi_groups_tickets` ON (`glpi_tickets`.`id` = `glpi_groups_tickets`.`tickets_id` AND `glpi_groups_tickets`.`type` = '".CommonITILActor::REQUESTER."')"; } } $query .= getEntitiesRestrictRequest("WHERE", "glpi_tickets"); if ($foruser) { $query .= " AND (`glpi_tickets_users`.`users_id` = '".Session::getLoginUserID()."' OR `glpi_tickets`.`users_id_recipient` = '".Session::getLoginUserID()."' OR `glpi_ticketvalidations`.`users_id_validate` = '".Session::getLoginUserID()."'"; if (Session::haveRight(self::$rightname, self::READGROUP) && isset($_SESSION["glpigroups"]) && count($_SESSION["glpigroups"])) { $groups = implode(",",$_SESSION['glpigroups']); $query .= " OR `glpi_groups_tickets`.`groups_id` IN (".$groups.") "; } $query.= ")"; } $query_deleted = $query; $query .= " AND NOT `glpi_tickets`.`is_deleted` GROUP BY `status`"; $query_deleted .= " AND `glpi_tickets`.`is_deleted` GROUP BY `status`"; $result = $DB->query($query); $result_deleted = $DB->query($query_deleted); $status = array(); foreach (self::getAllStatusArray() as $key => $val) { $status[$key] = 0; } if ($DB->numrows($result) > 0) { while ($data = $DB->fetch_assoc($result)) { $status[$data["status"]] = $data["COUNT"]; } } $number_deleted = 0; if ($DB->numrows($result_deleted) > 0) { while ($data = $DB->fetch_assoc($result_deleted)) { $number_deleted += $data["COUNT"]; } } $options['criteria'][0]['field'] = 12; $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = 'process'; $options['criteria'][0]['link'] = 'AND'; $options['reset'] ='reset'; echo ""; echo ""; echo ""; foreach ($status as $key => $val) { $options['criteria'][0]['value'] = $key; echo ""; echo ""; echo ""; } $options['criteria'][0]['value'] = 'all'; $options['is_deleted'] = 1; echo ""; echo ""; echo ""; echo "
"; if ($_SESSION["glpiactiveprofile"]["interface"] != "central") { echo "". __('Create a ticket')." \"".__s('Add')."\""; } else { echo "".__('Ticket followup').""; } echo "
"._n('Ticket','Tickets', Session::getPluralNumber()).""._x('quantity', 'Number')."
".self::getStatus($key)."$val
".__('Deleted')."".$number_deleted."

"; } static function showCentralNewList() { global $DB, $CFG_GLPI; if (!Session::haveRight(self::$rightname, self::READALL)) { return false; } $query = "SELECT ".self::getCommonSelect()." FROM `glpi_tickets` ".self::getCommonLeftJoin()." WHERE `status` = '".self::INCOMING."' ". getEntitiesRestrictRequest("AND","glpi_tickets")." AND NOT `is_deleted` ORDER BY `glpi_tickets`.`date_mod` DESC LIMIT ".intval($_SESSION['glpilist_limit']); $result = $DB->query($query); $number = $DB->numrows($result); if ($number > 0) { Session::initNavigateListItems('Ticket'); $options['criteria'][0]['field'] = 12; $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = self::INCOMING; $options['criteria'][0]['link'] = 'AND'; $options['reset'] ='reset'; echo "
"; //TRANS: %d is the number of new tickets echo ""; self::commonListHeader(Search::HTML_OUTPUT); while ($data = $DB->fetch_assoc($result)) { Session::addToNavigateListItems('Ticket',$data["id"]); self::showShort($data["id"]); } echo "
".sprintf(_n('%d new ticket','%d new tickets', $number), $number); echo "".__('Show all').""; echo "
"; } else { echo "
"; echo ""; echo ""; echo "
".__('No ticket found.')."
"; echo "

"; } } /** * Display tickets for an item * * Will also display tickets of linked items * * @param $item CommonDBTM object * * @return nothing (display a table) **/ static function showListForItem(CommonDBTM $item) { global $DB, $CFG_GLPI; if (!Session::haveRightsOr(self::$rightname, array(self::READALL, self::READMY, self::READASSIGN, CREATE))) { return false; } if ($item->isNewID($item->getID())) { return false; } $restrict = ''; $order = ''; $options['reset'] = 'reset'; switch ($item->getType()) { case 'User' : $restrict = "(`glpi_tickets_users`.`users_id` = '".$item->getID()."' ". " AND `glpi_tickets_users`.`type` = ".CommonITILActor::REQUESTER.")"; $order = '`glpi_tickets`.`date_mod` DESC'; $options['reset'] = 'reset'; $options['criteria'][0]['field'] = 4; // status $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = $item->getID(); $options['criteria'][0]['link'] = 'AND'; break; case 'SLT' : $restrict = "`slts_tto_id` = '".$item->getID()."' OR `slts_ttr_id` = '".$item->getID()."'"; $order = '`glpi_tickets`.`due_date` DESC'; $options['criteria'][0]['field'] = 30; $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = $item->getID(); $options['criteria'][0]['link'] = 'AND'; break; case 'Supplier' : $restrict = "(`glpi_suppliers_tickets`.`suppliers_id` = '".$item->getID()."' ". " AND `glpi_suppliers_tickets`.`type` = ".CommonITILActor::ASSIGN.")"; $order = '`glpi_tickets`.`date_mod` DESC'; $options['criteria'][0]['field'] = 6; $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = $item->getID(); $options['criteria'][0]['link'] = 'AND'; break; case 'Group' : // Mini search engine if ($item->haveChildren()) { $tree = Session::getSavedOption(__CLASS__, 'tree', 0); echo ""; echo ""; echo "
".__('Last tickets')."
"; echo __('Child groups')." "; Dropdown::showYesNo('tree', $tree, -1, array('on_change' => 'reloadTab("start=0&tree="+this.value)')); } else { $tree = 0; } echo "
"; if ($tree) { $restrict = "IN (".implode(',', getSonsOf('glpi_groups', $item->getID())).")"; } else { $restrict = "='".$item->getID()."'"; } $restrict = "(`glpi_groups_tickets`.`groups_id` $restrict". " AND `glpi_groups_tickets`.`type` = ".CommonITILActor::REQUESTER.")"; $order = '`glpi_tickets`.`date_mod` DESC'; $options['criteria'][0]['field'] = 71; $options['criteria'][0]['searchtype'] = ($tree ? 'under' : 'equals'); $options['criteria'][0]['value'] = $item->getID(); $options['criteria'][0]['link'] = 'AND'; break; default : $restrict = "(`glpi_items_tickets`.`items_id` = '".$item->getID()."' ". " AND `glpi_items_tickets`.`itemtype` = '".$item->getType()."')"; // you can only see your tickets if (!Session::haveRight(self::$rightname, self::READALL)) { $restrict .= " AND (`glpi_tickets`.`users_id_recipient` = '".Session::getLoginUserID()."' OR (`glpi_tickets_users`.`tickets_id` = `glpi_tickets`.`id` AND `glpi_tickets_users`.`users_id` = '".Session::getLoginUserID()."') OR `glpi_groups_tickets`.`groups_id` IN (".implode(",",$_SESSION['glpigroups'])."))"; } if (Session::haveRightsAnd(self::$rightname, [self::READASSIGN, self::ASSIGN])) { $restrict .= " OR (glpi_tickets.status=".self::INCOMING.")"; } $order = '`glpi_tickets`.`date_mod` DESC'; $options['criteria'][0]['field'] = 12; $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = 'all'; $options['criteria'][0]['link'] = 'AND'; $options['metacriteria'][0]['itemtype'] = $item->getType(); $options['metacriteria'][0]['field'] = Search::getOptionNumber($item->getType(), 'id'); $options['metacriteria'][0]['searchtype'] = 'equals'; $options['metacriteria'][0]['value'] = $item->getID(); $options['metacriteria'][0]['link'] = 'AND'; break; } $query = "SELECT ".self::getCommonSelect()." FROM `glpi_tickets` ".self::getCommonLeftJoin()." WHERE $restrict ". getEntitiesRestrictRequest("AND","glpi_tickets")." ORDER BY $order LIMIT ".intval($_SESSION['glpilist_limit']); $result = $DB->query($query); $number = $DB->numrows($result); $colspan = 11; if (count($_SESSION["glpiactiveentities"]) > 1) { $colspan++; } // Ticket for the item echo "
"; // Link to open a new ticket if ($item->getID() && Ticket::isPossibleToAssignType($item->getType()) && self::canCreate()) { Html::showSimpleForm($CFG_GLPI["root_doc"]."/front/ticket.form.php", '_add_fromitem', __('New ticket for this item...'), array('itemtype' => $item->getType(), 'items_id' => $item->getID())); } echo "
"; if ($number > 0) { echo ""; if (Session::haveRight(self::$rightname, self::READALL)) { Session::initNavigateListItems('Ticket', //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->getName())); echo ""; } else { echo ""; } } else { echo "
"; $title = sprintf(_n('Last %d ticket', 'Last %d tickets', $number), $number); $link = "".__('Show all').""; $title = printf(__('%1$s (%2$s)'), $title, $link); echo "
".__("You don't have right to see all tickets")."
"; echo ""; } if ($item->getID() && ($item->getType() == 'User') && self::canCreate()) { echo ""; } // Ticket list if ($number > 0) { self::commonListHeader(Search::HTML_OUTPUT); while ($data = $DB->fetch_assoc($result)) { Session::addToNavigateListItems('Ticket',$data["id"]); self::showShort($data["id"]); } self::commonListHeader(Search::HTML_OUTPUT); } echo "
".__('No ticket found.')."
"; Html::showSimpleForm($CFG_GLPI["root_doc"]."/front/ticket.form.php", '_add_fromitem', __('New ticket for this item...'), array('_users_id_requester' => $item->getID())); echo "
"; // Tickets for linked items $linkeditems = $item->getLinkedItems(); $restrict = array(); if (count($linkeditems)) { foreach ($linkeditems as $ltype => $tab) { foreach ($tab as $lID) { $restrict[] = "(`glpi_items_tickets`.`itemtype` = '$ltype' AND `glpi_items_tickets`.`items_id` = '$lID')"; } } } if (count($restrict) && Session::haveRight(self::$rightname, self::READALL)) { $query = "SELECT ".self::getCommonSelect()." FROM `glpi_tickets` ".self::getCommonLeftJoin()." WHERE ".implode(' OR ', $restrict). getEntitiesRestrictRequest(' AND ', 'glpi_tickets') . " ORDER BY `glpi_tickets`.`date_mod` DESC LIMIT ".intval($_SESSION['glpilist_limit']); $result = $DB->query($query); $number = $DB->numrows($result); echo "
"; echo ""; if ($number > 0) { self::commonListHeader(Search::HTML_OUTPUT); while ($data = $DB->fetch_assoc($result)) { // Session::addToNavigateListItems(TRACKING_TYPE,$data["id"]); self::showShort($data["id"]); } self::commonListHeader(Search::HTML_OUTPUT); } else { echo ""; } echo "
"; echo _n('Ticket on linked items', 'Tickets on linked items', $number); echo "
".__('No ticket found.')."
"; } // Subquery for linked item } /** * @param $ID * @param $forcetab string name of the tab to force at the display (default '') **/ static function showVeryShort($ID, $forcetab='') { global $CFG_GLPI; // Prints a job in short form // Should be called in a -segment // Print links or not in case of user view // Make new job object and fill it from database, if success, print it $showprivate = false; if (Session::haveRight('followup', TicketFollowup::SEEPRIVATE)) { $showprivate = true; } $job = new self(); $rand = mt_rand(); if ($job->getFromDBwithData($ID, 0)) { $bgcolor = $_SESSION["glpipriority_".$job->fields["priority"]]; // $rand = mt_rand(); echo ""; echo ""; echo ""; echo ""; // Finish Line echo ""; } else { echo ""; echo ""; } } static function getCommonSelect() { $SELECT = ""; if (count($_SESSION["glpiactiveentities"])>1) { $SELECT .= ", `glpi_entities`.`completename` AS entityname, `glpi_tickets`.`entities_id` AS entityID "; } return " DISTINCT `glpi_tickets`.*, `glpi_itilcategories`.`completename` AS catname $SELECT"; } static function getCommonLeftJoin() { $FROM = ""; if (count($_SESSION["glpiactiveentities"])>1) { $FROM .= " LEFT JOIN `glpi_entities` ON (`glpi_entities`.`id` = `glpi_tickets`.`entities_id`) "; } return " LEFT JOIN `glpi_groups_tickets` ON (`glpi_tickets`.`id` = `glpi_groups_tickets`.`tickets_id`) LEFT JOIN `glpi_tickets_users` ON (`glpi_tickets`.`id` = `glpi_tickets_users`.`tickets_id`) LEFT JOIN `glpi_suppliers_tickets` ON (`glpi_tickets`.`id` = `glpi_suppliers_tickets`.`tickets_id`) LEFT JOIN `glpi_itilcategories` ON (`glpi_tickets`.`itilcategories_id` = `glpi_itilcategories`.`id`) LEFT JOIN `glpi_tickettasks` ON (`glpi_tickets`.`id` = `glpi_tickettasks`.`tickets_id`) LEFT JOIN `glpi_items_tickets` ON (`glpi_tickets`.`id` = `glpi_items_tickets`.`tickets_id`) $FROM"; } /** * @param $output **/ static function showPreviewAssignAction($output) { //If ticket is assign to an object, display this information first if (isset($output["entities_id"]) && isset($output["items_id"]) && isset($output["itemtype"])) { if ($item = getItemForItemtype($output["itemtype"])) { if ($item->getFromDB($output["items_id"])) { echo ""; echo ""; echo ""; echo ""; } } unset($output["items_id"]); unset($output["itemtype"]); } unset($output["entities_id"]); return $output; } /** * Give cron information * * @param $name : task's name * * @return arrray of information **/ static function cronInfo($name) { switch ($name) { case 'closeticket' : return array('description' => __('Automatic tickets closing')); case 'alertnotclosed' : return array('description' => __('Not solved tickets')); case 'createinquest' : return array('description' => __('Generation of satisfaction surveys')); } return array(); } /** * Cron for ticket's automatic close * * @param $task : crontask object * * @return integer (0 : nothing done - 1 : done) **/ static function cronCloseTicket($task) { global $DB; $ticket = new self(); // Recherche des entités $tot = 0; foreach (Entity::getEntitiesToNotify('autoclose_delay') as $entity => $delay) { if ($delay >= 0) { $query = "SELECT * FROM `glpi_tickets` WHERE `entities_id` = '".$entity."' AND `status` = '".self::SOLVED."' AND `is_deleted` = 0"; if ($delay > 0) { $query .= " AND ADDDATE(`solvedate`, INTERVAL ".$delay." DAY) < NOW()"; } $nb = 0; foreach ($DB->request($query) as $tick) { $ticket->update(array('id' => $tick['id'], 'status' => self::CLOSED, '_auto_update' => true)); $nb++; } if ($nb) { $tot += $nb; $task->addVolume($nb); $task->log(Dropdown::getDropdownName('glpi_entities', $entity)." : $nb"); } } } return ($tot > 0); } /** * Cron for alert old tickets which are not solved * * @param $task : crontask object * * @return integer (0 : nothing done - 1 : done) **/ static function cronAlertNotClosed($task) { global $DB, $CFG_GLPI; if (!$CFG_GLPI["use_mailing"]) { return 0; } // Recherche des entités $tot = 0; foreach (Entity::getEntitiesToNotify('notclosed_delay') as $entity => $value) { $query = "SELECT `glpi_tickets`.* FROM `glpi_tickets` WHERE `glpi_tickets`.`entities_id` = '".$entity."' AND `glpi_tickets`.`is_deleted` = 0 AND `glpi_tickets`.`status` IN ('".self::INCOMING."', '".self::ASSIGNED."', '".self::PLANNED."', '".self::WAITING."') AND `glpi_tickets`.`closedate` IS NULL AND ADDDATE(`glpi_tickets`.`date`, INTERVAL ".$value." DAY) < NOW()"; $tickets = array(); foreach ($DB->request($query) as $tick) { $tickets[] = $tick; } if (!empty($tickets)) { if (NotificationEvent::raiseEvent('alertnotclosed', new self(), array('items' => $tickets, 'entities_id' => $entity))) { $tot += count($tickets); $task->addVolume(count($tickets)); $task->log(sprintf(__('%1$s: %2$s'), Dropdown::getDropdownName('glpi_entities', $entity), count($tickets))); } } } return ($tot > 0); } /** * Cron for ticketsatisfaction's automatic generated * * @param $task : crontask object * * @return integer (0 : nothing done - 1 : done) **/ static function cronCreateInquest($task) { global $DB; $conf = new Entity(); $inquest = new TicketSatisfaction(); $tot = 0; $maxentity = array(); $tabentities = array(); $rate = Entity::getUsedConfig('inquest_config', 0, 'inquest_rate'); if ($rate > 0) { $tabentities[0] = $rate; } foreach ($DB->request('glpi_entities') as $entity) { $rate = Entity::getUsedConfig('inquest_config', $entity['id'], 'inquest_rate'); $parent = Entity::getUsedConfig('inquest_config', $entity['id'], 'entities_id'); if ($rate > 0) { $tabentities[$entity['id']] = $rate; } } foreach ($tabentities as $entity => $rate) { $parent = Entity::getUsedConfig('inquest_config', $entity, 'entities_id'); $delay = Entity::getUsedConfig('inquest_config', $entity, 'inquest_delay'); $duration = Entity::getUsedConfig('inquest_config', $entity, 'inquest_duration'); $type = Entity::getUsedConfig('inquest_config', $entity); $max_closedate = Entity::getUsedConfig('inquest_config', $entity, 'max_closedate'); $query = "SELECT `glpi_tickets`.`id`, `glpi_tickets`.`closedate`, `glpi_tickets`.`entities_id` FROM `glpi_tickets` LEFT JOIN `glpi_ticketsatisfactions` ON `glpi_ticketsatisfactions`.`tickets_id` = `glpi_tickets`.`id` LEFT JOIN `glpi_entities` ON `glpi_tickets`.`entities_id` = `glpi_entities`.`id` WHERE `glpi_tickets`.`entities_id` = '$entity' AND `glpi_tickets`.`is_deleted` = 0 AND `glpi_tickets`.`status` = '".self::CLOSED."' AND `glpi_tickets`.`closedate` > '$max_closedate' AND ADDDATE(`glpi_tickets`.`closedate`, INTERVAL $delay DAY)<=NOW() AND ADDDATE(`glpi_entities`.`max_closedate`, INTERVAL $duration DAY)<=NOW() AND `glpi_ticketsatisfactions`.`id` IS NULL ORDER BY `closedate` ASC"; $nb = 0; $max_closedate = ''; foreach ($DB->request($query) as $tick) { $max_closedate = $tick['closedate']; if (mt_rand(1,100) <= $rate) { if ($inquest->add(array('tickets_id' => $tick['id'], 'date_begin' => $_SESSION["glpi_currenttime"], 'entities_id' => $tick['entities_id'], 'type' => $type))) { $nb++; } } } // conservation de toutes les max_closedate des entites filles if (!empty($max_closedate) && (!isset($maxentity[$parent]) || ($max_closedate > $maxentity[$parent]))) { $maxentity[$parent] = $max_closedate; } if ($nb) { $tot += $nb; $task->addVolume($nb); $task->log(sprintf(__('%1$s: %2$s'), Dropdown::getDropdownName('glpi_entities', $entity), $nb)); } } // Sauvegarde du max_closedate pour ne pas tester les même tickets 2 fois foreach ($maxentity as $parent => $maxdate) { $conf->getFromDB($parent); $conf->update(array('id' => $conf->fields['id'], //'entities_id' => $parent, 'max_closedate' => $maxdate)); } return ($tot > 0); } /** * Display debug information for current object **/ function showDebug() { NotificationEvent::debugEvent($this); } /** * @since version 0.85 * * @see commonDBTM::getRights() **/ function getRights($interface='central') { $values = parent::getRights(); unset($values[READ]); $values[self::READMY] = __('See my ticket'); //TRANS: short for : See tickets created by my groups $values[self::READGROUP] = array('short' => __('See group ticket'), 'long' => __('See tickets created by my groups')); if ($interface == 'central') { $values[self::READALL] = __('See all tickets'); //TRANS: short for : See assigned tickets (group associated) $values[self::READASSIGN] = array('short' => __('See assigned'), 'long' => __('See assigned tickets')); //TRANS: short for : Assign a ticket $values[self::ASSIGN] = array('short' => __('Assign'), 'long' => __('Assign a ticket')); //TRANS: short for : Steal a ticket $values[self::STEAL] = array('short' => __('Steal'), 'long' => __('Steal a ticket')); //TRANS: short for : To be in charge of a ticket $values[self::OWN] = array('short' => __('Beeing in charge'), 'long' => __('To be in charge of a ticket')); $values[self::CHANGEPRIORITY] = __('Change the priority'); $values[self::SURVEY] = array('short' => __('Approve solution/Reply survey (my ticket)'), 'long' => __('Approve solution and reply to survey for ticket created by me')); } if ($interface == 'helpdesk') { unset($values[UPDATE], $values[DELETE], $values[PURGE]); } return $values; } /** * Add image pasted to GLPI doc after ADD and before UPDATE of the item in the database * * @since version 0.85 * * @return nothing **/ function addImagePaste() { if (count($this->input['_stock_image']) > 0) { $count_files = 0; // Filename if (isset($this->input['_filename'])) { $count_files = count($this->input['_stock_image']); foreach($this->input['_filename'] as $key => $filename){ $this->input['_filename'][$count_files] = $filename; $count_files++; } $count_files = count($this->input['_stock_image']); foreach($this->input['_tag_filename'] as $key => $tag){ $this->input['_tag'][$count_files] = $tag; $count_files++; } unset($this->input['_tag_filename']); } // Stock_image foreach ($this->input['_stock_image'] as $key => $filename) { $this->input['_filename'][$key] = $filename; } unset($this->input['_stock_image']); foreach($this->input['_tag_stock_image'] as $key => $tag){ $this->input['_tag'][$key] = $tag; $count_files++; } unset($this->input['_tag_stock_image']); ksort($this->input['_filename']); ksort($this->input['_tag']); // $this->input['_forcenotif'] = 1; } } /** * Convert tag to image * * @since version 0.85 * * @param $content_text text content of input * @param $force_update force update of content in item (false by default * @param $doc_data array of filenames and tags * * @return nothing **/ function convertTagToImage($content_text, $force_update=false, $doc_data=array()) { global $CFG_GLPI; $matches = array(); // If no doc data available we match all tags in content if (!count($doc_data)) { $doc = new Document(); preg_match_all('/'.Document::getImageTag('(([a-z0-9]+|[\.\-]?)+)').'/', $content_text, $matches, PREG_PATTERN_ORDER); if (isset($matches[1]) && count($matches[1])) { $doc_data = $doc->find("`tag` IN('".implode("','", array_unique($matches[1]))."')"); } } if (count($doc_data)) { foreach ($doc_data as $id => $image) { // Add only image files : try to detect mime type $ok = false; $mime = ''; if (isset($image['filepath'])) { $fullpath = GLPI_DOC_DIR."/".$image['filepath']; $mime = Toolbox::getMime($fullpath); $ok = Toolbox::getMime($fullpath, 'image'); } if (isset($image['tag'])) { if ($ok || empty($mime)) { // Replace tags by image in textarea $img = "".$image["; // Replace tag by the image $content_text = preg_replace('/'.Document::getImageTag($image['tag']).'/', Html::entities_deep($img), $content_text); // Replace
TinyMce bug $content_text = str_replace(array('>rn<','>\r\n<','>\r<','>\n<'), '><', $content_text); // If the tag is from another ticket : link document to ticket /// TODO : comment maybe not used // if($image['tickets_id'] != $this->fields['id']){ // $docitem = new Document_Item(); // $docitem->add(array('documents_id' => $image['id'], // '_do_notif' => false, // '_disablenotif' => true, // 'itemtype' => $this->getType(), // 'items_id' => $this->fields['id'])); // } } else { // Remove tag $content_text = preg_replace('/'.Document::getImageTag($image['tag']).'/', '', $content_text); } } } } if ($force_update) { $this->fields['content'] = $content_text; $this->updateInDB(array('content')); } return $content_text; } /** * Convert image to tag * * @since version 0.85 * * @param $content_html html content of input * @param $force_update force update of content in item (false by default * * @return htlm content **/ function convertImageToTag($content_html, $force_update=false) { if (!empty($content_html)) { preg_match_all("/alt\s*=\s*['|\"](.+?)['|\"]/", $content_html, $matches, PREG_PATTERN_ORDER); if (isset($matches[1]) && count($matches[1])) { // Get all image src foreach ($matches[1] as $src) { // Set tag if image matches $content_html = preg_replace(array("/]*\>/", "/]*\>/"), Document::getImageTag($src), $content_html); } } return $content_html; } } /** * Convert img of the collector for ticket * * @since version 0.85 * * @param $content_html html content of input * @param $files array of filename * @param $tags array of image tag * * @return htlm content **/ static function convertContentForTicket($content_html, $files, $tags) { // We inject another meta tag $html = Html::entity_decode_deep($content_html); preg_match_all("/src\s*=\s*['|\"](.+?)['|\"]/", $html, $matches, PREG_PATTERN_ORDER); if (isset($matches[1]) && count($matches[1])) { // Get all image src foreach ($matches[1] as $src) { // Set tag if image matches foreach ($files as $data => $filename) { if (preg_match("/".$data."/i", $src)) { $html = preg_replace("`]*\>`", "

".Document::getImageTag($tags[$filename])."

", $html); } } } } return $html; } /** * Convert img or tag of ticket for notification mails * * @since version 0.85 * * @param $content : html content of input * @param $item : item to store filenames and tags found for each image in $content * * @return htlm content **/ function convertContentForNotification($content, $item) { global $CFG_GLPI, $DB; $html = str_replace(array('&','&nbsp;'), array('&',' '), html_entity_decode($content, ENT_QUOTES, "UTF-8")); // If is html content if ($CFG_GLPI["use_rich_text"]) { preg_match_all('/img\s*alt=[\'|"](([a-z0-9]+|[\.\-]?)+)[\'|"]/', $html, $matches, PREG_PATTERN_ORDER); if (isset($matches[1]) && count($matches[1])) { if (count($matches[1])) { foreach ($matches[1] as $image) { //Replace tags by image in textarea $img = "img src='cid:".Document::getImageTag($image)."'"; //Replace tag by the image $html = preg_replace("/img alt=['|\"]".$image."['|\"].*src=['|\"](.+)['|\"]/", $img, $html); } } } $content = $html; // If is text content } else { $doc = new Document(); $doc_data = array(); preg_match_all('/'.Document::getImageTag('(([a-z0-9]+|[\.\-]?)+)').'/', $content, $matches, PREG_PATTERN_ORDER); if (isset($matches[1]) && count($matches[1])) { $doc_data = $doc->find("tag IN('".implode("','", array_unique($matches[1]))."')"); } if (count($doc_data)) { foreach ($doc_data as $image) { // Replace tags by image in textarea $img = ""; // Replace tag by the image $content = preg_replace('/'.Document::getImageTag($image['tag']).'/', $img, $content); } } } // Get all attached documents of ticket $query = "SELECT `glpi_documents_items`.`id` AS assocID, `glpi_entities`.`id` AS entity, `glpi_documents`.`name` AS assocName, `glpi_documents`.* FROM `glpi_documents_items` LEFT JOIN `glpi_documents` ON (`glpi_documents_items`.`documents_id`=`glpi_documents`.`id`) LEFT JOIN `glpi_entities` ON (`glpi_documents`.`entities_id`=`glpi_entities`.`id`) WHERE `glpi_documents_items`.`items_id` = '".$item->fields['id']."' AND `glpi_documents_items`.`itemtype` = '".$item->getType()."' "; if (Session::getLoginUserID()) { $query .= getEntitiesRestrictRequest(" AND","glpi_documents",'','',true); } else { // Anonymous access from Crontask $query .= " AND `glpi_documents`.`entities_id`= '0' "; } $result = $DB->query($query); if ($DB->numrows($result)) { while ($data = $DB->fetch_assoc($result)) { if (!empty($data['id'])) { // Image document if (!empty($data['tag'])) { $item->documents[] = $data['id']; // Other document } else if ($CFG_GLPI['attach_ticket_documents_to_mail']) { $item->documents[] = $data['id']; } } } } return $content; } /** * Delete tag or image from ticket content * * @since version 0.85 * * @param $content html content of input * @param $tags * * @return htlm content **/ function cleanTagOrImage($content, $tags) { global $CFG_GLPI; // RICH TEXT : delete img tag if ($CFG_GLPI["use_rich_text"]) { $content = Html::entity_decode_deep($content); foreach ($tags as $tag) { $content = preg_replace("/]*\>/", "

", $content); } // SIMPLE TEXT : delete tag } else { foreach ($tags as $tag) { $content = preg_replace('/'.Document::getImageTag($tag).'/', '\r\n', $content); } } return $content; } /** * Convert rich text content to simple text content * * @since version 0.85 * * @param $content : content to convert in html * * @return $content **/ function setSimpleTextContent($content) { $content = Html::entity_decode_deep($content); // If is html content if ($content != strip_tags($content)) { $content = Html::clean($this->convertImageToTag($content), false, 1); $content = Html::entity_decode_deep(Html::clean($this->convertImageToTag($content))); } return $content; } /** * Convert simple text content to rich text content, init html editor * * @since version 0.85 * * @param $name name of textarea * @param $content content to convert in html * @param $rand * @param $readonly boolean true will set editor in readonly mode * * @return $content **/ function setRichTextContent($name, $content, $rand, $readonly=false) { // Init html editor Html::initEditorSystem($name, $rand, true, $readonly); // If no html if ($content == strip_tags($content)) { $content = $this->convertTagToImage($content); } // Neutralize non valid HTML tags $content = html::clean($content, false, 1); // If content does not contain
or

html tag, use nl2br if (!preg_match("//", $content) && !preg_match("/

/", $content)) { $content = nl2br($content); } return $content; } /** * @since version 0.90 * **/ function getTimelineItems() { global $DB, $CFG_GLPI; $timeline = array(); $user = new User(); $group = new Group(); $followup_obj = new TicketFollowup(); $task_obj = new TicketTask(); $document_item_obj = new Document_Item(); $ticket_valitation_obj = new TicketValidation(); //checks rights $showpublic = Session::haveRightsOr("followup", array(TicketFollowup::SEEPUBLIC, TicketFollowup::SEEPRIVATE)) || Session::haveRightsOr("task", array(TicketTask::SEEPUBLIC, TicketTask::SEEPRIVATE)); $restrict_fup = $restrict_task = ""; if (!Session::haveRight("followup", TicketFollowup::SEEPRIVATE)) { $restrict_fup = " AND (`is_private` = '0' OR `users_id` ='" . Session::getLoginUserID() . "') "; } if (!Session::haveRight("task", TicketTask::SEEPRIVATE)) { $restrict_task = " AND (`is_private` = '0' OR `users_id` ='" . Session::getLoginUserID() . "') "; } if (!$showpublic) { $restrict = " AND 1 = 0"; } //add ticket followups to timeline if ($followup_obj->canview()) { $followups = $followup_obj->find("tickets_id = ".$this->getID()." $restrict_fup", 'date DESC'); foreach ($followups as $followups_id => $followup) { $followup_obj->getFromDB($followups_id); $followup['can_edit'] = $followup_obj->canUpdateItem();; $timeline[$followup['date']."_followup_".$followups_id] = array('type' => 'TicketFollowup', 'item' => $followup); } } //add ticket tasks to timeline if ($task_obj->canview()) { $tasks = $task_obj->find("tickets_id = ".$this->getID()." $restrict_task", 'date DESC'); foreach ($tasks as $tasks_id => $task) { $task_obj->getFromDB($tasks_id); $task['can_edit'] = $task_obj->canUpdateItem(); $timeline[$task['date']."_task_".$tasks_id] = array('type' => 'TicketTask', 'item' => $task); } } //add ticket documents to timeline $document_obj = new Document(); $document_items = $document_item_obj->find("itemtype = 'Ticket' AND items_id = ".$this->getID()); foreach ($document_items as $document_item) { $document_obj->getFromDB($document_item['documents_id']); $timeline[$document_item['date_mod']."_document_".$document_item['documents_id']] = array('type' => 'Document_Item', 'item' => $document_obj->fields); } //add existing solution if (!empty($this->fields['solution']) || !empty($this->fields['solutiontypes_id'])) { $users_id = 0; $solution_date = $this->fields['solvedate']; //search date and user of last solution in glpi_logs if ($res_solution = $DB->query("SELECT `date_mod` AS solution_date, `user_name`, `id` FROM `glpi_logs` WHERE `itemtype` = 'Ticket' AND `items_id` = ".$this->getID()." AND `id_search_option` = 24 ORDER BY `id` DESC LIMIT 1")) { $data_solution = $DB->fetch_assoc($res_solution); if (!empty($data_solution['solution_date'])) { $solution_date = $data_solution['solution_date']; } // find user if (!empty($data_solution['user_name'])) { $users_id = addslashes(trim(preg_replace("/.*\(([0-9]+)\)/", "$1", $data_solution['user_name']))); } } // fix trouble with html_entity_decode who skip accented characters (on windows browser) $solution_content = preg_replace_callback("/(&#[0-9]+;)/", function($m) { return mb_convert_encoding($m[1], "UTF-8", "HTML-ENTITIES"); }, $this->fields['solution']); $timeline[$solution_date."_solution"] = array('type' => 'Solution', 'item' => array('id' => 0, 'content' => Toolbox::unclean_cross_side_scripting_deep($solution_content), 'date' => $solution_date, 'users_id' => $users_id, 'solutiontypes_id' => $this->fields['solutiontypes_id'], 'can_edit' => Ticket::canUpdate() && $this->canSolve())); } // add ticket validation to timeline if ((($this->fields['type'] == Ticket::DEMAND_TYPE) && (Session::haveRight('ticketvalidation', TicketValidation::VALIDATEREQUEST) || Session::haveRight('ticketvalidation', TicketValidation::CREATEREQUEST))) || (($this->fields['type'] == Ticket::INCIDENT_TYPE) && (Session::haveRight('ticketvalidation', TicketValidation::VALIDATEINCIDENT) || Session::haveRight('ticketvalidation', TicketValidation::CREATEINCIDENT)))) { $ticket_validations = $ticket_valitation_obj->find('tickets_id = '.$this->getID()); foreach ($ticket_validations as $validations_id => $validation) { $canedit = $ticket_valitation_obj->can($validations_id, UPDATE); $user->getFromDB($validation['users_id_validate']); $timeline[$validation['submission_date']."_validation_".$validations_id] = array('type' => 'TicketValidation', 'item' => array('id' => $validations_id, 'date' => $validation['submission_date'], 'content' => __('Validation request')." => ".$user->getlink(). "
".$validation['comment_submission'], 'users_id' => $validation['users_id'], 'can_edit' => $canedit)); if (!empty($validation['validation_date'])) { $timeline[$validation['validation_date']."_validation_".$validations_id] = array('type' => 'TicketValidation', 'item' => array('id' => $validations_id, 'date' => $validation['validation_date'], 'content' => __('Validation request answer')." : ". _sx('status', ucfirst(TicketValidation::getStatus($validation['status']))) ."
".$validation['comment_validation'], 'users_id' => $validation['users_id_validate'], 'status' => "status_".$validation['status'], 'can_edit' => $canedit)); } } } //reverse sort timeline items by key (date) krsort($timeline); return $timeline; } /** * @since version 0.90 * * @param $rand **/ function showTimeline($rand) { global $CFG_GLPI, $DB, $autolink_options; //get ticket actors $ticket_users_keys = $this->getTicketActors(); $user = new User(); $group = new Group(); $followup_obj = new TicketFollowup(); $pics_url = $CFG_GLPI['root_doc']."/pics/timeline"; $timeline = $this->getTimelineItems(); $autolink_options['strip_protocols'] = false; //display timeline echo "

"; $tmp = array_values($timeline); $first_item = array_shift($tmp); // show approbation form on top when ticket is solved if ($this->fields["status"] == CommonITILObject::SOLVED) { echo "
"; $followup_obj->showApprobationForm($this); echo "
"; } // show title for timeline self::showTimelineHeader(); $timeline_index = 0; foreach ($timeline as $item) { $options = array( 'parent' => $this, 'rand' => $rand ) ; if( $obj = getItemForItemtype($item['type']) ){ $obj->fields = $item['item'] ; } else { $obj = $item ; } Plugin::doHook('pre_show_item', array('item' => $obj, 'options' => &$options)); if( is_array( $obj ) ){ $item_i = $obj['item']; } else { $item_i = $obj->fields; } $date = ""; if (isset($item_i['date'])) { $date = $item_i['date']; } if (isset($item_i['date_mod'])) { $date = $item_i['date_mod']; } // check if curent item user is assignee or requester $user_position = 'left'; if ((isset($ticket_users_keys[$item_i['users_id']]) && ($ticket_users_keys[$item_i['users_id']] == CommonItilActor::ASSIGN)) || ($item['type'] == 'Assign')) { $user_position = 'right'; } //display solution in middle if (($item['type'] == "Solution") && in_array($this->fields["status"], [CommonITILObject::SOLVED, CommonITILObject::CLOSED])) { $user_position.= ' middle'; } echo "
"; echo "
"; echo "
".Html::convDateTime($date)."
"; if ($item_i['users_id'] !== false) { echo "
"; if (isset($item_i['users_id']) && ($item_i['users_id'] != 0)) { $user->getFromDB($item_i['users_id']); echo "
"; echo "\"".__s('Picture')."\""; echo "
"; echo ""; $userdata = getUserName($item_i['users_id'], 2); echo $user->getLink()." "; echo Html::showToolTip($userdata["comment"], array('link' => $userdata['link'])); echo ""; } else { _e("Requester"); } echo "
"; // h_user } echo "
"; //h_date echo "
"; if (isset($item_i['can_edit']) && $item_i['can_edit']) { echo "
"; echo ""; } echo "
"; if (!in_array($item['type'], array('Document_Item', 'Assign')) && $item_i['can_edit']) { echo ""; } if (isset($item_i['requesttypes_id']) && file_exists("$pics_url/".$item_i['requesttypes_id'].".png")) { echo ""; } if (isset($item_i['content'])) { $content = $item_i['content']; $content = autolink($content, 40); //$content = nl2br($content); $long_text = ""; if ((substr_count($content, " 30) || (strlen($content) > 2000)) { $long_text = "long_text"; } echo "
"; echo "

"; if (isset($item_i['state'])) { $onClick = "onclick='change_task_state(".$item_i['id'].", this)'"; if( !$item_i['can_edit'] ) { $onClick = "style='cursor: not-allowed;'" ; } echo ""; echo ""; } echo $content; echo "

"; if (!empty($long_text)) { echo "

"; echo "....."; echo "

"; } echo "
"; } echo "
"; if (isset($item_i['solutiontypes_id']) && !empty($item_i['solutiontypes_id'])) { echo Dropdown::getDropdownName("glpi_solutiontypes", $item_i['solutiontypes_id'])."
"; } if (isset($item_i['taskcategories_id']) && !empty($item_i['taskcategories_id'])) { echo Dropdown::getDropdownName("glpi_taskcategories", $item_i['taskcategories_id'])."
"; } if (isset($item_i['requesttypes_id']) && !empty($item_i['requesttypes_id'])) { echo Dropdown::getDropdownName("glpi_requesttypes", $item_i['requesttypes_id'])."
"; } if (isset($item_i['actiontime']) && !empty($item_i['actiontime'])) { echo ""; echo Html::timestampToString($item_i['actiontime'], false); echo ""; } if (isset($item_i['begin'])) { echo ""; echo Html::convDateTime($item_i["begin"]); echo " ⇒ "; echo Html::convDateTime($item_i["end"]); echo ""; } if (isset($item_i['users_id_tech']) && ($item_i['users_id_tech'] > 0)) { echo "
"; $user->getFromDB($item_i['users_id_tech']); echo Html::image($CFG_GLPI['root_doc']."/pics/user.png")." "; $userdata = getUserName($item_i['users_id_tech'], 2); echo $user->getLink()." "; echo Html::showToolTip($userdata["comment"], array('link' => $userdata['link'])); echo "
"; } if (isset($item_i['groups_id_tech']) && ($item_i['groups_id_tech'] > 0)) { echo "
"; $group->getFromDB($item_i['groups_id_tech']); echo Html::image($CFG_GLPI['root_doc']."/pics/group.png")." "; echo $group->getLink()." "; echo Html::showToolTip($group->getComments(), array('link' => $group->getLinkURL())); echo "
"; } // show "is_private" icon if (isset($item_i['is_private']) && $item_i['is_private']) { echo "
".__('Private')."
"; } echo "
"; // b_right if ($item['type'] == 'Document_Item') { if ($item_i['filename']) { $filename = $item_i['filename']; $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); echo " "; echo "$filename"; if (in_array($ext, array('jpg', 'jpeg', 'png', 'bmp'))) { echo "
"; echo ""; echo "
"; } echo "
"; } if ($item_i['link']) { echo "{$item_i['name']}"; } if (!empty($item_i['mime'])) { echo " (".$item_i['mime'].")"; } echo ""; echo ""; $doc = new Document(); $doc->getFromDB($item_i['id']); if ($doc->can($item_i['id'], UPDATE)) { echo ""; echo ""; } } echo "
"; // displayed_content echo "
"; //end h_content echo "
"; //end h_info $timeline_index++; Plugin::doHook('post_show_item', array('item' => $obj, 'options' => $options)); } // end foreach timeline echo "
"; // recall ticket content (not needed in classic and splitted layout) if (!CommonGLPI::isLayoutWithMain()) { echo "
"; echo "
"; echo "
".Html::convDateTime($this->fields['date'])."
"; echo "
"; $dem = '0'; foreach ($DB->request("glpi_tickets_users", "`tickets_id` = ".$this->fields['id']." AND `type` = 1") AS $req) { $dem = $req['users_id']; } if ((!isset($item_i['users_id_recipient']) || ($item_i['users_id_recipient'] == 0)) && ($dem == 0)) { _e("Requester"); } else { if (isset($item_i['users_id_recipient']) && ($item_i['users_id_recipient'] != 0)) { $user->getFromDB($this->fields['users_id_recipient']); } else if ($dem > 0) { $requester = new User(); if ($requester->getFromDB($dem)) { $user = $requester; } } echo "
"; $picture = ""; if (isset($user->fields['picture'])) { $picture = $user->fields['picture']; } echo "\"".__s('Picture')."\""; echo "
"; echo $user->getLink(); } echo "
"; // h_user echo "
"; //h_info echo "
"; echo "
".__("Ticket recall")."
"; echo "
"; echo html_entity_decode($this->fields['name']); echo "
"; echo "
"; echo $this->setSimpleTextContent($this->fields['content']); echo "
"; echo "
"; // h_content TicketContent echo "
"; // h_item middle echo "
"; } // end timeline echo "
"; // h_item $user_position echo ""; } /** * @since version 0.90 **/ function getTicketActors() { global $DB; $query = "SELECT DISTINCT `users_id`, `type` FROM (SELECT usr.`id` AS users_id, tu.`type` AS type FROM `glpi_tickets_users` tu LEFT JOIN `glpi_users` usr ON tu.`users_id` = usr.`id` WHERE tu.`tickets_id` = ".$this->getId()." UNION SELECT usr.`id` AS users_id, gt.`type` AS type FROM `glpi_groups_tickets` gt LEFT JOIN `glpi_groups_users` gu ON gu.`groups_id` = gt.`groups_id` LEFT JOIN `glpi_users` usr ON gu.`users_id` = usr.`id` WHERE gt.`tickets_id` = ".$this->getId()." ) AS allactors WHERE `type` != ".CommonItilActor::OBSERVER." GROUP BY `users_id` ORDER BY `type` DESC"; $res = $DB->query($query); $ticket_users_keys = array(); while ($current_tu = $DB->fetch_assoc($res)) { $ticket_users_keys[$current_tu['users_id']] = $current_tu['type']; } return $ticket_users_keys; } /** * @since version 0.90 **/ function showTimelineHeader() { echo "

".__("Actions historical")." :

"; $this->filterTimeline(); } /** * @since version 0.90 */ function filterTimeline() { global $CFG_GLPI; $pics_url = $CFG_GLPI['root_doc']."/pics/timeline"; echo "
"; echo ""; echo "
    "; echo "
  • "; echo "
  • "; echo "
  • "; echo "
  • "; echo "
  • "; echo "
  • "; echo "
"; echo "
"; echo ""; } /** * @since version 0.90 * * @param $rand **/ function showTimelineForm($rand) { global $CFG_GLPI; //check global rights if (!Session::haveRight("ticket", Ticket::READMY) && !Session::haveRightsOr("followup", array(TicketFollowup::SEEPUBLIC, TicketFollowup::SEEPRIVATE))) { return false; } // javascript function for add and edit items echo "\n"; //check sub-items rights $tmp = array('tickets_id' => $this->getID()); $fup = new TicketFollowup; $ttask = new TicketTask; $doc = new Document; $canadd_fup = $fup->can(-1, CREATE, $tmp); $canadd_task = $ttask->can(-1, CREATE, $tmp); $canadd_document = $doc->can(-1, CREATE, $tmp) && $this->canAddItem('Document'); $canadd_solution = Ticket::canUpdate() && $this->canSolve(); if (!$canadd_fup && !$canadd_task && !$canadd_document && !$canadd_solution ) { return false; } //show choices echo "

"._sx('button', 'Add')." :

"; echo "
"; echo "
    "; if ($canadd_fup) { echo "
  • " .__("Followup")."
  • "; } if ($canadd_task) { echo "
  • " .__("Task")."
  • "; } if ($canadd_document) { echo "
  • " .__("Document")."
  • "; } if ($canadd_solution) { echo "
  • " .__("Solution")."
  • "; } echo "
"; // timeline_choices echo "
 
"; echo "
"; //end timeline_form echo "
\n"; } /** * @since version 0.90 * * @param $item * @param $id * @param $params **/ static function showSubForm(CommonDBTM $item, $id, $params) { if ($item instanceof Document_Item) { Document_Item::showAddFormForItem($params['parent'], ''); } else if (method_exists($item, "showForm")) { $item->showForm($id, $params); } } /** * @since version 0.90 * * @param $tickets_id * @param $action (default 'add') **/ static function getSplittedSubmitButtonHtml($tickets_id, $action="add") { $locale = _sx('button', 'Add'); if ($action == 'update') { $locale = _x('button','Save'); } $ticket = new self(); $ticket->getFromDB($tickets_id); $all_status = Ticket::getAllowedStatusArray($ticket->fields['status']); $rand = mt_rand(); $html = "
 
    "; foreach ($all_status as $status_key => $status_label) { $checked = ""; if ($status_key == $ticket->fields['status']) { $checked = "checked='checked'"; } $html .= "
  • "; $html .= ""; $html .= ""; $html .= "
  • "; } $html .= "
"; $html.= ""; return $html; } /** * Get correct Calendar: Entity or Sla * * @since 0.90.4 * **/ function getCalendar() { if (isset($this->fields['slts_ttr_id']) && $this->fields['slts_ttr_id'] > 0) { $sla = new SLA(); if ($sla->getFromDB($this->fields['slts_ttr_id'])) { // not -1: calendar of the entity if ($sla->getField('calendars_id') >= 0) { return $sla->getField('calendars_id'); } } } return parent::getCalendar(); } /** * Select a field using standard system * * @since version 9.1 */ function getValueToSelect($field_id_or_search_options, $name = '', $values = '', $options = array()){ if (isset($field_id_or_search_options['linkfield'])) { switch( $field_id_or_search_options['linkfield'] ) { case 'requesttypes_id': $opt = 'is_ticketheader = 1'; if (isset($field_id_or_search_options['joinparams']) && Toolbox::in_array_recursive('glpi_ticketfollowups', $field_id_or_search_options['joinparams'])) { $opt = 'is_ticketfollowup = 1'; } if( $field_id_or_search_options['linkfield'] == $name ) { $opt .= ' AND is_active = 1' ; } if(isset( $options['condition'] )) { $opt .= ' AND '.$options['condition']; } $options['condition'] = $opt; break ; } } return parent::getValueToSelect($field_id_or_search_options, $name, $values, $options); } }
".sprintf(__('%1$s: %2$s'), __('ID'), $job->fields["id"]).""; if (isset($job->users[CommonITILActor::REQUESTER]) && count($job->users[CommonITILActor::REQUESTER])) { foreach ($job->users[CommonITILActor::REQUESTER] as $d) { if ($d["users_id"] > 0) { $userdata = getUserName($d["users_id"],2); $name = "".$userdata['name'].""; $name = sprintf(__('%1$s %2$s'), $name, Html::showToolTip($userdata["comment"], array('link' => $userdata["link"], 'display' => false))); echo $name; } else { echo $d['alternative_email']." "; } echo "
"; } } if (isset($job->groups[CommonITILActor::REQUESTER]) && count($job->groups[CommonITILActor::REQUESTER])) { foreach ($job->groups[CommonITILActor::REQUESTER] as $d) { echo Dropdown::getDropdownName("glpi_groups", $d["groups_id"]); echo "
"; } } echo "
"; if (!empty($job->hardwaredatas)) { foreach ($job->hardwaredatas as $hardwaredatas) { if ($hardwaredatas->canView()) { echo $hardwaredatas->getTypeName()." - "; echo "".$hardwaredatas->getLink()."
"; } else if ($hardwaredatas) { echo $hardwaredatas->getTypeName()." - "; echo "".$hardwaredatas->getNameID()."
"; } } } else { echo __('General'); } echo "
"; $link = "getNameID().""; $link = sprintf(__('%1$s (%2$s)'), $link, sprintf(__('%1$s - %2$s'), $job->numberOfFollowups($showprivate), $job->numberOfTasks($showprivate))); $content = Toolbox::unclean_cross_side_scripting_deep(html_entity_decode($job->fields['content'], ENT_QUOTES, "UTF-8")); $link = printf(__('%1$s %2$s'), $link, Html::showToolTip(nl2br(Html::Clean($content)), array('applyto' => 'ticket'.$job->fields["id"].$rand, 'display' => false))); echo "
".__('No ticket in progress.')."
".__('Assign equipment')."".$item->getLink(array('comments' => true))."