. * --------------------------------------------------------------------- */ if (!defined('GLPI_ROOT')) { die("Sorry. You can't access this file directly"); } /** * ITILSolution Class **/ class ITILSolution extends CommonDBChild { // From CommonDBTM public $dohistory = true; private $item = null; static public $itemtype = 'itemtype'; // Class name or field name (start with itemtype) for link to Parent static public $items_id = 'items_id'; // Field name public static function getNameField() { return 'id'; } static function getTypeName($nb = 0) { return _n('Solution', 'Solutions', $nb); } function getTabNameForItem(CommonGLPI $item, $withtemplate = 0) { if ($item->isNewItem()) { return; } if ($item->maySolve()) { $nb = 0; $title = self::getTypeName(Session::getPluralNumber()); if ($_SESSION['glpishow_count_on_tabs']) { $nb = self::countFor($item->getType(), $item->getID()); } return self::createTabEntry($title, $nb); } } static function canView() { return Session::haveRight('ticket', READ) || Session::haveRight('change', READ) || Session::haveRight('problem', READ); } public static function canUpdate() { //always true, will rely on ITILSolution::canUpdateItem return true; } public function canUpdateItem() { return $this->item->maySolve(); } public static function canCreate() { //always true, will rely on ITILSolution::canCreateItem return true; } public function canCreateItem() { $item = new $this->fields['itemtype']; $item->getFromDB($this->fields['items_id']); return $item->canSolve(); } function canEdit($ID) { return $this->item->maySolve(); } function post_getFromDB() { $this->item = new $this->fields['itemtype']; $this->item->getFromDB($this->fields['items_id']); } /** * Print the phone form * * @param $ID integer ID of the item * @param $options array * - item: CommonITILObject instance * - kb_id_toload: load new item content from KB entry * * @return boolean item found **/ function showForm($ID, $options = []) { global $CFG_GLPI; if ($this->isNewItem()) { $this->getEmpty(); } if (!isset($options['item']) && isset($options['parent'])) { //when we came from aja/viewsubitem.php $options['item'] = $options['parent']; } $options['formoptions'] = ($options['formoptions'] ?? '') . ' data-track-changes=true'; $item = $options['item']; $this->item = $item; $item->check($item->getID(), READ); if ($item instanceof Ticket && $this->isNewItem()) { $ti = new Ticket_Ticket(); $open_child = $ti->countOpenChildren($item->getID()); if ($open_child > 0) { echo "
" . __('Warning: non closed children tickets depends on current ticket. Are you sure you want to close it?') . "
"; } } $canedit = $item->maySolve(); if (isset($options['kb_id_toload']) && $options['kb_id_toload'] > 0) { $kb = new KnowbaseItem(); if ($kb->getFromDB($options['kb_id_toload'])) { $this->fields['content'] = $kb->getField('answer'); } } // Alert if validation waiting $validationtype = $item->getType().'Validation'; if (method_exists($validationtype, 'alertValidation') && $this->isNewItem()) { $validationtype::alertValidation($item, 'solution'); } if (!isset($options['noform'])) { $this->showFormHeader($options); } $show_template = $canedit; $rand_template = mt_rand(); $rand_text = $rand_type = 0; if ($canedit) { $rand_text = mt_rand(); $rand_type = mt_rand(); } if ($show_template) { echo ""; echo ""._n('Solution template', 'Solution templates', 1).""; $entity = isset($options['entities_id']) ? $options['entities_id'] : $this->getEntityID(); SolutionTemplate::dropdown([ 'value' => 0, 'entity' => $entity, 'rand' => $rand_template, // Load type and solution from bookmark 'toupdate' => [ 'value_fieldname' => 'value', 'to_update' => 'solution'.$rand_text, 'url' => $CFG_GLPI["root_doc"]. "/ajax/solution.php", 'moreparams' => [ 'type_id' => 'dropdown_solutiontypes_id'.$rand_type ] ] ]); echo ""; if (Session::haveRightsOr('knowbase', [READ, KnowbaseItem::READFAQ])) { echo "".__('Search a solution').""; } echo ""; } echo ""; echo "".SolutionType::getTypeName(1).""; echo Html::hidden('itemtype', ['value' => $item->getType()]); echo Html::hidden('items_id', ['value' => $item->getID()]); echo Html::hidden('_no_message_link', ['value' => 1]); // Settings a solution will set status to solved if ($canedit) { SolutionType::dropdown(['value' => $this->getField('solutiontypes_id'), 'rand' => $rand_type, 'entity' => $this->getEntityID()]); } else { echo Dropdown::getDropdownName('glpi_solutiontypes', $this->getField('solutiontypes_id')); } echo ""; if (Session::haveRightsOr('knowbase', [READ, KnowbaseItem::READFAQ]) && isset($options['kb_id_toload']) && $options['kb_id_toload'] != 0) { echo '
'; echo ' '; } else { echo ' '; } echo ""; if ($canedit && Session::haveRight('knowbase', UPDATE) && !isset($options['nokb'])) { echo "".__('Save and add to the knowledge base').""; Dropdown::showYesNo('_sol_to_kb', false); echo " "; } echo ""; echo "".__('Description').""; if ($canedit) { $rand = mt_rand(); Html::initEditorSystem("content$rand"); echo "
"; echo "
"; // Hide file input to handle only images pasted in text editor echo '
'; Html::file(['editor_id' => "content$rand", 'filecontainer' => "filecontainer$rand", 'onlyimages' => true, 'showtitle' => false, 'multiple' => true]); echo '
'; } else { echo Toolbox::unclean_cross_side_scripting_deep($this->getField('content')); } echo ""; if (!isset($options['noform'])) { $options['candel'] = false; $options['canedit'] = $canedit; $this->showFormButtons($options); } } /** * Count solutions for specific item * * @param string $itemtype Item type * @param integer $items_id Item ID * * @return integer */ public static function countFor($itemtype, $items_id) { return countElementsInTable( self::getTable(), [ 'WHERE' => [ 'itemtype' => $itemtype, 'items_id' => $items_id ] ] ); } function prepareInputForAdd($input) { $input['users_id'] = Session::getLoginUserID(); if ($this->item == null || (isset($input['itemtype']) && isset($input['items_id'])) ) { $this->item = new $input['itemtype']; $this->item->getFromDB($input['items_id']); } // check itil object is not already solved if (in_array($this->item->fields["status"], $this->item->getSolvedStatusArray())) { Session::addMessageAfterRedirect(__("The item is already solved, did anyone pushed a solution before you ?"), false, ERROR); return false; } //default status for global solutions $status = CommonITILValidation::ACCEPTED; //handle autoclose, for tickets only if ($input['itemtype'] == Ticket::getType()) { $autoclosedelay = Entity::getUsedConfig( 'autoclose_delay', $this->item->getEntityID(), '', Entity::CONFIG_NEVER ); // 0 = immediatly if ($autoclosedelay != 0) { $status = CommonITILValidation::WAITING; } } //Accepted; store user and date if ($status == CommonITILValidation::ACCEPTED) { $input['users_id_approval'] = Session::getLoginUserID(); $input['date_approval'] = $_SESSION["glpi_currenttime"]; } $input['status'] = $status; return $input; } function post_addItem() { //adding a solution mean the ITIL object is now solved //and maybe closed (according to entitiy configuration) if ($this->item == null) { $this->item = new $this->fields['itemtype']; $this->item->getFromDB($this->fields['items_id']); } $item = $this->item; // Replace inline pictures $this->input["_job"] = $this->item; $this->input = $this->addFiles( $this->input, [ 'force_update' => true, 'name' => 'content', 'content_field' => 'content', ] ); // Add solution to duplicates if ($this->item->getType() == 'Ticket' && !isset($this->input['_linked_ticket'])) { Ticket_Ticket::manageLinkedTicketsOnSolved($this->item->getID(), $this); } $status = $item::SOLVED; //handle autoclose, for tickets only if ($item->getType() == Ticket::getType()) { $autoclosedelay = Entity::getUsedConfig( 'autoclose_delay', $this->item->getEntityID(), '', Entity::CONFIG_NEVER ); // 0 = immediatly if ($autoclosedelay == 0) { $status = $item::CLOSED; } } $this->item->update([ 'id' => $this->item->getID(), 'status' => $status ]); parent::post_addItem(); } function prepareInputForUpdate($input) { if (!isset($this->fields['itemtype'])) { return false; } $input["_job"] = new $this->fields['itemtype'](); if (!$input["_job"]->getFromDB($this->fields["items_id"])) { return false; } return $input; } function post_updateItem($history = 1) { // Replace inline pictures $options = [ 'force_update' => true, 'name' => 'content', 'content_field' => 'content', ]; $this->input = $this->addFiles($this->input, $options); } /** * {@inheritDoc} * @see CommonDBTM::getSpecificValueToDisplay() */ static function getSpecificValueToDisplay($field, $values, array $options = []) { if (!is_array($values)) { $values = [$field => $values]; } switch ($field) { case 'status': $value = $values[$field]; $statuses = self::getStatuses(); return (isset($statuses[$value]) ? $statuses[$value] : $value); break; } return parent::getSpecificValueToDisplay($field, $values, $options); } /** * {@inheritDoc} * @see CommonDBTM::getSpecificValueToSelect() */ static function getSpecificValueToSelect($field, $name = '', $values = '', array $options = []) { if (!is_array($values)) { $values = [$field => $values]; } switch ($field) { case 'status': $options['display'] = false; $options['value'] = $values[$field]; return Dropdown::showFromArray($name, self::getStatuses(), $options); break; } return parent::getSpecificValueToSelect($field, $name, $values, $options); } /** * Return list of statuses. * Key as status values, values as labels. * * @return string[] */ static function getStatuses() { return [ CommonITILValidation::WAITING => __('Waiting for approval'), CommonITILValidation::REFUSED => __('Refused'), CommonITILValidation::ACCEPTED => __('Accepted'), ]; } }