· 9 years ago · Nov 10, 2016, 06:46 PM
1<?php
2/*
3 * @version $Id$
4 -------------------------------------------------------------------------
5 GLPI - Gestionnaire Libre de Parc Informatique
6 Copyright (C) 2015 Teclib'.
7
8 http://glpi-project.org
9
10 based on GLPI - Gestionnaire Libre de Parc Informatique
11 Copyright (C) 2003-2014 by the INDEPNET Development Team.
12
13 -------------------------------------------------------------------------
14
15 LICENSE
16
17 This file is part of GLPI.
18
19 GLPI is free software; you can redistribute it and/or modify
20 it under the terms of the GNU General Public License as published by
21 the Free Software Foundation; either version 2 of the License, or
22 (at your option) any later version.
23
24 GLPI is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 GNU General Public License for more details.
28
29 You should have received a copy of the GNU General Public License
30 along with GLPI. If not, see <http://www.gnu.org/licenses/>.
31 --------------------------------------------------------------------------
32 */
33
34/** @file
35* @brief
36*/
37
38if (!defined('GLPI_ROOT')) {
39 die("Sorry. You can't access directly to this file");
40}
41
42/**
43 * Ticket Class
44**/
45class Ticket extends CommonITILObject {
46
47 // From CommonDBTM
48 public $dohistory = true;
49 static protected $forward_entity_to = array('TicketValidation', 'TicketCost');
50
51 // From CommonITIL
52 public $userlinkclass = 'Ticket_User';
53 public $grouplinkclass = 'Group_Ticket';
54 public $supplierlinkclass = 'Supplier_Ticket';
55
56 static $rightname = 'ticket';
57
58 protected $userentity_oncreate = true;
59
60 const MATRIX_FIELD = 'priority_matrix';
61 const URGENCY_MASK_FIELD = 'urgency_mask';
62 const IMPACT_MASK_FIELD = 'impact_mask';
63 const STATUS_MATRIX_FIELD = 'ticket_status';
64
65 // HELPDESK LINK HARDWARE DEFINITION : CHECKSUM SYSTEM : BOTH=1*2^0+1*2^1=3
66 const HELPDESK_MY_HARDWARE = 0;
67 const HELPDESK_ALL_HARDWARE = 1;
68
69 // Specific ones
70 /// Hardware datas used by getFromDBwithData
71 var $hardwaredatas = array();
72 /// Is a hardware found in getHardwareData / getFromDBwithData : hardware link to the job
73 var $computerfound = 0;
74
75 // Request type
76 const INCIDENT_TYPE = 1;
77 // Demand type
78 const DEMAND_TYPE = 2;
79
80 const READMY = 1;
81 const READALL = 1024;
82 const READGROUP = 2048;
83 const READASSIGN = 4096;
84 const ASSIGN = 8192;
85 const STEAL = 16384;
86 const OWN = 32768;
87 const CHANGEPRIORITY = 65536;
88
89
90 function getForbiddenStandardMassiveAction() {
91
92 $forbidden = parent::getForbiddenStandardMassiveAction();
93
94 if (!Session::haveRightsOr(self::$rightname, array(DELETE, PURGE))) {
95 $forbidden[] = 'delete';
96 $forbidden[] = 'purge';
97 $forbidden[] = 'restore';
98 }
99
100 return $forbidden;
101 }
102
103
104 /**
105 * Name of the type
106 *
107 * @param $nb : number of item in the type (default 0)
108 **/
109 static function getTypeName($nb=0) {
110 return _n('Ticket','Tickets',$nb);
111 }
112
113
114 /**
115 * @see CommonGLPI::getMenuShorcut()
116 *
117 * @since version 0.85
118 **/
119 static function getMenuShorcut() {
120 return 't';
121 }
122
123
124 /**
125 * @see CommonGLPI::getAdditionalMenuOptions()
126 *
127 * @since version 0.85
128 **/
129 static function getAdditionalMenuOptions() {
130
131 if (TicketTemplate::canView()) {
132 $menu['TicketTemplate']['title'] = TicketTemplate::getTypeName(Session::getPluralNumber());
133 $menu['TicketTemplate']['page'] = TicketTemplate::getSearchURL(false);
134 $menu['TicketTemplate']['links']['search'] = TicketTemplate::getSearchURL(false);
135 if (TicketTemplate::canCreate()) {
136 $menu['TicketTemplate']['links']['add'] = TicketTemplate::getFormURL(false);
137 }
138 return $menu;
139 }
140 return false;
141 }
142
143
144 /**
145 * @see CommonGLPI::getAdditionalMenuContent()
146 *
147 * @since version 0.85
148 **/
149 static function getAdditionalMenuContent() {
150
151 if (static::canCreate()) {
152 $menu['create_ticket']['title'] = __('Create ticket');
153 $menu['create_ticket']['page'] = static::getFormURL(false);
154 return $menu;
155 }
156 }
157
158
159 /**
160 * @see CommonGLPI::getAdditionalMenuLinks()
161 *
162 * @since version 0.85
163 **/
164 static function getAdditionalMenuLinks() {
165 global $CFG_GLPI;
166
167 $links = array();
168 if (TicketTemplate::canView()) {
169 $links['template'] = TicketTemplate::getSearchURL(false);
170 }
171 if (Session::haveRightsOr('ticketvalidation', TicketValidation::getValidateRights())) {
172 $opt = array();
173 $opt['reset'] = 'reset';
174 $opt['criteria'][0]['field'] = 55; // validation status
175 $opt['criteria'][0]['searchtype'] = 'equals';
176 $opt['criteria'][0]['value'] = CommonITILValidation::WAITING;
177 $opt['criteria'][0]['link'] = 'AND';
178
179 $opt['criteria'][1]['field'] = 59; // validation aprobator
180 $opt['criteria'][1]['searchtype'] = 'equals';
181 $opt['criteria'][1]['value'] = Session::getLoginUserID();
182 $opt['criteria'][1]['link'] = 'AND';
183
184 $pic_validate = "<img title=\"".__s('Ticket waiting for your approval')."\" alt=\"".
185 __s('Ticket waiting for your approval')."\" src='".
186 $CFG_GLPI["root_doc"]."/pics/menu_showall.png' class='pointer'>";
187
188 $links[$pic_validate] = '/front/ticket.php?'.Toolbox::append_params($opt, '&');
189 }
190 if (count($links)) {
191 return $links;
192 }
193 return false;
194 }
195
196
197 function canAdminActors() {
198
199 if ($this->fields['is_deleted'] == 1) {
200 return false;
201 }
202 return Session::haveRight(self::$rightname, UPDATE);
203 }
204
205
206 function canAssign() {
207
208 if (isset($this->fields['is_deleted']) && ($this->fields['is_deleted'] == 1)) {
209 return false;
210 }
211 return Session::haveRight(self::$rightname, self::ASSIGN);
212 }
213
214
215 function canAssignToMe() {
216
217 if ($this->fields['is_deleted'] == 1) {
218 return false;
219 }
220 return (Session::haveRight(self::$rightname, self::STEAL)
221 || (Session::haveRight(self::$rightname, self::OWN)
222 && ($this->countUsers(CommonITILActor::ASSIGN) == 0)));
223 }
224
225
226 static function canUpdate() {
227
228 // To allow update of urgency and category for post-only
229 if ($_SESSION["glpiactiveprofile"]["interface"] == "helpdesk") {
230 return Session::haveRight(self::$rightname, CREATE);
231 }
232 return Session::haveRightsOr(self::$rightname,
233 array(UPDATE, self::ASSIGN, self::STEAL, self::OWN));
234 }
235
236
237 static function canView() {
238/*
239 if (isset($_SESSION['glpiactiveprofile']['interface'])
240 && $_SESSION['glpiactiveprofile']['interface'] == 'helpdesk') {
241 return true;
242 }*/
243 return (Session::haveRightsOr(self::$rightname,
244 array(self::READALL, self::READMY, UPDATE, self::READASSIGN,
245 self::READGROUP, self::OWN))
246 || Session::haveRightsOr('ticketvalidation', TicketValidation::getValidateRights()));
247 }
248
249
250 /**
251 * Is the current user have right to show the current ticket ?
252 *
253 * @return boolean
254 **/
255 function canViewItem() {
256
257 if (!Session::haveAccessToEntity($this->getEntityID())) {
258 return false;
259 }
260 return (Session::haveRight(self::$rightname, self::READALL)
261 || (Session::haveRight(self::$rightname, self::READMY)
262 && (($this->fields["users_id_recipient"] === Session::getLoginUserID())
263 || $this->isUser(CommonITILActor::REQUESTER, Session::getLoginUserID())
264 || $this->isUser(CommonITILActor::OBSERVER, Session::getLoginUserID())))
265 || (Session::haveRight(self::$rightname, self::READGROUP)
266 && isset($_SESSION["glpigroups"])
267 && ($this->haveAGroup(CommonITILActor::REQUESTER, $_SESSION["glpigroups"])
268 || $this->haveAGroup(CommonITILActor::OBSERVER, $_SESSION["glpigroups"])))
269 || (Session::haveRight(self::$rightname, self::READASSIGN)
270 && ($this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID())
271 || (isset($_SESSION["glpigroups"])
272 && $this->haveAGroup(CommonITILActor::ASSIGN, $_SESSION["glpigroups"]))
273 || (Session::haveRight(self::$rightname, self::ASSIGN)
274 && ($this->fields["status"] == self::INCOMING))))
275 || (Session::haveRightsOr('ticketvalidation', TicketValidation::getValidateRights())
276 && TicketValidation::canValidate($this->fields["id"])));
277 }
278
279
280 /**
281 * Is the current user have right to solve the current ticket ?
282 *
283 * @return boolean
284 **/
285 function canSolve() {
286
287 return ((Session::haveRight(self::$rightname, UPDATE)
288 || $this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID())
289 || (isset($_SESSION["glpigroups"])
290 && $this->haveAGroup(CommonITILActor::ASSIGN, $_SESSION["glpigroups"])))
291 && self::isAllowedStatus($this->fields['status'], self::SOLVED)
292 // No edition on closed status
293 && !in_array($this->fields['status'], $this->getClosedStatusArray()));
294 }
295
296
297 /**
298 * Is the current user have right to approve solution of the current ticket ?
299 *
300 * @return boolean
301 **/
302 function canApprove() {
303
304 return (($this->fields["users_id_recipient"] === Session::getLoginUserID())
305 || $this->isUser(CommonITILActor::REQUESTER, Session::getLoginUserID())
306 || (isset($_SESSION["glpigroups"])
307 && $this->haveAGroup(CommonITILActor::REQUESTER, $_SESSION["glpigroups"])));
308 }
309
310
311 /**
312 * @see CommonDBTM::canMassiveAction()
313 **/
314 function canMassiveAction($action, $field, $value) {
315
316 switch ($action) {
317 case 'update' :
318 switch ($field) {
319 case 'status' :
320 if (!self::isAllowedStatus($this->fields['status'], $value)) {
321 return false;
322 }
323 break;
324 }
325 break;
326 }
327 return true;
328 }
329
330
331 /**
332 * Get Datas to be added for SLA add
333 *
334 * @param $slas_id SLA id
335 * @param $entities_id entity ID of the ticket
336 * @param $date begin date of the ticket
337 *
338 * @return array of datas to add in ticket
339 **/
340 function getDatasToAddSLA($slas_id, $entities_id, $date) {
341
342 $calendars_id = Entity::getUsedConfig('calendars_id', $entities_id);
343 $data = array();
344
345 $sla = new SLA();
346 if ($sla->getFromDB($slas_id)) {
347 $sla->setTicketCalendar($calendars_id);
348 // Get first SLA Level
349 $data["slalevels_id"] = SlaLevel::getFirstSlaLevel($slas_id);
350 // Compute due_date
351 $data['due_date'] = $sla->computeDueDate($date);
352 $data['sla_waiting_duration'] = 0;
353
354 } else {
355 $data["slalevels_id"] = 0;
356 $data["slas_id"] = 0;
357 $data['sla_waiting_duration'] = 0;
358 }
359 return $data;
360
361 }
362
363
364 /**
365 * Delete SLA for the ticket
366 *
367 * @param $id ID of the ticket
368 * @param $delete_due_date 1 to delete due_date (default 0)
369 *
370 * @return boolean
371 **/
372 function deleteSLA($id, $delete_due_date=0) {
373
374 $input['slas_id'] = 0;
375 $input['slalevels_id'] = 0;
376 $input['sla_waiting_duration'] = 0;
377 $input['id'] = $id;
378 if ($delete_due_date) {
379 $input['due_date'] = '';
380 }
381
382 SlaLevel_Ticket::deleteForTicket($id);
383
384 return $this->update($input);
385 }
386
387
388 /**
389 * Is the current user have right to create the current ticket ?
390 *
391 * @return boolean
392 **/
393 function canCreateItem() {
394
395 if (!Session::haveAccessToEntity($this->getEntityID())) {
396 return false;
397 }
398 return self::canCreate();
399 }
400
401
402 /**
403 * Is the current user have right to update the current ticket ?
404 *
405 * @return boolean
406 **/
407 function canUpdateItem() {
408
409 if (!Session::haveAccessToEntity($this->getEntityID())) {
410 return false;
411 }
412
413 if (($this->numberOfFollowups() == 0)
414 && ($this->numberOfTasks() == 0)
415 && ($this->isUser(CommonITILActor::REQUESTER,Session::getLoginUserID())
416 || ($this->fields["users_id_recipient"] === Session::getLoginUserID()))) {
417 return true;
418 }
419
420 return static::canUpdate();
421 }
422
423
424 /**
425 * @since version 0.85
426 **/
427 static function canDelete() {
428
429 // to allow delete for self-service only if no action on the ticket
430 if ($_SESSION["glpiactiveprofile"]["interface"] == "helpdesk") {
431 return Session::haveRight(self::$rightname, CREATE);
432 }
433 return Session::haveRight(self::$rightname, DELETE);
434 }
435
436
437 /**
438 * Is the current user have right to delete the current ticket ?
439 *
440 * @return boolean
441 **/
442 function canDeleteItem() {
443
444 if (!Session::haveAccessToEntity($this->getEntityID())) {
445 return false;
446 }
447
448 // user can delete his ticket if no action on it
449 if (($this->isUser(CommonITILActor::REQUESTER, Session::getLoginUserID())
450 || ($this->fields["users_id_recipient"] === Session::getLoginUserID()))
451 && ($this->numberOfFollowups() == 0)
452 && ($this->numberOfTasks() == 0)
453 && ($this->fields["date"] == $this->fields["date_mod"])) {
454 return true;
455 }
456
457 return static::canDelete();
458 }
459
460
461 /**
462 * @see CommonITILObject::getDefaultActor()
463 **/
464 function getDefaultActor($type) {
465
466 if ($type == CommonITILActor::ASSIGN) {
467 if (Session::haveRight(self::$rightname, self::OWN)
468 && $_SESSION['glpiset_default_tech']) {
469 return Session::getLoginUserID();
470 }
471 }
472 return 0;
473 }
474
475
476 /**
477 * @see CommonITILObject::getDefaultActorRightSearch()
478 **/
479 function getDefaultActorRightSearch($type) {
480
481 $right = "all";
482 if ($type == CommonITILActor::ASSIGN) {
483 $right = "own_ticket";
484 if (!Session::haveRight(self::$rightname, self::ASSIGN)) {
485 $right = 'id';
486 }
487 }
488 return $right;
489 }
490
491
492 function pre_deleteItem() {
493
494 NotificationEvent::raiseEvent('delete',$this);
495 return true;
496 }
497
498
499 function getTabNameForItem(CommonGLPI $item, $withtemplate=0) {
500
501 if ($item->isNewItem()) {
502 return;
503 }
504
505 if (static::canView()) {
506 $nb = 0;
507 $title = self::getTypeName(Session::getPluralNumber());
508 if ($_SESSION['glpishow_count_on_tabs']) {
509 switch ($item->getType()) {
510 case 'User' :
511 $nb = countElementsInTable(array('glpi_tickets', 'glpi_tickets_users'),
512 getEntitiesRestrictRequest("", 'glpi_tickets').
513 "AND `glpi_tickets_users`.`tickets_id` = `glpi_tickets`.`id`
514 AND `glpi_tickets_users`.`users_id` = '".$item->getID()."'
515 AND `glpi_tickets_users`.`type` = ".CommonITILActor::REQUESTER);
516 $title = __('Created tickets');
517 break;
518
519 case 'Supplier' :
520 $nb = countElementsInTable(array('glpi_tickets', 'glpi_suppliers_tickets'),
521 getEntitiesRestrictRequest("", 'glpi_tickets').
522 "AND `glpi_suppliers_tickets`.`tickets_id` = `glpi_tickets`.`id`
523 AND `glpi_suppliers_tickets`.`suppliers_id` = '".$item->getID()."'");
524 break;
525
526 case 'SLA' :
527 $nb = countElementsInTable('glpi_tickets',
528 "`slas_id` = '".$item->getID()."'");
529 break;
530
531 case 'Group' :
532 $nb = countElementsInTable(array('glpi_tickets', 'glpi_groups_tickets'),
533 getEntitiesRestrictRequest("", 'glpi_tickets').
534 "AND `glpi_groups_tickets`.`tickets_id` = `glpi_tickets`.`id`
535 AND `glpi_groups_tickets`.`groups_id` = '".$item->getID()."'
536 AND `glpi_groups_tickets`.`type` = ".CommonITILActor::REQUESTER);
537 $title = __('Created tickets');
538 break;
539
540 default :
541 // Direct one
542 $nb = countElementsInTable('glpi_items_tickets',
543 " `itemtype` = '".$item->getType()."'
544 AND `items_id` = '".$item->getID()."'");
545 // Linked items
546 $linkeditems = $item->getLinkedItems();
547
548 if (count($linkeditems)) {
549 foreach ($linkeditems as $type => $tab) {
550 foreach ($tab as $ID) {
551 $nb += countElementsInTable('glpi_items_tickets',
552 " `itemtype` = '$type'
553 AND `items_id` = '$ID'");
554 }
555 }
556 }
557 break;
558 }
559
560 } // glpishow_count_on_tabs
561 // Not for Ticket class
562 if ($item->getType() != __CLASS__) {
563 return self::createTabEntry($title, $nb);
564 }
565 } // self::READALL right check
566
567 // Not check self::READALL for Ticket itself
568 switch ($item->getType()) {
569 case __CLASS__ :
570 $ong = array();
571
572 if ($_SESSION['glpiticket_timeline']) {
573 $timeline = $item->getTimelineItems();
574 $nb_elements = count($timeline);
575 $ong[1] = __("Processing ticket")." <sup class='tab_nb'>$nb_elements</sup>";
576 }
577
578 if ((!$_SESSION['glpiticket_timeline']
579 || $_SESSION['glpiticket_timeline_keep_replaced_tabs'])) {
580 $ong[2] = _n('Solution', 'Solutions', 1);
581 }
582 // enquete si statut clos
583 if ($item->fields['status'] == self::CLOSED) {
584 $satisfaction = new TicketSatisfaction();
585 if ($satisfaction->getFromDB($item->getID())) {
586 $ong[3] = __('Satisfaction');
587 }
588 }
589 if ($item->canUpdate()) {
590 $ong[4] = __('Statistics');
591 }
592 return $ong;
593
594 // default :
595 // return _n('Ticket','Tickets', Session::getPluralNumber());
596 }
597
598 return '';
599 }
600
601
602 static function displayTabContentForItem(CommonGLPI $item, $tabnum=1, $withtemplate=0) {
603
604 switch ($item->getType()) {
605 case __CLASS__ :
606 switch ($tabnum) {
607
608 case 1 :
609 echo "<div class='timeline_box'>";
610 $rand = mt_rand();
611 $item->showTimelineForm($rand);
612 $item->showTimeline($rand);
613 echo "</div>";
614 break;
615
616 case 2 :
617 if (!isset($_GET['load_kb_sol'])) {
618 $_GET['load_kb_sol'] = 0;
619 }
620 $item->showSolutionForm($_GET['load_kb_sol']);
621
622 if ($item->canApprove()) {
623 $fup = new TicketFollowup();
624 $fup->showApprobationForm($item);
625 }
626 break;
627
628 case 3 :
629 $satisfaction = new TicketSatisfaction();
630 if (($item->fields['status'] == self::CLOSED)
631 && $satisfaction->getFromDB($_GET["id"])) {
632
633 $duration = Entity::getUsedConfig('inquest_duration', $item->fields['entities_id']);
634 $date2 = strtotime($satisfaction->fields['date_begin']);
635 if (($duration == 0)
636 || (strtotime("now") - $date2) <= $duration*DAY_TIMESTAMP) {
637 $satisfaction->showForm($item);
638 } else {
639 echo "<p class='center b'>".__('Satisfaction survey expired')."</p>";
640 }
641
642 } else {
643 echo "<p class='center b'>".__('No generated survey')."</p>";
644 }
645 break;
646
647 case 4 :
648 $item->showStats();
649 break;
650 }
651 break;
652
653 case 'Group' :
654 case 'SLA' :
655 default :
656 self::showListForItem($item);
657 }
658 return true;
659 }
660
661
662 function defineTabs($options=array()) {
663
664 $ong = array();
665 $this->addDefaultFormTab($ong);
666 if (!$_SESSION['glpiticket_timeline']
667 || $_SESSION['glpiticket_timeline_keep_replaced_tabs']) {
668 $this->addStandardTab('TicketFollowup',$ong, $options);
669 $this->addStandardTab('TicketTask', $ong, $options);
670 $this->addStandardTab('Document_Item', $ong, $options);
671 }
672 $this->addStandardTab(__CLASS__, $ong, $options);
673 $this->addStandardTab('TicketValidation', $ong, $options);
674 $this->addStandardTab('Item_Ticket', $ong, $options);
675 $this->addStandardTab('TicketCost', $ong, $options);
676 $this->addStandardTab('Projecttask_Ticket', $ong, $options);
677 $this->addStandardTab('Problem_Ticket', $ong, $options);
678 $this->addStandardTab('Change_Ticket', $ong, $options);
679 $this->addStandardTab('Log', $ong, $options);
680
681 return $ong;
682 }
683
684
685 /**
686 * Retrieve data of the hardware linked to the ticket if exists
687 *
688 * @return nothing : set computerfound to 1 if founded
689 **/
690 function getAdditionalDatas() {
691
692 $this->hardwaredatas = array();
693
694
695 if (!empty($this->fields["id"])) {
696 $item_ticket = new Item_Ticket();
697 $data = $item_ticket->find("`tickets_id` = ".$this->fields["id"]);
698
699 foreach ($data as $val) {
700 if (!empty($val["itemtype"]) && ($item = getItemForItemtype($val["itemtype"]))) {
701 if ($item->getFromDB($val["items_id"])) {
702 $this->hardwaredatas[] = $item;
703 }
704 }
705 }
706 }
707
708 }
709
710
711 function cleanDBonPurge() {
712 global $DB;
713
714 $query1 = "DELETE
715 FROM `glpi_tickettasks`
716 WHERE `tickets_id` = '".$this->fields['id']."'";
717 $DB->query($query1);
718
719 $query1 = "DELETE
720 FROM `glpi_ticketfollowups`
721 WHERE `tickets_id` = '".$this->fields['id']."'";
722 $DB->query($query1);
723
724 $ts = new TicketValidation();
725 $ts->cleanDBonItemDelete($this->getType(), $this->fields['id']);
726
727
728 $query1 = "DELETE
729 FROM `glpi_ticketsatisfactions`
730 WHERE `tickets_id` = '".$this->fields['id']."'";
731 $DB->query($query1);
732
733 $pt = new Problem_Ticket();
734 $pt->cleanDBonItemDelete('Ticket', $this->fields['id']);
735
736 $ts = new TicketCost();
737 $ts->cleanDBonItemDelete($this->getType(), $this->fields['id']);
738
739 SlaLevel_Ticket::deleteForTicket($this->getID());
740
741 $query1 = "DELETE
742 FROM `glpi_tickets_tickets`
743 WHERE `tickets_id_1` = '".$this->fields['id']."'
744 OR `tickets_id_2` = '".$this->fields['id']."'";
745 $DB->query($query1);
746
747 $ct = new Change_Ticket();
748 $ct->cleanDBonItemDelete(__CLASS__, $this->fields['id']);
749
750
751 $ip = new Item_Ticket();
752 $ip->cleanDBonItemDelete('Ticket', $this->fields['id']);
753
754
755 parent::cleanDBonPurge();
756
757 }
758
759
760 function prepareInputForUpdate($input) {
761 global $CFG_GLPI, $DB;
762
763 // Get ticket : need for comparison
764 $this->getFromDB($input['id']);
765
766 // Clean new lines before passing to rules
767 if ($CFG_GLPI["use_rich_text"] && isset($input["content"])) {
768 $input["content"] = preg_replace('/\\\\r\\\\n/',"\n",$input['content']);
769 $input["content"] = preg_replace('/\\\\n/',"\n",$input['content']);
770 }
771
772 // automatic recalculate if user changes urgence or technician change impact
773 if (isset($input['urgency'])
774 && isset($input['impact'])
775 && (($input['urgency'] != $this->fields['urgency'])
776 || $input['impact'] != $this->fields['impact'])
777 && !isset($input['priority'])) {
778 $input['priority'] = self::computePriority($input['urgency'], $input['impact']);
779 }
780
781 // Security checks
782 if (!Session::isCron()
783 && !Session::haveRight(self::$rightname, self::ASSIGN)) {
784 if (isset($input["_itil_assign"])
785 && isset($input['_itil_assign']['_type'])
786 && ($input['_itil_assign']['_type'] == 'user')) {
787
788 // must own_ticket to grab a non assign ticket
789 if ($this->countUsers(CommonITILActor::ASSIGN) == 0) {
790 if ((!Session::haveRightsOr(self::$rightname, array(self::STEAL, self::OWN)))
791 || !isset($input["_itil_assign"]['users_id'])
792 || ($input["_itil_assign"]['users_id'] != Session::getLoginUserID())) {
793 unset($input["_itil_assign"]);
794 }
795
796 } else {
797 // Can not steal or can steal and not assign to me
798 if (!Session::haveRight(self::$rightname, self::STEAL)
799 || !isset($input["_itil_assign"]['users_id'])
800 || ($input["_itil_assign"]['users_id'] != Session::getLoginUserID())) {
801 unset($input["_itil_assign"]);
802 }
803 }
804 }
805
806 // No supplier assign
807 if (isset($input["_itil_assign"])
808 && isset($input['_itil_assign']['_type'])
809 && ($input['_itil_assign']['_type'] == 'supplier')) {
810 unset($input["_itil_assign"]);
811 }
812
813 // No group
814 if (isset($input["_itil_assign"])
815 && isset($input['_itil_assign']['_type'])
816 && ($input['_itil_assign']['_type'] == 'group')) {
817 unset($input["_itil_assign"]);
818 }
819 }
820 $check_allowed_fields_for_template = false;
821 $allowed_fields = array();
822 if (!Session::isCron()
823 && (!Session::haveRight(self::$rightname, UPDATE)
824 // Closed tickets
825 || in_array($this->fields['status'],$this->getClosedStatusArray()))
826 ) {
827
828 $allowed_fields = array('id');
829 $check_allowed_fields_for_template = true;
830
831 if (in_array($this->fields['status'],$this->getClosedStatusArray())) {
832 $allowed_fields[] = 'status';
833
834 // probably transfer
835 $allowed_fields[] = 'entities_id';
836 } else {
837 if ($this->canApprove()
838 && isset($input["status"])) {
839 $allowed_fields[] = 'status';
840 }
841 // for post-only with validate right or validation created by rules
842 if (TicketValidation::canValidate($this->fields['id'])
843 || TicketValidation::canCreate()
844 || isset($input["_rule_process"])) {
845 $allowed_fields[] = 'global_validation';
846 }
847 // Manage assign and steal right
848 if (Session::haveRightsOr(self::$rightname, array(self::ASSIGN, self::STEAL))) {
849 $allowed_fields[] = '_itil_assign';
850 }
851
852 // Can only update initial fields if no followup or task already added
853 if (($this->numberOfFollowups() == 0)
854 && ($this->numberOfTasks() == 0)
855 && $this->isUser(CommonITILActor::REQUESTER, Session::getLoginUserID())) {
856 $allowed_fields[] = 'content';
857 $allowed_fields[] = 'urgency';
858 $allowed_fields[] = 'priority'; // automatic recalculate if user changes urgence
859 $allowed_fields[] = 'itilcategories_id';
860 $allowed_fields[] = 'name';
861 }
862
863 if ($this->canSolve()) {
864 $allowed_fields[] = 'solutiontypes_id';
865 $allowed_fields[] = 'solution';
866 }
867 }
868
869 foreach ($allowed_fields as $field) {
870 if (isset($input[$field])) {
871 $ret[$field] = $input[$field];
872 }
873 }
874
875 $input = $ret;
876
877 // Only ID return false
878 if (count($input) == 1) {
879 return false;
880 }
881 }
882
883
884
885 //// check mandatory fields
886 // First get ticket template associated : entity and type/category
887 if (isset($input['entities_id'])) {
888 $entid = $input['entities_id'];
889 } else {
890 $entid = $this->fields['entities_id'];
891 }
892
893 if (isset($input['type'])) {
894 $type = $input['type'];
895 } else {
896 $type = $this->fields['type'];
897 }
898
899 if (isset($input['itilcategories_id'])) {
900 $categid = $input['itilcategories_id'];
901 } else {
902 $categid = $this->fields['itilcategories_id'];
903 }
904
905 $tt = $this->getTicketTemplateToUse(0, $type, $categid, $entid);
906
907 if (count($tt->mandatory)) {
908 $mandatory_missing = array();
909 $fieldsname = $tt->getAllowedFieldsNames(true);
910 foreach ($tt->mandatory as $key => $val) {
911 if ((!$check_allowed_fields_for_template || in_array($key,$allowed_fields))
912 && (isset($input[$key])
913 && (empty($input[$key]) || ($input[$key] == 'NULL'))
914 // Take only into account already set items : do not block old tickets
915 && (!empty($this->fields[$key]))
916 )) {
917 $mandatory_missing[$key] = $fieldsname[$val];
918 }
919 }
920 if (count($mandatory_missing)) {
921 //TRANS: %s are the fields concerned
922 $message = sprintf(__('Mandatory fields are not filled. Please correct: %s'),
923 implode(", ",$mandatory_missing));
924 Session::addMessageAfterRedirect($message, false, ERROR);
925 return false;
926 }
927 }
928
929 /// Process Business Rules
930 // Add actors on standard input
931 $rules = new RuleTicketCollection($entid);
932 $rule = $rules->getRuleClass();
933 $changes = array();
934 $tocleanafterrules = array();
935 $usertypes = array('assign', 'requester', 'observer');
936 foreach ($usertypes as $t) {
937 if (isset($input['_itil_'.$t]) && isset($input['_itil_'.$t]['_type'])) {
938 $field = $input['_itil_'.$t]['_type'].'s_id';
939 if (isset($input['_itil_'.$t][$field])
940 && !isset($input[$field.'_'.$t])) {
941 $input['_'.$field.'_'.$t] = $input['_itil_'.$t][$field];
942 $tocleanafterrules['_'.$field.'_'.$t] = $input['_itil_'.$t][$field];
943 }
944 }
945
946 }
947
948 foreach ($rule->getCriterias() as $key => $val) {
949 if (array_key_exists($key,$input)) {
950 if (!isset($this->fields[$key])
951 || ($DB->escape($this->fields[$key]) != $input[$key])) {
952 $changes[] = $key;
953 }
954 }
955 }
956
957 // Business Rules do not override manual SLA
958 $manual_slas_id = 0;
959 if (isset($input['slas_id']) && ($input['slas_id'] > 0)) {
960 $manual_slas_id = $input['slas_id'];
961 }
962
963 // Only process rules on changes
964 if (count($changes)) {
965 if (in_array('_users_id_requester', $changes)) {
966 // If _users_id_requester changed : set users_locations
967 $user = new User();
968 if (isset($input["_users_id_requester"])
969 && $user->getFromDB($input["_users_id_requester"])) {
970 $input['users_locations'] = $user->fields['locations_id'];
971 $changes[] = 'users_locations';
972 }
973 // If _users_id_requester changed : add _groups_id_of_requester to changes
974 $changes[] = '_groups_id_of_requester';
975 }
976
977 $input = $rules->processAllRules(Toolbox::stripslashes_deep($input),
978 Toolbox::stripslashes_deep($input),
979 array('recursive' => true,
980 'entities_id' => $entid),
981 array('condition' => RuleTicket::ONUPDATE,
982 'only_criteria' => $changes));
983 }
984
985 // Restore slas_id
986 if ($manual_slas_id > 0) {
987 $input['slas_id'] = $manual_slas_id;
988 }
989
990 //Action for send_validation rule : do validation before clean
991 $this->manageValidationAdd($input);
992
993 // Clean actors fields added for rules
994 foreach ($tocleanafterrules as $key => $val) {
995 if ($input[$key] == $val) {
996 unset($input[$key]);
997 }
998 }
999
1000 // Manage fields from auto update or rules : map rule actions to standard additional ones
1001 $usertypes = array('assign', 'requester', 'observer');
1002 $actortypes = array('user','group','supplier');
1003 foreach ($usertypes as $t) {
1004 foreach ($actortypes as $a) {
1005 if (isset($input['_'.$a.'s_id_'.$t])) {
1006 switch ($a) {
1007 case 'user' :
1008 $additionalfield = '_additional_'.$t.'s';
1009 $input[$additionalfield][] = array('users_id' => $input['_'.$a.'s_id_'.$t]);
1010 break;
1011
1012 default :
1013 $additionalfield = '_additional_'.$a.'s_'.$t.'s';
1014 $input[$additionalfield][] = $input['_'.$a.'s_id_'.$t];
1015 break;
1016 }
1017 }
1018 }
1019 }
1020
1021 if (isset($input['_link'])) {
1022 $ticket_ticket = new Ticket_Ticket();
1023 if (!empty($input['_link']['tickets_id_2'])) {
1024 if ($ticket_ticket->can(-1, CREATE, $input['_link'])) {
1025 if ($ticket_ticket->add($input['_link'])) {
1026 $input['_forcenotif'] = true;
1027 }
1028 } else {
1029 Session::addMessageAfterRedirect(__('Unknown ticket'), false, ERROR);
1030 }
1031 }
1032 }
1033
1034 //// SLA affect by rules : reset due_date
1035 // Manual SLA defined : reset due date
1036 // No manual SLA and due date defined : reset auto SLA
1037 if (($manual_slas_id == 0)
1038 && isset($input["slas_id"])
1039 && ($input['slas_id'] > 0)
1040 && ($input['slas_id'] != $this->fields['slas_id'])) {
1041 if (isset($input['due_date'])) {
1042 // Unset due date
1043 unset($input["due_date"]);
1044 }
1045 }
1046
1047
1048 if (isset($input["slas_id"])
1049 && ($input["slas_id"] > 0)
1050 && ($input['slas_id'] != $this->fields['slas_id'])) {
1051
1052 $date = $this->fields['date'];
1053 /// Use updated date if also done
1054 if (isset($input["date"])) {
1055 $date = $input["date"];
1056 }
1057 // Get datas to initialize SLA and set it
1058 $sla_data = $this->getDatasToAddSLA($input["slas_id"], $this->fields['entities_id'],
1059 $date);
1060 if (count($sla_data)) {
1061 foreach ($sla_data as $key => $val) {
1062 $input[$key] = $val;
1063 }
1064 }
1065 }
1066
1067 if (isset($input['content'])) {
1068 if (isset($input['_stock_image'])) {
1069 $this->addImagePaste();
1070 $input['content'] = $input['content'];
1071 $input['_disablenotif'] = true;
1072 } else if ($CFG_GLPI["use_rich_text"]) {
1073 $input['content'] = $this->convertTagToImage($input['content']);
1074 if (!isset($input['_filename'])) {
1075 $input['_donotadddocs'] = true;
1076 }
1077 }
1078 }
1079
1080 $input = parent::prepareInputForUpdate($input);
1081 return $input;
1082 }
1083
1084
1085 function pre_updateInDB() {
1086
1087 // takeintoaccount :
1088 // - update done by someone who have update right
1089 // see also updatedatemod used by ticketfollowup updates
1090 if (($this->fields['takeintoaccount_delay_stat'] == 0)
1091 && (Session::haveRight("task", TicketTask::ADDALLTICKET)
1092 || Session::haveRightsOr('followup',
1093 array(TicketFollowup::ADDALLTICKET,
1094 TicketFollowup::ADDMYTICKET,
1095 TicketFollowup::ADDGROUPTICKET))
1096 || $this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID())
1097 || (isset($_SESSION["glpigroups"])
1098 && $this->haveAGroup(CommonITILActor::ASSIGN, $_SESSION['glpigroups'])))) {
1099
1100 $this->updates[] = "takeintoaccount_delay_stat";
1101 $this->fields['takeintoaccount_delay_stat'] = $this->computeTakeIntoAccountDelayStat();
1102 }
1103
1104 parent::pre_updateInDB();
1105
1106 }
1107
1108
1109 /// Compute take into account stat of the current ticket
1110 function computeTakeIntoAccountDelayStat() {
1111
1112 if (isset($this->fields['id'])
1113 && !empty($this->fields['date'])) {
1114 $calendars_id = $this->getCalendar();
1115 $calendar = new Calendar();
1116
1117 // Using calendar
1118 if (($calendars_id > 0) && $calendar->getFromDB($calendars_id)) {
1119 return max(1, $calendar->getActiveTimeBetween($this->fields['date'],
1120 $_SESSION["glpi_currenttime"]));
1121 }
1122 // Not calendar defined
1123 return max(1, strtotime($_SESSION["glpi_currenttime"])-strtotime($this->fields['date']));
1124 }
1125 return 0;
1126 }
1127
1128
1129
1130 function post_updateItem($history=1) {
1131 global $CFG_GLPI;
1132
1133 $donotif = count($this->updates);
1134
1135 if (isset($this->input['_forcenotif'])) {
1136 $donotif = true;
1137 }
1138
1139
1140 if (!empty($this->input['itemtype']) && !empty($this->input['items_id'])) {
1141 $item_ticket = new Item_Ticket();
1142 $item_ticket->add(array('items_id' => $this->input['items_id'],
1143 'itemtype' => $this->input['itemtype'],
1144 'tickets_id' => $this->fields['id']));
1145 }
1146
1147
1148 // Manage SLA Level : add actions
1149 if (in_array("slas_id", $this->updates)
1150 && ($this->fields["slas_id"] > 0)) {
1151
1152 // Add First Level
1153 $calendars_id = Entity::getUsedConfig('calendars_id', $this->fields['entities_id']);
1154
1155 $sla = new SLA();
1156 if ($sla->getFromDB($this->fields["slas_id"])) {
1157 $sla->setTicketCalendar($calendars_id);
1158 // Add first level in working table
1159 if ($this->fields["slalevels_id"] > 0) {
1160 $sla->addLevelToDo($this);
1161 }
1162 }
1163
1164 SlaLevel_Ticket::replayForTicket($this->getID());
1165 }
1166 $this->updates[] = "actiontime";
1167
1168 if (count($this->updates)) {
1169 // Update Ticket Tco
1170 if (in_array("actiontime", $this->updates)
1171 || in_array("cost_time", $this->updates)
1172 || in_array("cost_fixed", $this->updates)
1173 || in_array("cost_material", $this->updates)) {
1174
1175
1176 $item_ticket = new Item_Ticket();
1177 $linked_items = $item_ticket->find("`tickets_id` = ".$this->fields['id']);
1178
1179 if (!empty($linked_items)) {
1180 foreach($linked_items as $data){
1181 if ($data["itemtype"]
1182 && ($item = getItemForItemtype($data["itemtype"]))) {
1183
1184 if ($item->getFromDB($data["items_id"])) {
1185 $newinput = array();
1186 $newinput['id'] = $data["items_id"];
1187 $newinput['ticket_tco'] = self::computeTco($item);
1188 $item->update($newinput);
1189 }
1190 }
1191 }
1192 }
1193
1194 }
1195
1196 // Setting a solution type means the ticket is solved
1197 if ((in_array("solutiontypes_id", $this->updates)
1198 || in_array("solution", $this->updates))
1199 && (in_array($this->input["status"], $this->getSolvedStatusArray())
1200 || in_array($this->input["status"], $this->getClosedStatusArray()))) { // auto close case
1201 Ticket_Ticket::manageLinkedTicketsOnSolved($this->fields['id']);
1202 }
1203
1204 // Clean content to mail
1205 //$this->fields["content"] = stripslashes($this->fields["content"]);
1206 $donotif = true;
1207
1208 }
1209
1210 if (isset($this->input['_disablenotif'])) {
1211 $donotif = false;
1212 }
1213
1214 if ($donotif && $CFG_GLPI["use_mailing"]) {
1215 $mailtype = "update";
1216
1217 if (isset($this->input["status"])
1218 && $this->input["status"]
1219 && in_array("status", $this->updates)
1220 && in_array($this->input["status"], $this->getSolvedStatusArray())) {
1221
1222 $mailtype = "solved";
1223 }
1224
1225 if (isset($this->input["status"])
1226 && $this->input["status"]
1227 && in_array("status",$this->updates)
1228 && in_array($this->input["status"], $this->getClosedStatusArray())) {
1229
1230 $mailtype = "closed";
1231 }
1232 // to know if a solution is approved or not
1233 if ((isset($this->input['solvedate']) && ($this->input['solvedate'] == 'NULL')
1234 && isset($this->oldvalues['solvedate']) && $this->oldvalues['solvedate'])
1235 && (isset($this->input['status'])
1236 && ($this->input['status'] != $this->oldvalues['status'])
1237 && ($this->oldvalues['status'] == self::SOLVED))) {
1238
1239 $mailtype = "rejectsolution";
1240 }
1241
1242 // Read again ticket to be sure that all data are up to date
1243 $this->getFromDB($this->fields['id']);
1244 NotificationEvent::raiseEvent($mailtype, $this);
1245 }
1246
1247 // inquest created immediatly if delay = O
1248 $inquest = new TicketSatisfaction();
1249 $rate = Entity::getUsedConfig('inquest_config', $this->fields['entities_id'],
1250 'inquest_rate');
1251 $delay = Entity::getUsedConfig('inquest_config', $this->fields['entities_id'],
1252 'inquest_delay');
1253 $duration = Entity::getUsedConfig('inquest_duration', $this->fields['entities_id'],
1254 'inquest_duration');
1255 $type = Entity::getUsedConfig('inquest_config', $this->fields['entities_id']);
1256 $max_closedate = $this->fields['closedate'];
1257
1258 if (in_array("status",$this->updates)
1259 && in_array($this->input["status"], $this->getClosedStatusArray())
1260 && ($delay == 0)
1261 && ($rate > 0)
1262 && (mt_rand(1,100) <= $rate)) {
1263 $inquest->add(array('tickets_id' => $this->fields['id'],
1264 'date_begin'=> $_SESSION["glpi_currenttime"],
1265 'entities_id' => $this->fields['entities_id'],
1266 'type' => $type,
1267 'max_closedate' => $max_closedate));
1268 }
1269 }
1270
1271
1272 function prepareInputForAdd($input) {
1273 global $CFG_GLPI;
1274
1275 // save value before clean;
1276 $title = ltrim($input['name']);
1277 // Standard clean datas
1278 $input = parent::prepareInputForAdd($input);
1279
1280 // Do not check mandatory on auto import (mailgates)
1281 if (!isset($input['_auto_import'])) {
1282 if (isset($input['_tickettemplates_id']) && $input['_tickettemplates_id']) {
1283 $tt = new TicketTemplate();
1284 if ($tt->getFromDBWithDatas($input['_tickettemplates_id'])) {
1285 if (count($tt->mandatory)) {
1286 $mandatory_missing = array();
1287 $fieldsname = $tt->getAllowedFieldsNames(true);
1288 foreach ($tt->mandatory as $key => $val) {
1289 // for title if mandatory (restore initial value)
1290 if ($key == 'name') {
1291 $input['name'] = $title;
1292 }
1293 // Check only defined values : Not defined not in form
1294 if (isset($input[$key])) {
1295 // If content is also predefined need to be different from predefined value
1296 if (($key == 'content')
1297 && isset($tt->predefined['content'])) {
1298 // Clean new lines to be fix encoding
1299 if (strcmp(preg_replace("/\r?\n/", "",
1300 Html::cleanPostForTextArea($input[$key])),
1301 preg_replace("/\r?\n/", "",
1302 $tt->predefined['content'])) == 0) {
1303 $mandatory_missing[$key] = $fieldsname[$val];
1304 }
1305 }
1306
1307 if (empty($input[$key])
1308 || $input[$key] == 'NULL'
1309 || (is_array($input[$key])
1310 && $input[$key] === array(0 => "0"))) {
1311 $mandatory_missing[$key] = $fieldsname[$val];
1312 }
1313 }
1314
1315 if (($key == '_add_validation')
1316 && !empty($input['users_id_validate'])
1317 && isset($input['users_id_validate'][0])
1318 && ($input['users_id_validate'][0] > 0)) {
1319
1320 unset($mandatory_missing['_add_validation']);
1321 }
1322
1323 // For due_date : check also slas_id
1324 if (($key == 'due_date')
1325 && isset($input['slas_id']) && ($input['slas_id'] > 0)
1326 && isset($mandatory_missing['due_date'])) {
1327 unset($mandatory_missing['due_date']);
1328 }
1329
1330 // For document mandatory
1331 if (($key == '_documents_id')
1332 && !isset($input['_filename'])
1333 && !isset($input['_tag_filename'])
1334 && !isset($input['_stock_image'])
1335 && !isset($input['_tag_stock_image'])) {
1336
1337 $mandatory_missing[$key] = $fieldsname[$val];
1338 }
1339 }
1340 if (count($mandatory_missing)) {
1341 //TRANS: %s are the fields concerned
1342 $message = sprintf(__('Mandatory fields are not filled. Please correct: %s'),
1343 implode(", ",$mandatory_missing));
1344 Session::addMessageAfterRedirect($message, false, ERROR);
1345 return false;
1346 }
1347 }
1348 }
1349 }
1350 }
1351
1352 if (!isset($input["requesttypes_id"])) {
1353 $input["requesttypes_id"] = RequestType::getDefault('helpdesk');
1354 }
1355
1356 if (!isset($input['global_validation'])) {
1357 $input['global_validation'] = CommonITILValidation::NONE;
1358 }
1359
1360 // Set additional default dropdown
1361 $dropdown_fields = array('items_id', 'users_locations', 'items_locations');
1362 foreach ($dropdown_fields as $field ) {
1363 if (!isset($input[$field])) {
1364 $input[$field] = 0;
1365 }
1366 }
1367 if (!isset($input['itemtype']) || !($input['items_id'] > 0)) {
1368 $input['itemtype'] = '';
1369 }
1370
1371
1372 // Get item
1373 $item = NULL;
1374 if (($input["items_id"] > 0) && !empty($input["itemtype"])) {
1375 if ($item = getItemForItemtype($input["itemtype"])) {
1376 $item->getFromDB($input["items_id"]);
1377
1378 $input['items_locations'] = $item->fields['locations_id'];
1379 }
1380 }
1381
1382
1383 // Business Rules do not override manual SLA
1384 $manual_slas_id = 0;
1385 if (isset($input['slas_id']) && ($input['slas_id'] > 0)) {
1386 $manual_slas_id = $input['slas_id'];
1387 }
1388
1389 // Process Business Rules
1390 $rules = new RuleTicketCollection($input['entities_id']);
1391
1392 // Set unset variables with are needed
1393 $user = new User();
1394 if (isset($input["_users_id_requester"])
1395 && $user->getFromDB($input["_users_id_requester"])) {
1396 $input['users_locations'] = $user->fields['locations_id'];
1397 $tmprequester = $input["_users_id_requester"];
1398 } else {
1399 $tmprequester = 0;
1400 }
1401
1402 // Clean new lines before passing to rules
1403 if (isset($input["content"])) {
1404 $input["content"] = preg_replace('/\\\\r\\\\n/',"\n",$input['content']);
1405 $input["content"] = preg_replace('/\\\\n/',"\n",$input['content']);
1406 if (!$CFG_GLPI['use_rich_text']) {
1407 $input["content"] = Html::entity_decode_deep($input["content"]);
1408 $input["content"] = Html::entity_decode_deep($input["content"]);
1409 $input["content"] = Html::clean($input["content"]);
1410 }
1411 }
1412
1413 $input = $rules->processAllRules(Toolbox::stripslashes_deep($input),
1414 Toolbox::stripslashes_deep($input),
1415 array('recursive' => true),
1416 array('condition' => RuleTicket::ONADD));
1417
1418 // Recompute default values based on values computed by rules
1419 $input = $this->computeDefaultValuesForAdd($input);
1420
1421 if (isset($input['_users_id_requester'])
1422 && ($input['_users_id_requester'] != $tmprequester)) {
1423 // if requester set by rule, clear address from mailcollector
1424 unset($input['_users_id_requester_notif']);
1425 }
1426
1427 // Restore slas_id
1428 if ($manual_slas_id > 0) {
1429 $input['slas_id'] = $manual_slas_id;
1430 }
1431
1432 // Manage auto assign
1433 $auto_assign_mode = Entity::getUsedConfig('auto_assign_mode', $input['entities_id']);
1434
1435 switch ($auto_assign_mode) {
1436 case Entity::CONFIG_NEVER :
1437 break;
1438
1439 case Entity::AUTO_ASSIGN_HARDWARE_CATEGORY :
1440 if ($item != NULL) {
1441 // Auto assign tech from item
1442 if ((!isset($input['_users_id_assign']) || ($input['_users_id_assign'] == 0))
1443 && $item->isField('users_id_tech')) {
1444 $input['_users_id_assign'] = $item->getField('users_id_tech');
1445 }
1446 // Auto assign group from item
1447 if ((!isset($input['_groups_id_assign']) || ($input['_groups_id_assign'] == 0))
1448 && $item->isField('groups_id_tech')) {
1449 $input['_groups_id_assign'] = $item->getField('groups_id_tech');
1450 }
1451 }
1452 // Auto assign tech/group from Category
1453 if (($input['itilcategories_id'] > 0)
1454 && ((!isset($input['_users_id_assign']) || !$input['_users_id_assign'])
1455 || (!isset($input['_groups_id_assign']) || !$input['_groups_id_assign']))) {
1456
1457 $cat = new ITILCategory();
1458 $cat->getFromDB($input['itilcategories_id']);
1459 if ((!isset($input['_users_id_assign']) || !$input['_users_id_assign'])
1460 && $cat->isField('users_id')) {
1461 $input['_users_id_assign'] = $cat->getField('users_id');
1462 }
1463 if ((!isset($input['_groups_id_assign']) || !$input['_groups_id_assign'])
1464 && $cat->isField('groups_id')) {
1465 $input['_groups_id_assign'] = $cat->getField('groups_id');
1466 }
1467 }
1468 break;
1469
1470 case Entity::AUTO_ASSIGN_CATEGORY_HARDWARE :
1471 // Auto assign tech/group from Category
1472 if (($input['itilcategories_id'] > 0)
1473 && ((!isset($input['_users_id_assign']) || !$input['_users_id_assign'])
1474 || (!isset($input['_groups_id_assign']) || !$input['_groups_id_assign']))) {
1475
1476 $cat = new ITILCategory();
1477 $cat->getFromDB($input['itilcategories_id']);
1478 if ((!isset($input['_users_id_assign']) || !$input['_users_id_assign'])
1479 && $cat->isField('users_id')) {
1480 $input['_users_id_assign'] = $cat->getField('users_id');
1481 }
1482 if ((!isset($input['_groups_id_assign']) || !$input['_groups_id_assign'])
1483 && $cat->isField('groups_id')) {
1484 $input['_groups_id_assign'] = $cat->getField('groups_id');
1485 }
1486 }
1487 if ($item != NULL) {
1488 // Auto assign tech from item
1489 if ((!isset($input['_users_id_assign']) || ($input['_users_id_assign'] == 0))
1490 && $item->isField('users_id_tech')) {
1491 $input['_users_id_assign'] = $item->getField('users_id_tech');
1492 }
1493 // Auto assign group from item
1494 if ((!isset($input['_groups_id_assign']) || ($input['_groups_id_assign'] == 0))
1495 && $item->isField('groups_id_tech')) {
1496 $input['_groups_id_assign'] = $item->getField('groups_id_tech');
1497 }
1498 }
1499 break;
1500 }
1501
1502 // Replay setting auto assign if set in rules engine or by auto_assign_mode
1503 if (((isset($input["_users_id_assign"]) && ($input["_users_id_assign"] > 0))
1504 || (isset($input["_groups_id_assign"]) && ($input["_groups_id_assign"] > 0))
1505 || (isset($input["_suppliers_id_assign"]) && ($input["_suppliers_id_assign"] > 0)))
1506 && (in_array($input['status'], $this->getNewStatusArray()))) {
1507
1508 $input["status"] = self::ASSIGNED;
1509 }
1510
1511
1512 //// Manage SLA assignment
1513 // Manual SLA defined : reset due date
1514 // No manual SLA and due date defined : reset auto SLA
1515 if (($manual_slas_id == 0)
1516 && isset($input["due_date"]) && ($input['due_date'] != 'NULL')) {
1517 // Valid due date
1518 if ($input['due_date'] >= $input['date']) {
1519 if (isset($input["slas_id"])) {
1520 unset($input["slas_id"]);
1521 }
1522 } else {
1523 // Unset due date
1524 unset($input["due_date"]);
1525 }
1526 }
1527
1528 if (isset($input["slas_id"]) && ($input["slas_id"] > 0)) {
1529 // Get datas to initialize SLA and set it
1530 $sla_data = $this->getDatasToAddSLA($input["slas_id"], $input['entities_id'],
1531 $input['date']
1532 );
1533 if (count($sla_data)) {
1534 foreach ($sla_data as $key => $val) {
1535 $input[$key] = $val;
1536 }
1537 }
1538 }
1539
1540 // auto set type if not set
1541 if (!isset($input["type"])) {
1542 $input['type'] = Entity::getUsedConfig('tickettype', $input['entities_id'], '',
1543 Ticket::INCIDENT_TYPE);
1544 }
1545
1546 return $input;
1547 }
1548
1549
1550 function post_addItem() {
1551 global $CFG_GLPI;
1552
1553 if (isset($this->input['content'])) {
1554 if (isset($this->input['_stock_image'])) {
1555 $this->addImagePaste();
1556 }
1557 }
1558
1559 // Log this event
1560 $username = 'anonymous';
1561 if (isset($_SESSION["glpiname"])) {
1562 $username = $_SESSION["glpiname"];
1563 }
1564 Event::log($this->fields['id'], "ticket", 4, "tracking",
1565 sprintf(__('%1$s adds the item %2$s'), $username,
1566 $this->fields['id']));
1567
1568 if (isset($this->input["_followup"])
1569 && is_array($this->input["_followup"])
1570 && (strlen($this->input["_followup"]['content']) > 0)) {
1571
1572 $fup = new TicketFollowup();
1573 $type = "new";
1574 if (isset($this->fields["status"]) && ($this->fields["status"] == self::SOLVED)) {
1575 $type = "solved";
1576 }
1577 $toadd = array("type" => $type,
1578 "tickets_id" => $this->fields['id']);
1579
1580 if (isset($this->input["_followup"]['content'])
1581 && (strlen($this->input["_followup"]['content']) > 0)) {
1582 $toadd["content"] = $this->input["_followup"]['content'];
1583 }
1584
1585 if (isset($this->input["_followup"]['is_private'])) {
1586 $toadd["is_private"] = $this->input["_followup"]['is_private'];
1587 }
1588// $toadd['_no_notif'] = true;
1589
1590 $fup->add($toadd);
1591 }
1592
1593 if ((isset($this->input["plan"]) && count($this->input["plan"]))
1594 || (isset($this->input["actiontime"]) && ($this->input["actiontime"] > 0))) {
1595
1596 $task = new TicketTask();
1597 $type = "new";
1598 if (isset($this->fields["status"]) && ($this->fields["status"] == self::SOLVED)) {
1599 $type = "solved";
1600 }
1601 $toadd = array("type" => $type,
1602 "tickets_id" => $this->fields['id'],
1603 "actiontime" => $this->input["actiontime"]);
1604
1605 if (isset($this->input["plan"]) && count($this->input["plan"])) {
1606 $toadd["plan"] = $this->input["plan"];
1607 }
1608
1609 if (isset($_SESSION['glpitask_private'])) {
1610 $toadd['is_private'] = $_SESSION['glpitask_private'];
1611 }
1612
1613// $toadd['_no_notif'] = true;
1614
1615 $task->add($toadd);
1616 }
1617
1618 $ticket_ticket = new Ticket_Ticket();
1619
1620 // From interface
1621 if (isset($this->input['_link'])) {
1622 $this->input['_link']['tickets_id_1'] = $this->fields['id'];
1623 // message if ticket's ID doesn't exist
1624 if (!empty($this->input['_link']['tickets_id_2'])) {
1625 if ($ticket_ticket->can(-1, CREATE, $this->input['_link'])) {
1626 $ticket_ticket->add($this->input['_link']);
1627 } else {
1628 Session::addMessageAfterRedirect(__('Unknown ticket'), false, ERROR);
1629 }
1630 }
1631 }
1632
1633 // From mailcollector : do not check rights
1634 if (isset($this->input["_linkedto"])) {
1635 $input2['tickets_id_1'] = $this->fields['id'];
1636 $input2['tickets_id_2'] = $this->input["_linkedto"];
1637 $input2['link'] = Ticket_Ticket::LINK_TO;
1638 $ticket_ticket->add($input2);
1639 }
1640
1641 // Manage SLA Level : add actions
1642 if (isset($this->input["slas_id"]) && ($this->input["slas_id"] > 0)
1643 && isset($this->input["slalevels_id"]) && ($this->input["slalevels_id"] > 0)) {
1644
1645 $calendars_id = Entity::getUsedConfig('calendars_id', $this->fields['entities_id']);
1646
1647 $sla = new SLA();
1648 if ($sla->getFromDB($this->input["slas_id"])) {
1649 $sla->setTicketCalendar($calendars_id);
1650 // Add first level in working table
1651 if ($this->input["slalevels_id"] > 0) {
1652 $sla->addLevelToDo($this);
1653 }
1654 // Replay action in case of open date is set before now
1655 }
1656 SlaLevel_Ticket::replayForTicket($this->getID());
1657 }
1658
1659 // Add project task link if needed
1660 if (isset($this->input['_projecttasks_id'])) {
1661 $projecttask = new ProjectTask();
1662 if ($projecttask->getFromDB($this->input['_projecttasks_id'])) {
1663 $pt = new ProjectTask_Ticket();
1664 $pt->add(array('projecttasks_id' => $this->input['_projecttasks_id'],
1665 'tickets_id' => $this->fields['id'],
1666 /*'_no_notif' => true*/));
1667 }
1668 }
1669
1670
1671 if (!empty($this->input['itemtype']) && !empty($this->input['items_id'])) {
1672 $item_ticket = new Item_Ticket();
1673 $item_ticket->add(array('items_id' => $this->input['items_id'],
1674 'itemtype' => $this->input['itemtype'],
1675 'tickets_id' => $this->fields['id'],
1676 '_disablenotif' => true));
1677 }
1678
1679
1680 parent::post_addItem();
1681
1682 $this->manageValidationAdd($this->input);
1683
1684 // Processing Email
1685 if ($CFG_GLPI["use_mailing"]) {
1686 // Clean reload of the ticket
1687 $this->getFromDB($this->fields['id']);
1688
1689 $type = "new";
1690 if (isset($this->fields["status"]) && ($this->fields["status"] == self::SOLVED)) {
1691 $type = "solved";
1692 }
1693 NotificationEvent::raiseEvent($type, $this);
1694 }
1695
1696 if (isset($_SESSION['glpiis_ids_visible']) && !$_SESSION['glpiis_ids_visible']) {
1697 Session::addMessageAfterRedirect(sprintf(__('%1$s (%2$s)'),
1698 __('Your ticket has been registered, its treatment is in progress.'),
1699 sprintf(__('%1$s: %2$s'), __('Ticket'),
1700 "<a href='".$CFG_GLPI["root_doc"].
1701 "/front/ticket.form.php?id=".
1702 $this->fields['id']."'>".
1703 $this->fields['id']."</a>")));
1704 }
1705
1706 }
1707
1708
1709 /**
1710 * Manage Validation add from input
1711 *
1712 * @since version 0.85
1713 *
1714 * @param $input array : input array
1715 *
1716 * @return nothing
1717 **/
1718 function manageValidationAdd($input) {
1719
1720 //Action for send_validation rule
1721 if (isset($input["_add_validation"])) {
1722 if (isset($input['entities_id'])) {
1723 $entid = $input['entities_id'];
1724 } else if (isset($this->fields['entities_id'])){
1725 $entid = $this->fields['entities_id'];
1726 } else {
1727 return false;
1728 }
1729
1730 $validations_to_send = array();
1731 if (!is_array($input["_add_validation"])) {
1732 $input["_add_validation"] = array($input["_add_validation"]);
1733 }
1734
1735 foreach ($input["_add_validation"] as $key => $validation) {
1736 switch ($validation) {
1737 case 'requester_supervisor' :
1738 if (isset($input['_groups_id_requester'])
1739 && $input['_groups_id_requester']) {
1740 $users = Group_User::getGroupUsers($input['_groups_id_requester'],
1741 "is_manager='1'");
1742 foreach ($users as $data) {
1743 $validations_to_send[] = $data['id'];
1744 }
1745 }
1746 // Add to already set groups
1747 foreach ($this->getGroups(CommonITILActor::REQUESTER) as $d) {
1748 $users = Group_User::getGroupUsers($d['groups_id'], "is_manager='1'");
1749 foreach ($users as $data) {
1750 $validations_to_send[] = $data['id'];
1751 }
1752 }
1753 break;
1754
1755 case 'assign_supervisor' :
1756 if (isset($input['_groups_id_assign'])
1757 && $input['_groups_id_assign']) {
1758 $users = Group_User::getGroupUsers($input['_groups_id_assign'],
1759 "is_manager='1'");
1760 foreach ($users as $data) {
1761 $validations_to_send[] = $data['id'];
1762 }
1763 }
1764 foreach ($this->getGroups(CommonITILActor::ASSIGN) as $d) {
1765 $users = Group_User::getGroupUsers($d['groups_id'], "is_manager='1'");
1766 foreach ($users as $data) {
1767 $validations_to_send[] = $data['id'];
1768 }
1769 }
1770 break;
1771
1772 default :
1773 // Group case from rules
1774 if ($key === 'group') {
1775 foreach ($validation as $groups_id) {
1776 $validation_right = 'validate_incident';
1777 if (isset($input['type'])
1778 && ($input['type'] == Ticket::DEMAND_TYPE)) {
1779 $validation_right = 'validate_request';
1780 }
1781 $opt = array('groups_id' => $groups_id,
1782 'right' => $validation_right,
1783 'entity' => $entid);
1784
1785 $data_users = TicketValidation::getGroupUserHaveRights($opt);
1786
1787 foreach ($data_users as $user) {
1788 $validations_to_send[] = $user['id'];
1789 }
1790 }
1791 } else {
1792 $validations_to_send[] = $validation;
1793 }
1794 }
1795
1796 }
1797
1798 // Validation user added on ticket form
1799 if (isset($input['users_id_validate'])) {
1800 if (array_key_exists('groups_id', $input['users_id_validate'])) {
1801 foreach ($input['users_id_validate'] as $key => $validation_to_add){
1802 if (is_numeric($key)) {
1803 $validations_to_send[] = $validation_to_add;
1804 }
1805 }
1806 } else {
1807 foreach ($input['users_id_validate'] as $key => $validation_to_add) {
1808 if (is_numeric($key)) {
1809 $validations_to_send[] = $validation_to_add;
1810 }
1811 }
1812 }
1813 }
1814
1815 // Keep only one
1816 $validations_to_send = array_unique($validations_to_send);
1817
1818 $validation = new TicketValidation();
1819
1820 if (count($validations_to_send)) {
1821 $values = array();
1822 $values['tickets_id'] = $this->fields['id'];
1823 if ($input['id'] != $this->fields['id']) {
1824 $values['_ticket_add'] = true;
1825 }
1826
1827 // to know update by rules
1828 if (isset($input["_rule_process"])) {
1829 $values['_rule_process'] = $input["_rule_process"];
1830 }
1831 // if auto_import, tranfert it for validation
1832 if (isset($input['_auto_import'])) {
1833 $values['_auto_import'] = $input['_auto_import'];
1834 }
1835
1836 // Cron or rule process of hability to do
1837 if (Session::isCron()
1838 || isset($input["_auto_import"])
1839 || isset($input["_rule_process"])
1840 || $validation->can(-1, CREATE, $values)) { // cron or allowed user
1841
1842 $add_done = false;
1843 foreach ($validations_to_send as $user) {
1844 // Do not auto add twice same validation
1845 if (!TicketValidation::alreadyExists($values['tickets_id'], $user)) {
1846 $values["users_id_validate"] = $user;
1847 if ($validation->add($values)) {
1848 $add_done = true;
1849 }
1850 }
1851 }
1852 if ($add_done) {
1853 Event::log($this->fields['id'], "ticket", 4, "tracking",
1854 sprintf(__('%1$s updates the item %2$s'), $_SESSION["glpiname"],
1855 $this->fields['id']));
1856 }
1857 }
1858 }
1859 }
1860 return true;
1861 }
1862
1863
1864 /**
1865 * Number of followups of the ticket
1866 *
1867 * @param $with_private boolean : true : all followups / false : only public ones (default 1)
1868 *
1869 * @return followup count
1870 **/
1871 function numberOfFollowups($with_private=1) {
1872 global $DB;
1873
1874 $RESTRICT = "";
1875 if ($with_private!=1) {
1876 $RESTRICT = " AND `is_private` = '0'";
1877 }
1878
1879 // Set number of followups
1880 $query = "SELECT COUNT(*)
1881 FROM `glpi_ticketfollowups`
1882 WHERE `tickets_id` = '".$this->fields["id"]."'
1883 $RESTRICT";
1884 $result = $DB->query($query);
1885
1886 return $DB->result($result, 0, 0);
1887 }
1888
1889
1890 /**
1891 * Number of tasks of the ticket
1892 *
1893 * @param $with_private boolean : true : all ticket / false : only public ones (default 1)
1894 *
1895 * @return followup count
1896 **/
1897 function numberOfTasks($with_private=1) {
1898 global $DB;
1899
1900 $RESTRICT = "";
1901 if ($with_private!=1) {
1902 $RESTRICT = " AND `is_private` = '0'";
1903 }
1904
1905 // Set number of followups
1906 $query = "SELECT COUNT(*)
1907 FROM `glpi_tickettasks`
1908 WHERE `tickets_id` = '".$this->fields["id"]."'
1909 $RESTRICT";
1910 $result = $DB->query($query);
1911
1912 return $DB->result($result, 0, 0);
1913 }
1914
1915
1916 /**
1917 * Get active or solved tickets for an hardware last X days
1918 *
1919 * @since version 0.83
1920 *
1921 * @param $itemtype string Item type
1922 * @param $items_id integer ID of the Item
1923 * @param $days integer day number
1924 *
1925 * @return integer
1926 **/
1927 function getActiveOrSolvedLastDaysTicketsForItem($itemtype, $items_id, $days) {
1928 global $DB;
1929
1930 $result = array();
1931
1932 $query = "SELECT *
1933 FROM `".$this->getTable()."`
1934 LEFT JOIN `glpi_items_tickets`
1935 ON (`".$this->getTable()."`.`id` = `glpi_items_tickets`.`tickets_id`)
1936 WHERE `glpi_items_tickets`.`itemtype` = '$itemtype'
1937 AND `glpi_items_tickets`.`items_id` = '$items_id'
1938 AND (`".$this->getTable()."`.`status`
1939 NOT IN ('".implode("', '", array_merge($this->getSolvedStatusArray(),
1940 $this->getClosedStatusArray())
1941 )."')
1942 OR (`".$this->getTable()."`.`solvedate` IS NOT NULL
1943 AND ADDDATE(`".$this->getTable()."`.`solvedate`, INTERVAL $days DAY)
1944 > NOW()))";
1945
1946 foreach ($DB->request($query) as $tick) {
1947 $result[$tick['id']] = $tick['name'];
1948 }
1949
1950 return $result;
1951 }
1952
1953
1954 /**
1955 * Count active tickets for an hardware
1956 *
1957 * @since version 0.83
1958 *
1959 * @param $itemtype string Item type
1960 * @param $items_id integer ID of the Item
1961 *
1962 * @return integer
1963 **/
1964 function countActiveTicketsForItem($itemtype, $items_id) {
1965 global $DB;
1966
1967 $query = "SELECT COUNT(*) AS cpt
1968 FROM `".$this->getTable()."`
1969 LEFT JOIN `glpi_items_tickets`
1970 ON (`".$this->getTable()."`.`id` = `glpi_items_tickets`.`tickets_id`)
1971 WHERE `glpi_items_tickets`.`itemtype` = '$itemtype'
1972 AND `glpi_items_tickets`.`items_id` = '$items_id'
1973 AND `".$this->getTable()."`.`status`
1974 NOT IN ('".implode("', '",
1975 array_merge($this->getSolvedStatusArray(),
1976 $this->getClosedStatusArray())
1977 )."')";
1978
1979
1980 $result = $DB->query($query);
1981 $ligne = $DB->fetch_assoc($result);
1982
1983 return $ligne['cpt'];
1984 }
1985
1986
1987 /**
1988 * Count solved tickets for an hardware last X days
1989 *
1990 * @since version 0.83
1991 *
1992 * @param $itemtype string Item type
1993 * @param $items_id integer ID of the Item
1994 * @param $days integer day number
1995 *
1996 * @return integer
1997 **/
1998 function countSolvedTicketsForItemLastDays($itemtype, $items_id, $days) {
1999 global $DB;
2000
2001 $query = "SELECT COUNT(*) AS cpt
2002 FROM `".$this->getTable()."`
2003 LEFT JOIN `glpi_items_tickets`
2004 ON (`".$this->getTable()."`.`id` = `glpi_items_tickets`.`tickets_id`)
2005 WHERE `glpi_items_tickets`.`itemtype` = '$itemtype'
2006 AND `glpi_items_tickets`.`items_id` = '$items_id'
2007 AND `".$this->getTable()."`.`solvedate` IS NOT NULL
2008 AND ADDDATE(`".$this->getTable()."`.`solvedate`,
2009 INTERVAL $days DAY) > NOW()
2010 AND `".$this->getTable()."`.`status`
2011 IN ('".implode("', '",
2012 array_merge($this->getSolvedStatusArray(),
2013 $this->getClosedStatusArray())
2014 )."')";
2015
2016
2017 $result = $DB->query($query);
2018 $ligne = $DB->fetch_assoc($result);
2019
2020 return $ligne['cpt'];
2021 }
2022
2023
2024 /**
2025 * Update date mod of the ticket
2026 *
2027 * @since version 0.83.3 new proto
2028 *
2029 * @param $ID ID of the ticket
2030 * @param $no_stat_computation boolean do not cumpute take into account stat (false by default)
2031 * @param $users_id_lastupdater integer to force last_update id (default 0 = not used)
2032 **/
2033 function updateDateMod($ID, $no_stat_computation=false, $users_id_lastupdater=0) {
2034 global $DB;
2035
2036 if ($this->getFromDB($ID)) {
2037 if (!$no_stat_computation
2038 && (Session::haveRight('task', TicketTask::ADDALLTICKET)
2039 || Session::haveRightsOr('followup',
2040 array(TicketFollowup::ADDALLTICKET,
2041 TicketFollowup::ADDMYTICKET,
2042 TicketFollowup::ADDGROUPTICKET))
2043 || $this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID())
2044 || (isset($_SESSION["glpigroups"])
2045 && $this->haveAGroup(CommonITILActor::ASSIGN, $_SESSION['glpigroups']))
2046 || isCommandLine())) {
2047
2048 if ($this->fields['takeintoaccount_delay_stat'] == 0) {
2049 return $this->update(array('id' => $ID,
2050 'takeintoaccount_delay_stat'
2051 => $this->computeTakeIntoAccountDelayStat(),
2052 '_disablenotif' => true));
2053 }
2054
2055 }
2056 parent::updateDateMod($ID, $no_stat_computation, $users_id_lastupdater);
2057 }
2058 }
2059
2060
2061 /**
2062 * Overloaded from commonDBTM
2063 *
2064 * @since version 0.83
2065 *
2066 * @param $type itemtype of object to add
2067 *
2068 * @return rights
2069 **/
2070 function canAddItem($type) {
2071
2072 if (($type == 'Document')
2073 && ($this->getField('status') == self::CLOSED)) {
2074 return false;
2075 }
2076 return parent::canAddItem($type);
2077 }
2078
2079
2080 /**
2081 * Is the current user have right to add followups to the current ticket ?
2082 *
2083 * @return boolean
2084 **/
2085 function canAddFollowups() {
2086
2087 return ((Session::haveRight("followup", TicketFollowup::ADDMYTICKET)
2088 && ($this->isUser(CommonITILActor::REQUESTER, Session::getLoginUserID())
2089 || ($this->fields["users_id_recipient"] === Session::getLoginUserID())))
2090 || Session::haveRight('followup', TicketFollowup::ADDALLTICKET)
2091 || (Session::haveRight('followup', TicketFollowup::ADDGROUPTICKET)
2092 && isset($_SESSION["glpigroups"])
2093 && $this->haveAGroup(CommonITILActor::REQUESTER, $_SESSION['glpigroups']))
2094 || $this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID())
2095 || (isset($_SESSION["glpigroups"])
2096 && $this->haveAGroup(CommonITILActor::ASSIGN, $_SESSION['glpigroups'])));
2097 }
2098
2099
2100 /**
2101 * Get default values to search engine to override
2102 **/
2103 static function getDefaultSearchRequest() {
2104
2105 $search = array('criteria' => array(0 => array('field' => 12,
2106 'searchtype' => 'equals',
2107 'value' => 'notclosed')),
2108 'sort' => 19,
2109 'order' => 'DESC');
2110
2111 if (Session::haveRight(self::$rightname, self::READALL)) {
2112 $search['criteria'][0]['value'] = 'notold';
2113 }
2114 return $search;
2115 }
2116
2117
2118 /**
2119 * @see CommonDBTM::getSpecificMassiveActions()
2120 **/
2121 function getSpecificMassiveActions($checkitem=NULL) {
2122
2123 $isadmin = static::canUpdate();
2124 $actions = parent::getSpecificMassiveActions($checkitem);
2125
2126 if ($_SESSION['glpiactiveprofile']['interface'] == 'central') {
2127 if (TicketFollowup::canCreate()) {
2128 $actions['TicketFollowup'.MassiveAction::CLASS_ACTION_SEPARATOR.'add_followup']
2129 = __('Add a new followup');
2130 }
2131
2132 if (TicketTask::canCreate()) {
2133 $actions[__CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'add_task'] = __('Add a new task');
2134 }
2135
2136 if (TicketValidation::canCreate()) {
2137 $actions['TicketValidation'.MassiveAction::CLASS_ACTION_SEPARATOR.'submit_validation']
2138 = __('Approval request');
2139 }
2140
2141 if (Item_Ticket::canCreate()) {
2142 $actions['Item_Ticket'.MassiveAction::CLASS_ACTION_SEPARATOR.'add_item'] = _x('button', 'Add an item');
2143 }
2144
2145 if (Item_Ticket::canDelete()) {
2146 $actions['Item_Ticket'.MassiveAction::CLASS_ACTION_SEPARATOR.'delete_item'] = _x('button', 'Remove an item');
2147 }
2148
2149 if (Session::haveRight(self::$rightname, UPDATE)) {
2150 $actions[__CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'add_actor']
2151 = __('Add an actor');
2152 $actions[__CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'enable_notif']
2153 = __('Set notifications for all actors');
2154 $actions['Ticket_Ticket'.MassiveAction::CLASS_ACTION_SEPARATOR.'add']
2155 = _x('button', 'Link tickets');
2156
2157 }
2158
2159 if (Session::haveRight(self::$rightname, UPDATE)) {
2160 MassiveAction::getAddTransferList($actions);
2161 }
2162 }
2163 return $actions;
2164 }
2165
2166
2167
2168
2169
2170 function getSearchOptions() {
2171
2172 $tab = array();
2173
2174 $tab += $this->getSearchOptionsMain();
2175
2176 $tab[14]['table'] = $this->getTable();
2177 $tab[14]['field'] = 'type';
2178 $tab[14]['name'] = __('Type');
2179 $tab[14]['searchtype'] = 'equals';
2180 $tab[14]['datatype'] = 'specific';
2181
2182 $tab[13]['table'] = 'glpi_items_tickets';
2183 $tab[13]['field'] = 'items_id';
2184 $tab[13]['name'] = _n('Associated element', 'Associated elements', Session::getPluralNumber());
2185 $tab[13]['datatype'] = 'specific';
2186 $tab[13]['comments'] = true;
2187 $tab[13]['nosort'] = true;
2188 $tab[13]['nosearch'] = true;
2189 $tab[13]['additionalfields'] = array('itemtype');
2190 $tab[13]['joinparams'] = array('jointype' => 'child');
2191 $tab[13]['forcegroupby'] = true;
2192 $tab[13]['massiveaction'] = false;
2193
2194 $tab[131]['table'] = 'glpi_items_tickets';
2195 $tab[131]['field'] = 'itemtype';
2196 $tab[131]['name'] = _n('Associated item type', 'Associated item types', Session::getPluralNumber());
2197 $tab[131]['datatype'] = 'itemtypename';
2198 $tab[131]['itemtype_list'] = 'ticket_types';
2199 $tab[131]['nosort'] = true;
2200 $tab[131]['additionalfields'] = array('itemtype');
2201 $tab[131]['joinparams'] = array('jointype' => 'child');
2202 $tab[131]['forcegroupby'] = true;
2203 $tab[131]['massiveaction'] = false;
2204
2205 $tab[9]['table'] = 'glpi_requesttypes';
2206 $tab[9]['field'] = 'name';
2207 $tab[9]['name'] = __('Request source');
2208 $tab[9]['datatype'] = 'dropdown';
2209
2210 // Can't use Location::getSearchOptionsToAdd because id conflicts
2211 $tab[83]['table'] = 'glpi_locations';
2212 $tab[83]['field'] = 'completename';
2213 $tab[83]['name'] = __('Location');
2214 $tab[83]['datatype'] = 'dropdown';
2215
2216 $tab[80]['table'] = 'glpi_entities';
2217 $tab[80]['field'] = 'completename';
2218 $tab[80]['name'] = __('Entity');
2219 $tab[80]['massiveaction'] = false;
2220 $tab[80]['datatype'] = 'dropdown';
2221
2222 // For ticket template
2223 $tab[142]['table'] = 'glpi_documents';
2224 $tab[142]['field'] = 'name';
2225 $tab[142]['name'] = _n('Document', 'Documents', Session::getPluralNumber());
2226 $tab[142]['forcegroupby'] = true;
2227 $tab[142]['usehaving'] = true;
2228 $tab[142]['nosearch'] = true;
2229 $tab[142]['nodisplay'] = true;
2230 $tab[142]['datatype'] = 'dropdown';
2231 $tab[142]['massiveaction'] = false;
2232 $tab[142]['joinparams'] = array('jointype' => 'items_id',
2233 'beforejoin' => array('table'
2234 => 'glpi_documents_items',
2235 'joinparams'
2236 => array('jointype'
2237 => 'itemtype_item')));
2238
2239 $tab += $this->getSearchOptionsActors();
2240
2241
2242 $tab['sla'] = __('SLA');
2243
2244 $tab[30]['table'] = 'glpi_slas';
2245 $tab[30]['field'] = 'name';
2246 $tab[30]['name'] = __('SLA');
2247 $tab[30]['massiveaction'] = false;
2248 $tab[30]['datatype'] = 'dropdown';
2249
2250 $tab[32]['table'] = 'glpi_slalevels';
2251 $tab[32]['field'] = 'name';
2252 $tab[32]['name'] = __('Escalation level');
2253 $tab[32]['massiveaction'] = false;
2254 $tab[32]['datatype'] = 'dropdown';
2255
2256 $tab += TicketValidation::getSearchOptionsToAdd();
2257
2258 $tab['satisfaction'] = __('Satisfaction survey');
2259
2260 $tab[31]['table'] = 'glpi_ticketsatisfactions';
2261 $tab[31]['field'] = 'type';
2262 $tab[31]['name'] = __('Type');
2263 $tab[31]['massiveaction'] = false;
2264 $tab[31]['searchtype'] = array('equals', 'notequals');
2265 $tab[31]['searchequalsonfield'] = true;
2266 $tab[31]['joinparams'] = array('jointype' => 'child');
2267 $tab[31]['datatype'] = 'specific';
2268
2269 $tab[60]['table'] = 'glpi_ticketsatisfactions';
2270 $tab[60]['field'] = 'date_begin';
2271 $tab[60]['name'] = __('Creation date');
2272 $tab[60]['datatype'] = 'datetime';
2273 $tab[60]['massiveaction'] = false;
2274 $tab[60]['joinparams'] = array('jointype' => 'child');
2275
2276 $tab[61]['table'] = 'glpi_ticketsatisfactions';
2277 $tab[61]['field'] = 'date_answered';
2278 $tab[61]['name'] = __('Response date');
2279 $tab[61]['datatype'] = 'datetime';
2280 $tab[61]['massiveaction'] = false;
2281 $tab[61]['joinparams'] = array('jointype' => 'child');
2282
2283 $tab[62]['table'] = 'glpi_ticketsatisfactions';
2284 $tab[62]['field'] = 'satisfaction';
2285 $tab[62]['name'] = __('Satisfaction');
2286 $tab[62]['datatype'] = 'number';
2287 $tab[62]['massiveaction'] = false;
2288 $tab[62]['joinparams'] = array('jointype' => 'child');
2289
2290 $tab[63]['table'] = 'glpi_ticketsatisfactions';
2291 $tab[63]['field'] = 'comment';
2292 $tab[63]['name'] = __('Comments');
2293 $tab[63]['datatype'] = 'text';
2294 $tab[63]['massiveaction'] = false;
2295 $tab[63]['joinparams'] = array('jointype' => 'child');
2296
2297 $tab['followup'] = _n('Followup', 'Followups', Session::getPluralNumber());
2298
2299 $followup_condition = '';
2300 if (!Session::haveRight('followup', TicketFollowup::SEEPRIVATE)) {
2301 $followup_condition = "AND (`NEWTABLE`.`is_private` = '0'
2302 OR `NEWTABLE`.`users_id` = '".Session::getLoginUserID()."')";
2303 }
2304
2305 $tab[25]['table'] = 'glpi_ticketfollowups';
2306 $tab[25]['field'] = 'content';
2307 $tab[25]['name'] = __('Description');
2308 $tab[25]['forcegroupby'] = true;
2309 $tab[25]['splititems'] = true;
2310 $tab[25]['massiveaction'] = false;
2311 $tab[25]['joinparams'] = array('jointype' => 'child',
2312 'condition' => $followup_condition);
2313 $tab[25]['datatype'] = 'text';
2314
2315
2316 $tab[36]['table'] = 'glpi_ticketfollowups';
2317 $tab[36]['field'] = 'date';
2318 $tab[36]['name'] = __('Date');
2319 $tab[36]['datatype'] = 'datetime';
2320 $tab[36]['massiveaction'] = false;
2321 $tab[36]['forcegroupby'] = true;
2322 $tab[36]['joinparams'] = array('jointype' => 'child',
2323 'condition' => $followup_condition);
2324
2325 $tab[27]['table'] = 'glpi_ticketfollowups';
2326 $tab[27]['field'] = 'id';
2327 $tab[27]['name'] = _x('quantity', 'Number of followups');
2328 $tab[27]['forcegroupby'] = true;
2329 $tab[27]['usehaving'] = true;
2330 $tab[27]['datatype'] = 'count';
2331 $tab[27]['massiveaction'] = false;
2332 $tab[27]['joinparams'] = array('jointype' => 'child',
2333 'condition' => $followup_condition);
2334
2335 $tab[29]['table'] = 'glpi_requesttypes';
2336 $tab[29]['field'] = 'name';
2337 $tab[29]['name'] = __('Request source');
2338 $tab[29]['datatype'] = 'dropdown';
2339 $tab[29]['forcegroupby'] = true;
2340 $tab[29]['massiveaction'] = false;
2341 $tab[29]['joinparams'] = array('beforejoin'
2342 => array('table'
2343 => 'glpi_ticketfollowups',
2344 'joinparams'
2345 => array('jointype' => 'child',
2346 'condition' => $followup_condition)));
2347
2348 $tab[91]['table'] = 'glpi_ticketfollowups';
2349 $tab[91]['field'] = 'is_private';
2350 $tab[91]['name'] = __('Private followup');
2351 $tab[91]['datatype'] = 'bool';
2352 $tab[91]['forcegroupby'] = true;
2353 $tab[91]['splititems'] = true;
2354 $tab[91]['massiveaction'] = false;
2355 $tab[91]['joinparams'] = array('jointype' => 'child',
2356 'condition' => $followup_condition);
2357
2358 $tab[93]['table'] = 'glpi_users';
2359 $tab[93]['field'] = 'name';
2360 $tab[93]['name'] = __('Writer');
2361 $tab[93]['datatype'] = 'itemlink';
2362 $tab[93]['right'] = 'all';
2363 $tab[93]['forcegroupby'] = true;
2364 $tab[93]['massiveaction'] = false;
2365 $tab[93]['joinparams'] = array('beforejoin'
2366 => array('table'
2367 => 'glpi_ticketfollowups',
2368 'joinparams'
2369 => array('jointype' => 'child',
2370 'condition' => $followup_condition)));
2371
2372
2373 $tab += $this->getSearchOptionsStats();
2374
2375
2376 $tab[150]['table'] = $this->getTable();
2377 $tab[150]['field'] = 'takeintoaccount_delay_stat';
2378 $tab[150]['name'] = __('Take into account time');
2379 $tab[150]['datatype'] = 'timestamp';
2380 $tab[150]['forcegroupby'] = true;
2381 $tab[150]['massiveaction'] = false;
2382
2383
2384 if (Session::haveRightsOr(self::$rightname,
2385 array(self::READALL, self::READASSIGN, self::OWN))) {
2386
2387 $tab['linktickets'] = _n('Linked ticket', 'Linked tickets', Session::getPluralNumber());
2388
2389 $tab[40]['table'] = 'glpi_tickets_tickets';
2390 $tab[40]['field'] = 'tickets_id_1';
2391 $tab[40]['name'] = __('All linked tickets');
2392 $tab[40]['massiveaction'] = false;
2393 $tab[40]['forcegroupby'] = true;
2394 $tab[40]['searchtype'] = 'equals';
2395 $tab[40]['joinparams'] = array('jointype' => 'item_item');
2396 $tab[40]['additionalfields'] = array('tickets_id_2');
2397
2398 $tab[47]['table'] = 'glpi_tickets_tickets';
2399 $tab[47]['field'] = 'tickets_id_1';
2400 $tab[47]['name'] = __('Duplicated tickets');
2401 $tab[47]['massiveaction'] = false;
2402 $tab[47]['searchtype'] = 'equals';
2403 $tab[47]['joinparams'] = array('jointype' => 'item_item',
2404 'condition' => "AND NEWTABLE.`link` = ".
2405 Ticket_Ticket::DUPLICATE_WITH);
2406 $tab[47]['additionalfields'] = array('tickets_id_2');
2407 $tab[47]['forcegroupby'] = true;
2408
2409 $tab[41]['table'] = 'glpi_tickets_tickets';
2410 $tab[41]['field'] = 'id';
2411 $tab[41]['name'] = __('Number of all linked tickets');
2412 $tab[41]['massiveaction'] = false;
2413 $tab[41]['datatype'] = 'count';
2414 $tab[41]['usehaving'] = true;
2415 $tab[41]['joinparams'] = array('jointype' => 'item_item');
2416
2417 $tab[46]['table'] = 'glpi_tickets_tickets';
2418 $tab[46]['field'] = 'id';
2419 $tab[46]['name'] = __('Number of duplicated tickets');
2420 $tab[46]['massiveaction'] = false;
2421 $tab[46]['datatype'] = 'count';
2422 $tab[46]['usehaving'] = true;
2423 $tab[46]['joinparams'] = array('jointype' => 'item_item',
2424 'condition' => "AND NEWTABLE.`link` = ".
2425 Ticket_Ticket::DUPLICATE_WITH);
2426
2427 $tab += TicketTask::getSearchOptionsToAdd();
2428
2429 $tab += $this->getSearchOptionsSolution();
2430
2431 if (Session::haveRight('ticketcost', READ)) {
2432 $tab += TicketCost::getSearchOptionsToAdd();
2433 }
2434
2435 $tab['problem'] = Problem::getTypeName(Session::getPluralNumber());
2436
2437 $tab[141]['table'] = 'glpi_problems_tickets';
2438 $tab[141]['field'] = 'id';
2439 $tab[141]['name'] = _x('quantity', 'Number of problems');
2440 $tab[141]['forcegroupby'] = true;
2441 $tab[141]['usehaving'] = true;
2442 $tab[141]['datatype'] = 'count';
2443 $tab[141]['massiveaction'] = false;
2444 $tab[141]['joinparams'] = array('jointype' => 'child');
2445
2446 }
2447
2448 // Filter search fields for helpdesk
2449 if (!Session::isCron() // no filter for cron
2450 && (!isset($_SESSION['glpiactiveprofile']['interface'])
2451 || ($_SESSION['glpiactiveprofile']['interface'] == 'helpdesk'))) {
2452 $tokeep = array('common', 'requester','satisfaction');
2453 if (Session::haveRightsOr('ticketvalidation',
2454 array_merge(TicketValidation::getValidateRights(),
2455 TicketValidation::getCreateRights()))) {
2456 $tokeep[] = 'validation';
2457 }
2458 $keep = false;
2459 foreach ($tab as $key => $val) {
2460 if (!is_array($val)) {
2461 $keep = in_array($key, $tokeep);
2462 }
2463 if (!$keep) {
2464 if (is_array($val)) {
2465 $tab[$key]['nosearch'] = true;
2466 }
2467 }
2468 }
2469 // last updater no search
2470 $tab[64]['nosearch'] = true;
2471 }
2472 return $tab;
2473 }
2474
2475
2476 /**
2477 * @since version 0.84
2478 *
2479 * @param $field
2480 * @param $values
2481 * @param $options array
2482 **/
2483 static function getSpecificValueToDisplay($field, $values, array $options=array()) {
2484
2485 if (!is_array($values)) {
2486 $values = array($field => $values);
2487 }
2488 switch ($field) {
2489 case 'content' :
2490 $content = Toolbox::unclean_cross_side_scripting_deep(Html::entity_decode_deep($values[$field]));
2491 $content = Html::clean($content);
2492 if (empty($content)) {
2493 $content = ' ';
2494 }
2495 return nl2br($content);
2496
2497 case 'type':
2498 return self::getTicketTypeName($values[$field]);
2499 }
2500 return parent::getSpecificValueToDisplay($field, $values, $options);
2501 }
2502
2503
2504 /**
2505 * @since version 0.84
2506 *
2507 * @param $field
2508 * @param $name (default '')
2509 * @param $values (default '')
2510 * @param $options array
2511 *
2512 * @return string
2513 **/
2514 static function getSpecificValueToSelect($field, $name='', $values='', array $options=array()) {
2515
2516 if (!is_array($values)) {
2517 $values = array($field => $values);
2518 }
2519 $options['display'] = false;
2520 switch ($field) {
2521 case 'content' :
2522 return "<textarea cols='90' rows='6' name='$name'>".$values['content']."</textarea>";
2523
2524 case 'type':
2525 $options['value'] = $values[$field];
2526 return self::dropdownType($name, $options);
2527 }
2528 return parent::getSpecificValueToSelect($field, $name, $values, $options);
2529 }
2530
2531
2532 /**
2533 * Dropdown of ticket type
2534 *
2535 * @param $name select name
2536 * @param $options array of options:
2537 * - value : integer / preselected value (default 0)
2538 * - toadd : array / array of specific values to add at the begining
2539 * - on_change : string / value to transmit to "onChange"
2540 * - display : boolean / display or get string (default true)
2541 *
2542 * @return string id of the select
2543 **/
2544 static function dropdownType($name, $options=array()) {
2545
2546 $params['value'] = 0;
2547 $params['toadd'] = array();
2548 $params['on_change'] = '';
2549 $params['display'] = true;
2550
2551 if (is_array($options) && count($options)) {
2552 foreach ($options as $key => $val) {
2553 $params[$key] = $val;
2554 }
2555 }
2556
2557 $items = array();
2558 if (count($params['toadd']) > 0) {
2559 $items = $params['toadd'];
2560 }
2561
2562 $items += self::getTypes();
2563
2564 return Dropdown::showFromArray($name, $items, $params);
2565 }
2566
2567
2568 /**
2569 * Get ticket types
2570 *
2571 * @return array of types
2572 **/
2573 static function getTypes() {
2574
2575 $options[self::INCIDENT_TYPE] = __('Incident');
2576 //$options[self::DEMAND_TYPE] = __('Request'); - OAJ - Oculta a requisição na abertura de chamado.
2577 //echo Session::getLoginUserID(); // OAJ - ID da sessão
2578 //echo "Zac "; // OAJ - Exibição de variáveis
2579 $countSatisfaction = mysqli_query("select count(S.date_answered) as contSatisfaction from glpi_ticketsatisfactions S inner join glpi_tickets T on S.tickets_id = T.id inner join glpi_users U on T.users_id_lastupdater = U.id
2580 where T.status = 6 and S.date_begin is not null and S.date_answered is null and U.id = Session::getLoginUserID()");
2581 echo mysqli_fetch_assoc($countSatisfaction, 0);
2582 echo $countSatisfaction;
2583
2584 return $options;
2585 }
2586
2587
2588 /**
2589 * Get ticket type Name
2590 *
2591 * @param $value type ID
2592 **/
2593 static function getTicketTypeName($value) {
2594
2595 switch ($value) {
2596 case self::INCIDENT_TYPE :
2597 return __('Incident');
2598
2599 case self::DEMAND_TYPE :
2600 return __('Request');
2601
2602 default :
2603 // Return $value if not defined
2604 return $value;
2605 }
2606 }
2607
2608
2609 /**
2610 * get the Ticket status list
2611 *
2612 * @param $withmetaforsearch boolean (false by default)
2613 *
2614 * @return an array
2615 **/
2616 static function getAllStatusArray($withmetaforsearch=false) {
2617
2618 // To be overridden by class
2619 $tab = array(self::INCOMING => _x('status', 'New'),
2620 self::ASSIGNED => _x('status', 'Processing (assigned)'),
2621 self::PLANNED => _x('status', 'Processing (planned)'),
2622 self::WAITING => __('Pending'),
2623 self::SOLVED => _x('status', 'Solved'),
2624 self::CLOSED => _x('status', 'Closed'));
2625
2626 if ($withmetaforsearch) {
2627 $tab['notold'] = _x('status', 'Not solved');
2628 $tab['notclosed'] = _x('status', 'Not closed');
2629 $tab['process'] = __('Processing');
2630 $tab['old'] = _x('status', 'Solved + Closed');
2631 $tab['all'] = __('All');
2632 }
2633 return $tab;
2634 }
2635
2636
2637 /**
2638 * Get the ITIL object closed status list
2639 *
2640 * @since version 0.83
2641 *
2642 * @return an array
2643 **/
2644 static function getClosedStatusArray() {
2645 return array(self::CLOSED);
2646 }
2647
2648
2649 /**
2650 * Get the ITIL object solved status list
2651 *
2652 * @since version 0.83
2653 *
2654 * @return an array
2655 **/
2656 static function getSolvedStatusArray() {
2657 return array(self::SOLVED);
2658 }
2659
2660 /**
2661 * Get the ITIL object new status list
2662 *
2663 * @since version 0.83.8
2664 *
2665 * @return an array
2666 **/
2667 static function getNewStatusArray() {
2668 return array(self::INCOMING);
2669 }
2670
2671 /**
2672 * Get the ITIL object assign or plan status list
2673 *
2674 * @since version 0.83
2675 *
2676 * @return an array
2677 **/
2678 static function getProcessStatusArray() {
2679 return array(self::ASSIGNED, self::PLANNED);
2680 }
2681
2682 /**
2683 * Get the ITIL object closed, solved or waiting status list
2684 *
2685 * @since version 0.90.1
2686 *
2687 * @return an array
2688 **/
2689 static function getReopenableStatusArray() {
2690 return array(self::CLOSED, self::SOLVED, self::WAITING);
2691 }
2692
2693 /**
2694 * Calculate Ticket TCO for an item
2695 *
2696 *@param $item CommonDBTM object of the item
2697 *
2698 *@return float
2699 **/
2700 static function computeTco(CommonDBTM $item) {
2701 global $DB;
2702
2703 $totalcost = 0;
2704
2705 $query = "SELECT `glpi_ticketcosts`.*
2706 FROM `glpi_items_tickets`, `glpi_ticketcosts`
2707 WHERE `glpi_ticketcosts`.`tickets_id` = `glpi_items_tickets`.`tickets_id`
2708 AND `glpi_items_tickets`.`itemtype` = '".get_class($item)."'
2709 AND `glpi_items_tickets`.`items_id` = '".$item->getField('id')."'
2710 AND (`glpi_ticketcosts`.`cost_time` > '0'
2711 OR `glpi_ticketcosts`.`cost_fixed` > '0'
2712 OR `glpi_ticketcosts`.`cost_material` > '0')";
2713 $result = $DB->query($query);
2714
2715 $i = 0;
2716 if ($DB->numrows($result)) {
2717 while ($data = $DB->fetch_assoc($result)) {
2718 $totalcost += TicketCost::computeTotalCost($data["actiontime"], $data["cost_time"],
2719 $data["cost_fixed"], $data["cost_material"]);
2720 }
2721 }
2722 return $totalcost;
2723 }
2724
2725
2726 /**
2727 * Print the helpdesk form
2728 *
2729 * @param $ID integer ID of the user who want to display the Helpdesk
2730 * @param $ticket_template boolean ticket template for preview : false if not used for preview
2731 * (false by default)
2732 *
2733 * @return nothing (print the helpdesk)
2734 **/
2735 function showFormHelpdesk($ID, $ticket_template=false) {
2736 global $DB, $CFG_GLPI;
2737
2738 if (!self::canCreate()) {
2739 return false;
2740 }
2741
2742 if (!$ticket_template
2743 && Session::haveRightsOr('ticketvalidation', TicketValidation::getValidateRights())) {
2744
2745 $opt = array();
2746 $opt['reset'] = 'reset';
2747 $opt['criteria'][0]['field'] = 55; // validation status
2748 $opt['criteria'][0]['searchtype'] = 'equals';
2749 $opt['criteria'][0]['value'] = CommonITILValidation::WAITING;
2750 $opt['criteria'][0]['link'] = 'AND';
2751
2752 $opt['criteria'][1]['field'] = 59; // validation aprobator
2753 $opt['criteria'][1]['searchtype'] = 'equals';
2754 $opt['criteria'][1]['value'] = Session::getLoginUserID();
2755 $opt['criteria'][1]['link'] = 'AND';
2756
2757 $url_validate = $CFG_GLPI["root_doc"]."/front/ticket.php?".Toolbox::append_params($opt,
2758 '&');
2759
2760 if (TicketValidation::getNumberToValidate(Session::getLoginUserID()) > 0) {
2761 echo "<a href='$url_validate' title=\"".__s('Ticket waiting for your approval')."\"
2762 alt=\"".__s('Ticket waiting for your approval')."\">".
2763 __('Tickets awaiting approval')."</a><br><br>";
2764 }
2765 }
2766
2767 $email = UserEmail::getDefaultForUser($ID);
2768 $default_use_notif = Entity::getUsedConfig('is_notif_enable_default', $_SESSION['glpiactive_entity'], '', 1);
2769
2770 // Set default values...
2771 $default_values = array('_users_id_requester_notif'
2772 => array('use_notification'
2773 => (($email == "")?0:$default_use_notif)),
2774 'nodelegate' => 1,
2775 '_users_id_requester' => 0,
2776 '_users_id_observer' => array(0),
2777 '_users_id_observer_notif'
2778 => array('use_notification' => $default_use_notif),
2779 'name' => '',
2780 'content' => '',
2781 'itilcategories_id' => 0,
2782 'locations_id' => 0,
2783 'urgency' => 3,
2784 'itemtype' => '',
2785 'items_id' => 0,
2786 'entities_id' => $_SESSION['glpiactive_entity'],
2787 'plan' => array(),
2788 'global_validation' => CommonITILValidation::NONE,
2789 '_add_validation' => 0,
2790 'type' => Entity::getUsedConfig('tickettype',
2791 $_SESSION['glpiactive_entity'],
2792 '', Ticket::INCIDENT_TYPE),
2793 '_right' => "id",
2794 '_filename' => array(),
2795 '_tag_filename' => array());
2796
2797 // Get default values from posted values on reload form
2798 if (!$ticket_template) {
2799 if (isset($_POST)) {
2800 $values = Html::cleanPostForTextArea($_POST);
2801 }
2802 }
2803
2804 // Restore saved value or override with page parameter
2805 $saved = $this->restoreInput();
2806 foreach ($default_values as $name => $value) {
2807 if (!isset($values[$name])) {
2808 if (isset($saved[$name])) {
2809 $values[$name] = $saved[$name];
2810 } else {
2811 $values[$name] = $value;
2812 }
2813 }
2814 }
2815
2816 // Check category / type validity
2817 if ($values['itilcategories_id']) {
2818 $cat = new ITILCategory();
2819 if ($cat->getFromDB($values['itilcategories_id'])) {
2820 switch ($values['type']) {
2821 case self::INCIDENT_TYPE :
2822 if (!$cat->getField('is_incident')) {
2823 $values['itilcategories_id'] = 0;
2824 }
2825 break;
2826
2827 case self::DEMAND_TYPE :
2828 if (!$cat->getField('is_request')) {
2829 $values['itilcategories_id'] = 0;
2830 }
2831 break;
2832
2833 default :
2834 break;
2835 }
2836 }
2837 }
2838
2839 if (!$ticket_template) {
2840 echo "<form method='post' name='helpdeskform' action='".
2841 $CFG_GLPI["root_doc"]."/front/tracking.injector.php' enctype='multipart/form-data'>";
2842 }
2843
2844
2845 $delegating = User::getDelegateGroupsForUser($values['entities_id']);
2846
2847 if (count($delegating)) {
2848 echo "<div class='center'><table class='tab_cadre_fixe'>";
2849 echo "<tr><th colspan='2'>".__('This ticket concerns me')." ";
2850
2851 $rand = Dropdown::showYesNo("nodelegate", $values['nodelegate']);
2852
2853 $params = array('nodelegate' => '__VALUE__',
2854 'rand' => $rand,
2855 'right' => "delegate",
2856 '_users_id_requester'
2857 => $values['_users_id_requester'],
2858 '_users_id_requester_notif'
2859 => $values['_users_id_requester_notif'],
2860 'use_notification'
2861 => $values['_users_id_requester_notif']['use_notification'],
2862 'entity_restrict'
2863 => $_SESSION["glpiactive_entity"]);
2864
2865 Ajax::UpdateItemOnSelectEvent("dropdown_nodelegate".$rand, "show_result".$rand,
2866 $CFG_GLPI["root_doc"]."/ajax/dropdownDelegationUsers.php",
2867 $params);
2868
2869 $class = 'right';
2870 if ($CFG_GLPI['use_check_pref'] && $values['nodelegate']) {
2871 echo "</th><th>".__('Check your personnal information');
2872 $class = 'center';
2873 }
2874
2875 echo "</th></tr>";
2876 echo "<tr class='tab_bg_1'><td colspan='2' class='".$class."'>";
2877 echo "<div id='show_result$rand'>";
2878
2879 $self = new self();
2880 if ($values["_users_id_requester"] == 0) {
2881 $values['_users_id_requester'] = Session::getLoginUserID();
2882 } else {
2883 $values['_right'] = "delegate";
2884 }
2885 $self->showActorAddFormOnCreate(CommonITILActor::REQUESTER, $values);
2886 echo "</div>";
2887 if ($CFG_GLPI['use_check_pref'] && $values['nodelegate']) {
2888 echo "</td><td class='center'>";
2889 User::showPersonalInformation(Session::getLoginUserID());
2890 }
2891 echo "</td></tr>";
2892
2893 echo "</table></div>";
2894 echo "<input type='hidden' name='_users_id_recipient' value='".Session::getLoginUserID()."'>";
2895
2896 } else {
2897 // User as requester
2898 $values['_users_id_requester'] = Session::getLoginUserID();
2899
2900 if ($CFG_GLPI['use_check_pref']) {
2901 echo "<div class='center'><table class='tab_cadre_fixe'>";
2902 echo "<tr><th>".__('Check your personnal information')."</th></tr>";
2903 echo "<tr class='tab_bg_1'><td class='center'>";
2904 User::showPersonalInformation(Session::getLoginUserID());
2905 echo "</td></tr>";
2906 echo "</table></div>";
2907 }
2908 }
2909
2910 echo "<input type='hidden' name='_from_helpdesk' value='1'>";
2911 echo "<input type='hidden' name='requesttypes_id' value='".RequestType::getDefault('helpdesk').
2912 "'>";
2913
2914
2915 // Load ticket template if available :
2916 $tt = $this->getTicketTemplateToUse($ticket_template, $values['type'],
2917 $values['itilcategories_id'],
2918 $_SESSION["glpiactive_entity"]);
2919
2920 // Predefined fields from template : reset them
2921 if (isset($values['_predefined_fields'])) {
2922 $values['_predefined_fields']
2923 = Toolbox::decodeArrayFromInput($values['_predefined_fields']);
2924 } else {
2925 $values['_predefined_fields'] = array();
2926 }
2927
2928 // Store predefined fields to be able not to take into account on change template
2929 $predefined_fields = array();
2930
2931 if (isset($tt->predefined) && count($tt->predefined)) {
2932 foreach ($tt->predefined as $predeffield => $predefvalue) {
2933 if (isset($values[$predeffield]) && isset($default_values[$predeffield])) {
2934 // Is always default value : not set
2935 // Set if already predefined field
2936 // Set if ticket template change
2937 if (((count($values['_predefined_fields']) == 0)
2938 && ($values[$predeffield] == $default_values[$predeffield]))
2939 || (isset($values['_predefined_fields'][$predeffield])
2940 && ($values[$predeffield] == $values['_predefined_fields'][$predeffield]))
2941 || (isset($values['_tickettemplates_id'])
2942 && ($values['_tickettemplates_id'] != $tt->getID()))) {
2943 $values[$predeffield] = $predefvalue;
2944 $predefined_fields[$predeffield] = $predefvalue;
2945 }
2946 } else { // Not defined options set as hidden field
2947 echo "<input type='hidden' name='$predeffield' value='$predefvalue'>";
2948 }
2949 }
2950 // All predefined override : add option to say predifined exists
2951 if (count($predefined_fields) == 0) {
2952 $predefined_fields['_all_predefined_override'] = 1;
2953 }
2954 } else { // No template load : reset predefined values
2955 if (count($values['_predefined_fields'])) {
2956 foreach ($values['_predefined_fields'] as $predeffield => $predefvalue) {
2957 if ($values[$predeffield] == $predefvalue) {
2958 $values[$predeffield] = $default_values[$predeffield];
2959 }
2960 }
2961 }
2962 }
2963
2964 if (($CFG_GLPI['urgency_mask'] == (1<<3))
2965 || $tt->isHiddenField('urgency')) {
2966 // Dont show dropdown if only 1 value enabled or field is hidden
2967 echo "<input type='hidden' name='urgency' value='".$values['urgency']."'>";
2968 }
2969
2970 // Display predefined fields if hidden
2971 if ($tt->isHiddenField('itemtype')) {
2972 echo "<input type='hidden' name='itemtype' value='".$values['itemtype']."'>";
2973 echo "<input type='hidden' name='items_id' value='".$values['items_id']."'>";
2974 }
2975 if ($tt->isHiddenField('locations_id')) {
2976 echo "<input type='hidden' name='locations_id' value='".$values['locations_id']."'>";
2977 }
2978 echo "<input type='hidden' name='entities_id' value='".$_SESSION["glpiactive_entity"]."'>";
2979 echo "<div class='center'><table class='tab_cadre_fixe'>";
2980
2981 echo "<tr><th>".__('Describe the incident or request')."</th><th>";
2982 if (Session::isMultiEntitiesMode()) {
2983 echo "(".Dropdown::getDropdownName("glpi_entities", $_SESSION["glpiactive_entity"]).")";
2984 }
2985 echo "</th></tr>";
2986
2987 echo "<tr class='tab_bg_1'>";
2988 echo "<td>".sprintf(__('%1$s%2$s'), __('Type'), $tt->getMandatoryMark('type'))."</td>";
2989 echo "<td>";
2990 self::dropdownType('type', array('value' => $values['type'],
2991 'on_change' => 'this.form.submit()'));
2992 echo "</td></tr>";
2993
2994 echo "<tr class='tab_bg_1'>";
2995 echo "<td>".sprintf(__('%1$s%2$s'), __('Category'),
2996 $tt->getMandatoryMark('itilcategories_id'))."</td>";
2997 echo "<td>";
2998
2999 $condition = "`is_helpdeskvisible`='1'";
3000 switch ($values['type']) {
3001 case self::DEMAND_TYPE :
3002 $condition .= " AND `is_request`='1'";
3003 break;
3004
3005 default: // self::INCIDENT_TYPE :
3006 $condition .= " AND `is_incident`='1'";
3007 }
3008 $opt = array('value' => $values['itilcategories_id'],
3009 'condition' => $condition,
3010 'entity' => $_SESSION["glpiactive_entity"],
3011 'on_change' => 'this.form.submit()');
3012
3013 if ($values['itilcategories_id'] && $tt->isMandatoryField("itilcategories_id")) {
3014 $opt['display_emptychoice'] = false;
3015 }
3016
3017 ITILCategory::dropdown($opt);
3018 echo "</td></tr>";
3019
3020
3021 if ($CFG_GLPI['urgency_mask'] != (1<<3)) {
3022 if (!$tt->isHiddenField('urgency')) {
3023 echo "<tr class='tab_bg_1'>";
3024 echo "<td>".sprintf(__('%1$s%2$s'), __('Urgency'), $tt->getMandatoryMark('urgency')).
3025 "</td>";
3026 echo "<td>";
3027 self::dropdownUrgency(array('value' => $values["urgency"]));
3028 echo "</td></tr>";
3029 }
3030 }
3031
3032 if (empty($delegating)
3033 && NotificationTargetTicket::isAuthorMailingActivatedForHelpdesk()) {
3034 echo "<tr class='tab_bg_1'>";
3035 echo "<td>".__('Inform me about the actions taken')."</td>";
3036 echo "<td>";
3037 if ($values["_users_id_requester"] == 0) {
3038 $values['_users_id_requester'] = Session::getLoginUserID();
3039 }
3040 $_POST['value'] = $values['_users_id_requester'];
3041 $_POST['field'] = '_users_id_requester_notif';
3042 $_POST['use_notification'] = $values['_users_id_requester_notif']['use_notification'];
3043 include (GLPI_ROOT."/ajax/uemailUpdate.php");
3044
3045 echo "</td></tr>";
3046 }
3047 if (($_SESSION["glpiactiveprofile"]["helpdesk_hardware"] != 0)
3048 && (count($_SESSION["glpiactiveprofile"]["helpdesk_item_type"]))) {
3049 if (!$tt->isHiddenField('itemtype')) {
3050 echo "<tr class='tab_bg_1'>";
3051 echo "<td>".sprintf(__('%1$s%2$s'), __('Hardware type'),
3052 $tt->getMandatoryMark('itemtype'))."</td>";
3053 echo "<td>";
3054 Item_Ticket::dropdownMyDevices($values['_users_id_requester'], $_SESSION["glpiactive_entity"],
3055 $values['itemtype'], $values['items_id']);
3056 Item_Ticket::dropdownAllDevices("itemtype", $values['itemtype'], $values['items_id'], 0,
3057 $values['_users_id_requester'],
3058 $_SESSION["glpiactive_entity"]);
3059 echo "<span id='item_ticket_selection_information'></span>";
3060
3061 echo "</td></tr>";
3062 }
3063 }
3064
3065 if (!$tt->isHiddenField('locations_id')) {
3066 echo "<tr class='tab_bg_1'><td>";
3067 printf(__('%1$s%2$s'), __('Location'), $tt->getMandatoryMark('locations_id'));
3068 echo "</td><td>";
3069 Location::dropdown(array('value' => $values["locations_id"]));
3070 echo "</td></tr>";
3071 }
3072
3073 if (!$tt->isHiddenField('_users_id_observer')
3074 || $tt->isPredefinedField('_users_id_observer')) {
3075 echo "<tr class='tab_bg_1'>";
3076 echo "<td>".sprintf(__('%1$s%2$s'), _n('Watcher', 'Watchers', 2),
3077 $tt->getMandatoryMark('_users_id_observer'))."</td>";
3078 echo "<td>";
3079 $values['_right'] = "groups";
3080
3081 if (!$tt->isHiddenField('_users_id_observer')) {
3082 // Observer
3083
3084 if($tt->isPredefinedField('_users_id_observer')
3085 && !is_array($values['_users_id_observer'])) {
3086
3087 //convert predefined value to array
3088 $values['_users_id_observer'] = array($values['_users_id_observer']);
3089 $values['_users_id_observer_notif']['use_notification'] =
3090 array($values['_users_id_observer_notif']['use_notification']);
3091
3092 // add new line to permit adding more observers
3093 $values['_users_id_observer'][1] = 0;
3094 $values['_users_id_observer_notif']['use_notification'][1] = 1;
3095 }
3096
3097
3098 echo "<div class='actor_single first-actor'>";
3099 if (isset($values['_users_id_observer'])) {
3100 $observers = $values['_users_id_observer'];
3101 foreach($observers as $index_observer => $observer) {
3102 $options = array_merge($values, array('_user_index' => $index_observer));
3103 self::showFormHelpdeskObserver($options);
3104 }
3105 }
3106 echo "</div>";
3107
3108
3109 } else { // predefined value
3110 if (isset($values["_users_id_observer"]) && $values["_users_id_observer"]) {
3111 echo self::getActorIcon('user', CommonITILActor::OBSERVER)." ";
3112 echo Dropdown::getDropdownName("glpi_users", $values["_users_id_observer"]);
3113 echo "<input type='hidden' name='_users_id_observer' value=\"".
3114 $values["_users_id_observer"]."\">";
3115 }
3116 }
3117 echo "</td></tr>";
3118 }
3119
3120
3121 if (!$tt->isHiddenField('name')
3122 || $tt->isPredefinedField('name')) {
3123 echo "<tr class='tab_bg_1'>";
3124 echo "<td>".sprintf(__('%1$s%2$s'), __('Title'), $tt->getMandatoryMark('name'))."<td>";
3125 if (!$tt->isHiddenField('name')) {
3126 echo "<input type='text' maxlength='250' size='80' name='name'
3127 value=\"".$values['name']."\">";
3128 } else {
3129 echo $values['name'];
3130 echo "<input type='hidden' name='name' value=\"".$values['name']."\">";
3131 }
3132 echo "</td></tr>";
3133 }
3134
3135 if (!$tt->isHiddenField('content')
3136 || $tt->isPredefinedField('content')) {
3137 echo "<tr class='tab_bg_1'>";
3138 echo "<td>".sprintf(__('%1$s%2$s'), __('Description'), $tt->getMandatoryMark('content')).
3139 "</td><td>";
3140 $rand = mt_rand();
3141 $rand_text = mt_rand();
3142
3143 $cols = 90;
3144 $rows = 6;
3145 $content_id = "content$rand";
3146
3147 if ($CFG_GLPI["use_rich_text"]) {
3148 $values["content"] = $this->setRichTextContent($content_id, $values["content"], $rand);
3149 $cols = 100;
3150 $rows = 10;
3151 } else {
3152 $values["content"] = $this->setSimpleTextContent($values["content"]);
3153 }
3154
3155 echo "<div id='content$rand_text'>";
3156 echo "<textarea id='$content_id' name='content' cols='$cols' rows='$rows'>".
3157 $values['content']."</textarea></div>";
3158 echo "</td></tr>";
3159 }
3160
3161 // File upload system
3162 $width = '100%';
3163 if ($CFG_GLPI['use_rich_text']) {
3164 $width = '50%';
3165 }
3166 echo "<tr class='tab_bg_1'>";
3167 echo "<td class='top'>".sprintf(__('%1$s (%2$s)'), __('File'), Document::getMaxUploadSize());
3168 DocumentType::showAvailableTypesLink();
3169 echo "</td>";
3170 echo "<td class='top'>";
3171 echo "<div id='fileupload_info'></div>";
3172 echo "</td>";
3173 echo "</tr>";
3174
3175 echo "<tr class='tab_bg_1'>";
3176 echo "<td colspan='4'>";
3177 echo "<table width='100%'><tr>";
3178 echo "<td width='$width '>";
3179
3180 echo Html::file(array('multiple' => true,
3181 'values' => array('filename' => $values['_filename'],
3182 'tag' => $values['_tag_filename'])
3183 ));
3184// "<div id='uploadfiles'><input type='file' name='filename[]' value='' size='60'></div>";
3185 echo "</td>";
3186 if ($CFG_GLPI['use_rich_text']) {
3187 echo "<td width='$width '>";
3188 if (!isset($rand)) {
3189 $rand = mt_rand();
3190 }
3191 echo Html::initImagePasteSystem($content_id, $rand);
3192 echo "</td>";
3193 }
3194 echo "</tr></table>";
3195
3196 echo "</td>";
3197 echo "</tr>";
3198
3199
3200
3201 if (!$ticket_template) {
3202 echo "<tr class='tab_bg_1'>";
3203 echo "<td colspan='2' class='center'>";
3204
3205 if ($tt->isField('id') && ($tt->fields['id'] > 0)) {
3206 echo "<input type='hidden' name='_tickettemplates_id' value='".$tt->fields['id']."'>";
3207 echo "<input type='hidden' name='_predefined_fields'
3208 value=\"".Toolbox::prepareArrayForInput($predefined_fields)."\">";
3209 }
3210 echo "<input type='submit' name='add' value=\"".__s('Submit message')."\" class='submit'>";
3211 echo "</td></tr>";
3212 }
3213
3214 echo "</table></div>";
3215 if (!$ticket_template) {
3216 Html::closeForm();
3217 }
3218 }
3219
3220 /**
3221 * Display a single oberver selector
3222 *
3223 * * @param $options array options for default values ($options of showActorAddFormOnCreate)
3224 **/
3225 static function showFormHelpdeskObserver($options = array()) {
3226 global $CFG_GLPI;
3227
3228 //default values
3229 $ticket = new Ticket();
3230 $params['_users_id_observer_notif']['use_notification'] = true;
3231 $params['_users_id_observer'] = 0;
3232 $params['entities_id'] = $_SESSION["glpiactive_entity"];
3233 $values['_right'] = "groups";
3234
3235 // overide default value by function parameters
3236 if (is_array($options) && count($options)) {
3237 foreach ($options as $key => $val) {
3238 $params[$key] = $val;
3239 }
3240 }
3241
3242 // add a user selector
3243 $rand_observer = $ticket->showActorAddFormOnCreate(CommonITILActor::OBSERVER, $params);
3244
3245 // add an additionnal observer on user selection
3246 Ajax::updateItemOnSelectEvent("dropdown__users_id_observer[]$rand_observer",
3247 "observer_$rand_observer",
3248 $CFG_GLPI["root_doc"]."/ajax/helpdesk_observer.php",
3249 $params);
3250
3251 //remove 'new observer' anchor on user selection
3252 echo Html::scriptBlock("
3253 $('#dropdown__users_id_observer__$rand_observer').on('change', function(event) {
3254 $('#addObserver$rand_observer').remove();
3255 });");
3256
3257 // add "new observer" anchor
3258 echo "<a id='addObserver$rand_observer' class='add-observer' onclick='this.remove()'>";
3259 echo Html::image($CFG_GLPI['root_doc']."/pics/meta_plus.png", array('alt' => __('Add')));
3260 echo "</a>";
3261
3262 // add an additionnal observer on anchor click
3263 Ajax::updateItemOnEvent("addObserver$rand_observer",
3264 "observer_$rand_observer",
3265 $CFG_GLPI["root_doc"]."/ajax/helpdesk_observer.php",
3266 $params, array('click'));
3267
3268 // div for an additionnal observer
3269 echo "<div class='actor_single' id='observer_$rand_observer'></div>";
3270
3271 }
3272
3273
3274 /**
3275 * @since version 0.83
3276 *
3277 * @param $entity integer entities_id usefull is function called by cron (default 0)
3278 **/
3279 static function getDefaultValues($entity=0) {
3280 global $CFG_GLPI;
3281
3282 if (is_numeric(Session::getLoginUserID(false))) {
3283 $users_id_requester = Session::getLoginUserID();
3284 // No default requester if own ticket right = tech and update_ticket right to update requester
3285 if (Session::haveRightsOr(self::$rightname, array(UPDATE, self::OWN))) {
3286 $users_id_requester = 0;
3287 }
3288 $entity = $_SESSION['glpiactive_entity'];
3289 $requesttype = $_SESSION['glpidefault_requesttypes_id'];
3290 } else {
3291 $users_id_requester = 0;
3292 $requesttype = $CFG_GLPI['default_requesttypes_id'];
3293 }
3294
3295 $type = Entity::getUsedConfig('tickettype', $entity, '', Ticket::INCIDENT_TYPE);
3296
3297 $default_use_notif = Entity::getUsedConfig('is_notif_enable_default', $entity, '', 1);
3298
3299 // Set default values...
3300 return array('_users_id_requester' => $users_id_requester,
3301 '_users_id_requester_notif' => array('use_notification' => $default_use_notif,
3302 'alternative_email' => ''),
3303 '_groups_id_requester' => 0,
3304 '_users_id_assign' => 0,
3305 '_users_id_assign_notif' => array('use_notification' => $default_use_notif,
3306 'alternative_email' => ''),
3307 '_groups_id_assign' => 0,
3308 '_users_id_observer' => 0,
3309 '_users_id_observer_notif' => array('use_notification' => $default_use_notif,
3310 'alternative_email' => ''),
3311 '_groups_id_observer' => 0,
3312 '_link' => array('tickets_id_2' => '',
3313 'link' => ''),
3314 '_suppliers_id_assign' => 0,
3315 '_suppliers_id_assign_notif' => array('use_notification' => $default_use_notif,
3316 'alternative_email' => ''),
3317 'name' => '',
3318 'content' => '',
3319 'itilcategories_id' => 0,
3320 'urgency' => 3,
3321 'impact' => 3,
3322 'priority' => self::computePriority(3, 3),
3323 'requesttypes_id' => $requesttype,
3324 'actiontime' => 0,
3325 //'date' => $_SESSION["glpi_currenttime"], - OAJ - Não aparecer a data atual na abertura de chamado.
3326 'entities_id' => $entity,
3327 'status' => self::INCOMING,
3328 'followup' => array(),
3329 'itemtype' => '',
3330 'items_id' => 0,
3331 'locations_id' => 0,
3332 'plan' => array(),
3333 'global_validation' => CommonITILValidation::NONE,
3334 'due_date' => 'NULL',
3335 'slas_id' => 0,
3336 '_add_validation' => 0,
3337 'users_id_validate' => array(),
3338 'type' => $type,
3339 '_documents_id' => array(),
3340 '_filename' => array(),
3341 '_tag_filename' => array());
3342 }
3343
3344
3345 /**
3346 * Get ticket template to use
3347 * Use force_template first, then try on template define for type and category
3348 * then use default template of active profile of connected user and then use default entity one
3349 *
3350 * @param $force_template integer tickettemplate_id to used (case of preview for example)
3351 * (default 0)
3352 * @param $type integer type of the ticket (default 0)
3353 * @param $itilcategories_id integer ticket category (default 0)
3354 * @param $entities_id integer (default -1)
3355 *
3356 * @since version 0.84
3357 *
3358 * @return ticket template object
3359 **/
3360 function getTicketTemplateToUse($force_template=0, $type=0, $itilcategories_id=0,
3361 $entities_id=-1) {
3362
3363 // Load ticket template if available :
3364 $tt = new TicketTemplate();
3365 $template_loaded = false;
3366
3367 if ($force_template) {
3368 // with type and categ
3369 if ($tt->getFromDBWithDatas($force_template, true)) {
3370 $template_loaded = true;
3371 }
3372 }
3373
3374 if (!$template_loaded
3375 && $type
3376 && $itilcategories_id) {
3377
3378 $categ = new ITILCategory();
3379 if ($categ->getFromDB($itilcategories_id)) {
3380 $field = '';
3381 switch ($type) {
3382 case self::INCIDENT_TYPE :
3383 $field = 'tickettemplates_id_incident';
3384 break;
3385
3386 case self::DEMAND_TYPE :
3387 $field = 'tickettemplates_id_demand';
3388 break;
3389 }
3390
3391 if (!empty($field) && $categ->fields[$field]) {
3392 // without type and categ
3393 if ($tt->getFromDBWithDatas($categ->fields[$field], false)) {
3394 $template_loaded = true;
3395 }
3396 }
3397 }
3398 }
3399
3400 // If template loaded from type and category do not check after
3401 if ($template_loaded) {
3402 return $tt;
3403 }
3404
3405 if (!$template_loaded) {
3406 // load default profile one if not already loaded
3407 if (isset($_SESSION['glpiactiveprofile']['tickettemplates_id'])
3408 && $_SESSION['glpiactiveprofile']['tickettemplates_id']) {
3409 // with type and categ
3410 if ($tt->getFromDBWithDatas($_SESSION['glpiactiveprofile']['tickettemplates_id'],
3411 true)) {
3412 $template_loaded = true;
3413 }
3414 }
3415 }
3416
3417 if (!$template_loaded
3418 && ($entities_id >= 0)) {
3419
3420 // load default entity one if not already loaded
3421 if ($template_id = Entity::getUsedConfig('tickettemplates_id', $entities_id)) {
3422 // with type and categ
3423 if ($tt->getFromDBWithDatas($template_id, true)) {
3424 $template_loaded = true;
3425 }
3426 }
3427 }
3428
3429 // Check if profile / entity set type and category and try to load template for these values
3430 if ($template_loaded) { // template loaded for profile or entity
3431 $newtype = $type;
3432 $newitilcategories_id = $itilcategories_id;
3433 // Get predefined values for ticket template
3434 if (isset($tt->predefined['itilcategories_id']) && $tt->predefined['itilcategories_id']) {
3435 $newitilcategories_id = $tt->predefined['itilcategories_id'];
3436 }
3437 if (isset($tt->predefined['type']) && $tt->predefined['type']) {
3438 $newtype = $tt->predefined['type'];
3439 }
3440 if ($newtype
3441 && $newitilcategories_id) {
3442
3443 $categ = new ITILCategory();
3444 if ($categ->getFromDB($newitilcategories_id)) {
3445 $field = '';
3446 switch ($newtype) {
3447 case self::INCIDENT_TYPE :
3448 $field = 'tickettemplates_id_incident';
3449 break;
3450
3451 case self::DEMAND_TYPE :
3452 $field = 'tickettemplates_id_demand';
3453 break;
3454 }
3455
3456 if (!empty($field) && $categ->fields[$field]) {
3457 // without type and categ
3458 if ($tt->getFromDBWithDatas($categ->fields[$field], false)) {
3459 $template_loaded = true;
3460 }
3461 }
3462 }
3463 }
3464 }
3465 return $tt;
3466 }
3467
3468
3469 function showForm($ID, $options=array()) {
3470 global $DB, $CFG_GLPI;
3471
3472 $default_values = self::getDefaultValues();
3473
3474 // Get default values from posted values on reload form
3475 // On get because of tabs
3476 // we use REQUEST because method differ with layout (lefttab : GET, vsplit: POST)
3477 if (!isset($options['template_preview'])) {
3478 if (isset($_REQUEST)) {
3479 $values = Html::cleanPostForTextArea($_REQUEST);
3480 }
3481 }
3482
3483 // Restore saved value or override with page parameter
3484 $saved = $this->restoreInput();
3485
3486 foreach ($default_values as $name => $value) {
3487 if (!isset($values[$name])) {
3488 if (isset($saved[$name])) {
3489 $values[$name] = $saved[$name];
3490 } else {
3491 $values[$name] = $value;
3492 }
3493 }
3494 }
3495
3496 if (isset($values['content'])) {
3497 // Clean new lines to be fix encoding
3498 $order = array('\\r', '\\n', "\\");
3499 $replace = array("", "", "");
3500
3501 $values['content'] = str_replace($order,$replace,$values['content']);
3502 }
3503 if (isset($values['name'])) {
3504 $values['name'] = str_replace("\\", "", $values['name']);
3505 }
3506
3507 if (!$ID) {
3508 // Override defaut values from projecttask if needed
3509 if (isset($options['_projecttasks_id'])) {
3510 $pt = new ProjectTask();
3511 if ($pt->getFromDB($options['_projecttasks_id'])) {
3512 $values['name'] = $pt->getField('name');
3513 $values['content'] = $pt->getField('name');
3514 }
3515 }
3516 }
3517
3518 // Check category / type validity
3519 if ($values['itilcategories_id']) {
3520 $cat = new ITILCategory();
3521 if ($cat->getFromDB($values['itilcategories_id'])) {
3522 switch ($values['type']) {
3523 case self::INCIDENT_TYPE :
3524 if (!$cat->getField('is_incident')) {
3525 $values['itilcategories_id'] = 0;
3526 }
3527 break;
3528
3529 case self::DEMAND_TYPE :
3530 if (!$cat->getField('is_request')) {
3531 $values['itilcategories_id'] = 0;
3532 }
3533 break;
3534
3535 default :
3536 break;
3537 }
3538 }
3539 }
3540
3541
3542 // Default check
3543 if ($ID > 0) {
3544 $this->check($ID, READ);
3545 } else {
3546 // Create item
3547 $this->check(-1, CREATE, $values);
3548 }
3549
3550 if (!$ID) {
3551 $this->userentities = array();
3552 if ($values["_users_id_requester"]) {
3553 //Get all the user's entities
3554 $all_entities = Profile_User::getUserEntities($values["_users_id_requester"], true,
3555 true);
3556 //For each user's entity, check if the technician which creates the ticket have access to it
3557 foreach ($all_entities as $tmp => $ID_entity) {
3558 if (Session::haveAccessToEntity($ID_entity)) {
3559 $this->userentities[] = $ID_entity;
3560 }
3561 }
3562 }
3563 $this->countentitiesforuser = count($this->userentities);
3564
3565 if (($this->countentitiesforuser > 0)
3566 && !in_array($this->fields["entities_id"], $this->userentities)) {
3567 // If entity is not in the list of user's entities,
3568 // then use as default value the first value of the user's entites list
3569 $this->fields["entities_id"] = $this->userentities[0];
3570 // Pass to values
3571 $values['entities_id'] = $this->userentities[0];
3572 }
3573 }
3574
3575 if ($values['type'] <= 0) {
3576 $values['type'] = Entity::getUsedConfig('tickettype', $values['entities_id'], '',
3577 Ticket::INCIDENT_TYPE);
3578 }
3579
3580 if (!isset($options['template_preview'])) {
3581 $options['template_preview'] = 0;
3582 }
3583
3584 // Load ticket template if available :
3585 if ($ID) {
3586 $tt = $this->getTicketTemplateToUse($options['template_preview'], $this->fields['type'],
3587 $this->fields['itilcategories_id'], $this->fields['entities_id']);
3588 } else {
3589 $tt = $this->getTicketTemplateToUse($options['template_preview'], $values['type'],
3590 $values['itilcategories_id'], $values['entities_id']);
3591 }
3592
3593 // Predefined fields from template : reset them
3594 if (isset($values['_predefined_fields'])) {
3595 $values['_predefined_fields']
3596 = Toolbox::decodeArrayFromInput($values['_predefined_fields']);
3597 } else {
3598 $values['_predefined_fields'] = array();
3599 }
3600
3601 // Store predefined fields to be able not to take into account on change template
3602 // Only manage predefined values on ticket creation
3603 $predefined_fields = array();
3604 if (!$ID) {
3605
3606 if (isset($tt->predefined) && count($tt->predefined)) {
3607 foreach ($tt->predefined as $predeffield => $predefvalue) {
3608 if (isset($default_values[$predeffield])) {
3609 // Is always default value : not set
3610 // Set if already predefined field
3611 // Set if ticket template change
3612 if (((count($values['_predefined_fields']) == 0)
3613 && ($values[$predeffield] == $default_values[$predeffield]))
3614 || (isset($values['_predefined_fields'][$predeffield])
3615 && ($values[$predeffield] == $values['_predefined_fields'][$predeffield]))
3616 || (isset($values['_tickettemplates_id'])
3617 && ($values['_tickettemplates_id'] != $tt->getID()))
3618 // user pref for requestype can't overwrite requestype from template
3619 // when change category
3620 || (($predeffield == 'requesttypes_id')
3621 && empty($saved))) {
3622
3623 // Load template data
3624 $values[$predeffield] = $predefvalue;
3625 $this->fields[$predeffield] = $predefvalue;
3626 $predefined_fields[$predeffield] = $predefvalue;
3627 }
3628 }
3629 }
3630 // All predefined override : add option to say predifined exists
3631 if (count($predefined_fields) == 0) {
3632 $predefined_fields['_all_predefined_override'] = 1;
3633 }
3634
3635 } else { // No template load : reset predefined values
3636 if (count($values['_predefined_fields'])) {
3637 foreach ($values['_predefined_fields'] as $predeffield => $predefvalue) {
3638 if ($values[$predeffield] == $predefvalue) {
3639 $values[$predeffield] = $default_values[$predeffield];
3640 }
3641 }
3642 }
3643 }
3644 }
3645 // Put ticket template on $values for actors
3646 $values['_tickettemplate'] = $tt;
3647
3648 $canupdate = Session::haveRight(self::$rightname, UPDATE);
3649 $canpriority = Session::haveRight(self::$rightname, self::CHANGEPRIORITY);
3650 $canstatus = $canupdate;
3651
3652 if ($ID && in_array($this->fields['status'], $this->getClosedStatusArray())) {
3653 $canupdate = false;
3654 // No update for actors
3655 $values['_noupdate'] = true;
3656 }
3657
3658 $showuserlink = 0;
3659 if (Session::haveRight('user', READ)) {
3660 $showuserlink = 1;
3661 }
3662
3663
3664 if ($options['template_preview']) {
3665 // Add all values to fields of tickets for template preview
3666 foreach ($values as $key => $val) {
3667 if (!isset($this->fields[$key])) {
3668 $this->fields[$key] = $val;
3669 }
3670 }
3671 }
3672
3673 // In percent
3674 $colsize1 = '13';
3675 $colsize2 = '29';
3676 $colsize3 = '13';
3677 $colsize4 = '45';
3678
3679 $canupdate_descr = $canupdate
3680 || (($this->fields['status'] == self::INCOMING)
3681 && $this->isUser(CommonITILActor::REQUESTER, Session::getLoginUserID())
3682 && ($this->numberOfFollowups() == 0)
3683 && ($this->numberOfTasks() == 0));
3684
3685 if (!$options['template_preview']) {
3686 echo "<form method='post' name='form_ticket' enctype='multipart/form-data' action='".
3687 $CFG_GLPI["root_doc"]."/front/ticket.form.php'>";
3688 if (isset($options['_projecttasks_id'])) {
3689 echo "<input type='hidden' name='_projecttasks_id' value='".$options['_projecttasks_id']."'>";
3690 }
3691 }
3692 echo "<div class='spaced' id='tabsbody'>";
3693 echo "<table class='tab_cadre_fixe' id='mainformtable'>";
3694
3695 // Optional line
3696 $ismultientities = Session::isMultiEntitiesMode();
3697 echo "<tr class='headerRow responsive_hidden'>";
3698 echo "<th colspan='4'>";
3699
3700 if ($ID) {
3701 $text = sprintf(__('%1$s - %2$s'), $this->getTypeName(1),
3702 sprintf(__('%1$s: %2$s'), __('ID'), $ID));
3703 if ($ismultientities) {
3704 $text = sprintf(__('%1$s (%2$s)'), $text,
3705 Dropdown::getDropdownName('glpi_entities',
3706 $this->fields['entities_id']));
3707 }
3708 echo $text;
3709 } else {
3710 if ($ismultientities) {
3711 printf(__('The ticket will be added in the entity %s'),
3712 Dropdown::getDropdownName("glpi_entities", $this->fields['entities_id']));
3713 } else {
3714 _e('New ticket');
3715 }
3716 }
3717 echo "</th></tr>";
3718
3719 echo "<tr class='tab_bg_1'>";
3720 echo "<th width='$colsize1%'>";
3721 echo $tt->getBeginHiddenFieldText('date');
3722 if (!$ID) {
3723 printf(__('%1$s%2$s'), __('Opening date'), $tt->getMandatoryMark('date'));
3724 } else {
3725 _e('Opening date');
3726 }
3727 echo $tt->getEndHiddenFieldText('date');
3728 echo "</th>";
3729 echo "<td width='$colsize2%'>";
3730 echo $tt->getBeginHiddenFieldValue('date');
3731 $date = $this->fields["date"];
3732
3733 if ($canupdate) {
3734 Html::showDateTimeField("date", array('value' => $date,
3735 'timestep' => 1,
3736 'maybeempty' => false));
3737 } else {
3738 echo Html::convDateTime($date);
3739 }
3740 echo $tt->getEndHiddenFieldValue('date', $this);
3741 echo "</td>";
3742 // SLA
3743 echo "<th width='$colsize3%'>".$tt->getBeginHiddenFieldText('due_date');
3744
3745 if (!$ID) {
3746 printf(__('%1$s%2$s'), __('Due date'), $tt->getMandatoryMark('due_date'));
3747 } else {
3748 _e('Due date');
3749 }
3750 echo $tt->getEndHiddenFieldText('due_date');
3751 echo "</th>";
3752 echo "<td width='$colsize4%' class='nopadding'>";
3753 if ($ID) {
3754 if ($this->fields["slas_id"] > 0) {
3755 echo "<table width='100%'><tr><td class='nopadding'>";
3756 echo Html::convDateTime($this->fields["due_date"]);
3757 echo "</td><td class='b'>".__('SLA')."</td>";
3758 echo "<td class='nopadding'>";
3759 echo Dropdown::getDropdownName("glpi_slas", $this->fields["slas_id"]);
3760 $commentsla = "";
3761 $slalevel = new SlaLevel();
3762 if ($slalevel->getFromDB($this->fields['slalevels_id'])) {
3763 $commentsla .= '<span class="b spaced">'.
3764 sprintf(__('%1$s: %2$s'), __('Escalation level'),
3765 $slalevel->getName()).'</span><br>';
3766 }
3767
3768 $nextaction = new SlaLevel_Ticket();
3769 if ($nextaction->getFromDBForTicket($this->fields["id"])) {
3770 $commentsla .= '<span class="b spaced">'.
3771 sprintf(__('Next escalation: %s'),
3772 Html::convDateTime($nextaction->fields['date'])).
3773 '</span><br>';
3774 if ($slalevel->getFromDB($nextaction->fields['slalevels_id'])) {
3775 $commentsla .= '<span class="b spaced">'.
3776 sprintf(__('%1$s: %2$s'), __('Escalation level'),
3777 $slalevel->getName()).'</span>';
3778 }
3779 }
3780 $slaoptions = array();
3781 if (Session::haveRight('sla', READ)) {
3782 $slaoptions['link'] = Toolbox::getItemTypeFormURL('SLA').
3783 "?id=".$this->fields["slas_id"];
3784 }
3785 Html::showToolTip($commentsla,$slaoptions);
3786 if ($canupdate) {
3787 echo " ";
3788 $fields = array('sla_delete' => 'sla_delete',
3789 'id' => $this->getID(),
3790 '_glpi_csrf_token' => Session::getNewCSRFToken(),
3791 '_glpi_simple_form' => 1);
3792 $JS = " function delete_due_date(){
3793 if (confirm('".addslashes(__('Delete due date too?'))."')) {
3794 submitGetLink('".$this->getFormURL()."',
3795 ".json_encode(array_merge($fields,
3796 array('delete_due_date' => 1))).");
3797 } else {
3798 submitGetLink('".$this->getFormURL()."',
3799 ".json_encode(array_merge($fields,
3800 array('delete_due_date' => 0))).");
3801 }
3802 }";
3803 echo Html::scriptBlock($JS);
3804 echo "<a class='vsubmit' onclick='delete_due_date();'>"._x('button', 'Delete permanently')."</a>";
3805 }
3806 echo "</td>";
3807 echo "</tr></table>";
3808
3809 } else {
3810 echo "<table width='100%'><tr><td class='nopadding'>";
3811 echo $tt->getBeginHiddenFieldValue('due_date');
3812 if ($canupdate) {
3813 Html::showDateTimeField("due_date", array('value' => $this->fields["due_date"],
3814 'timestep' => 1,
3815 'maybeempty' => true));
3816 } else {
3817 echo Html::convDateTime($this->fields["due_date"]);
3818 }
3819 echo $tt->getEndHiddenFieldValue('due_date',$this);
3820 echo "</td>";
3821 if ($canupdate) {
3822 echo "<td>";
3823 echo $tt->getBeginHiddenFieldText('slas_id');
3824 echo "<span id='sla_action'>";
3825 echo "<a class='vsubmit' ".
3826 Html::addConfirmationOnAction(array(__('The assignment of a SLA to a ticket causes the recalculation of the due date.'),
3827 __("Escalations defined in the SLA will be triggered under this new date.")),
3828 "cleanhide('sla_action');cleandisplay('sla_choice');").
3829 ">".__('Assign a SLA').'</a>';
3830 echo "</span>";
3831 echo "<div id='sla_choice' style='display:none'>";
3832 echo "<span class='b'>".__('SLA')."</span> ";
3833 Sla::dropdown(array('entity' => $this->fields["entities_id"],
3834 'value' => $this->fields["slas_id"]));
3835 echo "</div>";
3836 echo $tt->getEndHiddenFieldText('slas_id');
3837 echo "</td>";
3838 }
3839 echo "</tr></table>";
3840 }
3841
3842 } else { // New Ticket
3843 echo "<table width='100%'><tr><td width='40%' class='nopadding'>";
3844 if ($this->fields["due_date"] == 'NULL') {
3845 $this->fields["due_date"]='';
3846 }
3847 echo $tt->getBeginHiddenFieldValue('due_date');
3848 Html::showDateTimeField("due_date", array('value' => $this->fields["due_date"],
3849 'timestep' => 1,
3850 'maybeempty' => false,
3851 'canedit' => $canupdate));
3852 echo $tt->getEndHiddenFieldValue('due_date',$this);
3853 echo "</td>";
3854 if ($canupdate) {
3855 echo "<td class='nopadding b'>".$tt->getBeginHiddenFieldText('slas_id');
3856 printf(__('%1$s%2$s'), __('SLA'), $tt->getMandatoryMark('slas_id'));
3857 echo $tt->getEndHiddenFieldText('slas_id')."</td>";
3858 echo "<td class='nopadding'>".$tt->getBeginHiddenFieldValue('slas_id');
3859 Sla::dropdown(array('entity' => $this->fields["entities_id"],
3860 'value' => $this->fields["slas_id"]));
3861 echo $tt->getEndHiddenFieldValue('slas_id',$this);
3862 echo "</td>";
3863 }
3864 echo "</tr></table>";
3865 }
3866 echo "</td></tr>";
3867
3868 if ($ID) {
3869 echo "<tr class='tab_bg_1'>";
3870 echo "<th width='$colsize1%'>".__('By')."</th>";
3871 echo "<td width='$colsize2%'>";
3872 if ($canupdate) {
3873 User::dropdown(array('name' => 'users_id_recipient',
3874 'value' => $this->fields["users_id_recipient"],
3875 'entity' => $this->fields["entities_id"],
3876 'right' => 'all'));
3877 } else {
3878 echo getUserName($this->fields["users_id_recipient"], $showuserlink);
3879 }
3880
3881 echo "</td>";
3882 echo "<th width='$colsize3%'>".__('Last update')."</th>";
3883 echo "<td width='$colsize4%'>";
3884 if ($this->fields['users_id_lastupdater'] > 0) {
3885 //TRANS: %1$s is the update date, %2$s is the last updater name
3886 printf(__('%1$s by %2$s'), Html::convDateTime($this->fields["date_mod"]),
3887 getUserName($this->fields["users_id_lastupdater"], $showuserlink));
3888 }
3889 echo "</td>";
3890 echo "</tr>";
3891 }
3892
3893 if ($ID
3894 && (in_array($this->fields["status"], $this->getSolvedStatusArray())
3895 || in_array($this->fields["status"], $this->getClosedStatusArray()))) {
3896
3897 echo "<tr class='tab_bg_1'>";
3898 echo "<th width='$colsize1%'>".__('Resolution date')."</th>";
3899 echo "<td width='$colsize2%'>";
3900 Html::showDateTimeField("solvedate", array('value' => $this->fields["solvedate"],
3901 'timestep' => 1,
3902 'maybeempty' => false,
3903 'canedit' => $canupdate));
3904 echo "</td>";
3905 if (in_array($this->fields["status"], $this->getClosedStatusArray())) {
3906 echo "<th width='$colsize3%'>".__('Close date')."</th>";
3907 echo "<td width='$colsize4%'>";
3908 Html::showDateTimeField("closedate", array('value' => $this->fields["closedate"],
3909 'timestep' => 1,
3910 'maybeempty' => false,
3911 'canedit' => $canupdate));
3912 echo "</td>";
3913 } else {
3914 echo "<td colspan='2'> </td>";
3915 }
3916 echo "</tr>";
3917 }
3918
3919 if ($ID) {
3920 echo "</table>";
3921 echo "<table class='tab_cadre_fixe' id='mainformtable2'>";
3922 }
3923
3924 echo "<tr class='tab_bg_1'>";
3925 echo "<th width='$colsize1%'>".sprintf(__('%1$s%2$s'), __('Type'),
3926 $tt->getMandatoryMark('type'))."</th>";
3927 echo "<td width='$colsize2%'>";
3928 // Permit to set type when creating ticket without update right
3929 if ($canupdate || !$ID) {
3930 $opt = array('value' => $this->fields["type"]);
3931 /// Auto submit to load template
3932 if (!$ID) {
3933 $opt['on_change'] = 'this.form.submit()';
3934 }
3935 $rand = self::dropdownType('type', $opt);
3936 if ($ID) {
3937 $params = array('type' => '__VALUE__',
3938 'entity_restrict' => $this->fields['entities_id'],
3939 'value' => $this->fields['itilcategories_id'],
3940 'currenttype' => $this->fields['type']);
3941
3942 Ajax::updateItemOnSelectEvent("dropdown_type$rand", "show_category_by_type",
3943 $CFG_GLPI["root_doc"]."/ajax/dropdownTicketCategories.php",
3944 $params);
3945 }
3946 } else {
3947 echo self::getTicketTypeName($this->fields["type"]);
3948 }
3949 echo "</td>";
3950 echo "<th width='$colsize3%'>".sprintf(__('%1$s%2$s'), __('Category'),
3951 $tt->getMandatoryMark('itilcategories_id'))."</th>";
3952 echo "<td width='$colsize4%'>";
3953 // Permit to set category when creating ticket without update right
3954 if ($canupdate
3955 || !$ID
3956 || $canupdate_descr) {
3957
3958 $opt = array('value' => $this->fields["itilcategories_id"],
3959 'entity' => $this->fields["entities_id"]);
3960 if ($_SESSION["glpiactiveprofile"]["interface"] == "helpdesk") {
3961 $opt['condition'] = "`is_helpdeskvisible`='1' AND ";
3962 } else {
3963 $opt['condition'] = '';
3964 }
3965 /// Auto submit to load template
3966 if (!$ID) {
3967 $opt['on_change'] = 'this.form.submit()';
3968 }
3969 /// if category mandatory, no empty choice
3970 /// no empty choice is default value set on ticket creation, else yes
3971 if (($ID || $values['itilcategories_id'])
3972 && $tt->isMandatoryField("itilcategories_id")
3973 && ($this->fields["itilcategories_id"] > 0)) {
3974 $opt['display_emptychoice'] = false;
3975 }
3976
3977 switch ($this->fields["type"]) {
3978 case self::INCIDENT_TYPE :
3979 $opt['condition'] .= "`is_incident`='1'";
3980 break;
3981
3982 case self::DEMAND_TYPE :
3983 $opt['condition'] .= "`is_request`='1'";
3984 break;
3985
3986 default :
3987 break;
3988 }
3989 echo "<span id='show_category_by_type'>";
3990 ITILCategory::dropdown($opt);
3991 echo "</span>";
3992 } else {
3993 echo Dropdown::getDropdownName("glpi_itilcategories", $this->fields["itilcategories_id"]);
3994 }
3995 echo "</td>";
3996 echo "</tr>";
3997
3998 if (!$ID) {
3999 echo "</table>";
4000 $this->showActorsPartForm($ID, $values);
4001 echo "<table class='tab_cadre_fixe' id='mainformtable3'>";
4002 }
4003
4004 echo "<tr class='tab_bg_1'>";
4005 echo "<th width='$colsize1%'>".$tt->getBeginHiddenFieldText('status');
4006 printf(__('%1$s%2$s'), __('Status'), $tt->getMandatoryMark('status'));
4007 echo $tt->getEndHiddenFieldText('status')."</th>";
4008 echo "<td width='$colsize2%'>";
4009 echo $tt->getBeginHiddenFieldValue('status');
4010 if ($canstatus) {
4011 self::dropdownStatus(array('value' => $this->fields["status"],
4012 'showtype' => 'allowed'));
4013 TicketValidation::alertValidation($this, 'status');
4014 } else {
4015 echo self::getStatus($this->fields["status"]);
4016 if (in_array($this->fields["status"], $this->getClosedStatusArray())
4017 && $this->isAllowedStatus($this->fields['status'], Ticket::INCOMING)) {
4018 echo " <a class='vsubmit' href='".$this->getLinkURL().
4019 "&forcetab=TicketFollowup$1&_openfollowup=1'>". __('Reopen')."</a>";
4020 }
4021 }
4022 echo $tt->getEndHiddenFieldValue('status',$this);
4023
4024 echo "</td>";
4025 echo "<th width='$colsize3%'>".$tt->getBeginHiddenFieldText('requesttypes_id');
4026 printf(__('%1$s%2$s'), __('Request source'), $tt->getMandatoryMark('requesttypes_id'));
4027 echo $tt->getEndHiddenFieldText('requesttypes_id')."</th>";
4028 echo "<td width='$colsize4%'>";
4029 echo $tt->getBeginHiddenFieldValue('requesttypes_id');
4030 if ($canupdate) {
4031 RequestType::dropdown(array('value' => $this->fields["requesttypes_id"]));
4032 } else {
4033 echo Dropdown::getDropdownName('glpi_requesttypes', $this->fields["requesttypes_id"]);
4034 echo Html::hidden('requesttypes_id', array('value' => $this->fields["requesttypes_id"]));
4035 }
4036 echo $tt->getEndHiddenFieldValue('requesttypes_id',$this);
4037 echo "</td>";
4038 echo "</tr>";
4039
4040 echo "<tr class='tab_bg_1'>";
4041 echo "<th>".$tt->getBeginHiddenFieldText('urgency');
4042 printf(__('%1$s%2$s'), __('Urgency'), $tt->getMandatoryMark('urgency'));
4043 echo $tt->getEndHiddenFieldText('urgency')."</th>";
4044 echo "<td>";
4045
4046 if (($canupdate && $canpriority)
4047 || !$ID
4048 || $canupdate_descr) {
4049 // Only change during creation OR when allowed to change priority OR when user is the creator
4050 echo $tt->getBeginHiddenFieldValue('urgency');
4051 $idurgency = self::dropdownUrgency(array('value' => $this->fields["urgency"]));
4052 echo $tt->getEndHiddenFieldValue('urgency', $this);
4053
4054 } else {
4055 $idurgency = "value_urgency".mt_rand();
4056 echo "<input id='$idurgency' type='hidden' name='urgency' value='".
4057 $this->fields["urgency"]."'>";
4058 echo $tt->getBeginHiddenFieldValue('urgency');
4059 echo parent::getUrgencyName($this->fields["urgency"]);
4060 echo $tt->getEndHiddenFieldValue('urgency', $this);
4061 }
4062 echo "</td>";
4063 // Display validation state
4064 echo "<th>";
4065 if (!$ID) {
4066 echo $tt->getBeginHiddenFieldText('_add_validation');
4067 printf(__('%1$s%2$s'), __('Approval request'), $tt->getMandatoryMark('_add_validation'));
4068 echo $tt->getEndHiddenFieldText('_add_validation');
4069 } else {
4070 echo $tt->getBeginHiddenFieldText('global_validation');
4071 _e('Approval');
4072 echo $tt->getEndHiddenFieldText('global_validation');
4073 }
4074 echo "</th>";
4075 echo "<td>";
4076 if (!$ID) {
4077 echo $tt->getBeginHiddenFieldValue('_add_validation');
4078 $validation_right = '';
4079 if (($values['type'] == self::INCIDENT_TYPE)
4080 && Session::haveRight('ticketvalidation', TicketValidation::CREATEINCIDENT)) {
4081 $validation_right = 'validate_incident';
4082 }
4083 if (($values['type'] == self::DEMAND_TYPE)
4084 && Session::haveRight('ticketvalidation', TicketValidation::CREATEREQUEST)) {
4085 $validation_right = 'validate_request';
4086 }
4087
4088 if (!empty($validation_right)) {
4089 echo "<input type='hidden' name='_add_validation' value='".
4090 $values['_add_validation']."'>";
4091
4092 $params = array('name' => "users_id_validate",
4093 'entity' => $this->fields['entities_id'],
4094 'right' => $validation_right,
4095 'users_id_validate' => $values['users_id_validate']);
4096 TicketValidation::dropdownValidator($params);
4097 }
4098 echo $tt->getEndHiddenFieldValue('_add_validation',$this);
4099 if ($tt->isPredefinedField('global_validation')) {
4100 echo "<input type='hidden' name='global_validation' value='".
4101 $tt->predefined['global_validation']."'>";
4102 }
4103 } else {
4104 echo $tt->getBeginHiddenFieldValue('global_validation');
4105
4106 if (Session::haveRightsOr('ticketvalidation', TicketValidation::getCreateRights())
4107 && $canupdate) {
4108 TicketValidation::dropdownStatus('global_validation',
4109 array('global' => true,
4110 'value' => $this->fields['global_validation']));
4111 } else {
4112 echo TicketValidation::getStatus($this->fields['global_validation']);
4113 }
4114 echo $tt->getEndHiddenFieldValue('global_validation',$this);
4115
4116 }
4117 echo "</td></tr>";
4118
4119 echo "<tr class='tab_bg_1'>";
4120 echo "<th>".$tt->getBeginHiddenFieldText('impact');
4121 printf(__('%1$s%2$s'), __('Impact'), $tt->getMandatoryMark('impact'));
4122 echo $tt->getEndHiddenFieldText('impact')."</th>";
4123 echo "<td>";
4124 echo $tt->getBeginHiddenFieldValue('impact');
4125
4126 if ($canupdate) {
4127 $idimpact = self::dropdownImpact(array('value' => $this->fields["impact"]));
4128 } else {
4129 $idimpact = "value_impact".mt_rand();
4130 echo "<input id='$idimpact' type='hidden' name='impact' value='".$this->fields["impact"]."'>";
4131 echo parent::getImpactName($this->fields["impact"]);
4132 }
4133 echo $tt->getEndHiddenFieldValue('impact',$this);
4134 echo "</td>";
4135
4136 echo "<th>".$tt->getBeginHiddenFieldText('locations_id');
4137 printf(__('%1$s%2$s'), __('Location'), $tt->getMandatoryMark('locations_id'));
4138 echo $tt->getEndHiddenFieldText('locations_id')."</th>";
4139 echo "<td>";
4140 echo $tt->getBeginHiddenFieldValue('locations_id');
4141 if ($canupdate || !$ID) {
4142 Location::dropdown(array('value' => $this->fields['locations_id'],
4143 'entity' => $this->fields['entities_id']));
4144 } else {
4145 echo Dropdown::getDropdownName('glpi_locations', $this->fields["locations_id"]);
4146 }
4147 echo $tt->getEndHiddenFieldValue('locations_id', $this);
4148 echo "</td>";
4149 echo "</tr>";
4150
4151
4152 echo "<tr class='tab_bg_1'>";
4153 echo "<th>".$tt->getBeginHiddenFieldText('priority');
4154 printf(__('%1$s%2$s'), __('Priority'), $tt->getMandatoryMark('priority'));
4155 echo $tt->getEndHiddenFieldText('priority')."</th>";
4156 echo "<td>";
4157 $idajax = 'change_priority_' . mt_rand();
4158
4159 if ($canupdate
4160 && $canpriority
4161 && !$tt->isHiddenField('priority')) {
4162 $idpriority = parent::dropdownPriority(array('value' => $this->fields["priority"],
4163 'withmajor' => true));
4164 $idpriority = 'dropdown_priority'.$idpriority;
4165 echo " <span id='$idajax' style='display:none'></span>";
4166
4167 } else {
4168 $idpriority = 0;
4169 echo $tt->getBeginHiddenFieldValue('priority');
4170 echo "<span id='$idajax'>".parent::getPriorityName($this->fields["priority"])."</span>";
4171 echo $tt->getEndHiddenFieldValue('priority', $this);
4172 }
4173
4174 if ($canupdate
4175 || $canupdate_descr) {
4176 $params = array('urgency' => '__VALUE0__',
4177 'impact' => '__VALUE1__',
4178 'priority' => $idpriority);
4179 Ajax::updateItemOnSelectEvent(array('dropdown_urgency'.$idurgency,
4180 'dropdown_impact'.$idimpact),
4181 $idajax,
4182 $CFG_GLPI["root_doc"]."/ajax/priority.php", $params);
4183 }
4184 echo "</td>";
4185
4186
4187
4188 echo "<th rowspan='2'>".$tt->getBeginHiddenFieldText('itemtype');
4189 printf(__('%1$s%2$s'), _n('Associated element', 'Associated elements', Session::getPluralNumber()), $tt->getMandatoryMark('itemtype'));
4190 if ($ID && $canupdate) {
4191 echo " <a href='".$this->getFormURL()."?id=".$ID.
4192 "&forcetab=Item_Ticket$1'><img title='".__s('Update')."' alt='".__s('Update')."'
4193 class='pointer' src='".$CFG_GLPI["root_doc"]."/pics/showselect.png'></a>";
4194 }
4195 echo $tt->getEndHiddenFieldText('itemtype');
4196 echo "</th>";
4197 echo "<td rowspan='2'>";
4198 if (!$ID) {
4199 echo $tt->getBeginHiddenFieldValue('itemtype');
4200
4201 // Select hardware on creation or if have update right
4202 if (Session::haveRight('ticket', CREATE)) {
4203 $dev_user_id = $values['_users_id_requester'];
4204 $dev_itemtype = $values["itemtype"];
4205 $dev_items_id = $values["items_id"];
4206
4207 if ($dev_user_id > 0) {
4208 Item_Ticket::dropdownMyDevices($dev_user_id, $this->fields["entities_id"], $dev_itemtype, $dev_items_id);
4209 }
4210 Item_Ticket::dropdownAllDevices("itemtype", $dev_itemtype, $dev_items_id, 1, $dev_user_id, $this->fields["entities_id"]);
4211
4212 echo "<span id='item_ticket_selection_information'></span>";
4213 }
4214 echo $tt->getEndHiddenFieldValue('itemtype', $this);
4215 } else {
4216 // display associated elements
4217 $item_tickets = getAllDatasFromTable(
4218 getTableForItemType('Item_Ticket'),
4219 "`tickets_id`='".$ID."'");
4220 $i = 0;
4221 foreach ($item_tickets as $itdata) {
4222 if ($i >= 5) {
4223 echo "<i><a href='".$this->getFormURL()."?id=".$ID.
4224 "&forcetab=Item_Ticket$1'>"
4225 .__('Display all items')." (".count($item_tickets).")</a></i>";
4226 break;
4227 }
4228 $item = new $itdata['itemtype'];
4229 $item->getFromDB($itdata['items_id']);
4230 echo $item->getTypeName(1).": ".$item->getLink(array('comments' => true))."<br/>";
4231 $i++;
4232 }
4233
4234 }
4235 echo "</td>";
4236 echo "</tr>";
4237
4238
4239 echo "<tr class='tab_bg_1'>";
4240 // Need comment right to add a followup with the actiontime
4241 if (!$ID
4242 && Session::haveRight('followup', TicketFollowup::ADDALLTICKET)) {
4243 echo "<th>".$tt->getBeginHiddenFieldText('actiontime');
4244 printf(__('%1$s%2$s'), __('Total duration'), $tt->getMandatoryMark('actiontime'));
4245 echo $tt->getEndHiddenFieldText('actiontime')."</th>";
4246 echo "<td>";
4247 echo $tt->getBeginHiddenFieldValue('actiontime');
4248 Dropdown::showTimeStamp('actiontime', array('value' => $values['actiontime'],
4249 'addfirstminutes' => true));
4250 echo $tt->getEndHiddenFieldValue('actiontime',$this);
4251 echo "</td>";
4252 }
4253
4254 echo "</tr>";
4255
4256 echo "</table>";
4257 if ($ID) {
4258 $this->showActorsPartForm($ID, $values);
4259 }
4260
4261 $view_linked_tickets = ($ID || $canupdate);
4262
4263 echo "<table class='tab_cadre_fixe' id='mainformtable4'>";
4264 echo "<tr class='tab_bg_1'>";
4265 echo "<th width='$colsize1%'>".$tt->getBeginHiddenFieldText('name');
4266 printf(__('%1$s%2$s'), __('Title'), $tt->getMandatoryMark('name'));
4267 echo $tt->getEndHiddenFieldText('name')."</th>";
4268 echo "<td width='".(100-$colsize1)."%' colspan='3'>";
4269 if (!$ID
4270 || $canupdate_descr) {
4271 echo $tt->getBeginHiddenFieldValue('name');
4272 echo "<input type='text' style='width:98%' maxlength=250 name='name' ".
4273 " value=\"".Html::cleanInputText($this->fields["name"])."\">";
4274 echo $tt->getEndHiddenFieldValue('name', $this);
4275 } else {
4276 if (empty($this->fields["name"])) {
4277 _e('Without title');
4278 } else {
4279 echo $this->fields["name"];
4280 }
4281 }
4282 echo "</td>";
4283 echo "</tr>";
4284
4285 echo "<tr class='tab_bg_1'>";
4286 echo "<th width='$colsize1%'>".$tt->getBeginHiddenFieldText('content');
4287 printf(__('%1$s%2$s'), __('Description'), $tt->getMandatoryMark('content'));
4288 if (!$ID
4289 || $canupdate_descr) {
4290 $content = Toolbox::unclean_cross_side_scripting_deep(Html::entity_decode_deep($this->fields['content']));
4291 Html::showTooltip(nl2br(Html::Clean($content)));
4292 }
4293 echo $tt->getEndHiddenFieldText('content')."</th>";
4294 echo "<td width='".(100-$colsize1)."%' colspan='3'>";
4295 if (!$ID
4296 || $canupdate_descr) { // Admin =oui on autorise la modification de la description
4297 echo $tt->getBeginHiddenFieldValue('content');
4298 $rand = mt_rand();
4299 $rand_text = mt_rand();
4300 $rows = 6;
4301 $content_id = "content$rand";
4302
4303 if ($CFG_GLPI["use_rich_text"]) {
4304 $this->fields["content"] = $this->setRichTextContent($content_id,
4305 $this->fields["content"],
4306 $rand);
4307 $rows = 10;
4308 } else {
4309 $this->fields["content"] = $this->setSimpleTextContent($this->fields["content"]);
4310 }
4311
4312 echo "<div id='content$rand_text'>";
4313 echo "<textarea id='$content_id' name='content' style='width:100%' rows='$rows'>".
4314 $this->fields["content"]."</textarea></div>";
4315 echo Html::scriptBlock("$(document).ready(function() { $('#$content_id').autogrow(); });");
4316 echo $tt->getEndHiddenFieldValue('content', $this);
4317
4318 } else {
4319 $content = Toolbox::unclean_cross_side_scripting_deep(Html::entity_decode_deep($this->fields['content']));
4320 echo nl2br(Html::Clean($content));
4321 }
4322 echo "</td>";
4323 echo "</tr>";
4324
4325 echo "<tr class='tab_bg_1'>";
4326 if ($view_linked_tickets) {
4327 echo "<th width='$colsize1%'>". _n('Linked ticket', 'Linked tickets', Session::getPluralNumber());
4328 $rand_linked_ticket = mt_rand();
4329 if ($canupdate) {
4330 echo " ";
4331 echo "<img onClick=\"".Html::jsShow("linkedticket$rand_linked_ticket")."\"
4332 title=\"".__s('Add')."\" alt=\"".__s('Add')."\"
4333 class='pointer' src='".$CFG_GLPI["root_doc"]."/pics/add_dropdown.png'>";
4334 }
4335 echo '</th>';
4336 echo "<td width='".(100-$colsize1)."%' colspan='3'>";
4337 if ($canupdate) {
4338 echo "<div style='display:none' id='linkedticket$rand_linked_ticket'>";
4339 echo "<table class='tab_format' width='100%'><tr><td width='30%'>";
4340 Ticket_Ticket::dropdownLinks('_link[link]',
4341 (isset($values["_link"])?$values["_link"]['link']:''));
4342 echo "<input type='hidden' name='_link[tickets_id_1]' value='$ID'>\n";
4343 echo "</td><td width='70%'>";
4344 $linkparam = array('name' => '_link[tickets_id_2]',
4345 'displaywith' => array('id'));
4346
4347 if (isset($values["_link"])) {
4348 $linkparam['value'] = $values["_link"]['tickets_id_2'];
4349 }
4350 Ticket::dropdown($linkparam);
4351 echo "</td></tr></table>";
4352 echo "</div>";
4353
4354 if (isset($values["_link"])
4355 && !empty($values["_link"]['tickets_id_2'])) {
4356 echo "<script language='javascript'>";
4357 echo Html::jsShow("linkedticket$rand_linked_ticket");
4358 echo "</script>";
4359 }
4360 }
4361
4362 Ticket_Ticket::displayLinkedTicketsTo($ID);
4363 echo "</td>";
4364 }
4365 echo "</tr>";
4366
4367 // View files added
4368 echo "<tr class='tab_bg_1'>";
4369 // Permit to add doc when creating a ticket
4370 echo "<th width='$colsize1%'>";
4371 echo $tt->getBeginHiddenFieldText('_documents_id');
4372 $doctitle = sprintf(__('File (%s)'), Document::getMaxUploadSize());
4373 printf(__('%1$s%2$s'), $doctitle, $tt->getMandatoryMark('_documents_id'));
4374 // Do not show if hidden.
4375 if (!$tt->isHiddenField('_documents_id')) {
4376 DocumentType::showAvailableTypesLink();
4377 }
4378 echo $tt->getEndHiddenFieldText('_documents_id');
4379 echo "</th>";
4380 echo "<td colspan='3' width='".(100-$colsize1)."%' >";
4381 // Do not set values
4382 echo $tt->getEndHiddenFieldValue('_documents_id');
4383 if ($tt->isPredefinedField('_documents_id')) {
4384 if (isset($values['_documents_id'])
4385 && is_array($values['_documents_id'])
4386 && count($values['_documents_id'])) {
4387
4388 echo "<span class='b'>".__('Default documents:').'</span>';
4389 echo "<br>";
4390 $doc = new Document();
4391 foreach ($values['_documents_id'] as $key => $val) {
4392 if ($doc->getFromDB($val)) {
4393 echo "<input type='hidden' name='_documents_id[$key]' value='$val'>";
4394 echo "- ".$doc->getNameID()."<br>";
4395 }
4396 }
4397 }
4398 }
4399 echo "<div id='fileupload_info'></div>";
4400 echo "</td>";
4401 echo "</tr>";
4402
4403 if ((!$ID
4404 || $canupdate
4405 || $canupdate_descr
4406 || Session::haveRightsOr(self::$rightname, array(self::ASSIGN, self::STEAL, DELETE, PURGE)))
4407 && !$options['template_preview']) {
4408
4409 echo "<tr class='tab_bg_1'>";
4410
4411 if ($ID) {
4412 if (Session::haveRightsOr(self::$rightname, array(UPDATE, DELETE, PURGE))
4413 || $this->canDeleteItem()
4414 || $this->canUpdateItem()) {
4415 echo "<td class='tab_bg_2 center' colspan='4'>";
4416 if ($this->fields["is_deleted"] == 1) {
4417 if (self::canPurge()) {
4418 echo "<input type='submit' class='submit' name='restore' value='".
4419 _sx('button', 'Restore')."'> ";
4420 }
4421 } else {
4422 if (self::canUpdate() ) {
4423 echo "<input type='submit' class='submit' name='update' value='".
4424 _sx('button', 'Save')."'> ";
4425 }
4426 }
4427 if ($this->fields["is_deleted"] == 1) {
4428 if (self::canPurge()) {
4429 echo "<input type='submit' class='submit' name='purge' value='".
4430 _sx('button', 'Delete permanently')."' ".
4431 Html::addConfirmationOnAction(__('Confirm the final deletion?')).">";
4432 }
4433 } else {
4434 if (self::canDelete()) {
4435 echo "<input type='submit' class='submit' name='delete' value='".
4436 _sx('button', 'Put in dustbin')."'>";
4437 }
4438 }
4439 echo "<input type='hidden' name='_read_date_mod' value='".$this->getField('date_mod')."'>";
4440 echo "</td>";
4441 }
4442
4443 } else {
4444 echo "<td class='tab_bg_2 center' colspan='4'>";
4445 echo "<input type='submit' name='add' value=\""._sx('button','Add')."\" class='submit'>";
4446 if ($tt->isField('id') && ($tt->fields['id'] > 0)) {
4447 echo "<input type='hidden' name='_tickettemplates_id' value='".$tt->fields['id']."'>";
4448 echo "<input type='hidden' name='_predefined_fields'
4449 value=\"".Toolbox::prepareArrayForInput($predefined_fields)."\">";
4450 }
4451 }
4452 }
4453
4454 // File upload system
4455 $colspan = 3;
4456 if (!$CFG_GLPI['use_rich_text']) {
4457 $colspan = 4;
4458 }
4459 echo "<tr class='tab_bg_1'>";
4460 echo "<td colspan='$colspan'>";
4461 echo $tt->getBeginHiddenFieldValue('_documents_id');
4462
4463 echo Html::file(array('multiple' => true,
4464 'showfilecontainer' => 'fileupload_info',
4465 'values' => array('filename' => $values['_filename'],
4466 'tag' => $values['_tag_filename'])
4467 ));
4468 echo "</td>";
4469 if ($CFG_GLPI['use_rich_text']) {
4470 echo "</tr>";
4471 echo "<tr class='tab_bg_1'>";
4472 echo "<td colspan='$colspan'>";
4473 if (!isset($rand)) {
4474 $rand = mt_rand();
4475 }
4476 if ($canupdate_descr) {
4477 echo Html::initImagePasteSystem($content_id, $rand);
4478 }
4479 echo "</td>";
4480 }
4481 echo "</tr>";
4482
4483 echo "</table>";
4484 echo "<input type='hidden' name='id' value='$ID'>";
4485
4486 echo "</div>";
4487
4488 if (!$options['template_preview']) {
4489 Html::closeForm();
4490 }
4491
4492 return true;
4493 }
4494
4495
4496 /**
4497 * @param $size (default 25)
4498 **/
4499 static function showDocumentAddButton($size=25) {
4500 global $CFG_GLPI;
4501
4502 echo "<script type='text/javascript'>var nbfiles=1; var maxfiles = 5;</script>";
4503 echo "<span id='addfilebutton'><img title=\"".__s('Add')."\" alt=\"".
4504 __s('Add')."\" onClick=\"if (nbfiles<maxfiles){
4505 var row = ".Html::jsGetElementbyID('uploadfiles').";
4506 row.append('<br><input type=\'file\' name=\'filename[]\' size=\'$size\'>');
4507 nbfiles++;
4508 if (nbfiles==maxfiles) {
4509 ".Html::jsHide('addfilebutton')."
4510 }
4511 }\"
4512 class='pointer' src='".$CFG_GLPI["root_doc"]."/pics/add_dropdown.png'></span>";
4513 }
4514
4515
4516 /**
4517 * @param $start
4518 * @param $status (default ''process)
4519 * @param $showgrouptickets (true by default)
4520 */
4521 static function showCentralList($start, $status="process", $showgrouptickets=true) {
4522 global $DB, $CFG_GLPI;
4523
4524 if (!Session::haveRightsOr(self::$rightname, array(CREATE, self::READALL, self::READASSIGN))
4525 && !Session::haveRightsOr('ticketvalidation', TicketValidation::getValidateRights())) {
4526
4527 return false;
4528 }
4529
4530 $search_users_id = " (`glpi_tickets_users`.`users_id` = '".Session::getLoginUserID()."'
4531 AND `glpi_tickets_users`.`type` = '".CommonITILActor::REQUESTER."') ";
4532 $search_assign = " (`glpi_tickets_users`.`users_id` = '".Session::getLoginUserID()."'
4533 AND `glpi_tickets_users`.`type` = '".CommonITILActor::ASSIGN."')";
4534 $search_observer = " (`glpi_tickets_users`.`users_id` = '".Session::getLoginUserID()."'
4535 AND `glpi_tickets_users`.`type` = '".CommonITILActor::OBSERVER."')";
4536 $is_deleted = " `glpi_tickets`.`is_deleted` = 0 ";
4537
4538
4539 if ($showgrouptickets) {
4540 $search_users_id = " 0 = 1 ";
4541 $search_assign = " 0 = 1 ";
4542
4543 if (count($_SESSION['glpigroups'])) {
4544 $groups = implode("','",$_SESSION['glpigroups']);
4545 $search_assign = " (`glpi_groups_tickets`.`groups_id` IN ('".$groups."')
4546 AND `glpi_groups_tickets`.`type` = '".CommonITILActor::ASSIGN."')";
4547
4548 if (Session::haveRight(self::$rightname, self::READGROUP)) {
4549 $search_users_id = " (`glpi_groups_tickets`.`groups_id` IN ('".$groups."')
4550 AND `glpi_groups_tickets`.`type`
4551 = '".CommonITILActor::REQUESTER."') ";
4552 $search_observer = " (`glpi_groups_tickets`.`groups_id` IN ('".$groups."')
4553 AND `glpi_groups_tickets`.`type`
4554 = '".CommonITILActor::OBSERVER."') ";
4555 }
4556 }
4557 }
4558
4559 $query = "SELECT DISTINCT `glpi_tickets`.`id`
4560 FROM `glpi_tickets`
4561 LEFT JOIN `glpi_tickets_users`
4562 ON (`glpi_tickets`.`id` = `glpi_tickets_users`.`tickets_id`)
4563 LEFT JOIN `glpi_groups_tickets`
4564 ON (`glpi_tickets`.`id` = `glpi_groups_tickets`.`tickets_id`)";
4565
4566 switch ($status) {
4567 case "waiting" : // on affiche les tickets en attente
4568 $query .= "WHERE $is_deleted
4569 AND ($search_assign)
4570 AND `status` = '".self::WAITING."' ".
4571 getEntitiesRestrictRequest("AND", "glpi_tickets");
4572 break;
4573
4574 case "process" : // on affiche les tickets planifiés ou assignés au user
4575 $query .= "WHERE $is_deleted
4576 AND ( $search_assign )
4577 AND (`status` IN ('".implode("','", self::getProcessStatusArray())."')) ".
4578 getEntitiesRestrictRequest("AND", "glpi_tickets");
4579 break;
4580
4581 case "toapprove" : // on affiche les tickets planifiés ou assignés au user
4582 $query .= "WHERE $is_deleted
4583 AND (`status` = '".self::SOLVED."')
4584 AND ($search_users_id";
4585 if (!$showgrouptickets) {
4586 $query .= " OR `glpi_tickets`.users_id_recipient = '".Session::getLoginUserID()."' ";
4587 }
4588 $query .= ")".
4589 getEntitiesRestrictRequest("AND", "glpi_tickets");
4590 break;
4591
4592 case "tovalidate" : // on affiche les tickets à valider
4593 $query .= " LEFT JOIN `glpi_ticketvalidations`
4594 ON (`glpi_tickets`.`id` = `glpi_ticketvalidations`.`tickets_id`)
4595 WHERE $is_deleted
4596 AND `users_id_validate` = '".Session::getLoginUserID()."'
4597 AND `glpi_ticketvalidations`.`status` = '".CommonITILValidation::WAITING."'
4598 AND (`glpi_tickets`.`status` NOT IN ('".self::CLOSED."',
4599 '".self::SOLVED."')) ".
4600 getEntitiesRestrictRequest("AND", "glpi_tickets");
4601 break;
4602
4603 case "rejected" : // on affiche les tickets rejetés
4604 $query .= "WHERE $is_deleted
4605 AND ($search_assign)
4606 AND `status` <> '".self::CLOSED."'
4607 AND `global_validation` = '".CommonITILValidation::REFUSED."' ".
4608 getEntitiesRestrictRequest("AND", "glpi_tickets");
4609 break;
4610
4611 case "observed" :
4612 $query .= "WHERE $is_deleted
4613 AND ($search_observer)
4614 AND (`status` IN ('".self::INCOMING."',
4615 '".self::PLANNED."',
4616 '".self::ASSIGNED."',
4617 '".self::WAITING."'))
4618 AND NOT ( $search_assign )
4619 AND NOT ( $search_users_id ) ".
4620 getEntitiesRestrictRequest("AND","glpi_tickets");
4621 break;
4622
4623 case "survey" : // on affiche les tickets dont l'enquête de satisfaction n'est pas remplie
4624 $query .= " INNER JOIN `glpi_ticketsatisfactions`
4625 ON (`glpi_tickets`.`id` = `glpi_ticketsatisfactions`.`tickets_id`)
4626 WHERE $is_deleted
4627 AND ($search_users_id
4628 OR `glpi_tickets`.`users_id_recipient` = '".Session::getLoginUserID()."')
4629 AND `glpi_tickets`.`status` = '".self::CLOSED."'
4630 AND `glpi_ticketsatisfactions`.`date_answered` IS NULL ".
4631 getEntitiesRestrictRequest("AND", "glpi_tickets");
4632 break;
4633
4634 case "requestbyself" : // on affiche les tickets demandés le user qui sont planifiés ou assignés
4635 // Ã quelqu'un d'autre (exclut les self-tickets)
4636
4637 default :
4638 $query .= "WHERE $is_deleted
4639 AND ($search_users_id)
4640 AND (`status` IN ('".self::INCOMING."',
4641 '".self::PLANNED."',
4642 '".self::ASSIGNED."',
4643 '".self::WAITING."'))
4644 AND NOT ( $search_assign ) ".
4645 getEntitiesRestrictRequest("AND","glpi_tickets");
4646 }
4647
4648 $query .= " ORDER BY date_mod DESC";
4649 $result = $DB->query($query);
4650 $numrows = $DB->numrows($result);
4651
4652 if ($_SESSION['glpidisplay_count_on_home'] > 0) {
4653 $query .= " LIMIT ".intval($start).','.intval($_SESSION['glpidisplay_count_on_home']);
4654 $result = $DB->query($query);
4655 $number = $DB->numrows($result);
4656 } else {
4657 $number = 0;
4658 }
4659
4660 if ($numrows > 0) {
4661 echo "<table class='tab_cadrehov'>";
4662 echo "<tr class='noHover'><th colspan='4'>";
4663
4664 $options['reset'] = 'reset';
4665 $forcetab = '';
4666 $num = 0;
4667 if ($showgrouptickets) {
4668 switch ($status) {
4669 case "toapprove" :
4670 $options['criteria'][0]['field'] = 12; // status
4671 $options['criteria'][0]['searchtype'] = 'equals';
4672 $options['criteria'][0]['value'] = self::SOLVED;
4673 $options['criteria'][0]['link'] = 'AND';
4674
4675 $options['criteria'][1]['field'] = 71; // groups_id
4676 $options['criteria'][1]['searchtype'] = 'equals';
4677 $options['criteria'][1]['value'] = 'mygroups';
4678 $options['criteria'][1]['link'] = 'AND';
4679 $forcetab = 'Ticket$2';
4680
4681 echo "<a href=\"".$CFG_GLPI["root_doc"]."/front/ticket.php?".
4682 Toolbox::append_params($options,'&')."\">".
4683 Html::makeTitle(__('Your tickets to close'), $number, $numrows)."</a>";
4684 break;
4685
4686 case "waiting" :
4687 $options['criteria'][0]['field'] = 12; // status
4688 $options['criteria'][0]['searchtype'] = 'equals';
4689 $options['criteria'][0]['value'] = self::WAITING;
4690 $options['criteria'][0]['link'] = 'AND';
4691
4692 $options['criteria'][1]['field'] = 8; // groups_id_assign
4693 $options['criteria'][1]['searchtype'] = 'equals';
4694 $options['criteria'][1]['value'] = 'mygroups';
4695 $options['criteria'][1]['link'] = 'AND';
4696
4697 echo "<a href=\"".$CFG_GLPI["root_doc"]."/front/ticket.php?".
4698 Toolbox::append_params($options,'&')."\">".
4699 Html::makeTitle(__('Tickets on pending status'), $number, $numrows)."</a>";
4700 break;
4701
4702 case "process" :
4703 $options['criteria'][0]['field'] = 12; // status
4704 $options['criteria'][0]['searchtype'] = 'equals';
4705 $options['criteria'][0]['value'] = 'process';
4706 $options['criteria'][0]['link'] = 'AND';
4707
4708 $options['criteria'][1]['field'] = 8; // groups_id_assign
4709 $options['criteria'][1]['searchtype'] = 'equals';
4710 $options['criteria'][1]['value'] = 'mygroups';
4711 $options['criteria'][1]['link'] = 'AND';
4712
4713 echo "<a href=\"".$CFG_GLPI["root_doc"]."/front/ticket.php?".
4714 Toolbox::append_params($options,'&')."\">".
4715 Html::makeTitle(__('Tickets to be processed'), $number, $numrows)."</a>";
4716 break;
4717
4718 case "observed":
4719 $options['criteria'][0]['field'] = 12; // status
4720 $options['criteria'][0]['searchtype'] = 'equals';
4721 $options['criteria'][0]['value'] = 'notold';
4722 $options['criteria'][0]['link'] = 'AND';
4723
4724 $options['criteria'][1]['field'] = 65; // groups_id
4725 $options['criteria'][1]['searchtype'] = 'equals';
4726 $options['criteria'][1]['value'] = 'mygroups';
4727 $options['criteria'][1]['link'] = 'AND';
4728
4729 echo "<a href=\"".$CFG_GLPI["root_doc"]."/front/ticket.php?".
4730 Toolbox::append_params($options,'&')."\">".
4731 Html::makeTitle(__('Your observed tickets'), $number, $numrows)."</a>";
4732 break;
4733
4734 case "requestbyself" :
4735 default :
4736 $options['criteria'][0]['field'] = 12; // status
4737 $options['criteria'][0]['searchtype'] = 'equals';
4738 $options['criteria'][0]['value'] = 'notold';
4739 $options['criteria'][0]['link'] = 'AND';
4740
4741 $options['criteria'][1]['field'] = 71; // groups_id
4742 $options['criteria'][1]['searchtype'] = 'equals';
4743 $options['criteria'][1]['value'] = 'mygroups';
4744 $options['criteria'][1]['link'] = 'AND';
4745
4746 echo "<a href=\"".$CFG_GLPI["root_doc"]."/front/ticket.php?".
4747 Toolbox::append_params($options,'&')."\">".
4748 Html::makeTitle(__('Your tickets in progress'), $number, $numrows)."</a>";
4749 }
4750
4751 } else {
4752 switch ($status) {
4753 case "waiting" :
4754 $options['criteria'][0]['field'] = 12; // status
4755 $options['criteria'][0]['searchtype'] = 'equals';
4756 $options['criteria'][0]['value'] = self::WAITING;
4757 $options['criteria'][0]['link'] = 'AND';
4758
4759 $options['criteria'][1]['field'] = 5; // users_id_assign
4760 $options['criteria'][1]['searchtype'] = 'equals';
4761 $options['criteria'][1]['value'] = Session::getLoginUserID();
4762 $options['criteria'][1]['link'] = 'AND';
4763
4764 echo "<a href=\"".$CFG_GLPI["root_doc"]."/front/ticket.php?".
4765 Toolbox::append_params($options,'&')."\">".
4766 Html::makeTitle(__('Tickets on pending status'), $number, $numrows)."</a>";
4767 break;
4768
4769 case "process" :
4770 $options['criteria'][0]['field'] = 5; // users_id_assign
4771 $options['criteria'][0]['searchtype'] = 'equals';
4772 $options['criteria'][0]['value'] = Session::getLoginUserID();
4773 $options['criteria'][0]['link'] = 'AND';
4774
4775 $options['criteria'][1]['field'] = 12; // status
4776 $options['criteria'][1]['searchtype'] = 'equals';
4777 $options['criteria'][1]['value'] = 'process';
4778 $options['criteria'][1]['link'] = 'AND';
4779
4780 echo "<a href=\"".$CFG_GLPI["root_doc"]."/front/ticket.php?".
4781 Toolbox::append_params($options,'&')."\">".
4782 Html::makeTitle(__('Tickets to be processed'), $number, $numrows)."</a>";
4783 break;
4784
4785 case "tovalidate" :
4786 $options['criteria'][0]['field'] = 55; // validation status
4787 $options['criteria'][0]['searchtype'] = 'equals';
4788 $options['criteria'][0]['value'] = CommonITILValidation::WAITING;
4789 $options['criteria'][0]['link'] = 'AND';
4790
4791 $options['criteria'][1]['field'] = 59; // validation aprobator
4792 $options['criteria'][1]['searchtype'] = 'equals';
4793 $options['criteria'][1]['value'] = Session::getLoginUserID();
4794 $options['criteria'][1]['link'] = 'AND';
4795
4796 $options['criteria'][2]['field'] = 12; // validation aprobator
4797 $options['criteria'][2]['searchtype'] = 'equals';
4798 $options['criteria'][2]['value'] = 'old';
4799 $options['criteria'][2]['link'] = 'AND NOT';
4800 $forcetab = 'TicketValidation$1';
4801
4802 echo "<a href=\"".$CFG_GLPI["root_doc"]."/front/ticket.php?".
4803 Toolbox::append_params($options,'&')."\">".
4804 Html::makeTitle(__('Your tickets to validate'), $number, $numrows)."</a>";
4805
4806 break;
4807
4808 case "rejected" :
4809 $options['criteria'][0]['field'] = 52; // validation status
4810 $options['criteria'][0]['searchtype'] = 'equals';
4811 $options['criteria'][0]['value'] = CommonITILValidation::REFUSED;
4812 $options['criteria'][0]['link'] = 'AND';
4813
4814 $options['criteria'][1]['field'] = 5; // assign user
4815 $options['criteria'][1]['searchtype'] = 'equals';
4816 $options['criteria'][1]['value'] = Session::getLoginUserID();
4817 $options['criteria'][1]['link'] = 'AND';
4818
4819 echo "<a href=\"".$CFG_GLPI["root_doc"]."/front/ticket.php?".
4820 Toolbox::append_params($options,'&')."\">".
4821 Html::makeTitle(__('Your rejected tickets'), $number, $numrows)."</a>";
4822
4823 break;
4824
4825 case "toapprove" :
4826 $options['criteria'][0]['field'] = 12; // status
4827 $options['criteria'][0]['searchtype'] = 'equals';
4828 $options['criteria'][0]['value'] = self::SOLVED;
4829 $options['criteria'][0]['link'] = 'AND';
4830
4831 $options['criteria'][1]['field'] = 4; // users_id_assign
4832 $options['criteria'][1]['searchtype'] = 'equals';
4833 $options['criteria'][1]['value'] = Session::getLoginUserID();
4834 $options['criteria'][1]['link'] = 'AND';
4835
4836 $options['criteria'][2]['field'] = 22; // users_id_recipient
4837 $options['criteria'][2]['searchtype'] = 'equals';
4838 $options['criteria'][2]['value'] = Session::getLoginUserID();
4839 $options['criteria'][2]['link'] = 'OR';
4840
4841 $options['criteria'][3]['field'] = 12; // status
4842 $options['criteria'][3]['searchtype'] = 'equals';
4843 $options['criteria'][3]['value'] = self::SOLVED;
4844 $options['criteria'][3]['link'] = 'AND';
4845
4846 $forcetab = 'Ticket$2';
4847
4848 echo "<a href=\"".$CFG_GLPI["root_doc"]."/front/ticket.php?".
4849 Toolbox::append_params($options,'&')."\">".
4850 Html::makeTitle(__('Your tickets to close'), $number, $numrows)."</a>";
4851 break;
4852
4853 case "observed" :
4854 $options['criteria'][0]['field'] = 66; // users_id
4855 $options['criteria'][0]['searchtype'] = 'equals';
4856 $options['criteria'][0]['value'] = Session::getLoginUserID();
4857 $options['criteria'][0]['link'] = 'AND';
4858
4859 $options['criteria'][1]['field'] = 12; // status
4860 $options['criteria'][1]['searchtype'] = 'equals';
4861 $options['criteria'][1]['value'] = 'notold';
4862 $options['criteria'][1]['link'] = 'AND';
4863
4864 echo "<a href=\"".$CFG_GLPI["root_doc"]."/front/ticket.php?".
4865 Toolbox::append_params($options,'&')."\">".
4866 Html::makeTitle(__('Your observed tickets'), $number, $numrows)."</a>";
4867 break;
4868
4869 case "survey" :
4870 $options['criteria'][0]['field'] = 12; // status
4871 $options['criteria'][0]['searchtype'] = 'equals';
4872 $options['criteria'][0]['value'] = self::CLOSED;
4873 $options['criteria'][0]['link'] = 'AND';
4874
4875 $options['criteria'][1]['field'] = 60; // enquete generee
4876 $options['criteria'][1]['searchtype'] = 'contains';
4877 $options['criteria'][1]['value'] = '^';
4878 $options['criteria'][1]['link'] = 'AND';
4879
4880 $options['criteria'][2]['field'] = 61; // date_answered
4881 $options['criteria'][2]['searchtype'] = 'contains';
4882 $options['criteria'][2]['value'] = 'NULL';
4883 $options['criteria'][2]['link'] = 'AND';
4884
4885 $options['criteria'][3]['field'] = 22; // auteur
4886 $options['criteria'][3]['searchtype'] = 'equals';
4887 $options['criteria'][3]['value'] = Session::getLoginUserID();
4888 $options['criteria'][3]['link'] = 'AND';
4889 $forcetab = 'Ticket$3';
4890
4891 echo "<a href=\"".$CFG_GLPI["root_doc"]."/front/ticket.php?".
4892 Toolbox::append_params($options,'&')."\">".
4893 Html::makeTitle(__('Satisfaction survey'), $number, $numrows)."</a>";
4894 break;
4895
4896 case "requestbyself" :
4897 default :
4898 $options['criteria'][0]['field'] = 4; // users_id
4899 $options['criteria'][0]['searchtype'] = 'equals';
4900 $options['criteria'][0]['value'] = Session::getLoginUserID();
4901 $options['criteria'][0]['link'] = 'AND';
4902
4903 $options['criteria'][1]['field'] = 12; // status
4904 $options['criteria'][1]['searchtype'] = 'equals';
4905 $options['criteria'][1]['value'] = 'notold';
4906 $options['criteria'][1]['link'] = 'AND';
4907
4908 echo "<a href=\"".$CFG_GLPI["root_doc"]."/front/ticket.php?".
4909 Toolbox::append_params($options,'&')."\">".
4910 Html::makeTitle(__('Your tickets in progress'), $number, $numrows)."</a>";
4911 }
4912 }
4913
4914 echo "</th></tr>";
4915 if ($number) {
4916 echo "<tr><th></th>";
4917 echo "<th>".__('Requester')."</th>";
4918 echo "<th>"._n('Associated element', 'Associated elements', Session::getPluralNumber())."</th>";
4919 echo "<th>".__('Description')."</th></tr>";
4920 for ($i = 0 ; $i < $number ; $i++) {
4921 $ID = $DB->result($result, $i, "id");
4922 self::showVeryShort($ID, $forcetab);
4923 }
4924 }
4925 echo "</table>";
4926
4927 }
4928 }
4929
4930 /**
4931 * Get tickets count
4932 *
4933 * @param $foruser boolean : only for current login user as requester (false by default)
4934 **/
4935 static function showCentralCount($foruser=false) {
4936 global $DB, $CFG_GLPI;
4937
4938 // show a tab with count of jobs in the central and give link
4939 if (!Session::haveRight(self::$rightname, self::READALL) && !self::canCreate()) {
4940 return false;
4941 }
4942 if (!Session::haveRight(self::$rightname, self::READALL)) {
4943 $foruser = true;
4944 }
4945
4946 $query = "SELECT `glpi_tickets`.`status`,
4947 COUNT(DISTINCT `glpi_tickets`.`id`) AS COUNT
4948 FROM `glpi_tickets` ";
4949
4950 if ($foruser) {
4951 $query .= " LEFT JOIN `glpi_tickets_users`
4952 ON (`glpi_tickets`.`id` = `glpi_tickets_users`.`tickets_id`
4953 AND `glpi_tickets_users`.`type` = '".CommonITILActor::REQUESTER."')
4954 LEFT JOIN `glpi_ticketvalidations`
4955 ON (`glpi_tickets`.`id` = `glpi_ticketvalidations`.`tickets_id`)";
4956
4957 if (Session::haveRight(self::$rightname, self::READGROUP)
4958 && isset($_SESSION["glpigroups"])
4959 && count($_SESSION["glpigroups"])) {
4960 $query .= " LEFT JOIN `glpi_groups_tickets`
4961 ON (`glpi_tickets`.`id` = `glpi_groups_tickets`.`tickets_id`
4962 AND `glpi_groups_tickets`.`type` = '".CommonITILActor::REQUESTER."')";
4963 }
4964 }
4965 $query .= getEntitiesRestrictRequest("WHERE", "glpi_tickets");
4966
4967 if ($foruser) {
4968 $query .= " AND (`glpi_tickets_users`.`users_id` = '".Session::getLoginUserID()."'
4969 OR `glpi_tickets`.`users_id_recipient` = '".Session::getLoginUserID()."'
4970 OR `glpi_ticketvalidations`.`users_id_validate` = '".Session::getLoginUserID()."'";
4971
4972 if (Session::haveRight(self::$rightname, self::READGROUP)
4973 && isset($_SESSION["glpigroups"])
4974 && count($_SESSION["glpigroups"])) {
4975 $groups = implode(",",$_SESSION['glpigroups']);
4976 $query .= " OR `glpi_groups_tickets`.`groups_id` IN (".$groups.") ";
4977 }
4978 $query.= ")";
4979 }
4980 $query_deleted = $query;
4981
4982 $query .= " AND NOT `glpi_tickets`.`is_deleted`
4983 GROUP BY `status`";
4984 $query_deleted .= " AND `glpi_tickets`.`is_deleted`
4985 GROUP BY `status`";
4986
4987 $result = $DB->query($query);
4988 $result_deleted = $DB->query($query_deleted);
4989
4990 $status = array();
4991 foreach (self::getAllStatusArray() as $key => $val) {
4992 $status[$key] = 0;
4993 }
4994
4995 if ($DB->numrows($result) > 0) {
4996 while ($data = $DB->fetch_assoc($result)) {
4997 $status[$data["status"]] = $data["COUNT"];
4998 }
4999 }
5000
5001 $number_deleted = 0;
5002 if ($DB->numrows($result_deleted) > 0) {
5003 while ($data = $DB->fetch_assoc($result_deleted)) {
5004 $number_deleted += $data["COUNT"];
5005 }
5006 }
5007 $options['criteria'][0]['field'] = 12;
5008 $options['criteria'][0]['searchtype'] = 'equals';
5009 $options['criteria'][0]['value'] = 'process';
5010 $options['criteria'][0]['link'] = 'AND';
5011 $options['reset'] ='reset';
5012
5013 echo "<table class='tab_cadrehov' >";
5014 echo "<tr class='noHover'><th colspan='2'>";
5015
5016 if ($_SESSION["glpiactiveprofile"]["interface"] != "central") {
5017 echo "<a href=\"".$CFG_GLPI["root_doc"]."/front/helpdesk.public.php?create_ticket=1\">".
5018 __('Create a ticket')." <img src='".$CFG_GLPI["root_doc"].
5019 "/pics/menu_add.png' title=\"". __s('Add')."\" alt=\"".__s('Add')."\"></a>";
5020 } else {
5021 echo "<a href=\"".$CFG_GLPI["root_doc"]."/front/ticket.php?".
5022 Toolbox::append_params($options,'&')."\">".__('Ticket followup')."</a>";
5023 }
5024 echo "</th></tr>";
5025 echo "<tr><th>"._n('Ticket','Tickets', Session::getPluralNumber())."</th><th>"._x('quantity', 'Number')."</th></tr>";
5026
5027 foreach ($status as $key => $val) {
5028 $options['criteria'][0]['value'] = $key;
5029 echo "<tr class='tab_bg_2'>";
5030 echo "<td><a href=\"".$CFG_GLPI["root_doc"]."/front/ticket.php?".
5031 Toolbox::append_params($options,'&')."\">".self::getStatus($key)."</a></td>";
5032 echo "<td class='numeric'>$val</td></tr>";
5033 }
5034
5035 $options['criteria'][0]['value'] = 'all';
5036 $options['is_deleted'] = 1;
5037 echo "<tr class='tab_bg_2'>";
5038 echo "<td><a href=\"".$CFG_GLPI["root_doc"]."/front/ticket.php?".
5039 Toolbox::append_params($options,'&')."\">".__('Deleted')."</a></td>";
5040 echo "<td class='numeric'>".$number_deleted."</td></tr>";
5041
5042 echo "</table><br>";
5043 }
5044
5045
5046 static function showCentralNewList() {
5047 global $DB, $CFG_GLPI;
5048
5049 if (!Session::haveRight(self::$rightname, self::READALL)) {
5050 return false;
5051 }
5052
5053 $query = "SELECT ".self::getCommonSelect()."
5054 FROM `glpi_tickets` ".self::getCommonLeftJoin()."
5055 WHERE `status` = '".self::INCOMING."' ".
5056 getEntitiesRestrictRequest("AND","glpi_tickets")."
5057 AND NOT `is_deleted`
5058 ORDER BY `glpi_tickets`.`date_mod` DESC
5059 LIMIT ".intval($_SESSION['glpilist_limit']);
5060
5061 $result = $DB->query($query);
5062 $number = $DB->numrows($result);
5063
5064 if ($number > 0) {
5065 Session::initNavigateListItems('Ticket');
5066 $options['criteria'][0]['field'] = 12;
5067 $options['criteria'][0]['searchtype'] = 'equals';
5068 $options['criteria'][0]['value'] = self::INCOMING;
5069 $options['criteria'][0]['link'] = 'AND';
5070 $options['reset'] ='reset';
5071
5072 echo "<div class='center'><table class='tab_cadre_fixe'>";
5073 //TRANS: %d is the number of new tickets
5074 echo "<tr><th colspan='12'>".sprintf(_n('%d new ticket','%d new tickets', $number), $number);
5075 echo "<a href='".$CFG_GLPI["root_doc"]."/front/ticket.php?".
5076 Toolbox::append_params($options,'&')."'>".__('Show all')."</a>";
5077 echo "</th></tr>";
5078
5079 self::commonListHeader(Search::HTML_OUTPUT);
5080
5081 while ($data = $DB->fetch_assoc($result)) {
5082 Session::addToNavigateListItems('Ticket',$data["id"]);
5083 self::showShort($data["id"]);
5084 }
5085 echo "</table></div>";
5086
5087 } else {
5088 echo "<div class='center'>";
5089 echo "<table class='tab_cadre_fixe'>";
5090 echo "<tr><th>".__('No ticket found.')."</th></tr>";
5091 echo "</table>";
5092 echo "</div><br>";
5093 }
5094 }
5095
5096 /**
5097 * Display tickets for an item
5098 *
5099 * Will also display tickets of linked items
5100 *
5101 * @param $item CommonDBTM object
5102 *
5103 * @return nothing (display a table)
5104 **/
5105 static function showListForItem(CommonDBTM $item) {
5106 global $DB, $CFG_GLPI;
5107
5108 if (!Session::haveRightsOr(self::$rightname,
5109 array(self::READALL, self::READMY, self::READASSIGN, CREATE))) {
5110 return false;
5111 }
5112
5113 if ($item->isNewID($item->getID())) {
5114 return false;
5115 }
5116
5117 $restrict = '';
5118 $order = '';
5119 $options['reset'] = 'reset';
5120
5121 switch ($item->getType()) {
5122 case 'User' :
5123 $restrict = "(`glpi_tickets_users`.`users_id` = '".$item->getID()."' ".
5124 " AND `glpi_tickets_users`.`type` = ".CommonITILActor::REQUESTER.")";
5125 $order = '`glpi_tickets`.`date_mod` DESC';
5126
5127 $options['reset'] = 'reset';
5128
5129 $options['criteria'][0]['field'] = 4; // status
5130 $options['criteria'][0]['searchtype'] = 'equals';
5131 $options['criteria'][0]['value'] = $item->getID();
5132 $options['criteria'][0]['link'] = 'AND';
5133 break;
5134
5135 case 'SLA' :
5136 $restrict = "(`slas_id` = '".$item->getID()."')";
5137 $order = '`glpi_tickets`.`due_date` DESC';
5138
5139 $options['criteria'][0]['field'] = 30;
5140 $options['criteria'][0]['searchtype'] = 'equals';
5141 $options['criteria'][0]['value'] = $item->getID();
5142 $options['criteria'][0]['link'] = 'AND';
5143 break;
5144
5145 case 'Supplier' :
5146 $restrict = "(`glpi_suppliers_tickets`.`suppliers_id` = '".$item->getID()."' ".
5147 " AND `glpi_suppliers_tickets`.`type` = ".CommonITILActor::ASSIGN.")";
5148 $order = '`glpi_tickets`.`date_mod` DESC';
5149
5150 $options['criteria'][0]['field'] = 6;
5151 $options['criteria'][0]['searchtype'] = 'equals';
5152 $options['criteria'][0]['value'] = $item->getID();
5153 $options['criteria'][0]['link'] = 'AND';
5154 break;
5155
5156 case 'Group' :
5157 // Mini search engine
5158 if ($item->haveChildren()) {
5159 $tree = Session::getSavedOption(__CLASS__, 'tree', 0);
5160 echo "<table class='tab_cadre_fixe'>";
5161 echo "<tr class='tab_bg_1'><th>".__('Last tickets')."</th></tr>";
5162 echo "<tr class='tab_bg_1'><td class='center'>";
5163 echo __('Child groups')." ";
5164 Dropdown::showYesNo('tree', $tree, -1,
5165 array('on_change' => 'reloadTab("start=0&tree="+this.value)'));
5166 } else {
5167 $tree = 0;
5168 }
5169 echo "</td></tr></table>";
5170
5171 if ($tree) {
5172 $restrict = "IN (".implode(',', getSonsOf('glpi_groups', $item->getID())).")";
5173 } else {
5174 $restrict = "='".$item->getID()."'";
5175 }
5176 $restrict = "(`glpi_groups_tickets`.`groups_id` $restrict".
5177 " AND `glpi_groups_tickets`.`type` = ".CommonITILActor::REQUESTER.")";
5178 $order = '`glpi_tickets`.`date_mod` DESC';
5179
5180 $options['criteria'][0]['field'] = 71;
5181 $options['criteria'][0]['searchtype'] = ($tree ? 'under' : 'equals');
5182 $options['criteria'][0]['value'] = $item->getID();
5183 $options['criteria'][0]['link'] = 'AND';
5184 break;
5185
5186 default :
5187 $restrict = "(`glpi_items_tickets`.`items_id` = '".$item->getID()."' ".
5188 " AND `glpi_items_tickets`.`itemtype` = '".$item->getType()."')";
5189
5190
5191 // you can only see your tickets
5192 if (!Session::haveRight(self::$rightname, self::READALL)) {
5193 $restrict .= " AND (`glpi_tickets`.`users_id_recipient` = '".Session::getLoginUserID()."'
5194 OR (`glpi_tickets_users`.`tickets_id` = '".$item->getID()."'
5195 AND `glpi_tickets_users`.`users_id`
5196 = '".Session::getLoginUserID()."'))";
5197 }
5198 $order = '`glpi_tickets`.`date_mod` DESC';
5199
5200 $options['criteria'][0]['field'] = 12;
5201 $options['criteria'][0]['searchtype'] = 'equals';
5202 $options['criteria'][0]['value'] = 'all';
5203 $options['criteria'][0]['link'] = 'AND';
5204
5205 $options['metacriteria'][0]['itemtype'] = $item->getType();
5206 $options['metacriteria'][0]['field'] = Search::getOptionNumber($item->getType(),
5207 'id');
5208 $options['metacriteria'][0]['searchtype'] = 'equals';
5209 $options['metacriteria'][0]['value'] = $item->getID();
5210 $options['metacriteria'][0]['link'] = 'AND';
5211 break;
5212 }
5213
5214
5215 $query = "SELECT ".self::getCommonSelect()."
5216 FROM `glpi_tickets` ".self::getCommonLeftJoin()."
5217 WHERE $restrict ".
5218 getEntitiesRestrictRequest("AND","glpi_tickets")."
5219 ORDER BY $order
5220 LIMIT ".intval($_SESSION['glpilist_limit']);
5221 $result = $DB->query($query);
5222 $number = $DB->numrows($result);
5223
5224
5225 $colspan = 11;
5226 if (count($_SESSION["glpiactiveentities"]) > 1) {
5227 $colspan++;
5228 }
5229
5230 // Ticket for the item
5231 echo "<div class='firstbloc'>";
5232 // Link to open a new ticket
5233 if ($item->getID()
5234 && Ticket::isPossibleToAssignType($item->getType())
5235 && self::canCreate()) {
5236 Html::showSimpleForm($CFG_GLPI["root_doc"]."/front/ticket.form.php",
5237 '_add_fromitem', __('New ticket for this item...'),
5238 array('itemtype' => $item->getType(),
5239 'items_id' => $item->getID()));
5240 }
5241 echo "</div><div>";
5242 echo "<table class='tab_cadre_fixehov'>";
5243
5244 if ($number > 0) {
5245 if (Session::haveRight(self::$rightname, self::READALL)) {
5246 Session::initNavigateListItems('Ticket',
5247 //TRANS : %1$s is the itemtype name, %2$s is the name of the item (used for headings of a list)
5248 sprintf(__('%1$s = %2$s'), $item->getTypeName(1),
5249 $item->getName()));
5250
5251 echo "<tr class='noHover'><th colspan='$colspan'>";
5252 $title = sprintf(_n('Last %d ticket', 'Last %d tickets', $number), $number);
5253 $link = "<a href='".$CFG_GLPI["root_doc"]."/front/ticket.php?".
5254 Toolbox::append_params($options,'&')."'>".__('Show all')."</a>";
5255 $title = printf(__('%1$s (%2$s)'), $title, $link);
5256 echo "</th></tr>";
5257 } else {
5258 echo "<tr><th colspan='$colspan'>".__("You don't have right to see all tickets")."</th></tr>";
5259 }
5260
5261 } else {
5262 echo "<tr><th>".__('No ticket found.')."</th></tr>";
5263 }
5264
5265 if ($item->getID()
5266 && ($item->getType() == 'User')
5267 && self::canCreate()) {
5268 echo "<tr><td class='tab_bg_2 center b' colspan='$colspan'>";
5269 Html::showSimpleForm($CFG_GLPI["root_doc"]."/front/ticket.form.php",
5270 '_add_fromitem', __('New ticket for this item...'),
5271 array('_users_id_requester' => $item->getID()));
5272 echo "</td></tr>";
5273 }
5274
5275 // Ticket list
5276 if ($number > 0) {
5277 self::commonListHeader(Search::HTML_OUTPUT);
5278
5279 while ($data = $DB->fetch_assoc($result)) {
5280 Session::addToNavigateListItems('Ticket',$data["id"]);
5281 self::showShort($data["id"]);
5282 }
5283 self::commonListHeader(Search::HTML_OUTPUT);
5284 }
5285
5286 echo "</table></div>";
5287
5288 // Tickets for linked items
5289 $linkeditems = $item->getLinkedItems();
5290 $restrict = array();
5291 if (count($linkeditems)) {
5292 foreach ($linkeditems as $ltype => $tab) {
5293 foreach ($tab as $lID) {
5294 $restrict[] = "(`glpi_items_tickets`.`itemtype` = '$ltype' AND `glpi_items_tickets`.`items_id` = '$lID')";
5295 }
5296 }
5297 }
5298
5299 if (count($restrict)
5300 && Session::haveRight(self::$rightname, self::READALL)) {
5301 $query = "SELECT ".self::getCommonSelect()."
5302 FROM `glpi_tickets` ".self::getCommonLeftJoin()."
5303 WHERE ".implode(' OR ', $restrict).
5304 getEntitiesRestrictRequest(' AND ', 'glpi_tickets') . "
5305 ORDER BY `glpi_tickets`.`date_mod` DESC
5306 LIMIT ".intval($_SESSION['glpilist_limit']);
5307 $result = $DB->query($query);
5308 $number = $DB->numrows($result);
5309
5310 echo "<div class='spaced'><table class='tab_cadre_fixe'>";
5311 echo "<tr><th colspan='12'>";
5312 echo _n('Ticket on linked items', 'Tickets on linked items', $number);
5313 echo "</th></tr>";
5314 if ($number > 0) {
5315 self::commonListHeader(Search::HTML_OUTPUT);
5316 while ($data = $DB->fetch_assoc($result)) {
5317 // Session::addToNavigateListItems(TRACKING_TYPE,$data["id"]);
5318 self::showShort($data["id"]);
5319 }
5320 self::commonListHeader(Search::HTML_OUTPUT);
5321 } else {
5322 echo "<tr><th>".__('No ticket found.')."</th></tr>";
5323 }
5324 echo "</table></div>";
5325
5326 } // Subquery for linked item
5327
5328 }
5329
5330 /**
5331 * @param $ID
5332 * @param $forcetab string name of the tab to force at the display (default '')
5333 **/
5334 static function showVeryShort($ID, $forcetab='') {
5335 global $CFG_GLPI;
5336
5337 // Prints a job in short form
5338 // Should be called in a <table>-segment
5339 // Print links or not in case of user view
5340 // Make new job object and fill it from database, if success, print it
5341 $showprivate = false;
5342 if (Session::haveRight('followup', TicketFollowup::SEEPRIVATE)) {
5343 $showprivate = true;
5344 }
5345
5346 $job = new self();
5347 $rand = mt_rand();
5348 if ($job->getFromDBwithData($ID, 0)) {
5349 $bgcolor = $_SESSION["glpipriority_".$job->fields["priority"]];
5350 // $rand = mt_rand();
5351 echo "<tr class='tab_bg_2'>";
5352 echo "<td class='center' bgcolor='$bgcolor'>".sprintf(__('%1$s: %2$s'), __('ID'),
5353 $job->fields["id"])."</td>";
5354 echo "<td class='center'>";
5355
5356 if (isset($job->users[CommonITILActor::REQUESTER])
5357 && count($job->users[CommonITILActor::REQUESTER])) {
5358 foreach ($job->users[CommonITILActor::REQUESTER] as $d) {
5359 if ($d["users_id"] > 0) {
5360 $userdata = getUserName($d["users_id"],2);
5361 $name = "<span class='b'>".$userdata['name']."</span>";
5362 $name = sprintf(__('%1$s %2$s'), $name,
5363 Html::showToolTip($userdata["comment"],
5364 array('link' => $userdata["link"],
5365 'display' => false)));
5366 echo $name;
5367 } else {
5368 echo $d['alternative_email']." ";
5369 }
5370 echo "<br>";
5371 }
5372 }
5373
5374
5375 if (isset($job->groups[CommonITILActor::REQUESTER])
5376 && count($job->groups[CommonITILActor::REQUESTER])) {
5377 foreach ($job->groups[CommonITILActor::REQUESTER] as $d) {
5378 echo Dropdown::getDropdownName("glpi_groups", $d["groups_id"]);
5379 echo "<br>";
5380 }
5381 }
5382
5383 echo "</td>";
5384
5385
5386 echo "<td class='center'>";
5387 if (!empty($job->hardwaredatas)) {
5388 foreach ($job->hardwaredatas as $hardwaredatas) {
5389 if ($hardwaredatas->canView()) {
5390 echo $hardwaredatas->getTypeName()." - ";
5391 echo "<span class='b'>".$hardwaredatas->getLink()."</span><br/>";
5392 } else if ($hardwaredatas) {
5393 echo $hardwaredatas->getTypeName()." - ";
5394 echo "<span class='b'>".$hardwaredatas->getNameID()."</span><br/>";
5395 }
5396 }
5397 } else {
5398 echo __('General');
5399 }
5400 echo "<td>";
5401
5402
5403 $link = "<a id='ticket".$job->fields["id"].$rand."' href='".$CFG_GLPI["root_doc"].
5404 "/front/ticket.form.php?id=".$job->fields["id"];
5405 if ($forcetab != '') {
5406 $link .= "&forcetab=".$forcetab;
5407 }
5408 $link .= "'>";
5409 $link .= "<span class='b'>".$job->getNameID()."</span></a>";
5410 $link = sprintf(__('%1$s (%2$s)'), $link,
5411 sprintf(__('%1$s - %2$s'), $job->numberOfFollowups($showprivate),
5412 $job->numberOfTasks($showprivate)));
5413 $content = Toolbox::unclean_cross_side_scripting_deep(html_entity_decode($job->fields['content'],
5414 ENT_QUOTES,
5415 "UTF-8"));
5416 $link = printf(__('%1$s %2$s'), $link,
5417 Html::showToolTip(nl2br(Html::Clean($content)),
5418 array('applyto' => 'ticket'.$job->fields["id"].$rand,
5419 'display' => false)));
5420 echo "</td>";
5421
5422 // Finish Line
5423 echo "</tr>";
5424 } else {
5425 echo "<tr class='tab_bg_2'>";
5426 echo "<td colspan='6' ><i>".__('No ticket in progress.')."</i></td></tr>";
5427 }
5428 }
5429
5430
5431 static function getCommonSelect() {
5432
5433 $SELECT = "";
5434 if (count($_SESSION["glpiactiveentities"])>1) {
5435 $SELECT .= ", `glpi_entities`.`completename` AS entityname,
5436 `glpi_tickets`.`entities_id` AS entityID ";
5437 }
5438
5439 return " DISTINCT `glpi_tickets`.*,
5440 `glpi_itilcategories`.`completename` AS catname
5441 $SELECT";
5442 }
5443
5444
5445 static function getCommonLeftJoin() {
5446
5447 $FROM = "";
5448 if (count($_SESSION["glpiactiveentities"])>1) {
5449 $FROM .= " LEFT JOIN `glpi_entities`
5450 ON (`glpi_entities`.`id` = `glpi_tickets`.`entities_id`) ";
5451 }
5452
5453 return " LEFT JOIN `glpi_groups_tickets`
5454 ON (`glpi_tickets`.`id` = `glpi_groups_tickets`.`tickets_id`)
5455 LEFT JOIN `glpi_tickets_users`
5456 ON (`glpi_tickets`.`id` = `glpi_tickets_users`.`tickets_id`)
5457 LEFT JOIN `glpi_suppliers_tickets`
5458 ON (`glpi_tickets`.`id` = `glpi_suppliers_tickets`.`tickets_id`)
5459 LEFT JOIN `glpi_itilcategories`
5460 ON (`glpi_tickets`.`itilcategories_id` = `glpi_itilcategories`.`id`)
5461 LEFT JOIN `glpi_tickettasks`
5462 ON (`glpi_tickets`.`id` = `glpi_tickettasks`.`tickets_id`)
5463 LEFT JOIN `glpi_items_tickets`
5464 ON (`glpi_tickets`.`id` = `glpi_items_tickets`.`tickets_id`)
5465 $FROM";
5466
5467 }
5468
5469
5470 /**
5471 * @param $output
5472 **/
5473 static function showPreviewAssignAction($output) {
5474
5475 //If ticket is assign to an object, display this information first
5476 if (isset($output["entities_id"])
5477 && isset($output["items_id"])
5478 && isset($output["itemtype"])) {
5479
5480 if ($item = getItemForItemtype($output["itemtype"])) {
5481 if ($item->getFromDB($output["items_id"])) {
5482 echo "<tr class='tab_bg_2'>";
5483 echo "<td>".__('Assign equipment')."</td>";
5484
5485 echo "<td>".$item->getLink(array('comments' => true))."</td>";
5486 echo "</tr>";
5487 }
5488 }
5489
5490 unset($output["items_id"]);
5491 unset($output["itemtype"]);
5492 }
5493 unset($output["entities_id"]);
5494 return $output;
5495 }
5496
5497
5498 /**
5499 * Give cron information
5500 *
5501 * @param $name : task's name
5502 *
5503 * @return arrray of information
5504 **/
5505 static function cronInfo($name) {
5506
5507 switch ($name) {
5508 case 'closeticket' :
5509 return array('description' => __('Automatic tickets closing'));
5510
5511 case 'alertnotclosed' :
5512 return array('description' => __('Not solved tickets'));
5513
5514 case 'createinquest' :
5515 return array('description' => __('Generation of satisfaction surveys'));
5516 }
5517 return array();
5518 }
5519
5520
5521 /**
5522 * Cron for ticket's automatic close
5523 *
5524 * @param $task : crontask object
5525 *
5526 * @return integer (0 : nothing done - 1 : done)
5527 **/
5528 static function cronCloseTicket($task) {
5529 global $DB;
5530
5531 $ticket = new self();
5532
5533 // Recherche des entités
5534 $tot = 0;
5535 foreach (Entity::getEntitiesToNotify('autoclose_delay') as $entity => $delay) {
5536 if ($delay >= 0) {
5537 $query = "SELECT *
5538 FROM `glpi_tickets`
5539 WHERE `entities_id` = '".$entity."'
5540 AND `status` = '".self::SOLVED."'
5541 AND `is_deleted` = 0";
5542
5543 if ($delay > 0) {
5544 $query .= " AND ADDDATE(`solvedate`, INTERVAL ".$delay." DAY) < NOW()";
5545 }
5546
5547 $nb = 0;
5548 foreach ($DB->request($query) as $tick) {
5549 $ticket->update(array('id' => $tick['id'],
5550 'status' => self::CLOSED,
5551 '_auto_update' => true));
5552 $nb++;
5553 }
5554
5555 if ($nb) {
5556 $tot += $nb;
5557 $task->addVolume($nb);
5558 $task->log(Dropdown::getDropdownName('glpi_entities', $entity)." : $nb");
5559 }
5560 }
5561 }
5562
5563 return ($tot > 0);
5564 }
5565
5566
5567 /**
5568 * Cron for alert old tickets which are not solved
5569 *
5570 * @param $task : crontask object
5571 *
5572 * @return integer (0 : nothing done - 1 : done)
5573 **/
5574 static function cronAlertNotClosed($task) {
5575 global $DB, $CFG_GLPI;
5576
5577 if (!$CFG_GLPI["use_mailing"]) {
5578 return 0;
5579 }
5580 // Recherche des entités
5581 $tot = 0;
5582 foreach (Entity::getEntitiesToNotify('notclosed_delay') as $entity => $value) {
5583 $query = "SELECT `glpi_tickets`.*
5584 FROM `glpi_tickets`
5585 WHERE `glpi_tickets`.`entities_id` = '".$entity."'
5586 AND `glpi_tickets`.`is_deleted` = 0
5587 AND `glpi_tickets`.`status` IN ('".self::INCOMING."',
5588 '".self::ASSIGNED."',
5589 '".self::PLANNED."',
5590 '".self::WAITING."')
5591 AND `glpi_tickets`.`closedate` IS NULL
5592 AND ADDDATE(`glpi_tickets`.`date`, INTERVAL ".$value." DAY) < NOW()";
5593 $tickets = array();
5594 foreach ($DB->request($query) as $tick) {
5595 $tickets[] = $tick;
5596 }
5597
5598 if (!empty($tickets)) {
5599 if (NotificationEvent::raiseEvent('alertnotclosed', new self(),
5600 array('items' => $tickets,
5601 'entities_id' => $entity))) {
5602
5603 $tot += count($tickets);
5604 $task->addVolume(count($tickets));
5605 $task->log(sprintf(__('%1$s: %2$s'),
5606 Dropdown::getDropdownName('glpi_entities', $entity),
5607 count($tickets)));
5608 }
5609 }
5610 }
5611
5612 return ($tot > 0);
5613 }
5614
5615
5616 /**
5617 * Cron for ticketsatisfaction's automatic generated
5618 *
5619 * @param $task : crontask object
5620 *
5621 * @return integer (0 : nothing done - 1 : done)
5622 **/
5623 static function cronCreateInquest($task) {
5624 global $DB;
5625
5626 $conf = new Entity();
5627 $inquest = new TicketSatisfaction();
5628 $tot = 0;
5629 $maxentity = array();
5630 $tabentities = array();
5631
5632 $rate = Entity::getUsedConfig('inquest_config', 0, 'inquest_rate');
5633 if ($rate > 0) {
5634 $tabentities[0] = $rate;
5635 }
5636
5637 foreach ($DB->request('glpi_entities') as $entity) {
5638 $rate = Entity::getUsedConfig('inquest_config', $entity['id'], 'inquest_rate');
5639 $parent = Entity::getUsedConfig('inquest_config', $entity['id'], 'entities_id');
5640
5641 if ($rate > 0) {
5642 $tabentities[$entity['id']] = $rate;
5643 }
5644 }
5645
5646 foreach ($tabentities as $entity => $rate) {
5647 $parent = Entity::getUsedConfig('inquest_config', $entity, 'entities_id');
5648 $delay = Entity::getUsedConfig('inquest_config', $entity, 'inquest_delay');
5649 $duration = Entity::getUsedConfig('inquest_config', $entity, 'inquest_duration');
5650 $type = Entity::getUsedConfig('inquest_config', $entity);
5651 $max_closedate = Entity::getUsedConfig('inquest_config', $entity, 'max_closedate');
5652
5653 $query = "SELECT `glpi_tickets`.`id`,
5654 `glpi_tickets`.`closedate`,
5655 `glpi_tickets`.`entities_id`
5656 FROM `glpi_tickets`
5657 LEFT JOIN `glpi_ticketsatisfactions`
5658 ON `glpi_ticketsatisfactions`.`tickets_id` = `glpi_tickets`.`id`
5659 LEFT JOIN `glpi_entities`
5660 ON `glpi_tickets`.`entities_id` = `glpi_entities`.`id`
5661 WHERE `glpi_tickets`.`entities_id` = '$entity'
5662 AND `glpi_tickets`.`is_deleted` = 0
5663 AND `glpi_tickets`.`status` = '".self::CLOSED."'
5664 AND `glpi_tickets`.`closedate` > '$max_closedate'
5665 AND ADDDATE(`glpi_tickets`.`closedate`, INTERVAL $delay DAY)<=NOW()
5666 AND ADDDATE(`glpi_entities`.`max_closedate`, INTERVAL $duration DAY)<=NOW()
5667 AND `glpi_ticketsatisfactions`.`id` IS NULL
5668 ORDER BY `closedate` ASC";
5669
5670 $nb = 0;
5671 $max_closedate = '';
5672
5673 foreach ($DB->request($query) as $tick) {
5674 $max_closedate = $tick['closedate'];
5675 if (mt_rand(1,100) <= $rate) {
5676 if ($inquest->add(array('tickets_id' => $tick['id'],
5677 'date_begin' => $_SESSION["glpi_currenttime"],
5678 'entities_id' => $tick['entities_id'],
5679 'type' => $type))) {
5680 $nb++;
5681 }
5682 }
5683 }
5684
5685 // conservation de toutes les max_closedate des entites filles
5686 if (!empty($max_closedate)
5687 && (!isset($maxentity[$parent])
5688 || ($max_closedate > $maxentity[$parent]))) {
5689 $maxentity[$parent] = $max_closedate;
5690 }
5691
5692 if ($nb) {
5693 $tot += $nb;
5694 $task->addVolume($nb);
5695 $task->log(sprintf(__('%1$s: %2$s'),
5696 Dropdown::getDropdownName('glpi_entities', $entity), $nb));
5697 }
5698 }
5699
5700 // Sauvegarde du max_closedate pour ne pas tester les même tickets 2 fois
5701 foreach ($maxentity as $parent => $maxdate) {
5702 $conf->getFromDB($parent);
5703 $conf->update(array('id' => $conf->fields['id'],
5704 //'entities_id' => $parent,
5705 'max_closedate' => $maxdate));
5706 }
5707
5708 return ($tot > 0);
5709 }
5710
5711
5712 /**
5713 * Display debug information for current object
5714 **/
5715 function showDebug() {
5716 NotificationEvent::debugEvent($this);
5717 }
5718
5719
5720 /**
5721 * @since version 0.85
5722 *
5723 * @see commonDBTM::getRights()
5724 **/
5725 function getRights($interface='central') {
5726
5727 $values = parent::getRights();
5728 unset($values[READ]);
5729 $values[self::READMY] = __('See my ticket');
5730 //TRANS: short for : See tickets created by my groups
5731 $values[self::READGROUP] = array('short' => __('See group ticket'),
5732 'long' => __('See tickets created by my groups'));
5733 if ($interface == 'central') {
5734 $values[self::READALL] = __('See all tickets');
5735 //TRANS: short for : See assigned tickets (group associated)
5736 $values[self::READASSIGN] = array('short' => __('See assigned'),
5737 'long' => __('See assigned tickets'));
5738 //TRANS: short for : Assign a ticket
5739 $values[self::ASSIGN] = array('short' => __('Assign'),
5740 'long' => __('Assign a ticket'));
5741 //TRANS: short for : Steal a ticket
5742 $values[self::STEAL] = array('short' => __('Steal'),
5743 'long' => __('Steal a ticket'));
5744 //TRANS: short for : To be in charge of a ticket
5745 $values[self::OWN] = array('short' => __('Beeing in charge'),
5746 'long' => __('To be in charge of a ticket'));
5747 $values[self::CHANGEPRIORITY] = __('Change the priority');
5748 }
5749 if ($interface == 'helpdesk') {
5750 unset($values[UPDATE], $values[DELETE], $values[PURGE]);
5751 }
5752 return $values;
5753 }
5754
5755
5756 /**
5757 * Add image pasted to GLPI doc after ADD and before UPDATE of the item in the database
5758 *
5759 * @since version 0.85
5760 *
5761 * @return nothing
5762 **/
5763 function addImagePaste() {
5764
5765 if (count($this->input['_stock_image']) > 0) {
5766 $count_files = 0;
5767 // Filename
5768 if (isset($this->input['_filename'])) {
5769 $count_files = count($this->input['_stock_image']);
5770 foreach($this->input['_filename'] as $key => $filename){
5771 $this->input['_filename'][$count_files] = $filename;
5772 $count_files++;
5773 }
5774 $count_files = count($this->input['_stock_image']);
5775 foreach($this->input['_tag_filename'] as $key => $tag){
5776 $this->input['_tag'][$count_files] = $tag;
5777 $count_files++;
5778 }
5779 unset($this->input['_tag_filename']);
5780 }
5781
5782 // Stock_image
5783 foreach ($this->input['_stock_image'] as $key => $filename) {
5784 $this->input['_filename'][$key] = $filename;
5785 }
5786 unset($this->input['_stock_image']);
5787 foreach($this->input['_tag_stock_image'] as $key => $tag){
5788 $this->input['_tag'][$key] = $tag;
5789 $count_files++;
5790 }
5791 unset($this->input['_tag_stock_image']);
5792
5793 ksort($this->input['_filename']);
5794 ksort($this->input['_tag']);
5795
5796// $this->input['_forcenotif'] = 1;
5797 }
5798
5799 }
5800
5801
5802 /**
5803 * Convert tag to image
5804 *
5805 * @since version 0.85
5806 *
5807 * @param $content_text text content of input
5808 * @param $force_update force update of content in item (false by default
5809 * @param $doc_data array of filenames and tags
5810 *
5811 * @return nothing
5812 **/
5813 function convertTagToImage($content_text, $force_update=false, $doc_data=array()) {
5814 global $CFG_GLPI;
5815
5816 $matches = array();
5817 // If no doc data available we match all tags in content
5818 if (!count($doc_data)) {
5819 $doc = new Document();
5820 preg_match_all('/'.Document::getImageTag('(([a-z0-9]+|[\.\-]?)+)').'/', $content_text,
5821 $matches, PREG_PATTERN_ORDER);
5822 if (isset($matches[1]) && count($matches[1])) {
5823 $doc_data = $doc->find("`tag` IN('".implode("','", array_unique($matches[1]))."')");
5824 }
5825 }
5826
5827
5828 if (count($doc_data)) {
5829 foreach ($doc_data as $id => $image) {
5830 // Add only image files : try to detect mime type
5831 $ok = false;
5832 $mime = '';
5833 if (isset($image['filepath'])) {
5834 $fullpath = GLPI_DOC_DIR."/".$image['filepath'];
5835 $mime = Toolbox::getMime($fullpath);
5836 $ok = Toolbox::getMime($fullpath, 'image');
5837 }
5838 if (isset($image['tag'])) {
5839 if ($ok || empty($mime)) {
5840 // Replace tags by image in textarea
5841 $img = "<img alt='".$image['tag']."' src='".$CFG_GLPI['root_doc'].
5842 "/front/document.send.php?docid=".$id."&tickets_id=".$this->fields['id']."'/>";
5843
5844 // Replace tag by the image
5845 $content_text = preg_replace('/'.Document::getImageTag($image['tag']).'/',
5846 Html::entities_deep($img), $content_text);
5847
5848 // Replace <br> TinyMce bug
5849 $content_text = str_replace(array('>rn<','>\r\n<','>\r<','>\n<'),
5850 '><', $content_text);
5851
5852 // If the tag is from another ticket : link document to ticket
5853 /// TODO : comment maybe not used
5854// if($image['tickets_id'] != $this->fields['id']){
5855// $docitem = new Document_Item();
5856// $docitem->add(array('documents_id' => $image['id'],
5857// '_do_notif' => false,
5858// '_disablenotif' => true,
5859// 'itemtype' => $this->getType(),
5860// 'items_id' => $this->fields['id']));
5861// }
5862 } else {
5863 // Remove tag
5864 $content_text = preg_replace('/'.Document::getImageTag($image['tag']).'/',
5865 '', $content_text);
5866 }
5867 }
5868 }
5869 }
5870
5871 if ($force_update) {
5872 $this->fields['content'] = $content_text;
5873 $this->updateInDB(array('content'));
5874 }
5875
5876 return $content_text;
5877 }
5878
5879
5880 /**
5881 * Convert image to tag
5882 *
5883 * @since version 0.85
5884 *
5885 * @param $content_html html content of input
5886 * @param $force_update force update of content in item (false by default
5887 *
5888 * @return htlm content
5889 **/
5890 function convertImageToTag($content_html, $force_update=false) {
5891
5892 if (!empty($content_html)) {
5893 preg_match_all("/alt\s*=\s*['|\"](.+?)['|\"]/", $content_html, $matches, PREG_PATTERN_ORDER);
5894 if (isset($matches[1]) && count($matches[1])) {
5895 // Get all image src
5896 foreach ($matches[1] as $src) {
5897 // Set tag if image matches
5898 $content_html = preg_replace(array("/<img.*alt=['|\"]".$src."['|\"][^>]*\>/", "/<object.*alt=['|\"]".$src."['|\"][^>]*\>/"), Document::getImageTag($src), $content_html);
5899 }
5900 }
5901
5902 return $content_html;
5903 }
5904 }
5905
5906
5907 /**
5908 * Convert img of the collector for ticket
5909 *
5910 * @since version 0.85
5911 *
5912 * @param $content_html html content of input
5913 * @param $files array of filename
5914 * @param $tags array of image tag
5915 *
5916 * @return htlm content
5917 **/
5918 static function convertContentForTicket($content_html, $files, $tags) {
5919
5920 // We inject another meta tag
5921 $html = Html::entity_decode_deep($content_html);
5922 preg_match_all("/src\s*=\s*['|\"](.+?)['|\"]/", $html, $matches, PREG_PATTERN_ORDER);
5923 if (isset($matches[1]) && count($matches[1])) {
5924 // Get all image src
5925
5926 foreach ($matches[1] as $src) {
5927 // Set tag if image matches
5928 foreach ($files as $data => $filename) {
5929 if (preg_match("/".$data."/i", $src)) {
5930 $html = preg_replace("`<img.*src=['|\"]".$src."['|\"][^>]*\>`", "<p>".Document::getImageTag($tags[$filename])."</p>", $html);
5931 }
5932 }
5933 }
5934 }
5935
5936 return $html;
5937
5938 }
5939
5940
5941 /**
5942 * Convert img or tag of ticket for notification mails
5943 *
5944 * @since version 0.85
5945 *
5946 * @param $content : html content of input
5947 * @param $item : item to store filenames and tags found for each image in $content
5948 *
5949 * @return htlm content
5950 **/
5951 function convertContentForNotification($content, $item) {
5952 global $CFG_GLPI, $DB;
5953
5954 $html = str_replace(array('&','&nbsp;'), array('&',' '),
5955 html_entity_decode($content, ENT_QUOTES, "UTF-8"));
5956
5957 // If is html content
5958 if ($CFG_GLPI["use_rich_text"]) {
5959 preg_match_all('/img\s*alt=[\'|"](([a-z0-9]+|[\.\-]?)+)[\'|"]/', $html,
5960 $matches, PREG_PATTERN_ORDER);
5961
5962 if (isset($matches[1]) && count($matches[1])) {
5963 if (count($matches[1])) {
5964 foreach ($matches[1] as $image) {
5965 //Replace tags by image in textarea
5966 $img = "img src='cid:".Document::getImageTag($image)."'";
5967
5968 //Replace tag by the image
5969 $html = preg_replace("/img alt=['|\"]".$image."['|\"].*src=['|\"](.+)['|\"]/", $img,
5970 $html);
5971 }
5972 }
5973 }
5974
5975 $content = $html;
5976
5977 // If is text content
5978 } else {
5979 $doc = new Document();
5980 $doc_data = array();
5981
5982 preg_match_all('/'.Document::getImageTag('(([a-z0-9]+|[\.\-]?)+)').'/', $content,
5983 $matches, PREG_PATTERN_ORDER);
5984 if (isset($matches[1]) && count($matches[1])) {
5985 $doc_data = $doc->find("tag IN('".implode("','", array_unique($matches[1]))."')");
5986 }
5987
5988 if (count($doc_data)) {
5989 foreach ($doc_data as $image) {
5990 // Replace tags by image in textarea
5991 $img = "<img src='cid:".Document::getImageTag($image['tag'])."'/>";
5992
5993 // Replace tag by the image
5994 $content = preg_replace('/'.Document::getImageTag($image['tag']).'/', $img,
5995 $content);
5996 }
5997 }
5998 }
5999
6000 // Get all attached documents of ticket
6001 $query = "SELECT `glpi_documents_items`.`id` AS assocID,
6002 `glpi_entities`.`id` AS entity,
6003 `glpi_documents`.`name` AS assocName,
6004 `glpi_documents`.*
6005 FROM `glpi_documents_items`
6006 LEFT JOIN `glpi_documents`
6007 ON (`glpi_documents_items`.`documents_id`=`glpi_documents`.`id`)
6008 LEFT JOIN `glpi_entities`
6009 ON (`glpi_documents`.`entities_id`=`glpi_entities`.`id`)
6010 WHERE `glpi_documents_items`.`items_id` = '".$item->fields['id']."'
6011 AND `glpi_documents_items`.`itemtype` = '".$item->getType()."' ";
6012
6013 if (Session::getLoginUserID()) {
6014 $query .= getEntitiesRestrictRequest(" AND","glpi_documents",'','',true);
6015 } else {
6016 // Anonymous access from Crontask
6017 $query .= " AND `glpi_documents`.`entities_id`= '0' ";
6018 }
6019 $result = $DB->query($query);
6020
6021 if ($DB->numrows($result)) {
6022 while ($data = $DB->fetch_assoc($result)) {
6023 if (!empty($data['id'])) {
6024 // Image document
6025 if (!empty($data['tag'])) {
6026 $item->documents[] = $data['id'];
6027 // Other document
6028 } else if ($CFG_GLPI['attach_ticket_documents_to_mail']) {
6029 $item->documents[] = $data['id'];
6030 }
6031 }
6032 }
6033 }
6034
6035 return $content;
6036 }
6037
6038
6039 /**
6040 * Delete tag or image from ticket content
6041 *
6042 * @since version 0.85
6043 *
6044 * @param $content html content of input
6045 * @param $tags
6046 *
6047 * @return htlm content
6048 **/
6049 function cleanTagOrImage($content, $tags) {
6050 global $CFG_GLPI;
6051
6052 // RICH TEXT : delete img tag
6053 if ($CFG_GLPI["use_rich_text"]) {
6054 $content = Html::entity_decode_deep($content);
6055
6056 foreach ($tags as $tag) {
6057 $content = preg_replace("/<img.*alt=['|\"]".$tag."['|\"][^>]*\>/", "<p></p>", $content);
6058 }
6059
6060 // SIMPLE TEXT : delete tag
6061 } else {
6062 foreach ($tags as $tag) {
6063 $content = preg_replace('/'.Document::getImageTag($tag).'/', '\r\n', $content);
6064 }
6065 }
6066
6067 return $content;
6068 }
6069
6070
6071 /**
6072 * Convert rich text content to simple text content
6073 *
6074 * @since version 0.85
6075 *
6076 * @param $content : content to convert in html
6077 *
6078 * @return $content
6079 **/
6080 function setSimpleTextContent($content) {
6081
6082 $content = Html::entity_decode_deep($content);
6083
6084 // If is html content
6085 if ($content != strip_tags($content)) {
6086 $content = Html::clean($this->convertImageToTag($content), false, 1);
6087 $content = Html::entity_decode_deep(Html::clean($this->convertImageToTag($content)));
6088 }
6089
6090 return $content;
6091 }
6092
6093 /**
6094 * Convert simple text content to rich text content, init html editor
6095 *
6096 * @since version 0.85
6097 *
6098 * @param $name name of textarea
6099 * @param $content content to convert in html
6100 * @param $rand
6101 *
6102 * @return $content
6103 **/
6104 function setRichTextContent($name, $content, $rand) {
6105
6106 // Init html editor
6107 Html::initEditorSystem($name, $rand);
6108
6109 // If no html
6110 if ($content == strip_tags($content)) {
6111 $content = $this->convertTagToImage($content);
6112 }
6113
6114 // Neutralize non valid HTML tags
6115 $content = html::clean($content, false, 1);
6116
6117 // If content does not contain <br> or <p> html tag, use nl2br
6118 if (!preg_match("/<br\s?\/?>/", $content) && !preg_match("/<p>/", $content)) {
6119 $content = nl2br($content);
6120 }
6121 return $content;
6122 }
6123
6124
6125 /**
6126 * @since version 0.90
6127 *
6128 **/
6129 function getTimelineItems() {
6130 global $DB, $CFG_GLPI;
6131
6132 $timeline = array();
6133
6134 $user = new User();
6135 $group = new Group();
6136 $followup_obj = new TicketFollowup();
6137 $task_obj = new TicketTask();
6138 $document_item_obj = new Document_Item();
6139 $ticket_valitation_obj = new TicketValidation();
6140
6141 //checks rights
6142 $showpublic = Session::haveRightsOr("followup", array(TicketFollowup::SEEPUBLIC,
6143 TicketFollowup::SEEPRIVATE))
6144 || Session::haveRightsOr("task", array(TicketTask::SEEPUBLIC,
6145 TicketTask::SEEPRIVATE));
6146 $restrict_fup = $restrict_task = "";
6147 if (!Session::haveRight("followup", TicketFollowup::SEEPRIVATE)) {
6148 $restrict_fup = " AND (`is_private` = '0'
6149 OR `users_id` ='" . Session::getLoginUserID() . "') ";
6150 }
6151 if (!Session::haveRight("task", TicketTask::SEEPRIVATE)) {
6152 $restrict_task = " AND (`is_private` = '0'
6153 OR `users_id` ='" . Session::getLoginUserID() . "') ";
6154 }
6155
6156 if (!$showpublic) {
6157 $restrict = " AND 1 = 0";
6158 }
6159
6160 //add ticket followups to timeline
6161 $followups = $followup_obj->find("tickets_id = ".$this->getID()." $restrict_fup", 'date DESC');
6162 foreach ($followups as $followups_id => $followup) {
6163 $followup_obj->getFromDB($followups_id);
6164 $can_edit = $followup_obj->canUpdateItem();
6165 $followup['can_edit'] = $can_edit;
6166 $timeline[$followup['date']."_followup_".$followups_id] = array('type' => 'TicketFollowup',
6167 'item' => $followup);
6168 }
6169
6170
6171 //add ticket tasks to timeline
6172 $tasks = $task_obj->find("tickets_id = ".$this->getID()." $restrict_task", 'date DESC');
6173 foreach ($tasks as $tasks_id => $task) {
6174 $task_obj->getFromDB($tasks_id);
6175 $can_edit = $task_obj->canUpdateItem();
6176 $task['can_edit'] = $can_edit;
6177 $timeline[$task['date']."_task_".$tasks_id] = array('type' => 'TicketTask',
6178 'item' => $task);
6179 }
6180
6181
6182 //add ticket documents to timeline
6183 $document_obj = new Document();
6184 $document_items = $document_item_obj->find("itemtype = 'Ticket' AND items_id = ".$this->getID());
6185 foreach ($document_items as $document_item) {
6186 $document_obj->getFromDB($document_item['documents_id']);
6187 $timeline[$document_obj->fields['date_mod']."_document_".$document_item['documents_id']]
6188 = array('type' => 'Document_Item', 'item' => $document_obj->fields);
6189 }
6190
6191 //add existing solution
6192 if (!empty($this->fields['solution'])
6193 || !empty($this->fields['solutiontypes_id'])) {
6194 $users_id = 0;
6195 $solution_date = $this->fields['solvedate'];
6196
6197 //search date and user of last solution in glpi_logs
6198 if ($res_solution = $DB->query("SELECT `date_mod` AS solution_date, `user_name`, `id`
6199 FROM `glpi_logs`
6200 WHERE `itemtype` = 'Ticket'
6201 AND `items_id` = ".$this->getID()."
6202 AND `id_search_option` = 24
6203 ORDER BY `id` DESC
6204 LIMIT 1")) {
6205 $data_solution = $DB->fetch_assoc($res_solution);
6206 if (!empty($data_solution['solution_date'])) {
6207 $solution_date = $data_solution['solution_date'];
6208 }
6209 // find user
6210 if (!empty($data_solution['user_name'])) {
6211 $users_id = addslashes(trim(preg_replace("/.*\(([0-9]+)\)/", "$1",
6212 $data_solution['user_name'])));
6213 }
6214 }
6215
6216 // fix trouble with html_entity_decode who skip accented characters (on windows browser)
6217 $solution_content = preg_replace_callback("/(&#[0-9]+;)/", function($m) {
6218 return mb_convert_encoding($m[1], "UTF-8", "HTML-ENTITIES");
6219 }, $this->fields['solution']);
6220
6221 $timeline[$solution_date."_solution"]
6222 = array('type' => 'Solution',
6223 'item' => array('id' => 0,
6224 'content' => Toolbox::unclean_cross_side_scripting_deep($solution_content),
6225 'date' => $solution_date,
6226 'users_id' => $users_id,
6227 'solutiontypes_id' => $this->fields['solutiontypes_id'],
6228 'can_edit' => Ticket::canUpdate() && $this->canSolve()));
6229 }
6230
6231 // add ticket validation to timeline
6232 if ((($this->fields['type'] == Ticket::DEMAND_TYPE)
6233 && (Session::haveRight('ticketvalidation', TicketValidation::VALIDATEREQUEST)
6234 || Session::haveRight('ticketvalidation', TicketValidation::CREATEREQUEST)))
6235 || (($this->fields['type'] == Ticket::INCIDENT_TYPE)
6236 && (Session::haveRight('ticketvalidation', TicketValidation::VALIDATEINCIDENT)
6237 || Session::haveRight('ticketvalidation', TicketValidation::CREATEINCIDENT)))) {
6238
6239 $ticket_validations = $ticket_valitation_obj->find('tickets_id = '.$this->getID());
6240 foreach ($ticket_validations as $validations_id => $validation) {
6241 $canedit = $ticket_valitation_obj->can($validations_id, UPDATE);
6242 $user->getFromDB($validation['users_id_validate']);
6243 $timeline[$validation['submission_date']."_validation_".$validations_id]
6244 = array('type' => 'TicketValidation',
6245 'item' => array('id' => $validations_id,
6246 'date' => $validation['submission_date'],
6247 'content' => __('Validation request')." => ".$user->getlink().
6248 "<br>".$validation['comment_submission'],
6249 'users_id' => $validation['users_id'],
6250 'can_edit' => $canedit));
6251
6252 if (!empty($validation['validation_date'])) {
6253 $timeline[$validation['validation_date']."_validation_".$validations_id]
6254 = array('type' => 'TicketValidation',
6255 'item' => array('id' => $validations_id,
6256 'date' => $validation['validation_date'],
6257 'content' => __('Validation request answer')." : ".
6258 _sx('status',
6259 ucfirst(TicketValidation::getStatus($validation['status'])))
6260 ."<br>".$validation['comment_validation'],
6261 'users_id' => $validation['users_id_validate'],
6262 'status' => "status_".$validation['status'],
6263 'can_edit' => $canedit));
6264 }
6265 }
6266 }
6267
6268 //reverse sort timeline items by key (date)
6269 krsort($timeline);
6270
6271 return $timeline;
6272 }
6273
6274
6275 /**
6276 * @since version 0.90
6277 *
6278 * @param $rand
6279 **/
6280 function showTimeline($rand) {
6281 global $CFG_GLPI, $DB;
6282
6283 //get ticket actors
6284 $ticket_users_keys = $this->getTicketActors();
6285
6286 $user = new User();
6287 $followup_obj = new TicketFollowup();
6288 $pics_url = $CFG_GLPI['root_doc']."/pics/timeline";
6289
6290 $timeline = $this->getTimelineItems();
6291
6292 //include lib for parsing url
6293 require GLPI_ROOT."/lib/urllinker/urllinker.php";
6294
6295 //display timeline
6296 echo "<div class='timeline_history'>";
6297
6298 $tmp = array_values($timeline);
6299 $first_item = array_shift($tmp);
6300
6301 // show approbation form on top when ticket is solved
6302 if ($this->fields["status"] == CommonITILObject::SOLVED) {
6303 echo "<div class='approbation_form' id='approbation_form$rand'>";
6304 $followup_obj->showApprobationForm($this);
6305 echo "</div>";
6306 }
6307
6308 // show title for timeline
6309 self::showTimelineHeader();
6310
6311 $timeline_index = 0;
6312 foreach ($timeline as $item) {
6313 $item_i = $item['item'];
6314
6315 // don't display empty followup (ex : solution approbation)
6316 if (($item['type'] == 'TicketFollowup')
6317 && empty($item_i['content'])) {
6318 continue;
6319 }
6320
6321 $date = "";
6322 if (isset($item_i['date'])) {
6323 $date = $item_i['date'];
6324 }
6325 if (isset($item_i['date_mod'])) {
6326 $date = $item_i['date_mod'];
6327 }
6328
6329 // check if curent item user is assignee or requester
6330 $user_position = 'left';
6331 if ((isset($ticket_users_keys[$item_i['users_id']])
6332 && ($ticket_users_keys[$item_i['users_id']] == CommonItilActor::ASSIGN))
6333 || ($item['type'] == 'Assign')) {
6334 $user_position = 'right';
6335 }
6336
6337 //display solution in middle
6338 if (($timeline_index == 0)
6339 && ($item['type'] == "Solution")
6340 && ($this->fields["status"] == CommonITILObject::SOLVED)) {
6341 $user_position.= ' middle';
6342 }
6343
6344 echo "<div class='h_item $user_position'>";
6345
6346 echo "<div class='h_info'>";
6347
6348 echo "<div class='h_date'>".Html::convDateTime($date)."</div>";
6349 if ($item_i['users_id'] !== false) {
6350
6351 echo "<div class='h_user'>";
6352 if (isset($item_i['users_id']) && ($item_i['users_id'] != 0)) {
6353 $user->getFromDB($item_i['users_id']);
6354
6355 echo "<div class='tooltip_picture_border'>";
6356 echo "<img class='user_picture' alt=\"".__s('Picture')."\" src='".
6357 User::getThumbnailURLForPicture($user->fields['picture'])."'>";
6358 echo "</div>";
6359
6360 echo $user->getLink();
6361 } else {
6362 _e("Requester");
6363 }
6364
6365 echo "</div>"; // h_user
6366 }
6367
6368 echo "</div>"; //h_date
6369
6370 echo "<div class='h_content ".$item['type'].
6371 ((isset($item_i['status'])) ? " ".$item_i['status'] : "")."'";
6372 if (!in_array($item['type'], array('Document_Item', 'Assign'))
6373 && $item_i['can_edit']) {
6374 echo " ondblclick='javascript:viewEditSubitem".$this->fields['id']."$rand(event, \"".$item['type']."\", ".$item_i['id'].", this)'";
6375 }
6376 echo ">";
6377 if (isset($item_i['requesttypes_id'])
6378 && file_exists("$pics_url/".$item_i['requesttypes_id'].".png")) {
6379 echo "<img src='$pics_url/".$item_i['requesttypes_id'].".png' title='' class='h_requesttype' />";
6380 }
6381
6382 if (isset($item_i['content'])) {
6383 $content = $item_i['content'];
6384 $content = linkUrlsInTrustedHtml($content);
6385 //$content = nl2br($content);
6386
6387 $long_text = "";
6388 if ((substr_count($content, "<br") > 30) || (strlen($content) > 2000)) {
6389 $long_text = "long_text";
6390 }
6391
6392 echo "<div class='item_content $long_text'>";
6393 echo "<p>$content</p>";
6394 if (!empty($long_text)) {
6395 echo "<p class='read_more'>";
6396 echo "<a class='read_more_button'>.....</a>";
6397 echo "</p>";
6398 }
6399 echo "</div>";
6400 }
6401
6402 echo "<div class='b_right'>";
6403 if (isset($item_i['solutiontypes_id']) && !empty($item_i['solutiontypes_id'])) {
6404 echo Dropdown::getDropdownName("glpi_solutiontypes", $item_i['solutiontypes_id'])."<br>";
6405 }
6406 if (isset($item_i['taskcategories_id']) && !empty($item_i['taskcategories_id'])) {
6407 echo Dropdown::getDropdownName("glpi_taskcategories", $item_i['taskcategories_id'])."<br>";
6408 }
6409 if (isset($item_i['actiontime']) && !empty($item_i['actiontime'])) {
6410 echo "<span class='actiontime'>";
6411 echo Html::timestampToString($item_i['actiontime'], false);
6412 echo "</span>";
6413 }
6414 if (isset($item_i['state'])) {
6415 echo "<span class='state state_".$item_i['state']."'>";
6416 echo Planning::getState($item_i['state']);
6417 echo "</span>";
6418 }
6419 if (isset($item_i['begin'])) {
6420 echo "<span class='planification'>";
6421 echo Html::convDateTime($item_i["begin"]);
6422 echo " ⇒ ";
6423 echo Html::convDateTime($item_i["end"]);
6424 echo "</span>";
6425 }
6426 if (isset($item_i['users_id_tech']) && ($item_i['users_id_tech'] > 0)) {
6427 echo "<div class='users_id_tech'>";
6428 $user->getFromDB($item_i['users_id_tech']);
6429
6430 echo "<div class='tooltip_picture_border'>";
6431 echo "<img class='user_picture' alt=\"".__s('Picture')."\" src='".
6432 User::getThumbnailURLForPicture($user->fields['picture'])."'>";
6433 echo "</div>";
6434
6435 echo $user->getLink();
6436
6437 echo "</div>";
6438 }
6439
6440 // show "is_private" icon
6441 if (isset($item_i['is_private']) && $item_i['is_private']) {
6442 echo "<div class='private'>".__('Private')."</div>";
6443 }
6444
6445 echo "</div>"; // b_right
6446
6447 if ($item['type'] == 'Document_Item') {
6448 $filename = $item_i['filename'];
6449 $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
6450 echo "<img src='";
6451 if (empty($filename)) {
6452 $filename = $item_i['name'];
6453 }
6454 if (file_exists(GLPI_ROOT."/pics/icones/$ext-dist.png")) {
6455 echo $CFG_GLPI['root_doc']."/pics/icones/$ext-dist.png";
6456 } else {
6457 echo "$pics_url/file.png";
6458 }
6459 echo "' title='file' /> ";
6460 echo "<a href='".$CFG_GLPI['root_doc']."/front/document.send.php?docid=".$item_i['id']
6461 ."&tickets_id=".$this->getID()."' target='_blank'>$filename";
6462 if (in_array($ext, array('jpg', 'jpeg', 'png', 'bmp'))) {
6463
6464 echo "<div class='timeline_img_preview'>";
6465 echo "<img src='".$CFG_GLPI['root_doc']."/front/document.send.php?docid=".$item_i['id']
6466 ."&tickets_id=".$this->getID()."'/>";
6467 echo "</div>";
6468 }
6469 echo "</a>";
6470 if (!empty($item_i['mime'])) {
6471 echo " (".$item_i['mime'].")";
6472 }
6473 echo "<a href='".$CFG_GLPI['root_doc'].
6474 "/front/document.form.php?id=".$item_i['id']."' class='edit_document' title='".
6475 _sx("button", "Update")."'>";
6476 echo "<img src='$pics_url/edit.png' /></a>";
6477 echo "<a href='".$CFG_GLPI['root_doc'].
6478 "/front/ticket.form.php?delete_document&documents_id=".$item_i['id'].
6479 "&tickets_id=".$this->getID()."' class='delete_document' title='".
6480 _sx("button", "Delete permanently")."'>";
6481 echo "<img src='$pics_url/delete.png' /></a>";
6482 }
6483
6484 echo "</div>"; //end h_content
6485
6486 echo "</div>"; //end h_info
6487
6488 $timeline_index++;
6489 } // end foreach timeline
6490
6491 echo "<div class='break'></div>";
6492
6493 // recall ticket content (not needed in classic and splitted layout)
6494 if (!CommonGLPI::isLayoutWithMain()) {
6495
6496 echo "<div class='h_item middle'>";
6497
6498 echo "<div class='h_info'>";
6499 echo "<div class='h_date'>".Html::convDateTime($this->fields['date'])."</div>";
6500 echo "<div class='h_user'>";
6501 $dem = '0';
6502 foreach ($DB->request("glpi_tickets_users",
6503 "`tickets_id` = ".$this->fields['id']." AND `type` = 1") AS $req) {
6504 $dem = $req['users_id'];
6505 }
6506 if ((!isset($item_i['users_id_recipient'])
6507 || ($item_i['users_id_recipient'] == 0))
6508 && ($dem == 0)) {
6509 _e("Requester");
6510 }
6511 else {
6512 if (isset($item_i['users_id_recipient'])
6513 && ($item_i['users_id_recipient'] != 0)) {
6514 $user->getFromDB($this->fields['users_id_recipient']);
6515 } else if ($dem > 0) {
6516 $requester = new User();
6517 if ($requester->getFromDB($dem)) {
6518 $user = $requester;
6519 }
6520 }
6521
6522 echo "<div class='tooltip_picture_border'>";
6523 $picture = "";
6524 if (isset($user->fields['picture'])) {
6525 $picture = $user->fields['picture'];
6526 }
6527 echo "<img class='user_picture' alt=\"".__s('Picture')."\" src='".
6528 User::getThumbnailURLForPicture($picture)."'>";
6529 echo "</div>";
6530
6531 echo $user->getLink();
6532 }
6533
6534 echo "</div>"; // h_user
6535 echo "</div>"; //h_info
6536
6537 echo "<div class='h_content TicketContent'>";
6538
6539 echo "<div class='b_right'>".__("Ticket recall")."</div>";
6540
6541 echo "<div class='ticket_title'>";
6542 echo html_entity_decode($this->fields['name']);
6543 echo "</div>";
6544
6545 echo "<div class='ticket_description'>";
6546
6547 echo $this->setSimpleTextContent($this->fields['content']);
6548
6549 echo "</div>";
6550
6551 echo "</div>"; // h_content TicketContent
6552
6553 echo "</div>"; // h_item middle
6554
6555 echo "<div class='break'></div>";
6556 }
6557
6558 // end timeline
6559 echo "</div>"; // h_item $user_position
6560 echo "<script type='text/javascript'>read_more();</script>";
6561 }
6562
6563
6564 /**
6565 * @since version 0.90
6566 **/
6567 function getTicketActors() {
6568 global $DB;
6569
6570 $query = "SELECT DISTINCT `users_id`, `type`
6571 FROM (SELECT usr.`id` AS users_id, tu.`type` AS type
6572 FROM `glpi_tickets_users` tu
6573 LEFT JOIN `glpi_users` usr ON tu.`users_id` = usr.`id`
6574 WHERE tu.`tickets_id` = ".$this->getId()."
6575 UNION
6576 SELECT usr.`id` AS users_id, gt.`type` AS type
6577 FROM `glpi_groups_tickets` gt
6578 LEFT JOIN `glpi_groups_users` gu ON gu.`groups_id` = gt.`groups_id`
6579 LEFT JOIN `glpi_users` usr ON gu.`users_id` = usr.`id`
6580 WHERE gt.`tickets_id` = ".$this->getId()."
6581 UNION
6582 SELECT usr.`id` AS users_id, '2' AS type
6583 FROM `glpi_profiles` prof
6584 LEFT JOIN `glpi_profiles_users` pu ON pu.`profiles_id` = prof.`id`
6585 LEFT JOIN `glpi_users` usr ON usr.`id` = pu.`users_id`
6586 LEFT JOIN `glpi_profilerights` pr ON pr.`profiles_id` = prof.`id`
6587 WHERE pr.`name` = 'ticket'
6588 AND pr.`rights` & ".Ticket::OWN." = ".Ticket::OWN."
6589 ) AS allactors
6590 WHERE `type` != ".CommonItilActor::OBSERVER."
6591 GROUP BY `users_id`
6592 ORDER BY `type` DESC";
6593
6594 $res = $DB->query($query);
6595 $ticket_users_keys = array();
6596 while ($current_tu = $DB->fetch_assoc($res)) {
6597 $ticket_users_keys[$current_tu['users_id']] = $current_tu['type'];
6598 }
6599
6600 return $ticket_users_keys;
6601 }
6602
6603
6604 /**
6605 * @since version 0.90
6606 **/
6607 function showTimelineHeader() {
6608
6609 echo "<h2>".__("Actions historical")." : </h2>";
6610 $this->filterTimeline();
6611 }
6612
6613
6614 /**
6615 * @since version 0.90
6616 */
6617 function filterTimeline() {
6618 global $CFG_GLPI;
6619
6620 $pics_url = $CFG_GLPI['root_doc']."/pics/timeline";
6621 echo "<div class='filter_timeline'>";
6622 echo "<label>".__("Timeline filter")." : </label>";
6623 echo "<ul>";
6624 echo "<li><a class='reset' title=\"".__("Reset display options").
6625 "\"><img src='$pics_url/reset.png' class='pointer' /></a></li>";
6626 echo "<li><a class='Solution' title='".__("Solution").
6627 "'><img src='$pics_url/solution_min.png' class='pointer' /></a></li>";
6628 echo "<li><a class='TicketValidation' title='".__("Validation").
6629 "'><img src='$pics_url/validation_min.png' class='pointer' /></a></li>";
6630 echo "<li><a class='Document_Item' title='".__("Document").
6631 "'><img src='$pics_url/document_min.png' class='pointer' /></a></li>";
6632 echo "<li><a class='TicketTask' title='".__("Task").
6633 "'><img src='$pics_url/task_min.png' class='pointer' /></a></li>";
6634 echo "<li><a class='TicketFollowup' title='".__("Followup").
6635 "'><img src='$pics_url/followup_min.png' class='pointer' /></a></li>";
6636 echo "</ul>";
6637 echo "</div>";
6638
6639 echo "<script type='text/javascript'>filter_timeline();</script>";
6640 }
6641
6642 /**
6643 * @since version 0.90
6644 *
6645 * @param $rand
6646 **/
6647 function showTimelineForm($rand) {
6648 global $CFG_GLPI;
6649
6650 //check global rights
6651 if (!Session::haveRight("ticket", Ticket::READMY)
6652 && !Session::haveRightsOr("followup", array(TicketFollowup::SEEPUBLIC,
6653 TicketFollowup::SEEPRIVATE))) {
6654 return false;
6655 }
6656
6657 // javascript function for add and edit items
6658 echo "<script type='text/javascript' >\n";
6659 echo "function viewAddSubitem" . $this->fields['id'] . "$rand(itemtype) {\n";
6660 $params = array('type' => 'itemtype',
6661 'parenttype' => 'Ticket',
6662 'tickets_id' => $this->fields['id'],
6663 'id' => -1);
6664 if (isset($_GET['load_kb_sol'])) {
6665 $params['load_kb_sol'] = $_GET['load_kb_sol'];
6666 }
6667 $out = Ajax::updateItemJsCode("viewitem" . $this->fields['id'] . "$rand",
6668 $CFG_GLPI["root_doc"]."/ajax/timeline_viewsubitem.php",
6669 $params, "", false);
6670 echo str_replace("\"itemtype\"", "itemtype", $out);
6671 echo "$('#approbation_form$rand').remove()";
6672 echo "};";
6673 $out = "function viewEditSubitem" . $this->fields['id'] . "$rand(e, itemtype, items_id, o) {\n
6674 var target = e.target || window.event.srcElement;
6675 if (target.nodeName == 'a') return;
6676 if (target.className == 'read_more_button') return;";
6677 $params = array('type' => 'itemtype',
6678 'parenttype' => 'Ticket',
6679 'tickets_id' => $this->fields['id'],
6680 'id' => 'items_id');
6681 $out.= Ajax::updateItemJsCode("viewitem" . $this->fields['id'] . "$rand",
6682 $CFG_GLPI["root_doc"]."/ajax/timeline_viewsubitem.php",
6683 $params, "", false);
6684 $out = str_replace("\"itemtype\"", "itemtype", $out);
6685 $out = str_replace("\"items_id\"", "items_id", $out);
6686 echo $out;
6687
6688 //scroll to edit form
6689 echo "$('body').scrollTop(0);";
6690 echo "$('.ui-tabs-panel').scrollTop(0);";
6691
6692 // add a mark to currently edited element
6693 echo "var found_active = $('.talk_active');
6694 i = found_active.length;
6695 while(i--) {
6696 var classes = found_active[i].className.replace( /(?:^|\s)talk_active(?!\S)/ , '' );
6697 found_active[i].className = classes;
6698 }
6699 o.className = o.className + ' talk_active';
6700
6701 $('#approbation_form$rand').remove();
6702 };";
6703
6704 if (isset($_GET['load_kb_sol'])) {
6705 echo "viewAddSubitem" . $this->fields['id'] . "$rand('Solution');";
6706 }
6707 echo "</script>\n";
6708
6709 //check sub-items rights
6710 $tmp = array('tickets_id' => $this->getID());
6711 $fup = new TicketFollowup;
6712 $ttask = new TicketTask;
6713 $doc = new Document;
6714
6715 $canadd_fup = $fup->can(-1, CREATE, $tmp);
6716 $canadd_task = $ttask->can(-1, CREATE, $tmp);
6717 $canadd_document = $doc->can(-1, CREATE, $tmp) && $this->canAddItem('Document');
6718 $canadd_solution = Ticket::canUpdate() && $this->canSolve();
6719
6720 if (!$canadd_fup && !$canadd_task && !$canadd_document && !$canadd_solution ) {
6721 return false;
6722 }
6723
6724 //show choices
6725 echo "<h2>"._sx('button', 'Add')." : </h2>";
6726 echo "<div class='timeline_form'>";
6727 echo "<ul class='timeline_choices'>";
6728
6729 if ($canadd_fup) {
6730 echo "<li class='followup' onclick='".
6731 "javascript:viewAddSubitem".$this->fields['id']."$rand(\"TicketFollowup\");'>"
6732 .__("Followup")."</li>";
6733 }
6734
6735 if ($canadd_task) {
6736 echo "<li class='task' onclick='".
6737 "javascript:viewAddSubitem".$this->fields['id']."$rand(\"TicketTask\");'>"
6738 .__("Task")."</li>";
6739 }
6740 if ($canadd_document) {
6741 echo "<li class='document' onclick='".
6742 "javascript:viewAddSubitem".$this->fields['id']."$rand(\"Document_Item\");'>"
6743 .__("Document")."</li>";
6744 }
6745 if ($canadd_solution) {
6746 echo "<li class='solution' onclick='".
6747 "javascript:viewAddSubitem".$this->fields['id']."$rand(\"Solution\");'>"
6748 .__("Solution")."</li>";
6749 }
6750
6751 echo "</ul>"; // timeline_choices
6752 echo "<div class='clear'> </div>";
6753
6754 echo "</div>"; //end timeline_form
6755
6756 echo "<div class='ajax_box' id='viewitem" . $this->fields['id'] . "$rand'></div>\n";
6757
6758 }
6759
6760
6761 /**
6762 * @since version 0.90
6763 *
6764 * @param $item
6765 * @param $id
6766 * @param $params
6767 **/
6768 static function showSubForm(CommonDBTM $item, $id, $params) {
6769
6770 if ($item instanceof Document_Item) {
6771 Document_Item::showAddFormForItem($params['parent'], '');
6772
6773 } else if (method_exists($item, "showForm")) {
6774 $item->showForm($id, $params);
6775 }
6776 }
6777
6778
6779 /**
6780 * @since version 0.90
6781 *
6782 * @param $tickets_id
6783 * @param $action (default 'add')
6784 **/
6785 static function getSplittedSubmitButtonHtml($tickets_id, $action="add") {
6786
6787 $locale = _sx('button', 'Add');
6788 if ($action == 'update') {
6789 $locale = _x('button','Save');
6790 }
6791 $ticket = new self();
6792 $ticket->getFromDB($tickets_id);
6793 $all_status = Ticket::getAllowedStatusArray($ticket->fields['status']);
6794
6795 $html = "<div class='x-split-button' id='x-split-button'>
6796 <input type='submit' value='$locale' name='$action' class='x-button x-button-main'>
6797 <span class='x-button x-button-drop'> </span>
6798 <ul class='x-button-drop-menu'>";
6799 foreach ($all_status as $status_key => $status_label) {
6800 $checked = "";
6801 if ($status_key == $ticket->fields['status']) {
6802 $checked = "checked='checked'";
6803 }
6804 $html .= "<li>";
6805 $html .= "<input type='radio' id='status_radio_$status_key' name='_status'
6806 $checked value='$status_key'>";
6807 $html .= "<label for='status_radio_$status_key'>";
6808 $html .= "<img src='".Ticket::getStatusIconURL($status_key)."' /> ";
6809 $html .= $status_label;
6810 $html .= "</label>";
6811 $html .= "</li>";
6812 }
6813 $html .= "</ul></div>";
6814
6815 $html.= "<script type='text/javascript'>split_button();</script>";
6816 return $html;
6817 }
6818
6819
6820 /**
6821 * Get correct Calendar: Entity or Sla
6822 *
6823 * @since 0.90.4
6824 *
6825 **/
6826 function getCalendar() {
6827
6828 if ($this->fields['slas_id'] > 0) {
6829 $sla = new SLA();
6830 if ($sla->getFromDB($this->fields['slas_id'])) {
6831 // not -1: calendar of the entity
6832 if ($sla->getField('calendars_id') >= 0) {
6833 return $sla->getField('calendars_id');
6834 }
6835 }
6836 }
6837 return parent::getCalendar();
6838 }
6839
6840}
6841?>