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 "
".__('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')."