dolibarr 18.0.6
ticket.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2013-2018 Jean-François Ferry <hello@librethic.io>
3 * Copyright (C) 2016 Christophe Battarel <christophe@altairis.fr>
4 * Copyright (C) 2019-2023 Frédéric France <frederic.france@netlogic.fr>
5 * Copyright (C) 2020 Laurent Destailleur <eldy@users.sourceforge.net>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
27// Put here all includes required by your class file
28require_once DOL_DOCUMENT_ROOT."/core/class/commonobject.class.php";
29require_once DOL_DOCUMENT_ROOT.'/fichinter/class/fichinter.class.php';
30require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php';
31
32
36class Ticket extends CommonObject
37{
41 public $element = 'ticket';
42
46 public $table_element = 'ticket';
47
51 public $fk_element = 'fk_ticket';
52
56 public $ismultientitymanaged = 1;
57
61 public $isextrafieldmanaged = 1;
62
66 public $picto = 'ticket';
67
68
72 public $track_id;
73
77 public $fk_soc;
78
82 public $fk_project;
83
87 public $origin_email;
88
92 public $fk_user_create;
93
97 public $fk_user_assign;
98
102 public $subject;
103
107 public $message;
108
114 public $fk_statut;
115
119 public $status;
120
124 public $resolution;
125
129 public $progress;
130
134 public $timing;
135
139 public $type_code;
140
144 public $category_code;
145
149 public $severity_code;
150
155
160
165
170
174 public $datec = '';
175
179 public $date_read = '';
180
184 public $date_last_msg_sent = '';
185
189 public $date_close = '';
190
194 public $cache_types_tickets;
195
199 public $cache_category_tickets;
200
204 public $cache_severity_tickets;
205
209 public $cache_msgs_ticket;
210
214 public $statuts;
215
219 public $statuts_short;
220
224 public $notify_tiers_at_create;
225
229 public $email_msgid;
230
234 public $email_date;
235
239 public $ip;
240
244 public $oldcopy;
245
249 public $lines;
250
251
255 public $regeximgext = '\.gif|\.jpg|\.jpeg|\.png|\.bmp|\.webp|\.xpm|\.xbm'; // See also into images.lib.php
256
261 const STATUS_READ = 1;
262 const STATUS_ASSIGNED = 2;
263 const STATUS_IN_PROGRESS = 3;
264 const STATUS_NEED_MORE_INFO = 5; // waiting requester feedback
265 const STATUS_WAITING = 7; // on hold
266 const STATUS_CLOSED = 8; // Closed - Solved
267 const STATUS_CANCELED = 9; // Closed - Not solved
268
269
296 // BEGIN MODULEBUILDER PROPERTIES
297 public $fields = array(
298 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'visible'=>-2, 'enabled'=>1, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>"Id"),
299 'entity' => array('type'=>'integer', 'label'=>'Entity', 'visible'=>0, 'enabled'=>1, 'position'=>5, 'notnull'=>1, 'index'=>1),
300 'ref' => array('type'=>'varchar(128)', 'label'=>'Ref', 'visible'=>1, 'enabled'=>1, 'position'=>10, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object", 'css'=>'', 'showoncombobox'=>1),
301 'track_id' => array('type'=>'varchar(255)', 'label'=>'TicketTrackId', 'visible'=>-2, 'enabled'=>1, 'position'=>11, 'notnull'=>-1, 'searchall'=>1, 'help'=>"Help text"),
302 'fk_user_create' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'Author', 'visible'=>1, 'enabled'=>1, 'position'=>15, 'notnull'=>1, 'csslist'=>'tdoverflowmax100 maxwidth150onsmartphone'),
303 'origin_email' => array('type'=>'mail', 'label'=>'OriginEmail', 'visible'=>-2, 'enabled'=>1, 'position'=>16, 'notnull'=>1, 'index'=>1, 'searchall'=>1, 'comment'=>"Reference of object", 'csslist'=>'tdoverflowmax150'),
304 'subject' => array('type'=>'varchar(255)', 'label'=>'Subject', 'visible'=>1, 'enabled'=>1, 'position'=>18, 'notnull'=>-1, 'searchall'=>1, 'help'=>"", 'css'=>'maxwidth200 tdoverflowmax200', 'autofocusoncreate'=>1),
305 'type_code' => array('type'=>'varchar(32)', 'label'=>'Type', 'visible'=>1, 'enabled'=>1, 'position'=>20, 'notnull'=>-1, 'help'=>"", 'csslist'=>'maxwidth125 tdoverflowmax50'),
306 'category_code' => array('type'=>'varchar(32)', 'label'=>'TicketCategory', 'visible'=>-1, 'enabled'=>1, 'position'=>21, 'notnull'=>-1, 'help'=>"", 'css'=>'maxwidth100 tdoverflowmax200'),
307 'severity_code' => array('type'=>'varchar(32)', 'label'=>'Severity', 'visible'=>1, 'enabled'=>1, 'position'=>22, 'notnull'=>-1, 'help'=>"", 'css'=>'maxwidth100'),
308 'fk_soc' => array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'visible'=>1, 'enabled'=>'isModEnabled("societe")', 'position'=>50, 'notnull'=>-1, 'index'=>1, 'searchall'=>1, 'help'=>"OrganizationEventLinkToThirdParty", 'css'=>'tdoverflowmax150 maxwidth150onsmartphone'),
309 'notify_tiers_at_create' => array('type'=>'integer', 'label'=>'NotifyThirdparty', 'visible'=>-1, 'enabled'=>0, 'position'=>51, 'notnull'=>1, 'index'=>1),
310 'fk_project' => array('type'=>'integer:Project:projet/class/project.class.php', 'label'=>'Project', 'visible'=>-1, 'enabled'=>'$conf->project->enabled', 'position'=>52, 'notnull'=>-1, 'index'=>1, 'help'=>"LinkToProject"),
311 //'timing' => array('type'=>'varchar(20)', 'label'=>'Timing', 'visible'=>-1, 'enabled'=>1, 'position'=>42, 'notnull'=>-1, 'help'=>""), // what is this ?
312 'datec' => array('type'=>'datetime', 'label'=>'DateCreation', 'visible'=>1, 'enabled'=>1, 'position'=>500, 'notnull'=>1, 'csslist'=>'nowraponall'),
313 'date_read' => array('type'=>'datetime', 'label'=>'TicketReadOn', 'visible'=>-1, 'enabled'=>1, 'position'=>501, 'notnull'=>1),
314 'date_last_msg_sent' => array('type'=>'datetime', 'label'=>'TicketLastMessageDate', 'visible'=>0, 'enabled'=>1, 'position'=>502, 'notnull'=>-1),
315 'fk_user_assign' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'AssignedTo', 'visible'=>1, 'enabled'=>1, 'position'=>505, 'notnull'=>1, 'csslist'=>'tdoverflowmax100 maxwidth150onsmartphone'),
316 'date_close' => array('type'=>'datetime', 'label'=>'TicketCloseOn', 'visible'=>-1, 'enabled'=>1, 'position'=>510, 'notnull'=>1),
317 'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'visible'=>-1, 'enabled'=>1, 'position'=>520, 'notnull'=>1),
318 'message' => array('type'=>'html', 'label'=>'Message', 'visible'=>-2, 'enabled'=>1, 'position'=>540, 'notnull'=>-1,),
319 'email_msgid' => array('type'=>'varchar(255)', 'label'=>'EmailMsgID', 'visible'=>-2, 'enabled'=>1, 'position'=>540, 'notnull'=>-1, 'help'=>'EmailMsgIDDesc', 'csslist'=>'tdoverflowmax100'),
320 'email_date' => array('type'=>'datetime', 'label'=>'EmailDate', 'visible'=>-2, 'enabled'=>1, 'position'=>541),
321 'progress' => array('type'=>'integer', 'label'=>'Progression', 'visible'=>-1, 'enabled'=>1, 'position'=>540, 'notnull'=>-1, 'css'=>'right', 'help'=>"", 'isameasure'=>2, 'csslist'=>'width50'),
322 'resolution' => array('type'=>'integer', 'label'=>'Resolution', 'visible'=>-1, 'enabled'=>'getDolGlobalString("TICKET_ENABLE_RESOLUTION")', 'position'=>550, 'notnull'=>1),
323 'fk_statut' => array('type'=>'integer', 'label'=>'Status', 'visible'=>1, 'enabled'=>1, 'position'=>600, 'notnull'=>1, 'index'=>1, 'arrayofkeyval'=>array(0 => 'Unread', 1 => 'Read', 3 => 'Answered', 4 => 'Assigned', 5 => 'InProgress', 6 => 'Waiting', 8 => 'SolvedClosed', 9 => 'Deleted')),
324 'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>900),
325 );
326 // END MODULEBUILDER PROPERTIES
327
328
334 public function __construct($db)
335 {
336 global $conf;
337
338 $this->db = $db;
339
340 $this->statuts_short = array(
341 self::STATUS_NOT_READ => 'Unread',
342 self::STATUS_READ => 'Read',
343 self::STATUS_ASSIGNED => 'Assigned',
344 self::STATUS_IN_PROGRESS => 'InProgress',
345 self::STATUS_WAITING => 'OnHold',
346 self::STATUS_NEED_MORE_INFO => 'NeedMoreInformationShort',
347 self::STATUS_CLOSED => 'SolvedClosed',
348 self::STATUS_CANCELED => 'Canceled'
349 );
350 $this->statuts = array(
351 self::STATUS_NOT_READ => 'Unread',
352 self::STATUS_READ => 'Read',
353 self::STATUS_ASSIGNED => 'Assigned',
354 self::STATUS_IN_PROGRESS => 'InProgress',
355 self::STATUS_WAITING => 'OnHold',
356 self::STATUS_NEED_MORE_INFO => 'NeedMoreInformation',
357 self::STATUS_CLOSED => 'SolvedClosed',
358 self::STATUS_CANCELED => 'Canceled'
359 );
360 }
361
368 private function verify()
369 {
370 $this->errors = array();
371
372 $result = 0;
373
374 // Clean parameters
375 if (isset($this->ref)) {
376 $this->ref = trim($this->ref);
377 }
378
379 if (isset($this->track_id)) {
380 $this->track_id = trim($this->track_id);
381 }
382
383 if (isset($this->fk_soc)) {
384 $this->fk_soc = (int) $this->fk_soc;
385 }
386
387 if (isset($this->fk_project)) {
388 $this->fk_project = (int) $this->fk_project;
389 }
390
391 if (isset($this->origin_email)) {
392 $this->origin_email = trim($this->origin_email);
393 }
394
395 if (isset($this->fk_user_create)) {
396 $this->fk_user_create = (int) $this->fk_user_create;
397 }
398
399 if (isset($this->fk_user_assign)) {
400 $this->fk_user_assign = (int) $this->fk_user_assign;
401 }
402
403 if (isset($this->subject)) {
404 $this->subject = trim($this->subject);
405 }
406
407 if (isset($this->message)) {
408 $this->message = trim($this->message);
409 if (dol_strlen($this->message) > 65000) {
410 $this->errors[] = 'ErrorFieldTooLong';
411 dol_syslog(get_class($this).'::create error -1 message too long', LOG_ERR);
412 $result = -1;
413 }
414 }
415
416 if (isset($this->fk_statut)) {
417 $this->fk_statut = (int) $this->fk_statut;
418 }
419
420 if (isset($this->resolution)) {
421 $this->resolution = trim($this->resolution);
422 }
423
424 if (isset($this->progress)) {
425 $this->progress = trim($this->progress);
426 }
427
428 if (isset($this->timing)) {
429 $this->timing = trim($this->timing);
430 }
431
432 if (isset($this->type_code)) {
433 $this->type_code = trim($this->type_code);
434 }
435
436 if (isset($this->category_code)) {
437 $this->category_code = trim($this->category_code);
438 }
439
440 if (isset($this->severity_code)) {
441 $this->severity_code = trim($this->severity_code);
442 }
443
444 if (empty($this->ref)) {
445 $this->errors[] = 'ErrorTicketRefRequired';
446 dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR);
447 $result = -1;
448 }
449
450 return $result;
451 }
452
460 public function create($user, $notrigger = 0)
461 {
462 global $conf;
463
464 $error = 0;
465
466 // Clean parameters
467 $this->datec = dol_now();
468 if (empty($this->track_id)) {
469 $this->track_id = generate_random_id(16);
470 }
471
472 // Check more parameters
473 // If error, this->errors[] is filled
474 $result = $this->verify();
475
476 if ($result >= 0) {
477 $this->entity = ((isset($this->entity) && is_numeric($this->entity)) ? $this->entity : $conf->entity);
478
479 // Insert request
480 $sql = "INSERT INTO ".MAIN_DB_PREFIX."ticket(";
481 $sql .= "ref,";
482 $sql .= "track_id,";
483 $sql .= "fk_soc,";
484 $sql .= "fk_project,";
485 $sql .= "origin_email,";
486 $sql .= "fk_user_create,";
487 $sql .= "fk_user_assign,";
488 $sql .= "email_msgid,";
489 $sql .= "email_date,";
490 $sql .= "subject,";
491 $sql .= "message,";
492 $sql .= "fk_statut,";
493 $sql .= "resolution,";
494 $sql .= "progress,";
495 $sql .= "timing,";
496 $sql .= "type_code,";
497 $sql .= "category_code,";
498 $sql .= "severity_code,";
499 $sql .= "datec,";
500 $sql .= "date_read,";
501 $sql .= "date_close,";
502 $sql .= "entity,";
503 $sql .= "notify_tiers_at_create,";
504 $sql .= "ip";
505 $sql .= ") VALUES (";
506 $sql .= " ".(!isset($this->ref) ? '' : "'".$this->db->escape($this->ref)."'").",";
507 $sql .= " ".(!isset($this->track_id) ? 'NULL' : "'".$this->db->escape($this->track_id)."'").",";
508 $sql .= " ".($this->fk_soc > 0 ? $this->db->escape($this->fk_soc) : "null").",";
509 $sql .= " ".($this->fk_project > 0 ? $this->db->escape($this->fk_project) : "null").",";
510 $sql .= " ".(!isset($this->origin_email) ? 'NULL' : "'".$this->db->escape($this->origin_email)."'").",";
511 $sql .= " ".(!isset($this->fk_user_create) ? ($user->id > 0 ? $user->id : 'NULL') : ($this->fk_user_create > 0 ? $this->fk_user_create : 'NULL')).",";
512 $sql .= " ".($this->fk_user_assign > 0 ? $this->fk_user_assign : 'NULL').",";
513 $sql .= " ".(empty($this->email_msgid) ? 'NULL' : "'".$this->db->escape($this->email_msgid)."'").",";
514 $sql .= " ".(empty($this->email_date) ? 'NULL' : "'".$this->db->idate($this->email_date)."'").",";
515 $sql .= " ".(!isset($this->subject) ? 'NULL' : "'".$this->db->escape($this->subject)."'").",";
516 $sql .= " ".(!isset($this->message) ? 'NULL' : "'".$this->db->escape($this->message)."'").",";
517 $sql .= " ".(!isset($this->fk_statut) ? '0' : "'".$this->db->escape($this->fk_statut)."'").",";
518 $sql .= " ".(!isset($this->resolution) ? 'NULL' : "'".$this->db->escape($this->resolution)."'").",";
519 $sql .= " ".(!isset($this->progress) ? '0' : "'".$this->db->escape($this->progress)."'").",";
520 $sql .= " ".(!isset($this->timing) ? 'NULL' : "'".$this->db->escape($this->timing)."'").",";
521 $sql .= " ".(!isset($this->type_code) ? 'NULL' : "'".$this->db->escape($this->type_code)."'").",";
522 $sql .= " ".(empty($this->category_code) || $this->category_code == '-1' ? 'NULL' : "'".$this->db->escape($this->category_code)."'").",";
523 $sql .= " ".(!isset($this->severity_code) ? 'NULL' : "'".$this->db->escape($this->severity_code)."'").",";
524 $sql .= " ".(!isset($this->datec) || dol_strlen($this->datec) == 0 ? 'NULL' : "'".$this->db->idate($this->datec)."'").",";
525 $sql .= " ".(!isset($this->date_read) || dol_strlen($this->date_read) == 0 ? 'NULL' : "'".$this->db->idate($this->date_read)."'").",";
526 $sql .= " ".(!isset($this->date_close) || dol_strlen($this->date_close) == 0 ? 'NULL' : "'".$this->db->idate($this->date_close)."'");
527 $sql .= ", ".((int) $this->entity);
528 $sql .= ", ".(!isset($this->notify_tiers_at_create) ? '1' : "'".$this->db->escape($this->notify_tiers_at_create)."'");
529 $sql .= ", ".(!isset($this->ip) ? 'NULL' : "'".$this->db->escape($this->ip)."'");
530 $sql .= ")";
531
532 $this->db->begin();
533
534 dol_syslog(get_class($this)."::create", LOG_DEBUG);
535 $resql = $this->db->query($sql);
536 if (!$resql) {
537 $error++;
538 $this->errors[] = "Error ".$this->db->lasterror();
539 }
540
541 if (!$error) {
542 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."ticket");
543 }
544
545 if (!$error && getDolGlobalString('TICKET_ADD_AUTHOR_AS_CONTACT')) {
546 // add creator as contributor
547 if ($this->add_contact($user->id, 'CONTRIBUTOR', 'internal') < 0) {
548 $error++;
549 }
550 }
551
552 if (!$error && $this->fk_user_assign > 0) {
553 if ($this->add_contact($this->fk_user_assign, 'SUPPORTTEC', 'internal') < 0) {
554 $error++;
555 }
556 }
557
558
559 //Update extrafield
560 if (!$error) {
561 $result = $this->insertExtraFields();
562 if ($result < 0) {
563 $error++;
564 }
565 }
566
567 if (!$error && !$notrigger) {
568 // Call trigger
569 $result = $this->call_trigger('TICKET_CREATE', $user);
570 if ($result < 0) {
571 $error++;
572 }
573 // End call triggers
574 }
575
576 // Commit or rollback
577 if ($error) {
578 foreach ($this->errors as $errmsg) {
579 dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
580 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
581 }
582 $this->db->rollback();
583 return -1 * $error;
584 } else {
585 $this->db->commit();
586 return $this->id;
587 }
588 } else {
589 $this->db->rollback();
590 dol_syslog(get_class($this)."::Create fails verify ".join(',', $this->errors), LOG_WARNING);
591 return -3;
592 }
593 }
594
604 public function fetch($id = '', $ref = '', $track_id = '', $email_msgid = '')
605 {
606 global $langs;
607
608 // Check parameters
609 if (empty($id) && empty($ref) && empty($track_id) && empty($email_msgid)) {
610 $this->error = 'ErrorWrongParameters';
611 dol_print_error('', get_class($this)."::fetch ".$this->error);
612 return -1;
613 }
614
615 $sql = "SELECT";
616 $sql .= " t.rowid,";
617 $sql .= " t.entity,";
618 $sql .= " t.ref,";
619 $sql .= " t.track_id,";
620 $sql .= " t.fk_soc,";
621 $sql .= " t.fk_project,";
622 $sql .= " t.origin_email,";
623 $sql .= " t.fk_user_create,";
624 $sql .= " t.fk_user_assign,";
625 $sql .= " t.email_msgid,";
626 $sql .= " t.email_date,";
627 $sql .= " t.subject,";
628 $sql .= " t.message,";
629 $sql .= " t.fk_statut as status,";
630 $sql .= " t.resolution,";
631 $sql .= " t.progress,";
632 $sql .= " t.timing,";
633 $sql .= " t.type_code,";
634 $sql .= " t.category_code,";
635 $sql .= " t.severity_code,";
636 $sql .= " t.datec,";
637 $sql .= " t.date_read,";
638 $sql .= " t.date_last_msg_sent,";
639 $sql .= " t.date_close,";
640 $sql .= " t.tms,";
641 $sql .= " t.ip,";
642 $sql .= " type.label as type_label, category.label as category_label, severity.label as severity_label";
643 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as t";
644 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_type as type ON type.code=t.type_code";
645 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_category as category ON category.code=t.category_code";
646 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_severity as severity ON severity.code=t.severity_code";
647
648 if ($id) {
649 $sql .= " WHERE t.rowid = ".((int) $id);
650 } else {
651 $sql .= " WHERE t.entity IN (".getEntity($this->element, 1).")";
652 if (!empty($ref)) {
653 $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
654 } elseif ($track_id) {
655 $sql .= " AND t.track_id = '".$this->db->escape($track_id)."'";
656 } else {
657 $sql .= " AND t.email_msgid = '".$this->db->escape($email_msgid)."'";
658 }
659 }
660
661 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
662 $resql = $this->db->query($sql);
663 if ($resql) {
664 if ($this->db->num_rows($resql)) {
665 $obj = $this->db->fetch_object($resql);
666
667 $this->id = $obj->rowid;
668 $this->entity = $obj->entity;
669 $this->ref = $obj->ref;
670 $this->track_id = $obj->track_id;
671 $this->fk_soc = $obj->fk_soc;
672 $this->socid = $obj->fk_soc; // for fetch_thirdparty() method
673 $this->fk_project = $obj->fk_project;
674 $this->origin_email = $obj->origin_email;
675 $this->fk_user_create = $obj->fk_user_create;
676 $this->fk_user_assign = $obj->fk_user_assign;
677 $this->email_msgid = $obj->email_msgid;
678 $this->email_date = $this->db->jdate($obj->email_date);
679 $this->subject = $obj->subject;
680 $this->message = $obj->message;
681 $this->ip = $obj->ip;
682
683 $this->status = $obj->status;
684 $this->fk_statut = $this->status; // For backward compatibility
685
686 $this->resolution = $obj->resolution;
687 $this->progress = $obj->progress;
688 $this->timing = $obj->timing;
689
690 $this->type_code = $obj->type_code;
691 $label_type = ($langs->trans("TicketTypeShort".$obj->type_code) != ("TicketTypeShort".$obj->type_code) ? $langs->trans("TicketTypeShort".$obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));
692 $this->type_label = $label_type;
693
694 $this->category_code = $obj->category_code;
695 $label_category = ($langs->trans("TicketCategoryShort".$obj->category_code) != ("TicketCategoryShort".$obj->category_code) ? $langs->trans("TicketCategoryShort".$obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));
696 $this->category_label = $label_category;
697
698 $this->severity_code = $obj->severity_code;
699 $label_severity = ($langs->trans("TicketSeverityShort".$obj->severity_code) != ("TicketSeverityShort".$obj->severity_code) ? $langs->trans("TicketSeverityShort".$obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));
700 $this->severity_label = $label_severity;
701
702 $this->datec = $this->db->jdate($obj->datec);
703 $this->date_creation = $this->db->jdate($obj->datec);
704 $this->date_read = $this->db->jdate($obj->date_read);
705 $this->date_validation = $this->db->jdate($obj->date_read);
706 $this->date_last_msg_sent = $this->db->jdate($obj->date_last_msg_sent);
707 $this->date_close = $this->db->jdate($obj->date_close);
708 $this->tms = $this->db->jdate($obj->tms);
709 $this->date_modification = $this->db->jdate($obj->tms);
710
711 $this->fetch_optionals();
712
713 $this->db->free($resql);
714 return 1;
715 } else {
716 return 0;
717 }
718 } else {
719 $this->error = "Error ".$this->db->lasterror();
720 dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
721 return -1;
722 }
723 }
724
738 public function fetchAll($user, $sortorder = 'ASC', $sortfield = 't.datec', $limit = '', $offset = 0, $arch = '', $filter = '')
739 {
740 global $langs;
741
742 $extrafields = new ExtraFields($this->db);
743
744 // fetch optionals attributes and labels
745 $extrafields->fetch_name_optionals_label($this->table_element);
746
747 $sql = "SELECT";
748 $sql .= " t.rowid,";
749 $sql .= " t.ref,";
750 $sql .= " t.track_id,";
751 $sql .= " t.fk_soc,";
752 $sql .= " t.fk_project,";
753 $sql .= " t.origin_email,";
754 $sql .= " t.fk_user_create, uc.lastname as user_create_lastname, uc.firstname as user_create_firstname,";
755 $sql .= " t.fk_user_assign, ua.lastname as user_assign_lastname, ua.firstname as user_assign_firstname,";
756 $sql .= " t.subject,";
757 $sql .= " t.message,";
758 $sql .= " t.fk_statut,";
759 $sql .= " t.resolution,";
760 $sql .= " t.progress,";
761 $sql .= " t.timing,";
762 $sql .= " t.type_code,";
763 $sql .= " t.category_code,";
764 $sql .= " t.severity_code,";
765 $sql .= " t.datec,";
766 $sql .= " t.date_read,";
767 $sql .= " t.date_last_msg_sent,";
768 $sql .= " t.date_close,";
769 $sql .= " t.tms";
770 $sql .= ", type.label as type_label, category.label as category_label, severity.label as severity_label";
771 // Add fields for extrafields
772 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
773 $sql .= ($extrafields->attributes[$this->table_element]['type'][$key] != 'separate' ? ",ef.".$key." as options_".$key : '');
774 }
775 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as t";
776 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_type as type ON type.code=t.type_code";
777 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_category as category ON category.code=t.category_code";
778 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_severity as severity ON severity.code=t.severity_code";
779 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid=t.fk_soc";
780 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as uc ON uc.rowid=t.fk_user_create";
781 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as ua ON ua.rowid=t.fk_user_assign";
782 if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {
783 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."ticket_extrafields as ef on (t.rowid = ef.fk_object)";
784 }
785 if (empty($user->rights->societe->client->voir) && !$user->socid) {
786 $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
787 }
788
789 $sql .= " WHERE t.entity IN (".getEntity('ticket').")";
790
791 // Manage filter
792 if (!empty($filter)) {
793 foreach ($filter as $key => $value) {
794 if (strpos($key, 'date')) { // To allow $filter['YEAR(s.dated)']=>$year
795 $sql .= " AND ".$key." = '".$this->db->escape($value)."'";
796 } elseif (($key == 't.fk_user_assign') || ($key == 't.type_code') || ($key == 't.category_code') || ($key == 't.severity_code') || ($key == 't.fk_soc')) {
797 $sql .= " AND ".$key." = '".$this->db->escape($value)."'";
798 } elseif ($key == 't.fk_statut') {
799 if (is_array($value) && count($value) > 0) {
800 $sql .= " AND ".$key." IN (".$this->db->sanitize(implode(',', $value)).")";
801 } else {
802 $sql .= " AND ".$key.' = '.((int) $value);
803 }
804 } else {
805 $sql .= " AND ".$key." LIKE '%".$this->db->escape($value)."%'";
806 }
807 }
808 }
809 if (empty($user->rights->societe->client->voir) && !$user->socid) {
810 $sql .= " AND t.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
811 } elseif ($user->socid) {
812 $sql .= " AND t.fk_soc = ".((int) $user->socid);
813 }
814
815 $sql .= $this->db->order($sortfield, $sortorder);
816 if (!empty($limit)) {
817 $sql .= $this->db->plimit($limit + 1, $offset);
818 }
819
820 dol_syslog(get_class($this)."::fetchAll", LOG_DEBUG);
821 $resql = $this->db->query($sql);
822
823 if ($resql) {
824 $this->lines = array();
825
826 $num = $this->db->num_rows($resql);
827 $i = 0;
828
829 if ($num) {
830 while ($i < $num) {
831 $obj = $this->db->fetch_object($resql);
832
833 $line = new TicketsLine();
834
835 $line->id = $obj->rowid;
836 $line->rowid = $obj->rowid;
837 $line->ref = $obj->ref;
838 $line->track_id = $obj->track_id;
839 $line->fk_soc = $obj->fk_soc;
840 $line->fk_project = $obj->fk_project;
841 $line->origin_email = $obj->origin_email;
842
843 $line->fk_user_create = $obj->fk_user_create;
844 $line->user_create_lastname = $obj->user_create_lastname;
845 $line->user_create_firstname = $obj->user_create_firstname;
846
847 $line->fk_user_assign = $obj->fk_user_assign;
848 $line->user_assign_lastname = $obj->user_assign_lastname;
849 $line->user_assign_firstname = $obj->user_assign_firstname;
850
851 $line->subject = $obj->subject;
852 $line->message = $obj->message;
853 $line->fk_statut = $obj->fk_statut;
854 $line->resolution = $obj->resolution;
855 $line->progress = $obj->progress;
856 $line->timing = $obj->timing;
857
858 $label_type = ($langs->trans("TicketTypeShort".$obj->type_code) != ("TicketTypeShort".$obj->type_code) ? $langs->trans("TicketTypeShort".$obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));
859 $line->type_label = $label_type;
860
861 $this->category_code = $obj->category_code;
862 $label_category = ($langs->trans("TicketCategoryShort".$obj->category_code) != ("TicketCategoryShort".$obj->category_code) ? $langs->trans("TicketCategoryShort".$obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));
863 $line->category_label = $label_category;
864
865 $this->severity_code = $obj->severity_code;
866 $label_severity = ($langs->trans("TicketSeverityShort".$obj->severity_code) != ("TicketSeverityShort".$obj->severity_code) ? $langs->trans("TicketSeverityShort".$obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));
867 $line->severity_label = $label_severity;
868
869 $line->datec = $this->db->jdate($obj->datec);
870 $line->date_read = $this->db->jdate($obj->date_read);
871 $line->date_last_msg_sent = $this->db->jdate($obj->date_last_msg_sent);
872 $line->date_close = $this->db->jdate($obj->date_close);
873
874 // Extra fields
875 if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {
876 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
877 $tmpkey = 'options_'.$key;
878 $line->{$tmpkey} = $obj->$tmpkey;
879 }
880 }
881
882 $this->lines[$i] = $line;
883 $i++;
884 }
885 }
886 $this->db->free($resql);
887 return $num;
888 } else {
889 $this->error = "Error ".$this->db->lasterror();
890 dol_syslog(get_class($this)."::fetchAll ".$this->error, LOG_ERR);
891 return -1;
892 }
893 }
894
902 public function update($user = 0, $notrigger = 0)
903 {
904 global $conf, $langs, $hookmanager;
905 $error = 0;
906
907 // $this->oldcopy should have been set by the caller of update (here properties were already modified)
908 //if (empty($this->oldcopy)) {
909 // $this->oldcopy = dol_clone($this);
910 //}
911
912 // Clean parameters
913 if (isset($this->ref)) {
914 $this->ref = trim($this->ref);
915 }
916
917 if (isset($this->track_id)) {
918 $this->track_id = trim($this->track_id);
919 }
920
921 if (isset($this->fk_soc)) {
922 $this->fk_soc = (int) $this->fk_soc;
923 }
924
925 if (isset($this->fk_project)) {
926 $this->fk_project = (int) $this->fk_project;
927 }
928
929 if (isset($this->origin_email)) {
930 $this->origin_email = trim($this->origin_email);
931 }
932
933 if (isset($this->fk_user_create)) {
934 $this->fk_user_create = (int) $this->fk_user_create;
935 }
936
937 if (isset($this->fk_user_assign)) {
938 $this->fk_user_assign = (int) $this->fk_user_assign;
939 }
940
941 if (isset($this->subject)) {
942 $this->subject = trim($this->subject);
943 }
944
945 if (isset($this->message)) {
946 $this->message = trim($this->message);
947 if (dol_strlen($this->message) > 65000) {
948 $this->errors[] = 'ErrorFieldTooLong';
949 dol_syslog(get_class($this).'::update error -1 message too long', LOG_ERR);
950 return -1;
951 }
952 }
953
954 if (isset($this->fk_statut)) {
955 $this->fk_statut = (int) $this->fk_statut;
956 }
957
958 if (isset($this->resolution)) {
959 $this->resolution = trim($this->resolution);
960 }
961
962 if (isset($this->progress)) {
963 $this->progress = trim($this->progress);
964 }
965
966 if (isset($this->timing)) {
967 $this->timing = trim($this->timing);
968 }
969
970 if (isset($this->type_code)) {
971 $this->timing = trim($this->type_code);
972 }
973
974 if (isset($this->category_code)) {
975 $this->timing = trim($this->category_code);
976 }
977
978 if (isset($this->severity_code)) {
979 $this->timing = trim($this->severity_code);
980 }
981
982 // Check parameters
983 // Put here code to add a control on parameters values
984 // Update request
985 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket SET";
986 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "").",";
987 $sql .= " track_id=".(isset($this->track_id) ? "'".$this->db->escape($this->track_id)."'" : "null").",";
988 $sql .= " fk_soc=".(isset($this->fk_soc) ? "'".$this->db->escape($this->fk_soc)."'" : "null").",";
989 $sql .= " fk_project=".(isset($this->fk_project) ? "'".$this->db->escape($this->fk_project)."'" : "null").",";
990 $sql .= " origin_email=".(isset($this->origin_email) ? "'".$this->db->escape($this->origin_email)."'" : "null").",";
991 $sql .= " fk_user_create=".(isset($this->fk_user_create) ? $this->fk_user_create : "null").",";
992 $sql .= " fk_user_assign=".(isset($this->fk_user_assign) ? $this->fk_user_assign : "null").",";
993 $sql .= " subject=".(isset($this->subject) ? "'".$this->db->escape($this->subject)."'" : "null").",";
994 $sql .= " message=".(isset($this->message) ? "'".$this->db->escape($this->message)."'" : "null").",";
995 $sql .= " fk_statut=".(isset($this->fk_statut) ? $this->fk_statut : "0").",";
996 $sql .= " resolution=".(isset($this->resolution) ? $this->resolution : "null").",";
997 $sql .= " progress=".(isset($this->progress) ? "'".$this->db->escape($this->progress)."'" : "null").",";
998 $sql .= " timing=".(isset($this->timing) ? "'".$this->db->escape($this->timing)."'" : "null").",";
999 $sql .= " type_code=".(isset($this->type_code) ? "'".$this->db->escape($this->type_code)."'" : "null").",";
1000 $sql .= " category_code=".(isset($this->category_code) ? "'".$this->db->escape($this->category_code)."'" : "null").",";
1001 $sql .= " severity_code=".(isset($this->severity_code) ? "'".$this->db->escape($this->severity_code)."'" : "null").",";
1002 $sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1003 $sql .= " date_read=".(dol_strlen($this->date_read) != 0 ? "'".$this->db->idate($this->date_read)."'" : 'null').",";
1004 $sql .= " date_last_msg_sent=".(dol_strlen($this->date_last_msg_sent) != 0 ? "'".$this->db->idate($this->date_last_msg_sent)."'" : 'null').",";
1005 $sql .= " date_close=".(dol_strlen($this->date_close) != 0 ? "'".$this->db->idate($this->date_close)."'" : 'null');
1006 $sql .= " WHERE rowid=".((int) $this->id);
1007
1008 $this->db->begin();
1009
1010 $resql = $this->db->query($sql);
1011 if (!$resql) {
1012 $error++;
1013 $this->errors[] = "Error ".$this->db->lasterror();
1014 }
1015
1016 if (!$error) {
1017 // Update extrafields
1018 $result = $this->insertExtraFields();
1019 if ($result < 0) {
1020 $error++;
1021 }
1022 }
1023
1024 if (!$error && !$notrigger) {
1025 // Call trigger
1026 $result = $this->call_trigger('TICKET_MODIFY', $user);
1027 if ($result < 0) {
1028 $error++;
1029 }
1030 // End call triggers
1031 }
1032
1033 // Commit or rollback
1034 if ($error) {
1035 foreach ($this->errors as $errmsg) {
1036 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1037 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1038 }
1039 $this->db->rollback();
1040 return -1 * $error;
1041 } else {
1042 $this->db->commit();
1043 return 1;
1044 }
1045 }
1046
1054 public function delete($user, $notrigger = 0)
1055 {
1056 global $conf, $langs;
1057 $error = 0;
1058
1059 $this->db->begin();
1060
1061 if (!$error) {
1062 if (!$notrigger) {
1063 // Call trigger
1064 $result = $this->call_trigger('TICKET_DELETE', $user);
1065 if ($result < 0) {
1066 $error++;
1067 }
1068 // End call triggers
1069 }
1070 }
1071
1072 if (!$error) {
1073 // Delete linked contacts
1074 $res = $this->delete_linked_contact();
1075 if ($res < 0) {
1076 dol_syslog(get_class($this)."::delete error", LOG_ERR);
1077 $error++;
1078 }
1079 }
1080
1081 if (!$error) {
1082 // Delete linked object
1083 $res = $this->deleteObjectLinked();
1084 if ($res < 0) {
1085 $error++;
1086 }
1087 }
1088
1089 // Removed extrafields
1090 if (!$error) {
1091 $result = $this->deleteExtraFields();
1092 if ($result < 0) {
1093 $error++;
1094 dol_syslog(get_class($this)."::delete error -3 ".$this->error, LOG_ERR);
1095 }
1096 }
1097
1098 // Delete all child tables
1099
1100 if (!$error) {
1101 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_ticket";
1102 $sql .= " WHERE fk_ticket = ".(int) $this->id;
1103
1104 $result = $this->db->query($sql);
1105 if (!$result) {
1106 $error++;
1107 $this->errors[] = $this->db->lasterror();
1108 }
1109 }
1110
1111 if (!$error) {
1112 $sql = "DELETE FROM ".MAIN_DB_PREFIX."ticket";
1113 $sql .= " WHERE rowid=".((int) $this->id);
1114
1115 dol_syslog(get_class($this)."::delete sql=".$sql);
1116 $resql = $this->db->query($sql);
1117 if (!$resql) {
1118 $error++;
1119 $this->errors[] = "Error ".$this->db->lasterror();
1120 }
1121 }
1122
1123 // Commit or rollback
1124 if ($error) {
1125 foreach ($this->errors as $errmsg) {
1126 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1127 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1128 }
1129 $this->db->rollback();
1130 return -1 * $error;
1131 } else {
1132 $this->db->commit();
1133 return 1;
1134 }
1135 }
1136
1144 public function createFromClone(User $user, $fromid)
1145 {
1146 $error = 0;
1147
1148 $object = new Ticket($this->db);
1149
1150 $this->db->begin();
1151
1152 // Load source object
1153 $object->fetch($fromid);
1154 $object->id = 0;
1155 $object->statut = 0;
1156
1157 // Clear fields
1158 // ...
1159 // Create clone
1160 $object->context['createfromclone'] = 'createfromclone';
1161 $result = $object->create($user);
1162
1163 // Other options
1164 if ($result < 0) {
1165 $this->error = $object->error;
1166 $error++;
1167 }
1168
1169 if (!$error) {
1170 }
1171
1172 unset($object->context['createfromclone']);
1173
1174 // End
1175 if (!$error) {
1176 $this->db->commit();
1177 return $object->id;
1178 } else {
1179 $this->db->rollback();
1180 return -1;
1181 }
1182 }
1183
1190 public function initAsSpecimen()
1191 {
1192 $this->id = 0;
1193 $this->entity = 1;
1194 $this->ref = 'TI0501-001';
1195 $this->track_id = 'XXXXaaaa';
1196 $this->origin_email = 'email@email.com';
1197 $this->fk_project = 1;
1198 $this->fk_user_create = 1;
1199 $this->fk_user_assign = 1;
1200 $this->subject = 'Subject of ticket';
1201 $this->message = 'Message of ticket';
1202 $this->status = 0;
1203 $this->resolution = '1';
1204 $this->progress = '10';
1205 //$this->timing = '30';
1206 $this->type_code = 'TYPECODE';
1207 $this->category_code = 'CATEGORYCODE';
1208 $this->severity_code = 'SEVERITYCODE';
1209 $this->datec = '';
1210 $this->date_read = '';
1211 $this->date_last_msg_sent = '';
1212 $this->date_close = '';
1213 $this->tms = '';
1214 return 1;
1215 }
1216
1223 public function printSelectStatus($selected = "")
1224 {
1225 print Form::selectarray('search_fk_statut', $this->statuts_short, $selected, $show_empty = 1, $key_in_label = 0, $value_as_key = 0, $option = '', $translate = 1, $maxlen = 0, $disabled = 0, $sort = '', $morecss = '');
1226 }
1227
1228
1234 public function loadCacheTypesTickets()
1235 {
1236 global $langs;
1237
1238 if (!empty($this->cache_types_tickets) && count($this->cache_types_tickets)) {
1239 return 0;
1240 }
1241 // Cache deja charge
1242
1243 $sql = "SELECT rowid, code, label, use_default, pos, description";
1244 $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_type";
1245 $sql .= " WHERE entity IN (".getEntity('c_ticket_type').")";
1246 $sql .= " AND active > 0";
1247 $sql .= " ORDER BY pos";
1248 dol_syslog(get_class($this)."::load_cache_type_tickets", LOG_DEBUG);
1249 $resql = $this->db->query($sql);
1250 if ($resql) {
1251 $num = $this->db->num_rows($resql);
1252 $i = 0;
1253 while ($i < $num) {
1254 $obj = $this->db->fetch_object($resql);
1255 $label = ($langs->trans("TicketTypeShort".$obj->code) != ("TicketTypeShort".$obj->code) ? $langs->trans("TicketTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1256 $this->cache_types_tickets[$obj->rowid]['code'] = $obj->code;
1257 $this->cache_types_tickets[$obj->rowid]['label'] = $label;
1258 $this->cache_types_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1259 $this->cache_types_tickets[$obj->rowid]['pos'] = $obj->pos;
1260 $i++;
1261 }
1262 return $num;
1263 } else {
1264 dol_print_error($this->db);
1265 return -1;
1266 }
1267 }
1268
1275 public function loadCacheCategoriesTickets($publicgroup = -1)
1276 {
1277 global $conf, $langs;
1278
1279 if ($publicgroup == -1 && !empty($this->cache_category_ticket) && count($this->cache_category_tickets)) {
1280 // Cache already loaded
1281 return 0;
1282 }
1283
1284 $sql = "SELECT rowid, code, label, use_default, pos, description, public, active, force_severity, fk_parent";
1285 $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_category";
1286 $sql .= " WHERE entity IN (".getEntity('c_ticket_category').")";
1287 $sql .= " AND active > 0";
1288 if ($publicgroup > -1) {
1289 $sql .= " AND public = ".((int) $publicgroup);
1290 }
1291 $sql .= " ORDER BY pos";
1292
1293 dol_syslog(get_class($this)."::load_cache_categories_tickets", LOG_DEBUG);
1294
1295 $resql = $this->db->query($sql);
1296 if ($resql) {
1297 $num = $this->db->num_rows($resql);
1298 $i = 0;
1299 while ($i < $num) {
1300 $obj = $this->db->fetch_object($resql);
1301 $this->cache_category_tickets[$obj->rowid]['code'] = $obj->code;
1302 $this->cache_category_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1303 $this->cache_category_tickets[$obj->rowid]['pos'] = $obj->pos;
1304 $this->cache_category_tickets[$obj->rowid]['public'] = $obj->public;
1305 $this->cache_category_tickets[$obj->rowid]['active'] = $obj->active;
1306 $this->cache_category_tickets[$obj->rowid]['force_severity'] = $obj->force_severity;
1307 $this->cache_category_tickets[$obj->rowid]['fk_parent'] = $obj->fk_parent;
1308
1309 // If translation exists, we use it to store already translated string.
1310 // Warning: You should not use this and recompute the translated string into caller code to get the value into expected language
1311 $label = ($langs->trans("TicketCategoryShort".$obj->code) != ("TicketCategoryShort".$obj->code) ? $langs->trans("TicketCategoryShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1312 $this->cache_category_tickets[$obj->rowid]['label'] = $label;
1313
1314 $i++;
1315 }
1316 return $num;
1317 } else {
1318 dol_print_error($this->db);
1319 return -1;
1320 }
1321 }
1322
1329 {
1330 global $langs;
1331
1332 if (!empty($this->cache_severity_tickets) && count($this->cache_severity_tickets)) {
1333 return 0;
1334 }
1335 // Cache deja charge
1336
1337 $sql = "SELECT rowid, code, label, use_default, pos, description";
1338 $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_severity";
1339 $sql .= " WHERE entity IN (".getEntity('c_ticket_severity').")";
1340 $sql .= " AND active > 0";
1341 $sql .= " ORDER BY pos";
1342 dol_syslog(get_class($this)."::loadCacheSeveritiesTickets", LOG_DEBUG);
1343 $resql = $this->db->query($sql);
1344 if ($resql) {
1345 $num = $this->db->num_rows($resql);
1346 $i = 0;
1347 while ($i < $num) {
1348 $obj = $this->db->fetch_object($resql);
1349
1350 $this->cache_severity_tickets[$obj->rowid]['code'] = $obj->code;
1351 $label = ($langs->trans("TicketSeverityShort".$obj->code) != ("TicketSeverityShort".$obj->code) ? $langs->trans("TicketSeverityShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1352 $this->cache_severity_tickets[$obj->rowid]['label'] = $label;
1353 $this->cache_severity_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1354 $this->cache_severity_tickets[$obj->rowid]['pos'] = $obj->pos;
1355 $i++;
1356 }
1357 return $num;
1358 } else {
1359 dol_print_error($this->db);
1360 return -1;
1361 }
1362 }
1363
1364
1371 public function getLibStatut($mode = 0)
1372 {
1373 return $this->libStatut($this->fk_statut, $mode, 0, $this->progress);
1374 }
1375
1376
1377 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1387 public function LibStatut($status, $mode = 0, $notooltip = 0, $progress = 0)
1388 {
1389 // phpcs:enable
1390 global $langs, $hookmanager;
1391
1392 $labelStatus = $this->statuts[$status];
1393 $labelStatusShort = $this->statuts_short[$status];
1394
1395 if ($status == self::STATUS_NOT_READ) {
1396 $statusType = 'status0';
1397 } elseif ($status == self::STATUS_READ) {
1398 $statusType = 'status1';
1399 } elseif ($status == self::STATUS_ASSIGNED) {
1400 $statusType = 'status2';
1401 } elseif ($status == self::STATUS_IN_PROGRESS) {
1402 $statusType = 'status4';
1403 } elseif ($status == self::STATUS_WAITING) {
1404 $statusType = 'status7';
1405 } elseif ($status == self::STATUS_NEED_MORE_INFO) {
1406 $statusType = 'status3';
1407 } elseif ($status == self::STATUS_CANCELED) {
1408 $statusType = 'status9';
1409 } elseif ($status == self::STATUS_CLOSED) {
1410 $statusType = 'status6';
1411 } else {
1412 $labelStatus = 'Unknown';
1413 $labelStatusShort = 'Unknown';
1414 $statusType = 'status0';
1415 $mode = 0;
1416 }
1417
1418 $parameters = array(
1419 'status' => $status,
1420 'mode' => $mode,
1421 );
1422
1423 // Note that $action and $object may have been modified by hook
1424 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this);
1425
1426 if ($reshook > 0) {
1427 return $hookmanager->resPrint;
1428 }
1429
1430 $params = array();
1431 if ($notooltip) {
1432 $params = array('tooltip' => 'no');
1433 }
1434
1435 $labelStatus = $langs->transnoentitiesnoconv($labelStatus);
1436 $labelStatusShort = $langs->transnoentitiesnoconv($labelStatusShort);
1437
1438 if ($status == self::STATUS_IN_PROGRESS && $progress > 0) {
1439 $labelStatus .= ' ('.round($progress).'%)';
1440 $labelStatusShort .= ' ('.round($progress).'%)';
1441 }
1442
1443 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', $params);
1444 }
1445
1453 public function getTooltipContentArray($params)
1454 {
1455 global $langs;
1456
1457 $langs->load('ticket');
1458 $nofetch = !empty($params['nofetch']);
1459
1460 $datas = array();
1461 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Ticket").'</u>';
1462 $datas['picto'] .= ' '.$this->getLibStatut(4);
1463 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1464 $datas['track_id'] = '<br><b>'.$langs->trans('TicketTrackId').':</b> '.$this->track_id;
1465 $datas['subject'] = '<br><b>'.$langs->trans('Subject').':</b> '.$this->subject;
1466 if ($this->date_creation) {
1467 $datas['date_creation'] = '<br><b>'.$langs->trans('DateCreation').':</b> '.dol_print_date($this->date_creation, 'dayhour');
1468 }
1469 if ($this->date_modification) {
1470 $datas['date_modification'] = '<br><b>'.$langs->trans('DateModification').':</b> '.dol_print_date($this->date_modification, 'dayhour');
1471 }
1472 // show categories for this record only in ajax to not overload lists
1473 if (isModEnabled('categorie') && !$nofetch) {
1474 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
1475 $form = new Form($this->db);
1476 $datas['categories'] = '<br>' . $form->showCategories($this->id, Categorie::TYPE_TICKET, 1);
1477 }
1478
1479 return $datas;
1480 }
1481
1492 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1493 {
1494 global $conf, $langs;
1495
1496 if (!empty($conf->dol_no_mouse_hover)) {
1497 $notooltip = 1; // Force disable tooltips
1498 }
1499
1500 $result = '';
1501
1502 $params = [
1503 'id' => $this->id,
1504 'objecttype' => $this->element,
1505 'option' => $option,
1506 'nofetch' => 1,
1507 ];
1508 $classfortooltip = 'classfortooltip';
1509 $dataparams = '';
1510 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1511 $classfortooltip = 'classforajaxtooltip';
1512 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1513 $label = '';
1514 } else {
1515 $label = implode($this->getTooltipContentArray($params));
1516 }
1517
1518 $url = DOL_URL_ROOT.'/ticket/card.php?id='.$this->id;
1519
1520 if ($option != 'nolink') {
1521 // Add param to save lastsearch_values or not
1522 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1523 if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1524 $add_save_lastsearch_values = 1;
1525 }
1526 if ($add_save_lastsearch_values) {
1527 $url .= '&save_lastsearch_values=1';
1528 }
1529 }
1530
1531 $linkclose = '';
1532 if (empty($notooltip)) {
1533 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1534 $label = $langs->trans("ShowTicket");
1535 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1536 }
1537 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1538 $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
1539 } else {
1540 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1541 }
1542
1543 $linkstart = '<a href="'.$url.'"';
1544 $linkstart .= $linkclose.'>';
1545 $linkend = '</a>';
1546
1547 $result .= $linkstart;
1548 if ($withpicto) {
1549 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1550 }
1551 if ($withpicto != 2) {
1552 $result .= $this->ref;
1553 }
1554 $result .= $linkend;
1555 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1556
1557 return $result;
1558 }
1559
1560
1568 public function markAsRead($user, $notrigger = 0)
1569 {
1570 global $langs;
1571
1572 $error = 0;
1573
1574 if ($this->statut != self::STATUS_CANCELED) { // no closed
1575 $this->oldcopy = dol_clone($this);
1576
1577 $this->db->begin();
1578 $oldStatus = $this->fk_statut;
1579 $this->fk_statut = Ticket::STATUS_READ;
1580
1581 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1582 $sql .= " SET fk_statut = ".Ticket::STATUS_READ.", date_read = '".$this->db->idate(dol_now())."'";
1583 $sql .= " WHERE rowid = ".((int) $this->id);
1584
1585 dol_syslog(get_class($this)."::markAsRead");
1586 $resql = $this->db->query($sql);
1587 if ($resql) {
1588 $this->context['actionmsg'] = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1589 $this->context['actionmsg2'] = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1590
1591 if (!$notrigger) {
1592 // Call trigger
1593 $result = $this->call_trigger('TICKET_MODIFY', $user);
1594 if ($result < 0) {
1595 $this->fk_statut = $oldStatus;
1596 $error++;
1597 }
1598 // End call triggers
1599 }
1600
1601 if (!$error) {
1602 $this->db->commit();
1603 return 1;
1604 } else {
1605 $this->fk_statut = $oldStatus;
1606 $this->db->rollback();
1607 $this->error = implode(',', $this->errors);
1608 dol_syslog(get_class($this)."::markAsRead ".$this->error, LOG_ERR);
1609 return -1;
1610 }
1611 } else {
1612 $this->fk_statut = $oldStatus;
1613 $this->db->rollback();
1614 $this->error = $this->db->lasterror();
1615 dol_syslog(get_class($this)."::markAsRead ".$this->error, LOG_ERR);
1616 return -1;
1617 }
1618 }
1619
1620 return 0;
1621 }
1622
1631 public function assignUser($user, $id_assign_user, $notrigger = 0)
1632 {
1633 global $conf, $langs;
1634
1635 $error = 0;
1636
1637 $this->oldcopy = dol_clone($this);
1638
1639 $this->db->begin();
1640
1641 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1642 if ($id_assign_user > 0) {
1643 $sql .= " SET fk_user_assign=".((int) $id_assign_user).", fk_statut = ".Ticket::STATUS_ASSIGNED;
1644 } else {
1645 $sql .= " SET fk_user_assign=null, fk_statut = ".Ticket::STATUS_READ;
1646 }
1647 $sql .= " WHERE rowid = ".((int) $this->id);
1648
1649 dol_syslog(get_class($this)."::assignUser sql=".$sql);
1650 $resql = $this->db->query($sql);
1651 if ($resql) {
1652 $this->fk_user_assign = $id_assign_user; // May be used by trigger
1653
1654 if (!$notrigger) {
1655 // Call trigger
1656 $result = $this->call_trigger('TICKET_ASSIGNED', $user);
1657 if ($result < 0) {
1658 $error++;
1659 }
1660 // End call triggers
1661 }
1662
1663 if (!$error) {
1664 $this->db->commit();
1665 return 1;
1666 } else {
1667 $this->db->rollback();
1668 $this->error = join(',', $this->errors);
1669 dol_syslog(get_class($this)."::assignUser ".$this->error, LOG_ERR);
1670 return -1;
1671 }
1672 } else {
1673 $this->db->rollback();
1674 $this->error = $this->db->lasterror();
1675 dol_syslog(get_class($this)."::assignUser ".$this->error, LOG_ERR);
1676 return -1;
1677 }
1678 }
1679
1691 public function createTicketMessage($user, $notrigger = 0, $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array(), $send_email = false)
1692 {
1693 global $conf, $langs;
1694 $error = 0;
1695
1696 $now = dol_now();
1697
1698 // Clean parameters
1699 if (isset($this->fk_track_id)) {
1700 $this->fk_track_id = trim($this->fk_track_id);
1701 }
1702
1703 if (isset($this->message)) {
1704 $this->message = trim($this->message);
1705 }
1706
1707 $this->db->begin();
1708
1709 // Insert entry into agenda with code 'TICKET_MSG'
1710 include_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
1711 $actioncomm = new ActionComm($this->db);
1712 $actioncomm->type_code = 'AC_OTH_AUTO'; // This is not an entry that must appears into manual calendar but only into CRM calendar
1713 $actioncomm->code = 'TICKET_MSG';
1714 if ($this->private) {
1715 $actioncomm->code = 'TICKET_MSG_PRIVATE';
1716 }
1717 if ($send_email) {
1718 $actioncomm->code .= '_SENTBYMAIL';
1719 }
1720 if ((empty($user->id) || $user->id == 0) && isset($_SESSION['email_customer'])) {
1721 $actioncomm->email_from = $_SESSION['email_customer'];
1722 }
1723 $actioncomm->socid = $this->socid;
1724 $actioncomm->label = $this->subject;
1725 $actioncomm->note_private = $this->message;
1726 $actioncomm->userassigned = array($user->id);
1727 $actioncomm->userownerid = $user->id;
1728 $actioncomm->datep = $now;
1729 $actioncomm->percentage = -1; // percentage is not relevant for punctual events
1730 $actioncomm->elementtype = 'ticket';
1731 $actioncomm->fk_element = $this->id;
1732 $actioncomm->fk_project = $this->fk_project;
1733
1734 $attachedfiles = array();
1735 $attachedfiles['paths'] = $filename_list;
1736 $attachedfiles['names'] = $mimefilename_list;
1737 $attachedfiles['mimes'] = $mimetype_list;
1738 if (is_array($attachedfiles) && count($attachedfiles) > 0) {
1739 $actioncomm->attachedfiles = $attachedfiles;
1740 }
1741
1742 if (!empty($mimefilename_list) && is_array($mimefilename_list)) {
1743 $actioncomm->note_private = dol_concatdesc($actioncomm->note_private, "\n".$langs->transnoentities("AttachedFiles").': '.join(';', $mimefilename_list));
1744 }
1745
1746 $actionid = $actioncomm->create($user);
1747 if ($actionid <= 0) {
1748 $error++;
1749 $this->error = $actioncomm->error;
1750 $this->errors = $actioncomm->errors;
1751 }
1752
1753 // Commit or rollback
1754 if ($error) {
1755 $this->db->rollback();
1756 return -1 * $error;
1757 } else {
1758 $this->db->commit();
1759 return 1;
1760 }
1761 }
1762
1768 public function loadCacheMsgsTicket()
1769 {
1770 if (!empty($this->cache_msgs_ticket) && is_array($this->cache_msgs_ticket) && count($this->cache_msgs_ticket)) {
1771 return 0;
1772 }
1773
1774 // Cache already loaded
1775
1776 $sql = "SELECT id as rowid, fk_user_author, email_from, datec, datep, label, note as message, code";
1777 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm";
1778 $sql .= " WHERE fk_element = ".(int) $this->id;
1779 $sql .= " AND elementtype = 'ticket'";
1780 $sql .= " ORDER BY datep DESC";
1781
1782 dol_syslog(get_class($this)."::load_cache_actions_ticket", LOG_DEBUG);
1783 $resql = $this->db->query($sql);
1784 if ($resql) {
1785 $num = $this->db->num_rows($resql);
1786 $i = 0;
1787 while ($i < $num) {
1788 $obj = $this->db->fetch_object($resql);
1789 $this->cache_msgs_ticket[$i]['id'] = $obj->rowid;
1790 $this->cache_msgs_ticket[$i]['fk_user_author'] = $obj->fk_user_author;
1791 if ($obj->code == 'TICKET_MSG' && empty($obj->fk_user_author)) {
1792 $this->cache_msgs_ticket[$i]['fk_contact_author'] = $obj->email_from;
1793 }
1794 $this->cache_msgs_ticket[$i]['datec'] = $this->db->jdate($obj->datec);
1795 $this->cache_msgs_ticket[$i]['datep'] = $this->db->jdate($obj->datep);
1796 $this->cache_msgs_ticket[$i]['subject'] = $obj->label;
1797 $this->cache_msgs_ticket[$i]['message'] = $obj->message;
1798 $this->cache_msgs_ticket[$i]['private'] = (preg_match('/^TICKET_MSG_PRIVATE/', $obj->code) ? 1 : 0);
1799 $i++;
1800 }
1801 return $num;
1802 } else {
1803 $this->error = "Error ".$this->db->lasterror();
1804 dol_syslog(get_class($this)."::load_cache_actions_ticket ".$this->error, LOG_ERR);
1805 return -1;
1806 }
1807 }
1808
1816 public function close(User $user, $mode = 0)
1817 {
1818 global $conf;
1819
1820 if ($this->status != Ticket::STATUS_CLOSED && $this->status != Ticket::STATUS_CANCELED) { // not closed
1821 $this->db->begin();
1822
1823 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1824 $sql .= " SET fk_statut=".($mode ? Ticket::STATUS_CANCELED : Ticket::STATUS_CLOSED).", progress=100, date_close='".$this->db->idate(dol_now())."'";
1825 $sql .= " WHERE rowid = ".((int) $this->id);
1826
1827 dol_syslog(get_class($this)."::close mode=".$mode);
1828 $resql = $this->db->query($sql);
1829 if ($resql) {
1830 $error = 0;
1831
1832 // Valid and close fichinter linked
1833 if (isModEnabled('ficheinter') && getDolGlobalString('WORKFLOW_TICKET_CLOSE_INTERVENTION')) {
1834 dol_syslog("We have closed the ticket, so we close all linked interventions");
1835 $this->fetchObjectLinked($this->id, $this->element, null, 'fichinter');
1836 if ($this->linkedObjectsIds) {
1837 foreach ($this->linkedObjectsIds['fichinter'] as $fichinter_id) {
1838 $fichinter = new Fichinter($this->db);
1839 $fichinter->fetch($fichinter_id);
1840 if ($fichinter->statut == 0) {
1841 $result = $fichinter->setValid($user);
1842 if (!$result) {
1843 $this->errors[] = $fichinter->error;
1844 $error++;
1845 }
1846 }
1847 if ($fichinter->statut < 3) {
1848 $result = $fichinter->setStatut(3);
1849 if (!$result) {
1850 $this->errors[] = $fichinter->error;
1851 $error++;
1852 }
1853 }
1854 }
1855 }
1856 }
1857
1858 // Call trigger
1859 $result = $this->call_trigger('TICKET_CLOSE', $user);
1860 if ($result < 0) {
1861 $error++;
1862 }
1863 // End call triggers
1864
1865 if (!$error) {
1866 $this->db->commit();
1867 return 1;
1868 } else {
1869 $this->db->rollback();
1870 $this->error = join(',', $this->errors);
1871 dol_syslog(get_class($this)."::close ".$this->error, LOG_ERR);
1872 return -1;
1873 }
1874 } else {
1875 $this->db->rollback();
1876 $this->error = $this->db->lasterror();
1877 dol_syslog(get_class($this)."::close ".$this->error, LOG_ERR);
1878 return -1;
1879 }
1880 }
1881
1882 return 0;
1883 }
1884
1894 public function searchSocidByEmail($email, $type = '0', $filters = array(), $clause = 'AND')
1895 {
1896 $thirdparties = array();
1897 $exact = 0;
1898
1899 // Generation requete recherche
1900 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."societe";
1901 $sql .= " WHERE entity IN (".getEntity('ticket', 1).")";
1902 if (!empty($type)) {
1903 if ($type == 1 || $type == 2) {
1904 $sql .= " AND client = ".((int) $type);
1905 } elseif ($type == 3) {
1906 $sql .= " AND fournisseur = 1";
1907 }
1908 }
1909 if (!empty($email)) {
1910 if (empty($exact)) {
1911 $regs = array();
1912 if (preg_match('/^([\*])?[^*]+([\*])?$/', $email, $regs) && count($regs) > 1) {
1913 $email = str_replace('*', '%', $email);
1914 } else {
1915 $email = '%'.$email.'%';
1916 }
1917 }
1918 $sql .= " AND ";
1919 if (is_array($filters) && !empty($filters)) {
1920 $sql .= "(";
1921 }
1922
1923 $sql .= "email LIKE '".$this->db->escape($email)."'";
1924 }
1925 if (is_array($filters) && !empty($filters)) {
1926 foreach ($filters as $field => $value) {
1927 $sql .= " ".$clause." ".$field." LIKE '".$this->db->escape($value)."'";
1928 }
1929 if (!empty($email)) {
1930 $sql .= ")";
1931 }
1932 }
1933
1934 $res = $this->db->query($sql);
1935 if ($res) {
1936 while ($rec = $this->db->fetch_array($res)) {
1937 $soc = new Societe($this->db);
1938 $soc->fetch($rec['rowid']);
1939 $thirdparties[] = $soc;
1940 }
1941
1942 return $thirdparties;
1943 } else {
1944 $this->error = $this->db->error().' sql='.$sql;
1945 dol_syslog(get_class($this)."::searchSocidByEmail ".$this->error, LOG_ERR);
1946 return -1;
1947 }
1948 }
1949
1958 public function searchContactByEmail($email, $socid = '', $case = '')
1959 {
1960 $contacts = array();
1961
1962 // Forge the search SQL
1963 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."socpeople";
1964 $sql .= " WHERE entity IN (".getEntity('contact').")";
1965 if (!empty($socid)) {
1966 $sql .= " AND fk_soc = ".((int) $socid);
1967 }
1968 if (!empty($email)) {
1969 $sql .= " AND ";
1970 if (!$case) {
1971 $sql .= "email = '".$this->db->escape($email)."'";
1972 } else {
1973 $sql .= "email LIKE BINARY '".$this->db->escape($this->db->escapeforlike($email))."'";
1974 }
1975 }
1976
1977 $res = $this->db->query($sql);
1978 if ($res) {
1979 while ($rec = $this->db->fetch_object($res)) {
1980 include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1981 $contactstatic = new Contact($this->db);
1982 $contactstatic->fetch($rec->rowid);
1983 $contacts[] = $contactstatic;
1984 }
1985
1986 return $contacts;
1987 } else {
1988 $this->error = $this->db->error().' sql='.$sql;
1989 dol_syslog(get_class($this)."::searchContactByEmail ".$this->error, LOG_ERR);
1990 return -1;
1991 }
1992 }
1993
2000 public function setCustomer($id)
2001 {
2002 if ($this->id) {
2003 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2004 $sql .= " SET fk_soc = ".($id > 0 ? $id : "null");
2005 $sql .= " WHERE rowid = ".((int) $this->id);
2006 dol_syslog(get_class($this).'::setCustomer sql='.$sql);
2007 $resql = $this->db->query($sql);
2008 if ($resql) {
2009 return 1;
2010 } else {
2011 return -1;
2012 }
2013 } else {
2014 return -1;
2015 }
2016 }
2017
2024 public function setProgression($percent)
2025 {
2026 if ($this->id) {
2027 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2028 $sql .= " SET progress = ".($percent > 0 ? $percent : "null");
2029 $sql .= " WHERE rowid = ".((int) $this->id);
2030 dol_syslog(get_class($this).'::set_progression sql='.$sql);
2031 $resql = $this->db->query($sql);
2032 if ($resql) {
2033 return 1;
2034 } else {
2035 return -1;
2036 }
2037 } else {
2038 return -1;
2039 }
2040 }
2041
2048 public function setContract($contractid)
2049 {
2050 if (!$this->table_element) {
2051 dol_syslog(get_class($this)."::setContract was called on objet with property table_element not defined", LOG_ERR);
2052 return -1;
2053 }
2054
2055 $result = $this->add_object_linked('contrat', $contractid);
2056 if ($result) {
2057 $this->fk_contract = $contractid;
2058 return 1;
2059 } else {
2060 dol_print_error($this->db);
2061 return -1;
2062 }
2063 }
2064
2065 /* gestion des contacts d'un ticket */
2066
2073 {
2074 return $this->getIdContact('internal', 'SUPPORTTEC');
2075 }
2076
2083 public function getInfosTicketInternalContact($status = -1)
2084 {
2085 return $this->listeContact(-1, 'internal', 0, '', $status);
2086 }
2087
2094 {
2095 return $this->getIdContact('external', 'SUPPORTCLI');
2096 }
2097
2104 public function getInfosTicketExternalContact($status = -1)
2105 {
2106 return $this->listeContact(-1, 'external', 0, '', $status);
2107 }
2108
2115 {
2116 return $this->getIdContact('internal', 'CONTRIBUTOR');
2117 }
2118
2125 {
2126 return $this->getIdContact('external', 'CONTRIBUTOR');
2127 }
2128
2134 public function getTicketAllContacts()
2135 {
2136 $array_contact = array();
2137
2138 $array_contact = $this->getIdTicketInternalContact();
2139
2140 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());
2141
2142 $array_contact = array_merge($array_contact, $this->getIdTicketInternalInvolvedContact());
2143
2144 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());
2145
2146 return $array_contact;
2147 }
2148
2155 {
2156 $array_contact = array();
2157
2158 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());
2159
2160 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());
2161
2162 return $array_contact;
2163 }
2164
2165
2177 public function listeContact($statusoflink = -1, $source = 'external', $list = 0, $code = '', $status = -1)
2178 {
2179 global $langs;
2180
2181 $tab = array();
2182
2183 $sql = "SELECT ec.rowid, ec.statut as statuslink, ec.fk_socpeople as id, ec.fk_c_type_contact"; // This field contains id of llx_socpeople or id of llx_user
2184 if ($source == 'internal') {
2185 $sql .= ", '-1' as socid, t.statut as statuscontact";
2186 }
2187
2188 if ($source == 'external' || $source == 'thirdparty') {
2189 $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
2190 }
2191
2192 $sql .= ", t.civility, t.lastname as lastname, t.firstname, t.email";
2193 if ($source == 'internal') {
2194 $sql .= ", t.office_phone as phone, t.user_mobile as phone_mobile";
2195 }
2196
2197 if ($source == 'external') {
2198 $sql .= ", t.phone as phone, t.phone_mobile as phone_mobile, t.phone_perso as phone_perso";
2199 }
2200
2201 $sql .= ", tc.source, tc.element, tc.code, tc.libelle as type_contact_label";
2202 $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
2203 $sql .= ", ".MAIN_DB_PREFIX."element_contact ec";
2204 if ($source == 'internal') {
2205 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
2206 }
2207
2208 if ($source == 'external' || $source == 'thirdparty') {
2209 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
2210 }
2211
2212 $sql .= " WHERE ec.element_id = ".((int) $this->id);
2213 $sql .= " AND ec.fk_c_type_contact=tc.rowid";
2214 $sql .= " AND tc.element='".$this->db->escape($this->element)."'";
2215 if ($source == 'internal') {
2216 $sql .= " AND tc.source = 'internal'";
2217 if ($status >= 0) {
2218 $sql .= " AND t.statut = ".((int) $status);
2219 }
2220 }
2221
2222 if ($source == 'external' || $source == 'thirdparty') {
2223 $sql .= " AND tc.source = 'external'";
2224 if ($status >= 0) {
2225 $sql .= " AND t.statut = ".((int) $status);
2226 }
2227 }
2228
2229 if (!empty($code)) {
2230 $sql .= " AND tc.code = '".$this->db->escape($code)."'";
2231 }
2232
2233 $sql .= " AND tc.active=1";
2234 if ($statusoflink >= 0) {
2235 $sql .= " AND ec.statut = ".((int) $statusoflink);
2236 }
2237
2238 $sql .= " ORDER BY t.lastname ASC";
2239
2240 $resql = $this->db->query($sql);
2241 if ($resql) {
2242 $num = $this->db->num_rows($resql);
2243 $i = 0;
2244 while ($i < $num) {
2245 $obj = $this->db->fetch_object($resql);
2246
2247 if (!$list) {
2248 $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
2249 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_contact_label);
2250 $tab[$i] = array(
2251 'source' => $obj->source,
2252 'socid' => $obj->socid,
2253 'id' => $obj->id,
2254 'nom' => $obj->lastname, // For backward compatibility
2255 'civility' => $obj->civility,
2256 'lastname' => $obj->lastname,
2257 'firstname' => $obj->firstname,
2258 'email' => $obj->email,
2259 'rowid' => $obj->rowid,
2260 'code' => $obj->code,
2261 'libelle' => $libelle_type,
2262 'status' => $obj->statuslink,
2263 'statuscontact'=>$obj->statuscontact,
2264 'fk_c_type_contact' => $obj->fk_c_type_contact,
2265 'phone' => $obj->phone,
2266 'phone_mobile' => $obj->phone_mobile);
2267 } else {
2268 $tab[$i] = $obj->id;
2269 }
2270
2271 $i++;
2272 }
2273
2274 return $tab;
2275 } else {
2276 $this->error = $this->db->error();
2277 dol_print_error($this->db);
2278 return -1;
2279 }
2280 }
2281
2288 public function getDefaultRef($thirdparty = '')
2289 {
2290 global $conf;
2291
2292 $defaultref = '';
2293 $modele = getDolGlobalString('TICKET_ADDON', 'mod_ticket_simple');
2294
2295 // Search template files
2296 $file = '';
2297 $classname = '';
2298 $filefound = 0;
2299 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2300 foreach ($dirmodels as $reldir) {
2301 $file = dol_buildpath($reldir."core/modules/ticket/".$modele.'.php', 0);
2302 if (file_exists($file)) {
2303 $filefound = 1;
2304 $classname = $modele;
2305 break;
2306 }
2307 }
2308
2309 if ($filefound) {
2310 $result = dol_include_once($reldir."core/modules/ticket/".$modele.'.php');
2311 $modTicket = new $classname;
2312
2313 $defaultref = $modTicket->getNextValue($thirdparty, $this);
2314 }
2315
2316 if (is_numeric($defaultref) && $defaultref <= 0) {
2317 $defaultref = '';
2318 }
2319
2320 return $defaultref;
2321 }
2322
2323
2324 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2331 public function is_photo_available($sdir)
2332 {
2333 // phpcs:enable
2334 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2335
2336 global $conf;
2337
2338 $dir = $sdir.'/';
2339 $nbphoto = 0;
2340
2341 $dir_osencoded = dol_osencode($dir);
2342 if (file_exists($dir_osencoded)) {
2343 $handle = opendir($dir_osencoded);
2344 if (is_resource($handle)) {
2345 while (($file = readdir($handle)) !== false) {
2346 if (!utf8_check($file)) {
2347 $file = utf8_encode($file); // To be sure data is stored in UTF8 in memory
2348 }
2349 if (dol_is_file($dir.$file)) {
2350 return true;
2351 }
2352 }
2353 }
2354 }
2355 return false;
2356 }
2357
2358
2367 public function copyFilesForTicket($forcetrackid = null)
2368 {
2369 global $conf;
2370
2371 // Create form object
2372 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
2373 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2374 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
2375
2376 $maxwidthsmall = 270;
2377 $maxheightsmall = 150;
2378 $maxwidthmini = 128;
2379 $maxheightmini = 72;
2380
2381 $formmail = new FormMail($this->db);
2382 $formmail->trackid = (is_null($forcetrackid) ? 'tic'.$this->id : '');
2383 $attachedfiles = $formmail->get_attached_files();
2384
2385 $filepath = $attachedfiles['paths']; // path is for example user->dir_temp.'/'.$user->id.'/'...
2386 $filename = $attachedfiles['names'];
2387 $mimetype = $attachedfiles['mimes'];
2388
2389 // Copy files into ticket directory
2390 $destdir = $conf->ticket->dir_output.'/'.$this->ref;
2391
2392 if (!dol_is_dir($destdir)) {
2393 dol_mkdir($destdir);
2394 }
2395
2396 $listofpaths = array();
2397 $listofnames = array();
2398 foreach ($filename as $i => $val) {
2399 $destfile = $destdir.'/'.$filename[$i];
2400 // If destination file already exists, we add a suffix to avoid to overwrite
2401 if (is_file($destfile)) {
2402 $pathinfo = pathinfo($filename[$i]);
2403 $now = dol_now();
2404 $destfile = $destdir.'/'.$pathinfo['filename'].' - '.dol_print_date($now, 'dayhourlog').'.'.$pathinfo['extension'];
2405 }
2406
2407 $res = dol_move($filepath[$i], $destfile, 0, 1, 0, 1);
2408 if (!$res) {
2409 // Move has failed
2410 $this->error = "Failed to move file ".dirbasename($filepath[$i])." into ".dirbasename($destfile);
2411 return -1;
2412 } else {
2413 // If file is an image, we create thumbs
2414 if (image_format_supported($destfile) == 1) {
2415 // Create small thumbs for image (Ratio is near 16/9)
2416 // Used on logon for example
2417 $imgThumbSmall = vignette($destfile, $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs");
2418 // Create mini thumbs for image (Ratio is near 16/9)
2419 // Used on menu or for setup page for example
2420 $imgThumbMini = vignette($destfile, $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs");
2421 }
2422 }
2423
2424 // Clear variables into session
2425 $formmail->remove_attached_files($i);
2426
2427 // Fill array with new names
2428 $listofpaths[$i] = $destfile;
2429 $listofnames[$i] = basename($destfile);
2430 }
2431
2432 return array('listofpaths'=>$listofpaths, 'listofnames'=>$listofnames, 'listofmimes'=>$mimetype);
2433 }
2434
2445 public function setCategories($categories)
2446 {
2447 // Handle single category
2448 if (!is_array($categories)) {
2449 $categories = array($categories);
2450 }
2451
2452 // Get current categories
2453 include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2454 $c = new Categorie($this->db);
2455 $existing = $c->containing($this->id, Categorie::TYPE_TICKET, 'id');
2456
2457 // Diff
2458 if (is_array($existing)) {
2459 $to_del = array_diff($existing, $categories);
2460 $to_add = array_diff($categories, $existing);
2461 } else {
2462 $to_del = array(); // Nothing to delete
2463 $to_add = $categories;
2464 }
2465
2466 // Process
2467 foreach ($to_del as $del) {
2468 if ($c->fetch($del) > 0) {
2469 $c->del_type($this, Categorie::TYPE_TICKET);
2470 }
2471 }
2472 foreach ($to_add as $add) {
2473 if ($c->fetch($add) > 0) {
2474 $c->add_type($this, Categorie::TYPE_TICKET);
2475 }
2476 }
2477
2478 return 1;
2479 }
2480
2491 public function newMessage($user, &$action, $private = 1, $public_area = 0)
2492 {
2493 global $mysoc, $conf, $langs;
2494
2495 $error = 0;
2496
2497 $object = new Ticket($this->db);
2498
2499 $ret = $object->fetch('', '', GETPOST('track_id', 'alpha'));
2500
2501 $object->socid = $object->fk_soc;
2502 $object->fetch_thirdparty();
2503 $object->fetch_project();
2504
2505 if ($ret < 0) {
2506 $error++;
2507 array_push($this->errors, $langs->trans("ErrorTicketIsNotValid"));
2508 $action = '';
2509 }
2510
2511 if (!GETPOST("message")) {
2512 $error++;
2513 array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("message")));
2514 $action = 'add_message';
2515 }
2516
2517 if (!$error) {
2518 $object->subject = GETPOST('subject', 'alphanohtml');
2519 $object->message = GETPOST("message", "restricthtml");
2520 $object->private = GETPOST("private_message", "alpha");
2521
2522 $send_email = GETPOST('send_email', 'int');
2523
2524 // Copy attached files (saved into $_SESSION) as linked files to ticket. Return array with final name used.
2525 $resarray = $object->copyFilesForTicket();
2526 if (is_numeric($resarray) && $resarray == -1) {
2527 setEventMessages($object->error, $object->errors, 'errors');
2528 return -1;
2529 }
2530
2531 $listofpaths = $resarray['listofpaths'];
2532 $listofnames = $resarray['listofnames'];
2533 $listofmimes = $resarray['listofmimes'];
2534
2535 $id = $object->createTicketMessage($user, 0, $listofpaths, $listofmimes, $listofnames, $send_email);
2536 if ($id <= 0) {
2537 $error++;
2538 $this->error = $object->error;
2539 $this->errors = $object->errors;
2540 $action = 'add_message';
2541 }
2542
2543 if (!$error && $id > 0) {
2544 setEventMessages($langs->trans('TicketMessageSuccessfullyAdded'), null, 'mesgs');
2545
2546 //var_dump($_SESSION);
2547 //var_dump($listofpaths);exit;
2548
2549 if (!empty($public_area)) {
2550 /*
2551 * Message created from the Public interface
2552 *
2553 * Send emails to assigned users (public area notification)
2554 */
2555 if (getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_ENABLED')) {
2556 // Retrieve internal contact datas
2557 $internal_contacts = $object->getInfosTicketInternalContact(1);
2558
2559 $assigned_user_dont_have_email = '';
2560
2561 $sendto = array();
2562
2563 if ($this->fk_user_assign > 0) {
2564 $assigned_user = new User($this->db);
2565 $assigned_user->fetch($this->fk_user_assign);
2566 if (!empty($assigned_user->email)) {
2567 $sendto[$assigned_user->email] = $assigned_user->getFullName($langs)." <".$assigned_user->email.">";
2568 } else {
2569 $assigned_user_dont_have_email = $assigned_user->getFullName($langs);
2570 }
2571 }
2572
2573 // Build array to display recipient list
2574 foreach ($internal_contacts as $key => $info_sendto) {
2575 // Avoid duplicate notifications
2576 if ($info_sendto['id'] == $user->id) {
2577 continue;
2578 }
2579
2580 // We check if the email address is not the assignee's address to prevent notification from being sent twice
2581 if (!empty($info_sendto['email']) && $assigned_user->email != $info_sendto['email']) {
2582 $sendto[] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";
2583 }
2584 }
2585
2586 if (empty($sendto)) {
2587 if (getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL')) {
2588 $sendto[getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL')] = getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL');
2589 } elseif (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2590 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2591 }
2592 }
2593
2594 // Add global email address recipient
2595 if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') &&
2596 getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)
2597 ) {
2598 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2599 }
2600
2601 if (!empty($sendto)) {
2602 $label_title = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2603 $subject = '['.$label_title.'- ticket #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2604
2605 // Message send
2606 $message = getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO', $langs->trans('TicketMessageMailIntroText'));
2607 $message .= '<br><br>';
2608 $messagePost = GETPOST('message', 'restricthtml');
2609 if (!dol_textishtml($messagePost)) {
2610 $messagePost = dol_nl2br($messagePost);
2611 }
2612 $message .= $messagePost;
2613
2614 // Customer company infos
2615 $message .= '<br><br>';
2616 $message .= "==============================================";
2617 $message .= !empty($object->thirdparty->name) ? '<br>'.$langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';
2618 $message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';
2619 $message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';
2620
2621 // Email send to
2622 $message .= '<br><br>';
2623 if (!empty($assigned_user_dont_have_email)) {
2624 $message .= '<br>'.$langs->trans('NoEMail').' : '.$assigned_user_dont_have_email;
2625 }
2626 foreach ($sendto as $val) {
2627 $message .= '<br>'.$langs->trans('TicketNotificationRecipient').' : '.$val;
2628 }
2629
2630 // URL ticket
2631 $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;
2632 $message .= '<br><br>';
2633 $message .= $langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a>';
2634
2635 $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2636 }
2637 }
2638 } else {
2639 /*
2640 * Message send from the Backoffice / Private area
2641 *
2642 * Send emails to internal users (linked contacts) then, if private is not set, to external users (linked contacts or thirdparty email if no contact set)
2643 */
2644 if ($send_email > 0) {
2645 // Retrieve internal contact datas
2646 $internal_contacts = $object->getInfosTicketInternalContact(1);
2647
2648 $sendto = array();
2649 if (is_array($internal_contacts) && count($internal_contacts) > 0) {
2650 // Set default subject
2651 $label_title = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2652 $appli = $label_title;
2653 $subject = GETPOST('subject', 'alphanohtml') ? GETPOST('subject', 'alphanohtml') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2654
2655 $message_intro = $langs->trans('TicketNotificationEmailBody', "#".$object->id);
2656 $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');
2657
2658 $message = getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO', $langs->trans('TicketMessageMailIntroText'));
2659 $message .= '<br><br>';
2660 $messagePost = GETPOST('message', 'restricthtml');
2661 if (!dol_textishtml($messagePost)) {
2662 $messagePost = dol_nl2br($messagePost);
2663 }
2664 $message .= $messagePost;
2665
2666 // Data about customer
2667 $message .= '<br><br>';
2668 $message .= "==============================================<br>";
2669 $message .= !empty($object->thirdparty->name) ? $langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';
2670 $message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';
2671 $message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';
2672
2673 // Build array to display recipient list
2674 foreach ($internal_contacts as $key => $info_sendto) {
2675 // Avoid duplicate notifications
2676 if ($info_sendto['id'] == $user->id) {
2677 continue;
2678 }
2679
2680 if ($info_sendto['email'] != '') {
2681 if (!empty($info_sendto['email'])) {
2682 $sendto[$info_sendto['email']] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";
2683 }
2684
2685 // Contact type
2686 $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1').' ('.strtolower($info_sendto['libelle']).')';
2687 $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');
2688 }
2689 }
2690 $message .= '<br>';
2691 // URL ticket
2692 $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;
2693
2694 // Add html link on url
2695 $message .= '<br>'.$langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a><br>';
2696
2697 // Add global email address recipient
2698 if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {
2699 if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2700 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2701 }
2702 }
2703
2704 // dont try to send email if no recipient
2705 if (!empty($sendto)) {
2706 $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2707 }
2708 }
2709
2710 /*
2711 * Send emails for externals users if not private (linked contacts)
2712 */
2713 if (empty($object->private)) {
2714 // Retrieve email of all contacts (external)
2715 $external_contacts = $object->getInfosTicketExternalContact(1);
2716
2717 // If no contact, get email from thirdparty
2718 if (is_array($external_contacts) && count($external_contacts) === 0) {
2719 if (!empty($object->fk_soc)) {
2720 $object->fetch_thirdparty($object->fk_soc);
2721 $array_company = array(array('firstname' => '', 'lastname' => $object->thirdparty->name, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
2722 $external_contacts = array_merge($external_contacts, $array_company);
2723 } elseif (empty($object->fk_soc) && !empty($object->origin_email)) {
2724 $array_external = array(array('firstname' => '', 'lastname' => $object->origin_email, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
2725 $external_contacts = array_merge($external_contacts, $array_external);
2726 }
2727 }
2728
2729 $sendto = array();
2730 if (is_array($external_contacts) && count($external_contacts) > 0) {
2731 // Get default subject for email to external contacts
2732 $label_title = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2733 $appli = $mysoc->name;
2734 $subject = GETPOST('subject') ? GETPOST('subject') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2735
2736 $message_intro = GETPOST('mail_intro') ? GETPOST('mail_intro', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO');
2737 $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');
2738 if (!dol_textishtml($message_intro)) {
2739 $message_intro = dol_nl2br($message_intro);
2740 }
2741 if (!dol_textishtml($message_signature)) {
2742 $message_signature = dol_nl2br($message_signature);
2743 }
2744
2745 // We put intro after
2746 $messagePost = GETPOST('message', 'restricthtml');
2747 if (!dol_textishtml($messagePost)) {
2748 $messagePost = dol_nl2br($messagePost);
2749 }
2750 $message = $messagePost;
2751 $message .= '<br><br>';
2752
2753 foreach ($external_contacts as $key => $info_sendto) {
2754 // altairis: avoid duplicate emails to external contacts
2755 if ($info_sendto['id'] == $user->contact_id) {
2756 continue;
2757 }
2758
2759 if ($info_sendto['email'] != '' && $info_sendto['email'] != $object->origin_email) {
2760 if (!empty($info_sendto['email'])) {
2761 $sendto[$info_sendto['email']] = trim($info_sendto['firstname']." ".$info_sendto['lastname'])." <".$info_sendto['email'].">";
2762 }
2763
2764 $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1').' ('.strtolower($info_sendto['libelle']).')';
2765 $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');
2766 }
2767 }
2768
2769 // If public interface is not enable, use link to internal page into mail
2770 $url_public_ticket = getDolGlobalString('TICKET_URL_PUBLIC_INTERFACE', dol_buildpath('/public/ticket/view.php', 2)) . '/view.php?track_id='.$object->track_id;
2771 $message .= '<br>'.$langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer').' : <a href="'.$url_public_ticket.'">'.$object->track_id.'</a><br>';
2772
2773 // Build final message
2774 $message = $message_intro.'<br><br>'.$message;
2775
2776 // Add signature
2777 $message .= '<br>'.$message_signature;
2778
2779 if (!empty($object->origin_email)) {
2780 $sendto[$object->origin_email] = $object->origin_email;
2781 }
2782
2783 if ($object->fk_soc > 0 && !array_key_exists($object->origin_email, $sendto)) {
2784 $object->socid = $object->fk_soc;
2785 $object->fetch_thirdparty();
2786 if (!empty($object->thirdparty->email)) {
2787 $sendto[$object->thirdparty->email] = $object->thirdparty->email;
2788 }
2789 }
2790
2791 // Add global email address recipient
2792 if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {
2793 if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2794 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2795 }
2796 }
2797
2798 // Dont try to send email when no recipient
2799 if (!empty($sendto)) {
2800 $result = $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2801 if ($result) {
2802 // update last_msg_sent date (for last message sent to external users)
2803 $this->date_last_msg_sent = dol_now();
2804 $this->update($user, 1); // disable trigger when updating date_last_msg_sent. sendTicketMessageByEmail already create an event in actioncomm table.
2805 }
2806 }
2807 }
2808 }
2809 }
2810 }
2811
2812 // Set status to "answered" if not set yet, but only if internal user and not private message
2813 // Or set status to "answered" if the client has answered and if the ticket has started
2814 if (($object->status < self::STATUS_IN_PROGRESS && !$user->socid && !$private) ||
2815 ($object->status > self::STATUS_IN_PROGRESS && $public_area)
2816 ) {
2817 $object->setStatut(3);
2818 }
2819 return 1;
2820 } else {
2821 setEventMessages($object->error, $object->errors, 'errors');
2822 return -1;
2823 }
2824 } else {
2825 setEventMessages($this->error, $this->errors, 'errors');
2826 return -1;
2827 }
2828 }
2829
2830
2843 public function sendTicketMessageByEmail($subject, $message, $send_internal_cc = 0, $array_receiver = array(), $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array())
2844 {
2845 global $conf, $langs;
2846
2847 if (getDolGlobalString('TICKET_DISABLE_ALL_MAILS')) {
2848 dol_syslog(get_class($this).'::sendTicketMessageByEmail: Emails are disable into ticket setup by option TICKET_DISABLE_ALL_MAILS', LOG_WARNING);
2849 return false;
2850 }
2851
2852 $langs->load("mails");
2853
2854 include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
2855 //$contactstatic = new Contact($this->db);
2856
2857 // If no receiver defined, load all ticket linked contacts
2858 if (!is_array($array_receiver) || !count($array_receiver) > 0) {
2859 $array_receiver = $this->getInfosTicketInternalContact(1);
2860 $array_receiver = array_merge($array_receiver, $this->getInfosTicketExternalContact(1));
2861 }
2862
2863 $sendtocc = "";
2864 if ($send_internal_cc) {
2865 $sendtocc = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
2866 }
2867
2868 $from = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
2869 $is_sent = false;
2870 if (is_array($array_receiver) && count($array_receiver) > 0) {
2871 foreach ($array_receiver as $key => $receiver) {
2872 $deliveryreceipt = 0;
2873 $filepath = $filename_list;
2874 $filename = $mimefilename_list;
2875 $mimetype = $mimetype_list;
2876
2877 // Send email
2878
2879 $old_MAIN_MAIL_AUTOCOPY_TO = getDolGlobalString('MAIN_MAIL_AUTOCOPY_TO');
2880
2881 if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
2882 $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
2883 }
2884
2885 $upload_dir_tmp = $conf->user->dir_output."/".$user->id.'/temp';
2886
2887 include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
2888 $trackid = "tic".$this->id;
2889
2890 $moreinheader = 'X-Dolibarr-Info: sendTicketMessageByEmail'."\r\n";
2891 if (!empty($this->email_msgid)) {
2892 // We must also add 1 entry In-Reply-To: <$this->email_msgid> with Message-ID we respond from (See RFC5322).
2893 $moreinheader .= 'In-Reply-To: <'.$this->email_msgid.'>'."\r\n";
2894 }
2895
2896 $mailfile = new CMailFile($subject, $receiver, $from, $message, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1, '', '', $trackid, $moreinheader, 'ticket', '', $upload_dir_tmp);
2897
2898 if ($mailfile->error) {
2899 setEventMessages($mailfile->error, null, 'errors');
2900 } else {
2901 $result = $mailfile->sendfile();
2902 if ($result) {
2903 setEventMessages($langs->trans('MailSuccessfulySent', $mailfile->getValidAddress($from, 2), $mailfile->getValidAddress($receiver, 2)), null, 'mesgs');
2904 $is_sent = true;
2905 } else {
2906 $langs->load("other");
2907 if ($mailfile->error) {
2908 setEventMessages($langs->trans('ErrorFailedToSendMail', $from, $receiver), null, 'errors');
2909 dol_syslog($langs->trans('ErrorFailedToSendMail', $from, $receiver).' : '.$mailfile->error);
2910 } else {
2911 setEventMessages('No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS', null, 'errors');
2912 }
2913 }
2914 }
2915
2916 if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
2917 $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
2918 }
2919 }
2920 } else {
2921 $langs->load("other");
2922 setEventMessages($langs->trans('ErrorMailRecipientIsEmptyForSendTicketMessage'), null, 'warnings');
2923 }
2924
2925 return $is_sent;
2926 }
2927
2928 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2936 public function load_board($user, $mode)
2937 {
2938 // phpcs:enable
2939 global $conf, $user, $langs;
2940
2941 $now = dol_now();
2942 $delay_warning = 0;
2943
2944 $this->nbtodo = $this->nbtodolate = 0;
2945 $clause = " WHERE";
2946
2947 $sql = "SELECT p.rowid, p.ref, p.datec as datec";
2948 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
2949 if (isModEnabled('societe') && empty($user->rights->societe->client->voir) && !$user->socid) {
2950 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2951 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2952 $clause = " AND";
2953 }
2954 $sql .= $clause." p.entity IN (".getEntity('ticket').")";
2955 if ($mode == 'opened') {
2956 $sql .= " AND p.fk_statut NOT IN (".Ticket::STATUS_CLOSED.", ".Ticket::STATUS_CANCELED.")";
2957 }
2958 if ($user->socid) {
2959 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2960 }
2961
2962 $resql = $this->db->query($sql);
2963 if ($resql) {
2964 $label = $labelShort = '';
2965 $status = '';
2966 if ($mode == 'opened') {
2967 $status = 'openall';
2968 //$delay_warning = $conf->ticket->warning_delay;
2969 $delay_warning = 0;
2970 $label = $langs->trans("MenuListNonClosed");
2971 $labelShort = $langs->trans("MenuListNonClosed");
2972 }
2973
2974 $response = new WorkboardResponse();
2975 //$response->warning_delay = $delay_warning / 60 / 60 / 24;
2976 $response->label = $label;
2977 $response->labelShort = $labelShort;
2978 $response->url = DOL_URL_ROOT.'/ticket/list.php?search_fk_statut[]='.$status;
2979 $response->img = img_object('', "ticket");
2980
2981 // This assignment in condition is not a bug. It allows walking the results.
2982 while ($obj = $this->db->fetch_object($resql)) {
2983 $response->nbtodo++;
2984 if ($mode == 'opened') {
2985 $datelimit = $this->db->jdate($obj->datec) + $delay_warning;
2986 if ($datelimit < $now) {
2987 //$response->nbtodolate++;
2988 }
2989 }
2990 }
2991 return $response;
2992 } else {
2993 $this->error = $this->db->lasterror();
2994 return -1;
2995 }
2996 }
2997
2998 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3004 public function load_state_board()
3005 {
3006 // phpcs:enable
3007 global $conf, $user;
3008
3009 $this->nb = array();
3010 $clause = "WHERE";
3011
3012 $sql = "SELECT count(p.rowid) as nb";
3013 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
3014 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3015 if (empty($user->rights->societe->client->voir) && !$user->socid) {
3016 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3017 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3018 $clause = "AND";
3019 }
3020 $sql .= " ".$clause." p.entity IN (".getEntity('ticket').")";
3021
3022 $resql = $this->db->query($sql);
3023 if ($resql) {
3024 // This assignment in condition is not a bug. It allows walking the results.
3025 while ($obj = $this->db->fetch_object($resql)) {
3026 $this->nb["ticket"] = $obj->nb;
3027 }
3028 $this->db->free($resql);
3029 return 1;
3030 } else {
3031 dol_print_error($this->db);
3032 $this->error = $this->db->lasterror();
3033 return -1;
3034 }
3035 }
3036
3045 public static function replaceThirdparty($db, $origin_id, $dest_id)
3046 {
3047 $tables = array('ticket');
3048
3049 return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3050 }
3051
3059 public function getKanbanView($option = '', $arraydata = null)
3060 {
3061 global $langs;
3062
3063 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3064
3065 $return = '<div class="box-flex-item box-flex-grow-zero">';
3066 $return .= '<div class="info-box info-box-sm">';
3067 $return .= '<span class="info-box-icon bg-infobox-action">';
3068 $return .= img_picto('', $this->picto);
3069 $return .= '</span>';
3070 $return .= '<div class="info-box-content">';
3071 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
3072 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3073 if (!empty($arraydata['user_assignment'])) {
3074 $return .= '<br><span class="info-box-label" title="'.dol_escape_htmltag($langs->trans("AssignedTo")).'">'.$arraydata['user_assignment'].'</span>';
3075 }
3076 if (property_exists($this, 'type_code') && !empty($this->type_code)) {
3077 $return .= '<br>';
3078 $return .= '<div class="tdoverflowmax125 inline-block">'.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$this->type_code, 'c_ticket_type', 'code', 'label', $this->type_code).'</div>';
3079 }
3080 if (method_exists($this, 'getLibStatut')) {
3081 $return .= '<br><div class="info-box-status margintoponly">'.$this->getLibStatut(3).'</div>';
3082 }
3083 $return .= '</div>';
3084 $return .= '</div>';
3085 $return .= '</div>';
3086 return $return;
3087 }
3088}
3089
3090
3095{
3100 public $rowid;
3101
3105 public $id;
3106
3110 public $ref;
3111
3116
3120 public $fk_soc;
3121
3126
3131
3136
3141
3145 public $subject;
3146
3150 public $message;
3151
3156
3161
3166
3170 public $timing;
3171
3176
3181
3186
3191
3196
3201
3205 public $datec = '';
3206
3210 public $date_read = '';
3211
3215 public $date_last_msg_sent = '';
3216
3220 public $date_close = '';
3221}
$object ref
Definition info.php:78
Class to manage agenda events (actions)
Class to send emails (with attachments or not) Usage: $mailfile = new CMailFile($subject,...
Class to manage categories.
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
call_trigger($triggerName, $user)
Call trigger based on this instance.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
Class to manage contact/addresses.
Class to manage standard extra fields.
Class to manage interventions.
Class to manage generation of HTML components Only common components must be here.
static selectarray($htmlname, $array, $id='', $show_empty=0, $key_in_label=0, $value_as_key=0, $moreparam='', $translate=0, $maxlen=0, $disabled=0, $sort='', $morecss='minwidth75', $addjscombo=1, $moreparamonempty='', $disablebademail=0, $nohtmlescape=0)
Return a HTML select string, built from an array of key+value.
Classe permettant la generation du formulaire html d'envoi de mail unitaire Usage: $formail = new For...
Class to manage third parties objects (customers, suppliers, prospects...)
update($user=0, $notrigger=0)
Update object into database.
$email_from
Email from user.
static replaceThirdparty($db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
fetch($id='', $ref='', $track_id='', $email_msgid='')
Load object in memory from the database.
listeContact($statusoflink=-1, $source='external', $list=0, $code='', $status=-1)
Get array of all contacts for a ticket Override method of file commonobject.class....
loadCacheMsgsTicket()
Load the list of event on ticket into ->cache_msgs_ticket.
setProgression($percent)
Define progression of current ticket.
loadCacheSeveritiesTickets()
Charge dans cache la liste des sévérité de tickets (paramétrable dans dictionnaire)
$fields
'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]',...
getInfosTicketExternalContact($status=-1)
Retrieve informations about external contacts.
searchContactByEmail($email, $socid='', $case='')
Search and fetch contacts by email.
getIdTicketCustomerInvolvedContact()
Return id des contacts clients des intervenants.
LibStatut($status, $mode=0, $notooltip=0, $progress=0)
Return status label of object.
$type_label
Type label.
setContract($contractid)
Link element with a contract.
getTicketAllContacts()
Return id of all contacts for ticket.
createFromClone(User $user, $fromid)
Load an object from its id and create a new one in database.
assignUser($user, $id_assign_user, $notrigger=0)
Mark a message as read.
getTooltipContentArray($params)
getTooltipContentArray
load_state_board()
Load indicator this->nb of global stats widget.
getKanbanView($option='', $arraydata=null)
Return clicable link of object (with eventually picto)
printSelectStatus($selected="")
Print selected status.
is_photo_available($sdir)
Return if at least one photo is available.
getIdTicketInternalContact()
Return id des contacts interne de suivi.
fetchAll($user, $sortorder='ASC', $sortfield='t.datec', $limit='', $offset=0, $arch='', $filter='')
Load all objects in memory from database.
loadCacheCategoriesTickets($publicgroup=-1)
Load into a cache array, the list of ticket categories (setup done into dictionary)
setCustomer($id)
Define parent commany of current ticket.
markAsRead($user, $notrigger=0)
Mark a message as read.
getDefaultRef($thirdparty='')
Get a default reference.
sendTicketMessageByEmail($subject, $message, $send_internal_cc=0, $array_receiver=array(), $filename_list=array(), $mimetype_list=array(), $mimefilename_list=array())
Send ticket by email to linked contacts.
const STATUS_NOT_READ
Status.
__construct($db)
Constructor.
getTicketAllCustomerContacts()
Return id of all contacts for ticket.
$category_label
Category label.
searchSocidByEmail($email, $type='0', $filters=array(), $clause='AND')
Search and fetch thirparties by email.
$severity_label
Severity label.
verify()
Check properties of ticket are ok (like ref, track_id, ...).
setCategories($categories)
Sets object to supplied categories.
initAsSpecimen()
Initialise object with example values Id must be 0 if object instance is a specimen.
newMessage($user, &$action, $private=1, $public_area=0)
Add new message on a ticket (private/public area).
$subject
var string Ticket subject
getIdTicketCustomerContact()
Return id des contacts clients pour le suivi ticket.
loadCacheTypesTickets()
Load into a cache the types of tickets (setup done into dictionaries)
getLibStatut($mode=0)
Return status label of object.
close(User $user, $mode=0)
Close a ticket.
getIdTicketInternalInvolvedContact()
Return id des contacts clients des intervenants.
copyFilesForTicket($forcetrackid=null)
Copy files defined into $_SESSION array into the ticket directory of attached files.
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionaly the picto)
load_board($user, $mode)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
getInfosTicketInternalContact($status=-1)
Retrieve informations about internal contacts.
createTicketMessage($user, $notrigger=0, $filename_list=array(), $mimetype_list=array(), $mimefilename_list=array(), $send_email=false)
Add message into database.
create($user, $notrigger=0)
Create object into database.
Ticket line Class.
$message
Ticket message.
$category_code
Category code.
$fk_user_assign
User id who have ticket assigned.
$date_read
Read date.
$type_label
Type label.
$fk_project
Project ID.
$timing
Duration for ticket.
$category_label
Category label.
$subject
Ticket subject.
$severity_code
Severity code.
$track_id
Hash to identify ticket.
$fk_statut
Ticket statut.
$datec
Creation date.
$progress
Progress in percent.
$date_close
Close ticket date.
$type_code
Type code.
$severity_label
Severity label.
$origin_email
Person email who have create ticket.
$resolution
State resolution.
$fk_user_create
User id who have create ticket.
Class to manage Dolibarr users.
print $langs trans("Ref").' m m m statut
Definition index.php:152
dirbasename($pathfile)
Return the relative dirname (relative to DOL_DATA_ROOT) of a full path string.
dol_move($srcfile, $destfile, $newmask=0, $overwriteifexists=1, $testvirus=0, $indexdatabase=1, $moreinfo=array())
Move a file into another name.
dol_is_file($pathoffile)
Return if path is a file.
dol_is_dir($folder)
Test if filename is a directory.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_clone($object, $native=0)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
dol_concatdesc($text1, $text2, $forxml=false, $invert=false)
Concat 2 descriptions with a new line between them (second operand after first one with appropriate n...
dol_textishtml($msg, $option=0)
Return if a text is a html content.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
utf8_check($str)
Check if a string is in UTF8.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
vignette($file, $maxWidth=160, $maxHeight=120, $extName='_small', $quality=50, $outdir='thumbs', $targetformat=0)
Create a thumbnail from an image file (Supported extensions are gif, jpg, png and bmp).
image_format_supported($file, $acceptsvg=0)
Return if a filename is file name of a supported image format.
Class to generate the form for creating a new ticket.
generate_random_id($car=16)
Generate a random id.