. * --------------------------------------------------------------------- */ if (!defined('GLPI_ROOT')) { die("Sorry. You can't access this file directly"); } /** * Problem class **/ class Problem extends CommonITILObject { // From CommonDBTM public $dohistory = true; static protected $forward_entity_to = ['ProblemCost']; // From CommonITIL public $userlinkclass = 'Problem_User'; public $grouplinkclass = 'Group_Problem'; public $supplierlinkclass = 'Problem_Supplier'; static $rightname = 'problem'; protected $usenotepad = true; const MATRIX_FIELD = 'priority_matrix'; const URGENCY_MASK_FIELD = 'urgency_mask'; const IMPACT_MASK_FIELD = 'impact_mask'; const STATUS_MATRIX_FIELD = 'problem_status'; const READMY = 1; const READALL = 1024; /** * Name of the type * * @param $nb : number of item in the type **/ static function getTypeName($nb = 0) { return _n('Problem', 'Problems', $nb); } function canSolve() { return (self::isAllowedStatus($this->fields['status'], self::SOLVED) // No edition on closed status && !in_array($this->fields['status'], $this->getClosedStatusArray()) && (Session::haveRight(self::$rightname, UPDATE) || (Session::haveRight(self::$rightname, self::READMY) && ($this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID()) || (isset($_SESSION["glpigroups"]) && $this->haveAGroup(CommonITILActor::ASSIGN, $_SESSION["glpigroups"])))))); } static function canView() { return Session::haveRightsOr(self::$rightname, [self::READALL, self::READMY]); } /** * Is the current user have right to show the current problem ? * * @return boolean **/ function canViewItem() { if (!Session::haveAccessToEntity($this->getEntityID(), $this->isRecursive())) { return false; } return (Session::haveRight(self::$rightname, self::READALL) || (Session::haveRight(self::$rightname, self::READMY) && ($this->isUser(CommonITILActor::REQUESTER, Session::getLoginUserID()) || $this->isUser(CommonITILActor::OBSERVER, Session::getLoginUserID()) || (isset($_SESSION["glpigroups"]) && ($this->haveAGroup(CommonITILActor::REQUESTER, $_SESSION["glpigroups"]) || $this->haveAGroup(CommonITILActor::OBSERVER, $_SESSION["glpigroups"]))) || ($this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID()) || (isset($_SESSION["glpigroups"]) && $this->haveAGroup(CommonITILActor::ASSIGN, $_SESSION["glpigroups"])))))); } /** * Is the current user have right to create the current problem ? * * @return boolean **/ function canCreateItem() { if (!Session::haveAccessToEntity($this->getEntityID())) { return false; } return Session::haveRight(self::$rightname, CREATE); } /** * is the current user could reopen the current problem * * @since 9.4.0 * * @return boolean */ function canReopen() { return Session::haveRight('followup', CREATE) && in_array($this->fields["status"], $this->getClosedStatusArray()) && ($this->isAllowedStatus($this->fields['status'], self::INCOMING) || $this->isAllowedStatus($this->fields['status'], self::ASSIGNED)); } function pre_deleteItem() { global $CFG_GLPI; if (!isset($this->input['_disablenotif']) && $CFG_GLPI['use_notifications']) { NotificationEvent::raiseEvent('delete', $this); } return true; } function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) { if (static::canView()) { switch ($item->getType()) { case __CLASS__ : $timeline = $item->getTimelineItems(); $nb_elements = count($timeline); $ong = [ 5 => __("Processing problem")." $nb_elements", 1 => __('Analysis') ]; if ($item->canUpdate()) { $ong[4] = __('Statistics'); } return $ong; } } return ''; } static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0) { switch ($item->getType()) { case __CLASS__ : switch ($tabnum) { case 1 : $item->showAnalysisForm(); break; case 4 : $item->showStats(); break; case 5 : echo "
"; $rand = mt_rand(); $item->showTimelineForm($rand); $item->showTimeline($rand); echo "
"; break; } } return true; } function defineTabs($options = []) { $ong = []; $this->defineDefaultObjectTabs($ong, $options); $this->addStandardTab('Problem_Ticket', $ong, $options); $this->addStandardTab('Change_Problem', $ong, $options); $this->addStandardTab('ProblemCost', $ong, $options); $this->addStandardTab('Itil_Project', $ong, $options); $this->addStandardTab('Item_Problem', $ong, $options); if ($this->hasImpactTab()) { $this->addStandardTab('Impact', $ong, $options); } $this->addStandardTab('Change_Problem', $ong, $options); $this->addStandardTab('Problem_Ticket', $ong, $options); $this->addStandardTab('Notepad', $ong, $options); $this->addStandardTab('KnowbaseItem_Item', $ong, $options); $this->addStandardTab('Log', $ong, $options); return $ong; } function cleanDBonPurge() { // CommonITILTask does not extends CommonDBConnexity $pt = new ProblemTask(); $pt->deleteByCriteria(['problems_id' => $this->fields['id']]); $this->deleteChildrenAndRelationsFromDb( [ Change_Problem::class, // Done by parent: Group_Problem::class, Item_Problem::class, // Done by parent: ITILSolution::class, // Done by parent: Problem_Supplier::class, Problem_Ticket::class, // Done by parent: Problem_User::class, ProblemCost::class, ] ); parent::cleanDBonPurge(); } function post_updateItem($history = 1) { global $CFG_GLPI; $donotif = count($this->updates); if (isset($this->input['_forcenotif'])) { $donotif = true; } if (isset($this->input['_disablenotif'])) { $donotif = false; } if ($donotif && $CFG_GLPI["use_notifications"]) { $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"; } // Read again problem to be sure that all data are up to date $this->getFromDB($this->fields['id']); NotificationEvent::raiseEvent($mailtype, $this); } } function prepareInputForAdd($input) { $input = parent::prepareInputForAdd($input); if (((isset($input["_users_id_assign"]) && ($input["_users_id_assign"] > 0)) || (isset($input["_groups_id_assign"]) && ($input["_groups_id_assign"] > 0)) || (isset($input["_suppliers_id_assign"]) && ($input["_suppliers_id_assign"] > 0))) && (in_array($input['status'], $this->getNewStatusArray()))) { $input["status"] = self::ASSIGNED; } return $input; } function post_addItem() { global $CFG_GLPI, $DB; parent::post_addItem(); if (isset($this->input['_tickets_id'])) { $ticket = new Ticket(); if ($ticket->getFromDB($this->input['_tickets_id'])) { $pt = new Problem_Ticket(); $pt->add(['tickets_id' => $this->input['_tickets_id'], 'problems_id' => $this->fields['id'], /*'_no_notif' => true*/]); if (!empty($ticket->fields['itemtype']) && ($ticket->fields['items_id'] > 0)) { $it = new Item_Problem(); $it->add(['problems_id' => $this->fields['id'], 'itemtype' => $ticket->fields['itemtype'], 'items_id' => $ticket->fields['items_id'], /*'_no_notif' => true*/]); } //Copy associated elements $iterator = $DB->request([ 'FROM' => Item_Ticket::getTable(), 'WHERE' => [ 'tickets_id' => $this->input['_tickets_id'] ] ]); $assoc = new Item_Problem; while ($row = $iterator->next()) { unset($row['tickets_id']); unset($row['id']); $row['problems_id'] = $this->fields['id']; $assoc->add(Toolbox::addslashes_deep($row)); } } } // Processing Email if (!isset($this->input['_disablenotif']) && $CFG_GLPI["use_notifications"]) { // Clean reload of the problem $this->getFromDB($this->fields['id']); $type = "new"; if (isset($this->fields["status"]) && in_array($this->input["status"], $this->getSolvedStatusArray())) { $type = "solved"; } NotificationEvent::raiseEvent($type, $this); } if (isset($this->input['_from_items_id']) && isset($this->input['_from_itemtype'])) { $item_problem = new Item_Problem(); $item_problem->add([ 'items_id' => (int)$this->input['_from_items_id'], 'itemtype' => $this->input['_from_itemtype'], 'problems_id' => $this->fields['id'], '_disablenotif' => true ]); } } /** * Get default values to search engine to override **/ static function getDefaultSearchRequest() { $search = ['criteria' => [0 => ['field' => 12, 'searchtype' => 'equals', 'value' => 'notold']], 'sort' => 19, 'order' => 'DESC']; return $search; } function getSpecificMassiveActions($checkitem = null) { $actions = parent::getSpecificMassiveActions($checkitem); if (ProblemTask::canCreate()) { $actions[__CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'add_task'] = __('Add a new task'); } if ($this->canAdminActors()) { $actions[__CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'add_actor'] = __('Add an actor'); $actions[__CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'update_notif'] = __('Set notifications for all actors'); } return $actions; } function rawSearchOptions() { $tab = []; $tab = array_merge($tab, $this->getSearchOptionsMain()); $tab[] = [ 'id' => '63', 'table' => 'glpi_items_problems', 'field' => 'id', 'name' => _x('quantity', 'Number of items'), 'forcegroupby' => true, 'usehaving' => true, 'datatype' => 'count', 'massiveaction' => false, 'joinparams' => [ 'jointype' => 'child' ] ]; $tab[] = [ 'id' => '13', 'table' => 'glpi_items_problems', 'field' => 'items_id', 'name' => _n('Associated element', 'Associated elements', Session::getPluralNumber()), 'datatype' => 'specific', 'comments' => true, 'nosort' => true, 'nosearch' => true, 'additionalfields' => ['itemtype'], 'joinparams' => [ 'jointype' => 'child' ], 'forcegroupby' => true, 'massiveaction' => false ]; $tab[] = [ 'id' => '131', 'table' => 'glpi_items_problems', 'field' => 'itemtype', 'name' => _n('Associated item type', 'Associated item types', Session::getPluralNumber()), 'datatype' => 'itemtypename', 'itemtype_list' => 'ticket_types', 'nosort' => true, 'additionalfields' => ['itemtype'], 'joinparams' => [ 'jointype' => 'child' ], 'forcegroupby' => true, 'massiveaction' => false ]; $tab = array_merge($tab, $this->getSearchOptionsActors()); $tab[] = [ 'id' => 'analysis', 'name' => __('Analysis') ]; $tab[] = [ 'id' => '60', 'table' => $this->getTable(), 'field' => 'impactcontent', 'name' => __('Impacts'), 'massiveaction' => false, 'datatype' => 'text' ]; $tab[] = [ 'id' => '61', 'table' => $this->getTable(), 'field' => 'causecontent', 'name' => __('Causes'), 'massiveaction' => false, 'datatype' => 'text' ]; $tab[] = [ 'id' => '62', 'table' => $this->getTable(), 'field' => 'symptomcontent', 'name' => __('Symptoms'), 'massiveaction' => false, 'datatype' => 'text' ]; $tab = array_merge($tab, Notepad::rawSearchOptionsToAdd()); $tab = array_merge($tab, ITILFollowup::rawSearchOptionsToAdd()); $tab = array_merge($tab, ProblemTask::rawSearchOptionsToAdd()); $tab = array_merge($tab, $this->getSearchOptionsSolution()); $tab = array_merge($tab, $this->getSearchOptionsStats()); $tab = array_merge($tab, ProblemCost::rawSearchOptionsToAdd()); $tab[] = [ 'id' => 'ticket', 'name' => Ticket::getTypeName(Session::getPluralNumber()) ]; $tab[] = [ 'id' => '141', 'table' => 'glpi_problems_tickets', 'field' => 'id', 'name' => _x('quantity', 'Number of tickets'), 'forcegroupby' => true, 'usehaving' => true, 'datatype' => 'count', 'massiveaction' => false, 'joinparams' => [ 'jointype' => 'child' ] ]; return $tab; } /** * get the problem status list * * @param $withmetaforsearch boolean (false by default) * * @return array **/ static function getAllStatusArray($withmetaforsearch = false) { // To be overridden by class $tab = [self::INCOMING => _x('status', 'New'), self::ACCEPTED => _x('status', 'Accepted'), self::ASSIGNED => _x('status', 'Processing (assigned)'), self::PLANNED => _x('status', 'Processing (planned)'), self::WAITING => __('Pending'), self::SOLVED => _x('status', 'Solved'), self::OBSERVED => __('Under observation'), 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 0.83 * * @return array **/ static function getClosedStatusArray() { // To be overridden by class $tab = [self::CLOSED]; return $tab; } /** * Get the ITIL object solved or observe status list * * @since 0.83 * * @return array **/ static function getSolvedStatusArray() { // To be overridden by class $tab = [self::OBSERVED, self::SOLVED]; return $tab; } /** * Get the ITIL object new status list * * @since 0.83.8 * * @return array **/ static function getNewStatusArray() { return [self::INCOMING, self::ACCEPTED]; } /** * Get the ITIL object assign, plan or accepted status list * * @since 0.83 * * @return array **/ static function getProcessStatusArray() { // To be overridden by class $tab = [self::ACCEPTED, self::ASSIGNED, self::PLANNED]; return $tab; } /** * @since 0.84 * * @param $start * @param $status (default 'proces) * @param $showgroupproblems (true by default) **/ static function showCentralList($start, $status = "process", $showgroupproblems = true) { global $DB, $CFG_GLPI; if (!static::canView()) { return false; } $WHERE = [ 'is_deleted' => 0 ]; $search_users_id = [ 'glpi_problems_users.users_id' => Session::getLoginUserID(), 'glpi_problems_users.type' => CommonITILActor::REQUESTER ]; $search_assign = [ 'glpi_problems_users.users_id' => Session::getLoginUserID(), 'glpi_problems_users.type' => CommonITILActor::ASSIGN ]; if ($showgroupproblems) { $search_users_id = [0]; $search_assign = [0]; if (count($_SESSION['glpigroups'])) { $search_users_id = [ 'glpi_groups_problems.groups_id' => $_SESSION['glpigroups'], 'glpi_groups_problems.type' => CommonITILActor::REQUESTER ]; $search_assign = [ 'glpi_groups_problems.groups_id' => $_SESSION['glpigroups'], 'glpi_groups_problems.type' => CommonITILActor::ASSIGN ]; } } switch ($status) { case "waiting" : // on affiche les problemes en attente $WHERE = array_merge( $WHERE, $search_assign, ['status' => self::WAITING] ); break; case "process" : // on affiche les problemes planifi??s ou assign??s au user $WHERE = array_merge( $WHERE, $search_assign, ['status' => [self::PLANNED, self::ASSIGNED]] ); break; default : $WHERE = array_merge( $WHERE, $search_users_id, [ 'status' => [ self::INCOMING, self::ACCEPTED, self::PLANNED, self::ASSIGNED, self::WAITING ] ] ); $WHERE['NOT'] = $search_assign; } $criteria = [ 'SELECT' => ['glpi_problems.id'], 'DISTINCT' => true, 'FROM' => 'glpi_problems', 'LEFT JOIN' => [ 'glpi_problems_users' => [ 'ON' => [ 'glpi_problems_users' => 'problems_id', 'glpi_problems' => 'id' ] ], 'glpi_groups_problems' => [ 'ON' => [ 'glpi_groups_problems' => 'problems_id', 'glpi_problems' => 'id' ] ] ], 'WHERE' => $WHERE + getEntitiesRestrictCriteria('glpi_problems'), 'ORDERBY' => 'date_mod DESC' ]; $iterator = $DB->request($criteria); $numrows = count($iterator); $number = 0; if ($_SESSION['glpidisplay_count_on_home'] > 0) { $citerator = $DB->request( $criteria + [ 'START' => (int)$start, 'LIMIT' => (int)$_SESSION['glpidisplay_count_on_home'] ] ); $number = count($citerator); } if ($numrows > 0) { echo ""; echo ""; if ($number) { echo ""; echo ""; echo ""; while ($result = $iterator->next()) { self::showVeryShort($result['id'], $forcetab); } } echo "
"; $options = [ 'criteria' => [], 'reset' => 'reset', ]; $forcetab = ''; if ($showgroupproblems) { 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'] = 8; // groups_id_assign $options['criteria'][1]['searchtype'] = 'equals'; $options['criteria'][1]['value'] = 'mygroups'; $options['criteria'][1]['link'] = 'AND'; echo "". Html::makeTitle(__('Problems 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(__('Problems to be processed'), $number, $numrows).""; break; 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 problems 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(__('Problems 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(__('Problems to be processed'), $number, $numrows).""; break; 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 problems in progress'), $number, $numrows).""; } } echo "
"._n('Requester', 'Requesters', 1)."".__('Description')."
"; } } /** * Get problems count * * @since 0.84 * * @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 (!static::canView()) { return false; } if (!Session::haveRight(self::$rightname, self::READALL)) { $foruser = true; } $table = self::getTable(); $criteria = [ 'SELECT' => [ 'status', 'COUNT' => '* AS COUNT', ], 'FROM' => $table, 'WHERE' => getEntitiesRestrictCriteria($table), 'GROUP' => 'status' ]; if ($foruser) { $criteria['LEFT JOIN'] = [ 'glpi_problems_users' => [ 'ON' => [ 'glpi_problems_users' => 'problems_id', $table => 'id', [ 'AND' => [ 'glpi_problems_users.type' => CommonITILActor::REQUESTER ] ] ] ] ]; $WHERE = ['glpi_problems_users.users_id' => Session::getLoginUserID()]; if (isset($_SESSION["glpigroups"]) && count($_SESSION["glpigroups"])) { $criteria['LEFT JOIN']['glpi_groups_problems'] = [ 'ON' => [ 'glpi_groups_problems' => 'problems_id', $table => 'id', [ 'AND' => [ 'glpi_groups_problems.type' => CommonITILActor::REQUESTER ] ] ] ]; $WHERE['glpi_groups_problems.groups_id'] = $_SESSION['glpigroups']; } $criteria['WHERE'][] = ['OR' => $WHERE]; } $deleted_criteria = $criteria; $criteria['WHERE']['glpi_problems.is_deleted'] = 0; $deleted_criteria['WHERE']['glpi_problems.is_deleted'] = 1; $iterator = $DB->request($criteria); $deleted_iterator = $DB->request($deleted_criteria); $status = []; foreach (self::getAllStatusArray() as $key => $val) { $status[$key] = 0; } while ($data = $iterator->next()) { $status[$data["status"]] = $data["COUNT"]; } $number_deleted = 0; while ($data = $deleted_iterator->next()) { $number_deleted += $data["COUNT"]; } $options = []; $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 "
"; echo "".__('Problem followup').""; echo "
".Problem::getTypeName(Session::getPluralNumber())." "._x('quantity', 'Number')."
".self::getStatus($key)."$val
".__('Deleted')."".$number_deleted."

