. * --------------------------------------------------------------------- */ if (!defined('GLPI_ROOT')) { die("Sorry. You can't access this file directly"); } /** * Change Class **/ class Change extends CommonITILObject { // From CommonDBTM public $dohistory = true; static protected $forward_entity_to = ['ChangeValidation', 'ChangeCost']; // From CommonITIL public $userlinkclass = 'Change_User'; public $grouplinkclass = 'Change_Group'; public $supplierlinkclass = 'Change_Supplier'; static $rightname = 'change'; protected $usenotepad = true; const MATRIX_FIELD = 'priority_matrix'; const URGENCY_MASK_FIELD = 'urgency_mask'; const IMPACT_MASK_FIELD = 'impact_mask'; const STATUS_MATRIX_FIELD = 'change_status'; const READMY = 1; const READALL = 1024; static function getTypeName($nb = 0) { return _n('Change', 'Changes', $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 change ? * * @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->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 change ? * * @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 change * * @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::EVALUATION)); } function pre_deleteItem() { global $CFG_GLPI; if (!isset($this->input['_disablenotif']) && $CFG_GLPI['use_notifications']) { NotificationEvent::raiseEvent('delete', $this); } return true; } function getSpecificMassiveActions($checkitem = null) { $actions = parent::getSpecificMassiveActions($checkitem); 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 getTabNameForItem(CommonGLPI $item, $withtemplate = 0) { if (static::canView()) { switch ($item->getType()) { case __CLASS__ : $timeline = $item->getTimelineItems(); $nb_elements = count($timeline); $ong = [ 5 => __("Processing change")." $nb_elements", 1 => __('Analysis'), 3 => __('Plans') ]; 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 3 : $item->showPlanForm(); break; case 4 : $item->showStats(); break; case 5 : echo "
"; $rand = mt_rand(); $item->showTimelineForm($rand); $item->showTimeline($rand); echo "
"; break; } break; } return true; } function defineTabs($options = []) { $ong = []; $this->defineDefaultObjectTabs($ong, $options); $this->addStandardTab('ChangeValidation', $ong, $options); $this->addStandardTab('ChangeCost', $ong, $options); $this->addStandardTab('Itil_Project', $ong, $options); $this->addStandardTab('Change_Problem', $ong, $options); $this->addStandardTab('Change_Ticket', $ong, $options); $this->addStandardTab('Change_Item', $ong, $options); if ($this->hasImpactTab()) { $this->addStandardTab('Impact', $ong, $options); } $this->addStandardTab('KnowbaseItem_Item', $ong, $options); $this->addStandardTab('Notepad', $ong, $options); $this->addStandardTab('Log', $ong, $options); return $ong; } function cleanDBonPurge() { // CommonITILTask does not extends CommonDBConnexity $ct = new ChangeTask(); $ct->deleteByCriteria(['changes_id' => $this->fields['id']]); $this->deleteChildrenAndRelationsFromDb( [ // Done by parent: Change_Group::class, Change_Item::class, Change_Problem::class, // Done by parent: Change_Supplier::class, Change_Ticket::class, // Done by parent: Change_User::class, ChangeCost::class, ChangeValidation::class, // Done by parent: ITILSolution::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 change to be sure that all data are up to date $this->getFromDB($this->fields['id']); NotificationEvent::raiseEvent($mailtype, $this); } } 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 Change_Ticket(); $pt->add(['tickets_id' => $this->input['_tickets_id'], 'changes_id' => $this->fields['id']]); if (!empty($ticket->fields['itemtype']) && $ticket->fields['items_id']>0) { $it = new Change_Item(); $it->add(['changes_id' => $this->fields['id'], 'itemtype' => $ticket->fields['itemtype'], 'items_id' => $ticket->fields['items_id']]); } //Copy associated elements $iterator = $DB->request([ 'FROM' => Item_Ticket::getTable(), 'WHERE' => [ 'tickets_id' => $this->input['_tickets_id'] ] ]); $assoc = new Change_Item; while ($row = $iterator->next()) { unset($row['tickets_id']); unset($row['id']); $row['changes_id'] = $this->fields['id']; $assoc->add(Toolbox::addslashes_deep($row)); } } } if (isset($this->input['_problems_id'])) { $problem = new Problem(); if ($problem->getFromDB($this->input['_problems_id'])) { $cp = new Change_Problem(); $cp->add(['problems_id' => $this->input['_problems_id'], 'changes_id' => $this->fields['id']]); //Copy associated elements $iterator = $DB->request([ 'FROM' => Item_Problem::getTable(), 'WHERE' => [ 'problems_id' => $this->input['_problems_id'] ] ]); $assoc = new Change_Item; while ($row = $iterator->next()) { unset($row['problems_id']); unset($row['id']); $row['changes_id'] = $this->fields['id']; $assoc->add(Toolbox::addslashes_deep($row)); } } } // Processing notifications if ($CFG_GLPI["use_notifications"]) { // Clean reload of the change $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'])) { $change_item = new Change_Item(); $change_item->add([ 'items_id' => (int)$this->input['_from_items_id'], 'itemtype' => $this->input['_from_itemtype'], 'changes_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 rawSearchOptions() { $tab = []; $tab = array_merge($tab, $this->getSearchOptionsMain()); $tab[] = [ 'id' => '68', 'table' => 'glpi_changes_items', 'field' => 'id', 'name' => _x('quantity', 'Number of items'), 'forcegroupby' => true, 'usehaving' => true, 'datatype' => 'count', 'massiveaction' => false, 'joinparams' => [ 'jointype' => 'child' ] ]; $tab[] = [ 'id' => '13', 'table' => 'glpi_changes_items', 'field' => 'items_id', 'name' => _n('Associated element', 'Associated elements', Session::getPluralNumber()), 'datatype' => 'specific', 'comments' => true, 'nosearch' => true, 'additionalfields' => ['itemtype'], 'joinparams' => [ 'jointype' => 'child' ], 'forcegroupby' => true, 'massiveaction' => false ]; $tab[] = [ 'id' => '131', 'table' => 'glpi_changes_items', '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' => __('Control list') ]; $tab[] = [ 'id' => '60', 'table' => $this->getTable(), 'field' => 'impactcontent', 'name' => __('Analysis impact'), 'massiveaction' => false, 'datatype' => 'text' ]; $tab[] = [ 'id' => '61', 'table' => $this->getTable(), 'field' => 'controlistcontent', 'name' => __('Control list'), 'massiveaction' => false, 'datatype' => 'text' ]; $tab[] = [ 'id' => '62', 'table' => $this->getTable(), 'field' => 'rolloutplancontent', 'name' => __('Deployment plan'), 'massiveaction' => false, 'datatype' => 'text' ]; $tab[] = [ 'id' => '63', 'table' => $this->getTable(), 'field' => 'backoutplancontent', 'name' => __('Backup plan'), 'massiveaction' => false, 'datatype' => 'text' ]; $tab[] = [ 'id' => '67', 'table' => $this->getTable(), 'field' => 'checklistcontent', 'name' => __('Checklist'), 'massiveaction' => false, 'datatype' => 'text' ]; $tab = array_merge($tab, Notepad::rawSearchOptionsToAdd()); $tab = array_merge($tab, ChangeValidation::rawSearchOptionsToAdd()); $tab = array_merge($tab, ITILFollowup::rawSearchOptionsToAdd()); $tab = array_merge($tab, ChangeTask::rawSearchOptionsToAdd()); $tab = array_merge($tab, $this->getSearchOptionsSolution()); $tab = array_merge($tab, ChangeCost::rawSearchOptionsToAdd()); return $tab; } /** * get the change status list * To be overridden by class * * @param $withmetaforsearch boolean (default false) * * @return array **/ static function getAllStatusArray($withmetaforsearch = false) { $tab = [self::INCOMING => _x('status', 'New'), self::EVALUATION => __('Evaluation'), self::APPROVAL => _n('Approval', 'Approvals', 1), self::ACCEPTED => _x('status', 'Accepted'), self::WAITING => __('Pending'), self::TEST => _x('change', 'Testing'), self::QUALIFICATION => __('Qualification'), self::SOLVED => __('Applied'), self::OBSERVED => __('Review'), 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, self::EVALUATION, self::APPROVAL]; } /** * Get the ITIL object test, qualification or accepted status list * To be overridden by class * * @since 0.83 * * @return array **/ static function getProcessStatusArray() { // To be overridden by class $tab = [self::ACCEPTED, self::QUALIFICATION, self::TEST]; return $tab; } 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'); } } if (isset($options['problems_id']) || isset($options['_problems_id'])) { $problems_id = $options['problems_id'] ?? $options['_problems_id']; $problem = new Problem(); if ($problem->getFromDB($problems_id)) { $options['content'] = $problem->getField('content'); $options['name'] = $problem->getField('name'); $options['impact'] = $problem->getField('impact'); $options['urgency'] = $problem->getField('urgency'); $options['priority'] = $problem->getField('priority'); if (isset($options['problems_id'])) { //page is reloaded on category change, we only want category on the very first load $options['itilcategories_id'] = $problem->getField('itilcategories_id'); } $options['time_to_resolve'] = $problem->getField('time_to_resolve'); $options['entities_id'] = $problem->getField('entities_id'); } } } if ($ID > 0) { $this->check($ID, READ); } else { // Create item $this->check(-1, CREATE, $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)) || (isset($problem) && $options[$predeffield] == $problem->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 $tt->getBeginHiddenFieldText('date'); if (!$ID) { printf(__('%1$s%2$s'), __('Opening date'), $tt->getMandatoryMark('date')); } else { echo __('Opening date'); } echo $tt->getEndHiddenFieldText('date'); echo ""; echo ""; if (isset($tickets_id)) { echo ""; } if (isset($problems_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 ""; 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 ""; 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 ""; if ($ID) { echo "".__('By').""; User::dropdown(['name' => 'users_id_recipient', 'value' => $this->fields["users_id_recipient"], 'entity' => $this->fields["entities_id"], 'right' => 'all']); echo ""; echo "".__('Last update').""; echo "".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 ""; } if ($ID && (in_array($this->fields["status"], $this->getSolvedStatusArray()) || in_array($this->fields["status"], $this->getClosedStatusArray()))) { echo ""; echo "".__('Date of solving').""; echo ""; Html::showDateTimeField("solvedate", ['value' => $this->fields["solvedate"], 'maybeempty' => false]); echo ""; if (in_array($this->fields["status"], $this->getClosedStatusArray())) { echo "".__('Closing date').""; echo ""; Html::showDateTimeField("closedate", ['value' => $this->fields["closedate"], 'maybeempty' => false]); echo ""; } else { echo " "; } echo ""; } 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 ""; echo ""; echo ""; echo ""; echo ""; echo ""; if (!$options['template_preview']) { 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_change' => 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 "
"; if (!$ID) { echo $tt->getBeginHiddenFieldText('_add_validation'); printf(__('%1$s%2$s'), __('Approval request'), $tt->getMandatoryMark('_add_validation')); echo $tt->getEndHiddenFieldText('_add_validation'); } else { echo $tt->getBeginHiddenFieldText('global_validation'); echo _n('Approval', 'Approvals', 1); echo $tt->getEndHiddenFieldText('global_validation'); } echo ""; if (!$ID) { echo $tt->getBeginHiddenFieldValue('_add_validation'); echo ""; $params = [ 'name' => "users_id_validate", 'entity' => $this->fields['entities_id'], 'users_id_validate' => $options['users_id_validate'] ]; ChangeValidation::dropdownValidator($params); echo $tt->getEndHiddenFieldValue('_add_validation', $this); if ($tt->isPredefinedField('global_validation')) { echo ""; } } else { echo $tt->getBeginHiddenFieldValue('global_validation'); if (Session::haveRightsOr('changevalidation', ChangeValidation::getCreateRights()) && $canupdate) { ChangeValidation::dropdownStatus('global_validation', ['global' => true, 'value' => $this->fields['global_validation']]); } else { echo ChangeValidation::getStatus($this->fields['global_validation']); } echo $tt->getEndHiddenFieldValue('global_validation', $this); } echo "
"; $this->showActorsPartForm($ID, $options); echo ""; } echo ""; echo ""; echo ""; echo ""; echo ""; echo ""; $options['colspan'] = 2; if (!$options['template_preview']) { if ($tt->isField('id') && ($tt->fields['id'] > 0)) { echo ""; echo ""; } if (!$ID) { $fields = [ 'controlistcontent', 'impactcontent', 'rolloutplancontent', 'backoutplancontent', 'checklistcontent' ]; foreach ($fields as $field) { if (isset($tt->predefined[$field])) { echo Html::hidden($field, ['value' => $tt->predefined[$field]]); } } } $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', $this); echo ""; $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 change **/ 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 "".__('Control list').""; if ($canedit) { echo ""; } else { echo $this->getField('controlistcontent'); } echo ""; $options['candel'] = false; $options['canedit'] = $canedit; $this->showFormButtons($options); } /** * Form to add an analysis to a change **/ function showPlanForm() { $this->check($this->getField('id'), READ); $canedit = $this->canEdit($this->getField('id')); $options = []; $options['canedit'] = false; CommonDBTM::showFormHeader($options); echo ""; echo "".__('Deployment plan').""; if ($canedit) { echo ""; } else { echo $this->getField('rolloutplancontent'); } echo ""; echo ""; echo "".__('Backup plan').""; if ($canedit) { echo ""; } else { echo $this->getField('backoutplancontent'); } echo ""; echo ""; echo "".__('Checklist').""; if ($canedit) { echo ""; } else { echo $this->getField('checklistcontent'); } echo ""; $options['candel'] = false; $options['canedit'] = $canedit; $this->showFormButtons($options); } function getRights($interface = 'central') { $values = parent::getRights(); unset($values[READ]); $values[self::READALL] = __('See all'); $values[self::READMY] = __('See (author)'); return $values; } /** * @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_changes`.`entities_id` AS entityID "; } return " DISTINCT `glpi_changes`.*, `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_changes`.`entities_id`) "; } return " LEFT JOIN `glpi_changes_groups` ON (`glpi_changes`.`id` = `glpi_changes_groups`.`changes_id`) LEFT JOIN `glpi_changes_users` ON (`glpi_changes`.`id` = `glpi_changes_users`.`changes_id`) LEFT JOIN `glpi_changes_suppliers` ON (`glpi_changes`.`id` = `glpi_changes_suppliers`.`changes_id`) LEFT JOIN `glpi_itilcategories` ON (`glpi_changes`.`itilcategories_id` = `glpi_itilcategories`.`id`) $FROM"; } /** * Display changes for an item * * Will also display changes of linked items * * @param CommonDBTM $item * @param boolean $withtemplate * * @return boolean|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_changes_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'] = 'OR'; $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_changes_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 changes')."
"; echo __('Child groups'); Dropdown::showYesNo('tree', $tree, -1, ['on_change' => 'reloadTab("start=0&tree="+this.value)']); } else { $tree = 0; } echo "
"; $restrict['glpi_changes_groups.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 change if ($item->getID() && Change::isPossibleToAssignType($item->getType()) && self::canCreate() && !(!empty($withtemplate) && $withtemplate == 2) && (!isset($item->fields['is_template']) || $item->fields['is_template'] == 0)) { echo "
"; Html::showSimpleForm( Change::getFormURL(), '_add_fromitem', __('New change 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('Change', //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 change', 'Last %d changes', $number), $number); echo "
".__('No change 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 __('Changes on linked items'); echo "
".__('No change found.')."
"; } // Subquery for linked item } /** * Display debug information for current object * * @since 0.90.2 **/ function showDebug() { NotificationEvent::debugEvent($this); } 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' => '', 'entities_id' => $_SESSION['glpiactive_entity'], 'name' => '', 'itilcategories_id' => 0, 'actiontime' => 0, '_add_validation' => 0, 'users_id_validate' => [], '_tasktemplates_id' => [], 'controlistcontent' => '', 'impactcontent' => '', 'rolloutplancontent' => '', 'backoutplancontent' => '', 'checklistcontent' => '' ]; } /** * Get active changes for an item * * @since 9.5 * * @param string $itemtype Item type * @param integer $items_id ID of the Item * * @return DBmysqlIterator */ public function getActiveChangesForItem($itemtype, $items_id) { global $DB; return $DB->request([ 'SELECT' => [ $this->getTable() . '.id', $this->getTable() . '.name', $this->getTable() . '.priority', ], 'FROM' => $this->getTable(), 'LEFT JOIN' => [ 'glpi_changes_items' => [ 'ON' => [ 'glpi_changes_items' => 'changes_id', $this->getTable() => 'id' ] ] ], 'WHERE' => [ 'glpi_changes_items.itemtype' => $itemtype, 'glpi_changes_items.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-clipboard-check"; } }