"; } /** * @since 0.84 * * @param $ID * @param $forcetab string name of the tab to force at the display (default '') **/ static function showVeryShort($ID, $forcetab = '') { // 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 $viewusers = User::canView(); $problem = new self(); $rand = mt_rand(); if ($problem->getFromDBwithData($ID, 0)) { $bgcolor = $_SESSION["glpipriority_".$problem->fields["priority"]]; $name = sprintf(__('%1$s: %2$s'), __('ID'), $problem->fields["id"]); echo ""; echo ""; echo ""; echo ""; // Finish Line echo ""; } else { echo ""; echo ""; } } /** * @param $ID * @param $options array **/ function showForm($ID, $options = []) { global $CFG_GLPI; if (!static::canView()) { return false; } // In percent $colsize1 = '13'; $colsize2 = '37'; $default_values = self::getDefaultValues(); // Set default options if (!$ID) { foreach ($default_values as $key => $val) { if (!isset($options[$key])) { $options[$key] = $val; } } if (isset($options['tickets_id']) || isset($options['_tickets_id'])) { $tickets_id = $options['tickets_id'] ?? $options['_tickets_id']; $ticket = new Ticket(); if ($ticket->getFromDB($tickets_id)) { $options['content'] = $ticket->getField('content'); $options['name'] = $ticket->getField('name'); $options['impact'] = $ticket->getField('impact'); $options['urgency'] = $ticket->getField('urgency'); $options['priority'] = $ticket->getField('priority'); if (isset($options['tickets_id'])) { //page is reloaded on category change, we only want category on the very first load $options['itilcategories_id'] = $ticket->getField('itilcategories_id'); } $options['time_to_resolve'] = $ticket->getField('time_to_resolve'); $options['entities_id'] = $ticket->getField('entities_id'); } } } $this->initForm($ID, $options); $canupdate = !$ID || $this->canUpdateItem(); $showuserlink = 0; if (User::canView()) { $showuserlink = 1; } if (!$this->isNewItem()) { $options['formtitle'] = sprintf( __('%1$s - ID %2$d'), $this->getTypeName(1), $ID ); //set ID as already defined $options['noid'] = true; } if (!isset($options['template_preview'])) { $options['template_preview'] = 0; } // Load template if available : $tt = $this->getITILTemplateToUse( $options['template_preview'], $this->getType(), ($ID ? $this->fields['itilcategories_id'] : $options['itilcategories_id']), ($ID ? $this->fields['entities_id'] : $options['entities_id']) ); // Predefined fields from template : reset them if (isset($options['_predefined_fields'])) { $options['_predefined_fields'] = Toolbox::decodeArrayFromInput($options['_predefined_fields']); } else { $options['_predefined_fields'] = []; } // Restore saved value or override with page parameter $saved = $this->restoreInput(); // Store predefined fields to be able not to take into account on change template // Only manage predefined values on ticket creation $predefined_fields = []; $tpl_key = $this->getTemplateFormFieldName(); 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($options['_predefined_fields']) == 0) && ($options[$predeffield] == $default_values[$predeffield])) || (isset($options['_predefined_fields'][$predeffield]) && ($options[$predeffield] == $options['_predefined_fields'][$predeffield])) || (isset($options[$tpl_key]) && ($options[$tpl_key] != $tt->getID())) // user pref for requestype can't overwrite requestype from template // when change category || (($predeffield == 'requesttypes_id') && empty($saved)) || (isset($ticket) && $options[$predeffield] == $ticket->getField($predeffield)) ) { // Load template data $options[$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($options['_predefined_fields'])) { foreach ($options['_predefined_fields'] as $predeffield => $predefvalue) { if ($options[$predeffield] == $predefvalue) { $options[$predeffield] = $default_values[$predeffield]; } } } } } foreach ($default_values as $name => $value) { if (!isset($options[$name])) { if (isset($saved[$name])) { $options[$name] = $saved[$name]; } else { $options[$name] = $value; } } } // Put ticket template on $options for actors $options[str_replace('s_id', '', $tpl_key)] = $tt; if ($options['template_preview']) { // Add all values to fields of tickets for template preview foreach ($options as $key => $val) { if (!isset($this->fields[$key])) { $this->fields[$key] = $val; } } } $this->showFormHeader($options); echo ""; echo ""; echo ""; echo ""; echo ""; if ($ID) { 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 ""; } echo "
 $name
"; if (isset($problem->users[CommonITILActor::REQUESTER]) && count($problem->users[CommonITILActor::REQUESTER])) { foreach ($problem->users[CommonITILActor::REQUESTER] as $d) { if ($d["users_id"] > 0) { $userdata = getUserName($d["users_id"], 2); $name = "".$userdata['name'].""; if ($viewusers) { $name = sprintf(__('%1$s %2$s'), $name, Html::showToolTip($userdata["comment"], ['link' => $userdata["link"], 'display' => false])); } echo $name; } else { echo $d['alternative_email']." "; } echo "
"; } } if (isset($problem->groups[CommonITILActor::REQUESTER]) && count($problem->groups[CommonITILActor::REQUESTER])) { foreach ($problem->groups[CommonITILActor::REQUESTER] as $d) { echo Dropdown::getDropdownName("glpi_groups", $d["groups_id"]); echo "
"; } } echo "
"; $link = "fields["name"].""; $link = printf(__('%1$s %2$s'), $link, Html::showToolTip($problem->fields['content'], ['applyto' => 'problem'.$problem->fields["id"].$rand, 'display' => false])); echo "
".__('No problem in progress.')."
"; echo $tt->getBeginHiddenFieldText('date'); if (!$ID) { printf(__('%1$s%2$s'), __('Opening date'), $tt->getMandatoryMark('date')); } else { echo __('Opening date'); } echo $tt->getEndHiddenFieldText('date'); echo ""; if (isset($tickets_id)) { echo ""; } if (isset($options['_add_fromitem']) && isset($options['_from_items_id']) && isset($options['_from_itemtype'])) { echo Html::hidden('_from_items_id', ['value' => $options['_from_items_id']]); echo Html::hidden('_from_itemtype', ['value' => $options['_from_itemtype']]); } echo $tt->getBeginHiddenFieldValue('date'); $date = $this->fields["date"]; if (!$ID) { $date = date("Y-m-d H:i:s"); } Html::showDateTimeField( "date", [ 'value' => $date, 'maybeempty' => false, 'required' => ($tt->isMandatoryField('date') && !$ID) ] ); echo $tt->getEndHiddenFieldValue('date', $this); echo "".$tt->getBeginHiddenFieldText('time_to_resolve'); if (!$ID) { printf(__('%1$s%2$s'), __('Time to resolve'), $tt->getMandatoryMark('time_to_resolve')); } else { echo __('Time to resolve'); } echo $tt->getEndHiddenFieldText('time_to_resolve'); echo ""; echo $tt->getBeginHiddenFieldValue('time_to_resolve'); if ($this->fields["time_to_resolve"] == 'NULL') { $this->fields["time_to_resolve"] = ''; } Html::showDateTimeField( "time_to_resolve", [ 'value' => $this->fields["time_to_resolve"], 'required' => ($tt->isMandatoryField('time_to_resolve') && !$ID) ] ); echo $tt->getEndHiddenFieldValue('time_to_resolve', $this); echo "
".__('By').""; User::dropdown(['name' => 'users_id_recipient', 'value' => $this->fields["users_id_recipient"], 'entity' => $this->fields["entities_id"], 'right' => 'all']); echo "".__('Last update')."".Html::convDateTime($this->fields["date_mod"])."\n"; if ($this->fields['users_id_lastupdater'] > 0) { printf(__('%1$s: %2$s'), __('By'), getUserName($this->fields["users_id_lastupdater"], $showuserlink)); } echo "
".__('Date of solving').""; Html::showDateTimeField("solvedate", ['value' => $this->fields["solvedate"], 'maybeempty' => false]); echo "".__('Closing date').""; Html::showDateTimeField("closedate", ['value' => $this->fields["closedate"], 'maybeempty' => false]); echo " 
"; echo ""; echo ""; echo ""; echo ""; // Only change during creation OR when allowed to change priority OR when user is the creator echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; 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 ($canupdate) { self::dropdownStatus([ 'value' => $this->fields["status"], 'showtype' => 'allowed', 'required' => ($tt->isMandatoryField('status') && !$ID) ]); ChangeValidation::alertValidation($this, 'status'); } else { echo self::getStatus($this->fields["status"]); if ($this->canReopen()) { $link = $this->getLinkURL(). "&_openfollowup=1&forcetab="; $link .= "Change$1"; echo " ". __('Reopen').""; } } echo $tt->getEndHiddenFieldValue('status', $this); echo "".$tt->getBeginHiddenFieldText('urgency'); printf(__('%1$s%2$s'), __('Urgency'), $tt->getMandatoryMark('urgency')); echo $tt->getEndHiddenFieldText('urgency').""; if ($canupdate) { echo $tt->getBeginHiddenFieldValue('urgency'); $idurgency = self::dropdownUrgency(['value' => $this->fields["urgency"]]); echo $tt->getEndHiddenFieldValue('urgency', $this); } else { $idurgency = "value_urgency".mt_rand(); echo ""; echo $tt->getBeginHiddenFieldValue('urgency'); echo self::getUrgencyName($this->fields["urgency"]); echo $tt->getEndHiddenFieldValue('urgency', $this); } echo "
".sprintf(__('%1$s%2$s'), __('Category'), $tt->getMandatoryMark('itilcategories_id')).""; // Permit to set category when creating ticket without update right if ($canupdate) { $conditions = ['is_problem' => 1]; $opt = ['value' => $this->fields["itilcategories_id"], 'entity' => $this->fields["entities_id"]]; /// 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 || $options['itilcategories_id']) && $tt->isMandatoryField("itilcategories_id") && ($this->fields["itilcategories_id"] > 0)) { $opt['display_emptychoice'] = false; } echo ""; $opt['condition'] = $conditions; ITILCategory::dropdown($opt); echo ""; } else { echo Dropdown::getDropdownName("glpi_itilcategories", $this->fields["itilcategories_id"]); } 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(['value' => $this->fields["impact"], 'required' => ($tt->isMandatoryField('date') && !$ID)]); } else { $idimpact = "value_impact".mt_rand(); echo ""; echo self::getImpactName($this->fields["impact"]); } echo $tt->getEndHiddenFieldValue('impact', $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', [ 'value' => $options['actiontime'], 'addfirstminutes' => true ] ); echo $tt->getEndHiddenFieldValue('actiontime', $this); echo "".$tt->getBeginHiddenFieldText('priority'); printf(__('%1$s%2$s'), __('Priority'), $tt->getMandatoryMark('priority')); echo $tt->getEndHiddenFieldText('priority').""; $idajax = 'change_priority_' . mt_rand(); if (!$tt->isHiddenField('priority')) { $idpriority = self::dropdownPriority([ 'value' => $this->fields["priority"], 'withmajor' => true ]); $idpriority = 'dropdown_priority'.$idpriority; echo " "; } else { $idpriority = 0; echo $tt->getBeginHiddenFieldValue('priority'); echo "".self::getPriorityName($this->fields["priority"]).""; echo ""; echo $tt->getEndHiddenFieldValue('priority', $this); } $idajax = 'change_priority_' . mt_rand(); echo " "; $params = [ 'urgency' => '__VALUE0__', 'impact' => '__VALUE1__', 'priority' => 'dropdown_priority'.$idpriority ]; Ajax::updateItemOnSelectEvent([ 'dropdown_urgency'.$idurgency, 'dropdown_impact'.$idimpact], $idajax, $CFG_GLPI["root_doc"]."/ajax/priority.php", $params ); echo "
"; $this->showActorsPartForm($ID, $options); echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; $options['colspan'] = 2; if (!$options['template_preview']) { if ($tt->isField('id') && ($tt->fields['id'] > 0)) { echo ""; echo ""; } $this->showFormButtons($options); } else { echo "
".$tt->getBeginHiddenFieldText('name'); printf(__('%1$s%2$s'), __('Title'), $tt->getMandatoryMark('name')); echo $tt->getEndHiddenFieldText('name').""; echo $tt->getBeginHiddenFieldValue('name'); echo "isMandatoryField('name') ? " required='required'" : '') . " value=\"".Html::cleanInputText($this->fields["name"])."\">"; echo $tt->getEndHiddenFieldValue('name', $this); echo "
".$tt->getBeginHiddenFieldText('content'); printf(__('%1$s%2$s'), __('Description'), $tt->getMandatoryMark('content')); echo $tt->getEndHiddenFieldText('content').""; $rand = mt_rand(); echo $tt->getBeginHiddenFieldValue('content'); $content = $this->fields['content']; if (!isset($options['template_preview'])) { $content = Html::cleanPostForTextArea($content); } $content_id = "content$rand"; $rows = 10; $canupdate = !$ID || (Session::getCurrentInterface() == "central" && $this->canUpdateItem()); $content = Html::setRichTextContent( $content_id, $content, $rand, !$canupdate ); echo ""; echo $tt->getEndHiddenFieldValue('content', $this); echo "
"; echo ""; } return true; } /** * Form to add an analysis to a problem **/ function showAnalysisForm() { $this->check($this->getField('id'), READ); $canedit = $this->canEdit($this->getField('id')); $options = []; $options['canedit'] = false; CommonDBTM::showFormHeader($options); echo ""; echo "".__('Impacts').""; if ($canedit) { echo ""; } else { echo $this->getField('impactcontent'); } echo ""; echo ""; echo "".__('Causes').""; if ($canedit) { echo ""; } else { echo $this->getField('causecontent'); } echo ""; echo ""; echo "".__('Symptoms').""; if ($canedit) { echo ""; } else { echo $this->getField('symptomcontent'); } echo ""; $options['candel'] = false; $options['canedit'] = $canedit; $this->showFormButtons($options); } /** * @deprecated 9.5.0 */ static function getCommonSelect() { Toolbox::deprecated('Use getCommonCriteria with db iterator'); $SELECT = ""; if (count($_SESSION["glpiactiveentities"])>1) { $SELECT .= ", `glpi_entities`.`completename` AS entityname, `glpi_problems`.`entities_id` AS entityID "; } return " DISTINCT `glpi_problems`.*, `glpi_itilcategories`.`completename` AS catname $SELECT"; } /** * @deprecated 9.5.0 */ static function getCommonLeftJoin() { Toolbox::deprecated('Use getCommonCriteria with db iterator'); $FROM = ""; if (count($_SESSION["glpiactiveentities"])>1) { $FROM .= " LEFT JOIN `glpi_entities` ON (`glpi_entities`.`id` = `glpi_problems`.`entities_id`) "; } return " LEFT JOIN `glpi_groups_problems` ON (`glpi_problems`.`id` = `glpi_groups_problems`.`problems_id`) LEFT JOIN `glpi_problems_users` ON (`glpi_problems`.`id` = `glpi_problems_users`.`problems_id`) LEFT JOIN `glpi_problems_suppliers` ON (`glpi_problems`.`id` = `glpi_problems_suppliers`.`problems_id`) LEFT JOIN `glpi_itilcategories` ON (`glpi_problems`.`itilcategories_id` = `glpi_itilcategories`.`id`) $FROM"; } /** * Display problems for an item * * Will also display problems of linked items * * @param CommonDBTM $item * @param boolean $withtemplate * * @return void **/ static function showListForItem(CommonDBTM $item, $withtemplate = 0) { global $DB; if (!Session::haveRight(self::$rightname, self::READALL)) { return false; } if ($item->isNewID($item->getID())) { return false; } $restrict = []; $options = [ 'criteria' => [], 'reset' => 'reset', ]; switch ($item->getType()) { case 'User' : $restrict['glpi_problems_users.users_id'] = $item->getID(); $options['criteria'][0]['field'] = 4; // status $options['criteria'][0]['searchtype'] = 'equals'; $options['criteria'][0]['value'] = $item->getID(); $options['criteria'][0]['link'] = 'AND'; $options['criteria'][1]['field'] = 66; // status $options['criteria'][1]['searchtype'] = 'equals'; $options['criteria'][1]['value'] = $item->getID(); $options['criteria'][1]['link'] = 'OR'; $options['criteria'][5]['field'] = 5; // status $options['criteria'][5]['searchtype'] = 'equals'; $options['criteria'][5]['value'] = $item->getID(); $options['criteria'][5]['link'] = 'OR'; break; case 'Supplier' : $restrict['glpi_problems_suppliers.suppliers_id'] = $item->getID(); $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 problems')."
"; echo __('Child groups'); Dropdown::showYesNo('tree', $tree, -1, ['on_change' => 'reloadTab("start=0&tree="+this.value)']); } else { $tree = 0; } echo "
"; $restrict['glpi_groups_problems.groups_id'] = ($tree ? getSonsOf('glpi_groups', $item->getID()) : $item->getID()); $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['items_id'] = $item->getID(); $restrict['itemtype'] = $item->getType(); break; } // Link to open a new problem if ($item->getID() && Problem::isPossibleToAssignType($item->getType()) && self::canCreate() && !(!empty($withtemplate) && $withtemplate == 2) && (!isset($item->fields['is_template']) || $item->fields['is_template'] == 0)) { echo "
"; Html::showSimpleForm( Problem::getFormURL(), '_add_fromitem', __('New problem for this item...'), [ '_from_itemtype' => $item->getType(), '_from_items_id' => $item->getID(), 'entities_id' => $item->fields['entities_id'] ] ); echo "
"; } $criteria = self::getCommonCriteria(); $criteria['WHERE'] = $restrict + getEntitiesRestrictCriteria(self::getTable()); $criteria['LIMIT'] = (int)$_SESSION['glpilist_limit']; $iterator = $DB->request($criteria); $number = count($iterator); // Ticket for the item echo "
"; $colspan = 11; if (count($_SESSION["glpiactiveentities"]) > 1) { $colspan++; } if ($number > 0) { Session::initNavigateListItems('Problem', //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 ""; } // Ticket list if ($number > 0) { self::commonListHeader(Search::HTML_OUTPUT); while ($data = $iterator->next()) { Session::addToNavigateListItems('Problem', $data["id"]); self::showShort($data["id"]); } self::commonListHeader(Search::HTML_OUTPUT); } echo "
"; //TRANS : %d is the number of problems echo sprintf(_n('Last %d problem', 'Last %d problems', $number), $number); // echo "".__('Show all').""; echo "
".__('No problem found.')."
"; // Tickets for linked items $linkeditems = $item->getLinkedItems(); $restrict = []; if (count($linkeditems)) { foreach ($linkeditems as $ltype => $tab) { foreach ($tab as $lID) { $restrict[] = ['AND' => ['itemtype' => $ltype, 'items_id' => $lID]]; } } } if (count($restrict)) { $criteria = self::getCommonCriteria(); $criteria['WHERE'] = ['OR' => $restrict] + getEntitiesRestrictCriteria(self::getTable()); $iterator = $DB->request($criteria); $number = count($iterator); echo "
"; echo ""; if ($number > 0) { self::commonListHeader(Search::HTML_OUTPUT); while ($data = $iterator->next()) { // Session::addToNavigateListItems(TRACKING_TYPE,$data["id"]); self::showShort($data["id"]); } self::commonListHeader(Search::HTML_OUTPUT); } else { echo ""; } echo "
"; echo __('Problems on linked items'); echo "
".__('No problem found.')."
"; } // Subquery for linked item } /** * @since 0.85 * * @see commonDBTM::getRights() **/ function getRights($interface = 'central') { $values = parent::getRights(); unset($values[READ]); $values[self::READALL] = __('See all'); $values[self::READMY] = __('See (author)'); return $values; } static function getDefaultValues($entity = 0) { $default_use_notif = Entity::getUsedConfig('is_notif_enable_default', $_SESSION['glpiactive_entity'], '', 1); return [ '_users_id_requester' => Session::getLoginUserID(), '_users_id_requester_notif' => [ 'use_notification' => $default_use_notif, 'alternative_email' => '' ], '_groups_id_requester' => 0, '_users_id_assign' => 0, '_users_id_assign_notif' => [ 'use_notification' => $default_use_notif, 'alternative_email' => ''], '_groups_id_assign' => 0, '_users_id_observer' => 0, '_users_id_observer_notif' => [ 'use_notification' => $default_use_notif, 'alternative_email' => '' ], '_suppliers_id_assign_notif' => [ 'use_notification' => $default_use_notif, 'alternative_email' => '' ], '_groups_id_observer' => 0, '_suppliers_id_assign' => 0, 'priority' => 3, 'urgency' => 3, 'impact' => 3, 'content' => '', 'name' => '', 'entities_id' => $_SESSION['glpiactive_entity'], 'itilcategories_id' => 0, 'actiontime' => 0, '_add_validation' => 0, 'users_id_validate' => [], '_tasktemplates_id' => [] ]; } /** * get active problems for an item * * @since 9.5 * * @param string $itemtype Item type * @param integer $items_id ID of the Item * * @return DBmysqlIterator */ public function getActiveProblemsForItem($itemtype, $items_id) { global $DB; return $DB->request([ 'SELECT' => [ $this->getTable() . '.id', $this->getTable() . '.name', $this->getTable() . '.priority', ], 'FROM' => $this->getTable(), 'LEFT JOIN' => [ 'glpi_items_problems' => [ 'ON' => [ 'glpi_items_problems' => 'problems_id', $this->getTable() => 'id' ] ] ], 'WHERE' => [ 'glpi_items_problems.itemtype' => $itemtype, 'glpi_items_problems.items_id' => $items_id, $this->getTable() . '.is_deleted' => 0, 'NOT' => [ $this->getTable() . '.status' => array_merge( $this->getSolvedStatusArray(), $this->getClosedStatusArray() ) ] ] ]); } static function getIcon() { return "fas fa-exclamation-triangle"; } }