dolibarr 18.0.8
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 $this->status = Ticket::STATUS_READ;
1577
1578 $oldStatus = $this->fk_statut;
1579 $this->fk_statut = Ticket::STATUS_READ;
1580
1581 $this->db->begin();
1582
1583 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1584 $sql .= " SET fk_statut = ".$this->status .", date_read = '".$this->db->idate(dol_now())."'";
1585 $sql .= " WHERE rowid = ".((int) $this->id);
1586
1587 dol_syslog(get_class($this)."::markAsRead");
1588 $resql = $this->db->query($sql);
1589 if ($resql) {
1590 $this->context['actionmsg'] = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1591 $this->context['actionmsg2'] = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1592
1593 if (!$notrigger) {
1594 // Call trigger
1595 $result = $this->call_trigger('TICKET_MODIFY', $user);
1596 if ($result < 0) {
1597 $error++;
1598 }
1599 // End call triggers
1600 }
1601
1602 if (!$error) {
1603 $this->db->commit();
1604 return 1;
1605 } else {
1606 $this->fk_statut = $oldStatus;
1607 $this->status = $this->oldcopy->status;
1608
1609 $this->db->rollback();
1610
1611 $this->error = implode(',', $this->errors);
1612
1613 dol_syslog(get_class($this)."::markAsRead ".$this->error, LOG_ERR);
1614 return -1;
1615 }
1616 } else {
1617 $this->fk_statut = $oldStatus;
1618 $this->status = $this->oldcopy->status;
1619
1620 $this->db->rollback();
1621 $this->error = $this->db->lasterror();
1622 dol_syslog(get_class($this)."::markAsRead ".$this->error, LOG_ERR);
1623 return -1;
1624 }
1625 }
1626
1627 return 0;
1628 }
1629
1638 public function assignUser($user, $id_assign_user, $notrigger = 0)
1639 {
1640 global $conf, $langs;
1641
1642 $error = 0;
1643
1644 $this->oldcopy = dol_clone($this);
1645
1646 $this->db->begin();
1647
1648 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1649 if ($id_assign_user > 0) {
1650 $sql .= " SET fk_user_assign=".((int) $id_assign_user).", fk_statut = ".Ticket::STATUS_ASSIGNED;
1651 } else {
1652 $sql .= " SET fk_user_assign=null, fk_statut = ".Ticket::STATUS_READ;
1653 }
1654 $sql .= " WHERE rowid = ".((int) $this->id);
1655
1656 dol_syslog(get_class($this)."::assignUser sql=".$sql);
1657 $resql = $this->db->query($sql);
1658 if ($resql) {
1659 $this->fk_user_assign = $id_assign_user; // May be used by trigger
1660
1661 if (!$notrigger) {
1662 // Call trigger
1663 $result = $this->call_trigger('TICKET_ASSIGNED', $user);
1664 if ($result < 0) {
1665 $error++;
1666 }
1667 // End call triggers
1668 }
1669
1670 if (!$error) {
1671 $this->db->commit();
1672 return 1;
1673 } else {
1674 $this->db->rollback();
1675 $this->error = join(',', $this->errors);
1676 dol_syslog(get_class($this)."::assignUser ".$this->error, LOG_ERR);
1677 return -1;
1678 }
1679 } else {
1680 $this->db->rollback();
1681 $this->error = $this->db->lasterror();
1682 dol_syslog(get_class($this)."::assignUser ".$this->error, LOG_ERR);
1683 return -1;
1684 }
1685 }
1686
1698 public function createTicketMessage($user, $notrigger = 0, $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array(), $send_email = false)
1699 {
1700 global $conf, $langs;
1701 $error = 0;
1702
1703 $now = dol_now();
1704
1705 // Clean parameters
1706 if (isset($this->fk_track_id)) {
1707 $this->fk_track_id = trim($this->fk_track_id);
1708 }
1709
1710 if (isset($this->message)) {
1711 $this->message = trim($this->message);
1712 }
1713
1714 $this->db->begin();
1715
1716 // Insert entry into agenda with code 'TICKET_MSG'
1717 include_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
1718 $actioncomm = new ActionComm($this->db);
1719 $actioncomm->type_code = 'AC_OTH_AUTO'; // This is not an entry that must appears into manual calendar but only into CRM calendar
1720 $actioncomm->code = 'TICKET_MSG';
1721 if ($this->private) {
1722 $actioncomm->code = 'TICKET_MSG_PRIVATE';
1723 }
1724 if ($send_email) {
1725 $actioncomm->code .= '_SENTBYMAIL';
1726 }
1727 if ((empty($user->id) || $user->id == 0) && isset($_SESSION['email_customer'])) {
1728 $actioncomm->email_from = $_SESSION['email_customer'];
1729 }
1730 $actioncomm->socid = $this->socid;
1731 $actioncomm->label = $this->subject;
1732 $actioncomm->note_private = $this->message;
1733 $actioncomm->userassigned = array($user->id);
1734 $actioncomm->userownerid = $user->id;
1735 $actioncomm->datep = $now;
1736 $actioncomm->percentage = -1; // percentage is not relevant for punctual events
1737 $actioncomm->elementtype = 'ticket';
1738 $actioncomm->fk_element = $this->id;
1739 $actioncomm->fk_project = $this->fk_project;
1740
1741 $attachedfiles = array();
1742 $attachedfiles['paths'] = $filename_list;
1743 $attachedfiles['names'] = $mimefilename_list;
1744 $attachedfiles['mimes'] = $mimetype_list;
1745 if (is_array($attachedfiles) && count($attachedfiles) > 0) {
1746 $actioncomm->attachedfiles = $attachedfiles;
1747 }
1748
1749 if (!empty($mimefilename_list) && is_array($mimefilename_list)) {
1750 $actioncomm->note_private = dol_concatdesc($actioncomm->note_private, "\n".$langs->transnoentities("AttachedFiles").': '.join(';', $mimefilename_list));
1751 }
1752
1753 $actionid = $actioncomm->create($user);
1754 if ($actionid <= 0) {
1755 $error++;
1756 $this->error = $actioncomm->error;
1757 $this->errors = $actioncomm->errors;
1758 }
1759
1760 // Commit or rollback
1761 if ($error) {
1762 $this->db->rollback();
1763 return -1 * $error;
1764 } else {
1765 $this->db->commit();
1766 return 1;
1767 }
1768 }
1769
1775 public function loadCacheMsgsTicket()
1776 {
1777 if (!empty($this->cache_msgs_ticket) && is_array($this->cache_msgs_ticket) && count($this->cache_msgs_ticket)) {
1778 return 0;
1779 }
1780
1781 // Cache already loaded
1782
1783 $sql = "SELECT id as rowid, fk_user_author, email_from, datec, datep, label, note as message, code";
1784 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm";
1785 $sql .= " WHERE fk_element = ".(int) $this->id;
1786 $sql .= " AND elementtype = 'ticket'";
1787 $sql .= " ORDER BY datep DESC";
1788
1789 dol_syslog(get_class($this)."::load_cache_actions_ticket", LOG_DEBUG);
1790 $resql = $this->db->query($sql);
1791 if ($resql) {
1792 $num = $this->db->num_rows($resql);
1793 $i = 0;
1794 while ($i < $num) {
1795 $obj = $this->db->fetch_object($resql);
1796 $this->cache_msgs_ticket[$i]['id'] = $obj->rowid;
1797 $this->cache_msgs_ticket[$i]['fk_user_author'] = $obj->fk_user_author;
1798 if ($obj->code == 'TICKET_MSG' && empty($obj->fk_user_author)) {
1799 $this->cache_msgs_ticket[$i]['fk_contact_author'] = $obj->email_from;
1800 }
1801 $this->cache_msgs_ticket[$i]['datec'] = $this->db->jdate($obj->datec);
1802 $this->cache_msgs_ticket[$i]['datep'] = $this->db->jdate($obj->datep);
1803 $this->cache_msgs_ticket[$i]['subject'] = $obj->label;
1804 $this->cache_msgs_ticket[$i]['message'] = $obj->message;
1805 $this->cache_msgs_ticket[$i]['private'] = (preg_match('/^TICKET_MSG_PRIVATE/', $obj->code) ? 1 : 0);
1806 $i++;
1807 }
1808 return $num;
1809 } else {
1810 $this->error = "Error ".$this->db->lasterror();
1811 dol_syslog(get_class($this)."::load_cache_actions_ticket ".$this->error, LOG_ERR);
1812 return -1;
1813 }
1814 }
1815
1823 public function close(User $user, $mode = 0)
1824 {
1825 global $conf;
1826
1827 if ($this->status != Ticket::STATUS_CLOSED && $this->status != Ticket::STATUS_CANCELED) { // not closed
1828 $this->db->begin();
1829 $this->oldcopy = dol_clone($this);
1830 $this->status = ($mode ? Ticket::STATUS_CANCELED : Ticket::STATUS_CLOSED);
1831
1832 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1833 $sql .= " SET fk_statut=".$this->status.", progress=100, date_close='".$this->db->idate(dol_now())."'";
1834 $sql .= " WHERE rowid = ".((int) $this->id);
1835
1836 dol_syslog(get_class($this)."::close mode=".$mode);
1837 $resql = $this->db->query($sql);
1838 if ($resql) {
1839 $error = 0;
1840
1841 // Valid and close fichinter linked
1842 if (isModEnabled('ficheinter') && getDolGlobalString('WORKFLOW_TICKET_CLOSE_INTERVENTION')) {
1843 dol_syslog("We have closed the ticket, so we close all linked interventions");
1844 $this->fetchObjectLinked($this->id, $this->element, null, 'fichinter');
1845 if ($this->linkedObjectsIds) {
1846 foreach ($this->linkedObjectsIds['fichinter'] as $fichinter_id) {
1847 $fichinter = new Fichinter($this->db);
1848 $fichinter->fetch($fichinter_id);
1849 if ($fichinter->statut == 0) {
1850 $result = $fichinter->setValid($user);
1851 if (!$result) {
1852 $this->errors[] = $fichinter->error;
1853 $error++;
1854 }
1855 }
1856 if ($fichinter->statut < 3) {
1857 $result = $fichinter->setStatut(3);
1858 if (!$result) {
1859 $this->errors[] = $fichinter->error;
1860 $error++;
1861 }
1862 }
1863 }
1864 }
1865 }
1866
1867 // Call trigger
1868 $result = $this->call_trigger('TICKET_CLOSE', $user);
1869 if ($result < 0) {
1870 $error++;
1871 }
1872 // End call triggers
1873
1874 if (!$error) {
1875 $this->db->commit();
1876 return 1;
1877 } else {
1878 $this->db->rollback();
1879 $this->error = join(',', $this->errors);
1880 dol_syslog(get_class($this)."::close ".$this->error, LOG_ERR);
1881 return -1;
1882 }
1883 } else {
1884 $this->db->rollback();
1885 $this->error = $this->db->lasterror();
1886 dol_syslog(get_class($this)."::close ".$this->error, LOG_ERR);
1887 return -1;
1888 }
1889 }
1890
1891 return 0;
1892 }
1893
1903 public function searchSocidByEmail($email, $type = '0', $filters = array(), $clause = 'AND')
1904 {
1905 $thirdparties = array();
1906 $exact = 0;
1907
1908 // Generation requete recherche
1909 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."societe";
1910 $sql .= " WHERE entity IN (".getEntity('ticket', 1).")";
1911 if (!empty($type)) {
1912 if ($type == 1 || $type == 2) {
1913 $sql .= " AND client = ".((int) $type);
1914 } elseif ($type == 3) {
1915 $sql .= " AND fournisseur = 1";
1916 }
1917 }
1918 if (!empty($email)) {
1919 if (empty($exact)) {
1920 $regs = array();
1921 if (preg_match('/^([\*])?[^*]+([\*])?$/', $email, $regs) && count($regs) > 1) {
1922 $email = str_replace('*', '%', $email);
1923 } else {
1924 $email = '%'.$email.'%';
1925 }
1926 }
1927 $sql .= " AND ";
1928 if (is_array($filters) && !empty($filters)) {
1929 $sql .= "(";
1930 }
1931
1932 $sql .= "email LIKE '".$this->db->escape($email)."'";
1933 }
1934 if (is_array($filters) && !empty($filters)) {
1935 foreach ($filters as $field => $value) {
1936 $sql .= " ".$clause." ".$field." LIKE '".$this->db->escape($value)."'";
1937 }
1938 if (!empty($email)) {
1939 $sql .= ")";
1940 }
1941 }
1942
1943 $res = $this->db->query($sql);
1944 if ($res) {
1945 while ($rec = $this->db->fetch_array($res)) {
1946 $soc = new Societe($this->db);
1947 $soc->fetch($rec['rowid']);
1948 $thirdparties[] = $soc;
1949 }
1950
1951 return $thirdparties;
1952 } else {
1953 $this->error = $this->db->error().' sql='.$sql;
1954 dol_syslog(get_class($this)."::searchSocidByEmail ".$this->error, LOG_ERR);
1955 return -1;
1956 }
1957 }
1958
1967 public function searchContactByEmail($email, $socid = '', $case = '')
1968 {
1969 $contacts = array();
1970
1971 // Forge the search SQL
1972 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."socpeople";
1973 $sql .= " WHERE entity IN (".getEntity('contact').")";
1974 if (!empty($socid)) {
1975 $sql .= " AND fk_soc = ".((int) $socid);
1976 }
1977 if (!empty($email)) {
1978 $sql .= " AND ";
1979 if (!$case) {
1980 $sql .= "email = '".$this->db->escape($email)."'";
1981 } else {
1982 $sql .= "email LIKE BINARY '".$this->db->escape($this->db->escapeforlike($email))."'";
1983 }
1984 }
1985
1986 $res = $this->db->query($sql);
1987 if ($res) {
1988 while ($rec = $this->db->fetch_object($res)) {
1989 include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1990 $contactstatic = new Contact($this->db);
1991 $contactstatic->fetch($rec->rowid);
1992 $contacts[] = $contactstatic;
1993 }
1994
1995 return $contacts;
1996 } else {
1997 $this->error = $this->db->error().' sql='.$sql;
1998 dol_syslog(get_class($this)."::searchContactByEmail ".$this->error, LOG_ERR);
1999 return -1;
2000 }
2001 }
2002
2009 public function setCustomer($id)
2010 {
2011 if ($this->id) {
2012 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2013 $sql .= " SET fk_soc = ".($id > 0 ? $id : "null");
2014 $sql .= " WHERE rowid = ".((int) $this->id);
2015 dol_syslog(get_class($this).'::setCustomer sql='.$sql);
2016 $resql = $this->db->query($sql);
2017 if ($resql) {
2018 return 1;
2019 } else {
2020 return -1;
2021 }
2022 } else {
2023 return -1;
2024 }
2025 }
2026
2033 public function setProgression($percent)
2034 {
2035 if ($this->id) {
2036 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2037 $sql .= " SET progress = ".($percent > 0 ? $percent : "null");
2038 $sql .= " WHERE rowid = ".((int) $this->id);
2039 dol_syslog(get_class($this).'::set_progression sql='.$sql);
2040 $resql = $this->db->query($sql);
2041 if ($resql) {
2042 return 1;
2043 } else {
2044 return -1;
2045 }
2046 } else {
2047 return -1;
2048 }
2049 }
2050
2057 public function setContract($contractid)
2058 {
2059 if (!$this->table_element) {
2060 dol_syslog(get_class($this)."::setContract was called on objet with property table_element not defined", LOG_ERR);
2061 return -1;
2062 }
2063
2064 $result = $this->add_object_linked('contrat', $contractid);
2065 if ($result) {
2066 $this->fk_contract = $contractid;
2067 return 1;
2068 } else {
2069 dol_print_error($this->db);
2070 return -1;
2071 }
2072 }
2073
2074 /* gestion des contacts d'un ticket */
2075
2082 {
2083 return $this->getIdContact('internal', 'SUPPORTTEC');
2084 }
2085
2092 public function getInfosTicketInternalContact($status = -1)
2093 {
2094 return $this->listeContact(-1, 'internal', 0, '', $status);
2095 }
2096
2103 {
2104 return $this->getIdContact('external', 'SUPPORTCLI');
2105 }
2106
2113 public function getInfosTicketExternalContact($status = -1)
2114 {
2115 return $this->listeContact(-1, 'external', 0, '', $status);
2116 }
2117
2124 {
2125 return $this->getIdContact('internal', 'CONTRIBUTOR');
2126 }
2127
2134 {
2135 return $this->getIdContact('external', 'CONTRIBUTOR');
2136 }
2137
2143 public function getTicketAllContacts()
2144 {
2145 $array_contact = array();
2146
2147 $array_contact = $this->getIdTicketInternalContact();
2148
2149 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());
2150
2151 $array_contact = array_merge($array_contact, $this->getIdTicketInternalInvolvedContact());
2152
2153 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());
2154
2155 return $array_contact;
2156 }
2157
2164 {
2165 $array_contact = array();
2166
2167 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());
2168
2169 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());
2170
2171 return $array_contact;
2172 }
2173
2174
2186 public function listeContact($statusoflink = -1, $source = 'external', $list = 0, $code = '', $status = -1)
2187 {
2188 global $langs;
2189
2190 $tab = array();
2191
2192 $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
2193 if ($source == 'internal') {
2194 $sql .= ", '-1' as socid, t.statut as statuscontact";
2195 }
2196
2197 if ($source == 'external' || $source == 'thirdparty') {
2198 $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
2199 }
2200
2201 $sql .= ", t.civility, t.lastname as lastname, t.firstname, t.email";
2202 if ($source == 'internal') {
2203 $sql .= ", t.office_phone as phone, t.user_mobile as phone_mobile";
2204 }
2205
2206 if ($source == 'external') {
2207 $sql .= ", t.phone as phone, t.phone_mobile as phone_mobile, t.phone_perso as phone_perso";
2208 }
2209
2210 $sql .= ", tc.source, tc.element, tc.code, tc.libelle as type_contact_label";
2211 $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
2212 $sql .= ", ".MAIN_DB_PREFIX."element_contact ec";
2213 if ($source == 'internal') {
2214 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
2215 }
2216
2217 if ($source == 'external' || $source == 'thirdparty') {
2218 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
2219 }
2220
2221 $sql .= " WHERE ec.element_id = ".((int) $this->id);
2222 $sql .= " AND ec.fk_c_type_contact=tc.rowid";
2223 $sql .= " AND tc.element='".$this->db->escape($this->element)."'";
2224 if ($source == 'internal') {
2225 $sql .= " AND tc.source = 'internal'";
2226 if ($status >= 0) {
2227 $sql .= " AND t.statut = ".((int) $status);
2228 }
2229 }
2230
2231 if ($source == 'external' || $source == 'thirdparty') {
2232 $sql .= " AND tc.source = 'external'";
2233 if ($status >= 0) {
2234 $sql .= " AND t.statut = ".((int) $status);
2235 }
2236 }
2237
2238 if (!empty($code)) {
2239 $sql .= " AND tc.code = '".$this->db->escape($code)."'";
2240 }
2241
2242 $sql .= " AND tc.active=1";
2243 if ($statusoflink >= 0) {
2244 $sql .= " AND ec.statut = ".((int) $statusoflink);
2245 }
2246
2247 $sql .= " ORDER BY t.lastname ASC";
2248
2249 $resql = $this->db->query($sql);
2250 if ($resql) {
2251 $num = $this->db->num_rows($resql);
2252 $i = 0;
2253 while ($i < $num) {
2254 $obj = $this->db->fetch_object($resql);
2255
2256 if (!$list) {
2257 $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
2258 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_contact_label);
2259 $tab[$i] = array(
2260 'source' => $obj->source,
2261 'socid' => $obj->socid,
2262 'id' => $obj->id,
2263 'nom' => $obj->lastname, // For backward compatibility
2264 'civility' => $obj->civility,
2265 'lastname' => $obj->lastname,
2266 'firstname' => $obj->firstname,
2267 'email' => $obj->email,
2268 'rowid' => $obj->rowid,
2269 'code' => $obj->code,
2270 'libelle' => $libelle_type,
2271 'status' => $obj->statuslink,
2272 'statuscontact'=>$obj->statuscontact,
2273 'fk_c_type_contact' => $obj->fk_c_type_contact,
2274 'phone' => $obj->phone,
2275 'phone_mobile' => $obj->phone_mobile);
2276 } else {
2277 $tab[$i] = $obj->id;
2278 }
2279
2280 $i++;
2281 }
2282
2283 return $tab;
2284 } else {
2285 $this->error = $this->db->error();
2286 dol_print_error($this->db);
2287 return -1;
2288 }
2289 }
2290
2297 public function getDefaultRef($thirdparty = '')
2298 {
2299 global $conf;
2300
2301 $defaultref = '';
2302 $modele = getDolGlobalString('TICKET_ADDON', 'mod_ticket_simple');
2303
2304 // Search template files
2305 $file = '';
2306 $classname = '';
2307 $filefound = 0;
2308 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2309 foreach ($dirmodels as $reldir) {
2310 $file = dol_buildpath($reldir."core/modules/ticket/".$modele.'.php', 0);
2311 if (file_exists($file)) {
2312 $filefound = 1;
2313 $classname = $modele;
2314 break;
2315 }
2316 }
2317
2318 if ($filefound) {
2319 $result = dol_include_once($reldir."core/modules/ticket/".$modele.'.php');
2320 $modTicket = new $classname;
2321
2322 $defaultref = $modTicket->getNextValue($thirdparty, $this);
2323 }
2324
2325 if (is_numeric($defaultref) && $defaultref <= 0) {
2326 $defaultref = '';
2327 }
2328
2329 return $defaultref;
2330 }
2331
2332
2333 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2340 public function is_photo_available($sdir)
2341 {
2342 // phpcs:enable
2343 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2344
2345 global $conf;
2346
2347 $dir = $sdir.'/';
2348 $nbphoto = 0;
2349
2350 $dir_osencoded = dol_osencode($dir);
2351 if (file_exists($dir_osencoded)) {
2352 $handle = opendir($dir_osencoded);
2353 if (is_resource($handle)) {
2354 while (($file = readdir($handle)) !== false) {
2355 if (!utf8_check($file)) {
2356 $file = utf8_encode($file); // To be sure data is stored in UTF8 in memory
2357 }
2358 if (dol_is_file($dir.$file)) {
2359 return true;
2360 }
2361 }
2362 }
2363 }
2364 return false;
2365 }
2366
2367
2376 public function copyFilesForTicket($forcetrackid = null)
2377 {
2378 global $conf;
2379
2380 // Create form object
2381 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
2382 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2383 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
2384
2385 $maxwidthsmall = 270;
2386 $maxheightsmall = 150;
2387 $maxwidthmini = 128;
2388 $maxheightmini = 72;
2389
2390 $formmail = new FormMail($this->db);
2391 $formmail->trackid = (is_null($forcetrackid) ? 'tic'.$this->id : '');
2392 $attachedfiles = $formmail->get_attached_files();
2393
2394 $filepath = $attachedfiles['paths']; // path is for example user->dir_temp.'/'.$user->id.'/'...
2395 $filename = $attachedfiles['names'];
2396 $mimetype = $attachedfiles['mimes'];
2397
2398 // Copy files into ticket directory
2399 $destdir = $conf->ticket->dir_output.'/'.$this->ref;
2400
2401 if (!dol_is_dir($destdir)) {
2402 dol_mkdir($destdir);
2403 }
2404
2405 $listofpaths = array();
2406 $listofnames = array();
2407 foreach ($filename as $i => $val) {
2408 $destfile = $destdir.'/'.$filename[$i];
2409 // If destination file already exists, we add a suffix to avoid to overwrite
2410 if (is_file($destfile)) {
2411 $pathinfo = pathinfo($filename[$i]);
2412 $now = dol_now();
2413 $destfile = $destdir.'/'.$pathinfo['filename'].' - '.dol_print_date($now, 'dayhourlog').'.'.$pathinfo['extension'];
2414 }
2415
2416 $res = dol_move($filepath[$i], $destfile, 0, 1, 0, 1);
2417 if (!$res) {
2418 // Move has failed
2419 $this->error = "Failed to move file ".dirbasename($filepath[$i])." into ".dirbasename($destfile);
2420 return -1;
2421 } else {
2422 // If file is an image, we create thumbs
2423 if (image_format_supported($destfile) == 1) {
2424 // Create small thumbs for image (Ratio is near 16/9)
2425 // Used on logon for example
2426 $imgThumbSmall = vignette($destfile, $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs");
2427 // Create mini thumbs for image (Ratio is near 16/9)
2428 // Used on menu or for setup page for example
2429 $imgThumbMini = vignette($destfile, $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs");
2430 }
2431 }
2432
2433 // Clear variables into session
2434 $formmail->remove_attached_files($i);
2435
2436 // Fill array with new names
2437 $listofpaths[$i] = $destfile;
2438 $listofnames[$i] = basename($destfile);
2439 }
2440
2441 return array('listofpaths'=>$listofpaths, 'listofnames'=>$listofnames, 'listofmimes'=>$mimetype);
2442 }
2443
2454 public function setCategories($categories)
2455 {
2456 // Handle single category
2457 if (!is_array($categories)) {
2458 $categories = array($categories);
2459 }
2460
2461 // Get current categories
2462 include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2463 $c = new Categorie($this->db);
2464 $existing = $c->containing($this->id, Categorie::TYPE_TICKET, 'id');
2465
2466 // Diff
2467 if (is_array($existing)) {
2468 $to_del = array_diff($existing, $categories);
2469 $to_add = array_diff($categories, $existing);
2470 } else {
2471 $to_del = array(); // Nothing to delete
2472 $to_add = $categories;
2473 }
2474
2475 // Process
2476 foreach ($to_del as $del) {
2477 if ($c->fetch($del) > 0) {
2478 $c->del_type($this, Categorie::TYPE_TICKET);
2479 }
2480 }
2481 foreach ($to_add as $add) {
2482 if ($c->fetch($add) > 0) {
2483 $c->add_type($this, Categorie::TYPE_TICKET);
2484 }
2485 }
2486
2487 return 1;
2488 }
2489
2500 public function newMessage($user, &$action, $private = 1, $public_area = 0)
2501 {
2502 global $mysoc, $conf, $langs;
2503
2504 $error = 0;
2505
2506 $object = new Ticket($this->db);
2507
2508 $ret = $object->fetch('', '', GETPOST('track_id', 'alpha'));
2509
2510 $object->socid = $object->fk_soc;
2511 $object->fetch_thirdparty();
2512 $object->fetch_project();
2513
2514 if ($ret < 0) {
2515 $error++;
2516 array_push($this->errors, $langs->trans("ErrorTicketIsNotValid"));
2517 $action = '';
2518 }
2519
2520 if (!GETPOST("message")) {
2521 $error++;
2522 array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("message")));
2523 $action = 'add_message';
2524 }
2525
2526 if (!$error) {
2527 $object->subject = GETPOST('subject', 'alphanohtml');
2528 $object->message = GETPOST("message", "restricthtml");
2529 $object->private = GETPOST("private_message", "alpha");
2530
2531 $send_email = GETPOST('send_email', 'int');
2532
2533 // Copy attached files (saved into $_SESSION) as linked files to ticket. Return array with final name used.
2534 $resarray = $object->copyFilesForTicket();
2535 if (is_numeric($resarray) && $resarray == -1) {
2536 setEventMessages($object->error, $object->errors, 'errors');
2537 return -1;
2538 }
2539
2540 $listofpaths = $resarray['listofpaths'];
2541 $listofnames = $resarray['listofnames'];
2542 $listofmimes = $resarray['listofmimes'];
2543
2544 $id = $object->createTicketMessage($user, 0, $listofpaths, $listofmimes, $listofnames, $send_email);
2545 if ($id <= 0) {
2546 $error++;
2547 $this->error = $object->error;
2548 $this->errors = $object->errors;
2549 $action = 'add_message';
2550 }
2551
2552 if (!$error && $id > 0) {
2553 setEventMessages($langs->trans('TicketMessageSuccessfullyAdded'), null, 'mesgs');
2554
2555 //var_dump($_SESSION);
2556 //var_dump($listofpaths);exit;
2557
2558 if (!empty($public_area)) {
2559 /*
2560 * Message created from the Public interface
2561 *
2562 * Send emails to assigned users (public area notification)
2563 */
2564 if (getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_ENABLED')) {
2565 // Retrieve internal contact datas
2566 $internal_contacts = $object->getInfosTicketInternalContact(1);
2567
2568 $assigned_user_dont_have_email = '';
2569
2570 $sendto = array();
2571
2572 if ($this->fk_user_assign > 0) {
2573 $assigned_user = new User($this->db);
2574 $assigned_user->fetch($this->fk_user_assign);
2575 if (!empty($assigned_user->email)) {
2576 $sendto[$assigned_user->email] = $assigned_user->getFullName($langs)." <".$assigned_user->email.">";
2577 } else {
2578 $assigned_user_dont_have_email = $assigned_user->getFullName($langs);
2579 }
2580 }
2581
2582 // Build array to display recipient list
2583 foreach ($internal_contacts as $key => $info_sendto) {
2584 // Avoid duplicate notifications
2585 if ($info_sendto['id'] == $user->id) {
2586 continue;
2587 }
2588
2589 // We check if the email address is not the assignee's address to prevent notification from being sent twice
2590 if (!empty($info_sendto['email']) && $assigned_user->email != $info_sendto['email']) {
2591 $sendto[] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";
2592 }
2593 }
2594
2595 if (empty($sendto)) {
2596 if (getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL')) {
2597 $sendto[getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL')] = getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL');
2598 } elseif (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2599 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2600 }
2601 }
2602
2603 // Add global email address recipient
2604 if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') &&
2605 getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)
2606 ) {
2607 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2608 }
2609
2610 if (!empty($sendto)) {
2611 $label_title = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2612 $subject = '['.$label_title.'- ticket #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2613
2614 // Message send
2615 $message = getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO', $langs->trans('TicketMessageMailIntroText'));
2616 $message .= '<br><br>';
2617 $messagePost = GETPOST('message', 'restricthtml');
2618 if (!dol_textishtml($messagePost)) {
2619 $messagePost = dol_nl2br($messagePost);
2620 }
2621 $message .= $messagePost;
2622
2623 // Customer company infos
2624 $message .= '<br><br>';
2625 $message .= "==============================================";
2626 $message .= !empty($object->thirdparty->name) ? '<br>'.$langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';
2627 $message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';
2628 $message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';
2629
2630 // Email send to
2631 $message .= '<br><br>';
2632 if (!empty($assigned_user_dont_have_email)) {
2633 $message .= '<br>'.$langs->trans('NoEMail').' : '.$assigned_user_dont_have_email;
2634 }
2635 foreach ($sendto as $val) {
2636 $message .= '<br>'.$langs->trans('TicketNotificationRecipient').' : '.$val;
2637 }
2638
2639 // URL ticket
2640 $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;
2641 $message .= '<br><br>';
2642 $message .= $langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a>';
2643
2644 $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2645 }
2646 }
2647 } else {
2648 /*
2649 * Message send from the Backoffice / Private area
2650 *
2651 * 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)
2652 */
2653 if ($send_email > 0) {
2654 // Retrieve internal contact datas
2655 $internal_contacts = $object->getInfosTicketInternalContact(1);
2656
2657 $sendto = array();
2658 if (is_array($internal_contacts) && count($internal_contacts) > 0) {
2659 // Set default subject
2660 $label_title = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2661 $appli = $label_title;
2662 $subject = GETPOST('subject', 'alphanohtml') ? GETPOST('subject', 'alphanohtml') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2663
2664 $message_intro = $langs->trans('TicketNotificationEmailBody', "#".$object->id);
2665 $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');
2666
2667 $message = getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO', $langs->trans('TicketMessageMailIntroText'));
2668 $message .= '<br><br>';
2669 $messagePost = GETPOST('message', 'restricthtml');
2670 if (!dol_textishtml($messagePost)) {
2671 $messagePost = dol_nl2br($messagePost);
2672 }
2673 $message .= $messagePost;
2674
2675 // Data about customer
2676 $message .= '<br><br>';
2677 $message .= "==============================================<br>";
2678 $message .= !empty($object->thirdparty->name) ? $langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';
2679 $message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';
2680 $message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';
2681
2682 // Build array to display recipient list
2683 foreach ($internal_contacts as $key => $info_sendto) {
2684 // Avoid duplicate notifications
2685 if ($info_sendto['id'] == $user->id) {
2686 continue;
2687 }
2688
2689 if ($info_sendto['email'] != '') {
2690 if (!empty($info_sendto['email'])) {
2691 $sendto[$info_sendto['email']] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";
2692 }
2693
2694 // Contact type
2695 $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1').' ('.strtolower($info_sendto['libelle']).')';
2696 $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');
2697 }
2698 }
2699 $message .= '<br>';
2700 // URL ticket
2701 $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;
2702
2703 // Add html link on url
2704 $message .= '<br>'.$langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a><br>';
2705
2706 // Add global email address recipient
2707 if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {
2708 if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2709 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2710 }
2711 }
2712
2713 // dont try to send email if no recipient
2714 if (!empty($sendto)) {
2715 $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2716 }
2717 }
2718
2719 /*
2720 * Send emails for externals users if not private (linked contacts)
2721 */
2722 if (empty($object->private)) {
2723 // Retrieve email of all contacts (external)
2724 $external_contacts = $object->getInfosTicketExternalContact(1);
2725
2726 // If no contact, get email from thirdparty
2727 if (is_array($external_contacts) && count($external_contacts) === 0) {
2728 if (!empty($object->fk_soc)) {
2729 $object->fetch_thirdparty($object->fk_soc);
2730 $array_company = array(array('firstname' => '', 'lastname' => $object->thirdparty->name, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
2731 $external_contacts = array_merge($external_contacts, $array_company);
2732 } elseif (empty($object->fk_soc) && !empty($object->origin_email)) {
2733 $array_external = array(array('firstname' => '', 'lastname' => $object->origin_email, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
2734 $external_contacts = array_merge($external_contacts, $array_external);
2735 }
2736 }
2737
2738 $sendto = array();
2739 if (is_array($external_contacts) && count($external_contacts) > 0) {
2740 // Get default subject for email to external contacts
2741 $label_title = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2742 $appli = $mysoc->name;
2743 $subject = GETPOST('subject') ? GETPOST('subject') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2744
2745 $message_intro = GETPOST('mail_intro') ? GETPOST('mail_intro', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO');
2746 $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');
2747 if (!dol_textishtml($message_intro)) {
2748 $message_intro = dol_nl2br($message_intro);
2749 }
2750 if (!dol_textishtml($message_signature)) {
2751 $message_signature = dol_nl2br($message_signature);
2752 }
2753
2754 // We put intro after
2755 $messagePost = GETPOST('message', 'restricthtml');
2756 if (!dol_textishtml($messagePost)) {
2757 $messagePost = dol_nl2br($messagePost);
2758 }
2759 $message = $messagePost;
2760 $message .= '<br><br>';
2761
2762 foreach ($external_contacts as $key => $info_sendto) {
2763 // altairis: avoid duplicate emails to external contacts
2764 if ($info_sendto['id'] == $user->contact_id) {
2765 continue;
2766 }
2767
2768 if ($info_sendto['email'] != '' && $info_sendto['email'] != $object->origin_email) {
2769 if (!empty($info_sendto['email'])) {
2770 $sendto[$info_sendto['email']] = trim($info_sendto['firstname']." ".$info_sendto['lastname'])." <".$info_sendto['email'].">";
2771 }
2772
2773 $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1').' ('.strtolower($info_sendto['libelle']).')';
2774 $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');
2775 }
2776 }
2777
2778 // If public interface is not enable, use link to internal page into mail
2779 $url_public_ticket = getDolGlobalString('TICKET_URL_PUBLIC_INTERFACE', dol_buildpath('/public/ticket/', 2)) . 'view.php?track_id='.$object->track_id;
2780 $message .= '<br>'.$langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer').' : <a href="'.$url_public_ticket.'">'.$object->track_id.'</a><br>';
2781
2782 // Build final message
2783 $message = $message_intro.'<br><br>'.$message;
2784
2785 // Add signature
2786 $message .= '<br>'.$message_signature;
2787
2788 if (!empty($object->origin_email)) {
2789 $sendto[$object->origin_email] = $object->origin_email;
2790 }
2791
2792 if ($object->fk_soc > 0 && !array_key_exists($object->origin_email, $sendto)) {
2793 $object->socid = $object->fk_soc;
2794 $object->fetch_thirdparty();
2795 if (!empty($object->thirdparty->email)) {
2796 $sendto[$object->thirdparty->email] = $object->thirdparty->email;
2797 }
2798 }
2799
2800 // Add global email address recipient
2801 if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {
2802 if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2803 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2804 }
2805 }
2806
2807 // Dont try to send email when no recipient
2808 if (!empty($sendto)) {
2809 $result = $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2810 if ($result) {
2811 // update last_msg_sent date (for last message sent to external users)
2812 $this->date_last_msg_sent = dol_now();
2813 $this->update($user, 1); // disable trigger when updating date_last_msg_sent. sendTicketMessageByEmail already create an event in actioncomm table.
2814 }
2815 }
2816 }
2817 }
2818 }
2819 }
2820
2821 // Set status to "answered" if not set yet, but only if internal user and not private message
2822 // Or set status to "answered" if the client has answered and if the ticket has started
2823 if (($object->status < self::STATUS_IN_PROGRESS && !$user->socid && !$private) ||
2824 ($object->status > self::STATUS_IN_PROGRESS && $public_area)
2825 ) {
2826 $object->setStatut(3, null, '', 'TICKET_MODIFY');
2827 }
2828 return 1;
2829 } else {
2830 setEventMessages($object->error, $object->errors, 'errors');
2831 return -1;
2832 }
2833 } else {
2834 setEventMessages($this->error, $this->errors, 'errors');
2835 return -1;
2836 }
2837 }
2838
2839
2852 public function sendTicketMessageByEmail($subject, $message, $send_internal_cc = 0, $array_receiver = array(), $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array())
2853 {
2854 global $conf, $langs;
2855
2856 if (getDolGlobalString('TICKET_DISABLE_ALL_MAILS')) {
2857 dol_syslog(get_class($this).'::sendTicketMessageByEmail: Emails are disable into ticket setup by option TICKET_DISABLE_ALL_MAILS', LOG_WARNING);
2858 return false;
2859 }
2860
2861 $langs->load("mails");
2862
2863 include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
2864 //$contactstatic = new Contact($this->db);
2865
2866 // If no receiver defined, load all ticket linked contacts
2867 if (!is_array($array_receiver) || !count($array_receiver) > 0) {
2868 $array_receiver = $this->getInfosTicketInternalContact(1);
2869 $array_receiver = array_merge($array_receiver, $this->getInfosTicketExternalContact(1));
2870 }
2871
2872 $sendtocc = "";
2873 if ($send_internal_cc) {
2874 $sendtocc = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
2875 }
2876
2877 $from = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
2878 $is_sent = false;
2879 if (is_array($array_receiver) && count($array_receiver) > 0) {
2880 foreach ($array_receiver as $key => $receiver) {
2881 $deliveryreceipt = 0;
2882 $filepath = $filename_list;
2883 $filename = $mimefilename_list;
2884 $mimetype = $mimetype_list;
2885
2886 // Send email
2887
2888 $old_MAIN_MAIL_AUTOCOPY_TO = getDolGlobalString('MAIN_MAIL_AUTOCOPY_TO');
2889
2890 if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
2891 $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
2892 }
2893
2894 $upload_dir_tmp = $conf->user->dir_output."/".$user->id.'/temp';
2895
2896 include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
2897 $trackid = "tic".$this->id;
2898
2899 $moreinheader = 'X-Dolibarr-Info: sendTicketMessageByEmail'."\r\n";
2900 if (!empty($this->email_msgid)) {
2901 // We must also add 1 entry In-Reply-To: <$this->email_msgid> with Message-ID we respond from (See RFC5322).
2902 $moreinheader .= 'In-Reply-To: <'.$this->email_msgid.'>'."\r\n";
2903 }
2904
2905 $mailfile = new CMailFile($subject, $receiver, $from, $message, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1, '', '', $trackid, $moreinheader, 'ticket', '', $upload_dir_tmp);
2906
2907 if ($mailfile->error) {
2908 setEventMessages($mailfile->error, null, 'errors');
2909 } else {
2910 $result = $mailfile->sendfile();
2911 if ($result) {
2912 setEventMessages($langs->trans('MailSuccessfulySent', $mailfile->getValidAddress($from, 2), $mailfile->getValidAddress($receiver, 2)), null, 'mesgs');
2913 $is_sent = true;
2914 } else {
2915 $langs->load("other");
2916 if ($mailfile->error) {
2917 setEventMessages($langs->trans('ErrorFailedToSendMail', $from, $receiver), null, 'errors');
2918 dol_syslog($langs->trans('ErrorFailedToSendMail', $from, $receiver).' : '.$mailfile->error);
2919 } else {
2920 setEventMessages('No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS', null, 'errors');
2921 }
2922 }
2923 }
2924
2925 if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
2926 $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
2927 }
2928 }
2929 } else {
2930 $langs->load("other");
2931 setEventMessages($langs->trans('ErrorMailRecipientIsEmptyForSendTicketMessage'), null, 'warnings');
2932 }
2933
2934 return $is_sent;
2935 }
2936
2937 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2945 public function load_board($user, $mode)
2946 {
2947 // phpcs:enable
2948 global $conf, $user, $langs;
2949
2950 $now = dol_now();
2951 $delay_warning = 0;
2952
2953 $this->nbtodo = $this->nbtodolate = 0;
2954 $clause = " WHERE";
2955
2956 $sql = "SELECT p.rowid, p.ref, p.datec as datec";
2957 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
2958 if (isModEnabled('societe') && empty($user->rights->societe->client->voir) && !$user->socid) {
2959 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2960 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2961 $clause = " AND";
2962 }
2963 $sql .= $clause." p.entity IN (".getEntity('ticket').")";
2964 if ($mode == 'opened') {
2965 $sql .= " AND p.fk_statut NOT IN (".Ticket::STATUS_CLOSED.", ".Ticket::STATUS_CANCELED.")";
2966 }
2967 if ($user->socid) {
2968 $sql .= " AND p.fk_soc = ".((int) $user->socid);
2969 }
2970
2971 $resql = $this->db->query($sql);
2972 if ($resql) {
2973 $label = $labelShort = '';
2974 $status = '';
2975 if ($mode == 'opened') {
2976 $status = 'openall';
2977 //$delay_warning = $conf->ticket->warning_delay;
2978 $delay_warning = 0;
2979 $label = $langs->trans("MenuListNonClosed");
2980 $labelShort = $langs->trans("MenuListNonClosed");
2981 }
2982
2983 $response = new WorkboardResponse();
2984 //$response->warning_delay = $delay_warning / 60 / 60 / 24;
2985 $response->label = $label;
2986 $response->labelShort = $labelShort;
2987 $response->url = DOL_URL_ROOT.'/ticket/list.php?search_fk_statut[]='.$status;
2988 $response->img = img_object('', "ticket");
2989
2990 // This assignment in condition is not a bug. It allows walking the results.
2991 while ($obj = $this->db->fetch_object($resql)) {
2992 $response->nbtodo++;
2993 if ($mode == 'opened') {
2994 $datelimit = $this->db->jdate($obj->datec) + $delay_warning;
2995 if ($datelimit < $now) {
2996 //$response->nbtodolate++;
2997 }
2998 }
2999 }
3000 return $response;
3001 } else {
3002 $this->error = $this->db->lasterror();
3003 return -1;
3004 }
3005 }
3006
3007 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3013 public function load_state_board()
3014 {
3015 // phpcs:enable
3016 global $conf, $user;
3017
3018 $this->nb = array();
3019 $clause = "WHERE";
3020
3021 $sql = "SELECT count(p.rowid) as nb";
3022 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
3023 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3024 if (empty($user->rights->societe->client->voir) && !$user->socid) {
3025 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3026 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3027 $clause = "AND";
3028 }
3029 $sql .= " ".$clause." p.entity IN (".getEntity('ticket').")";
3030
3031 $resql = $this->db->query($sql);
3032 if ($resql) {
3033 // This assignment in condition is not a bug. It allows walking the results.
3034 while ($obj = $this->db->fetch_object($resql)) {
3035 $this->nb["ticket"] = $obj->nb;
3036 }
3037 $this->db->free($resql);
3038 return 1;
3039 } else {
3040 dol_print_error($this->db);
3041 $this->error = $this->db->lasterror();
3042 return -1;
3043 }
3044 }
3045
3054 public static function replaceThirdparty($db, $origin_id, $dest_id)
3055 {
3056 $tables = array('ticket');
3057
3058 return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3059 }
3060
3068 public function getKanbanView($option = '', $arraydata = null)
3069 {
3070 global $langs;
3071
3072 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3073
3074 $return = '<div class="box-flex-item box-flex-grow-zero">';
3075 $return .= '<div class="info-box info-box-sm">';
3076 $return .= '<span class="info-box-icon bg-infobox-action">';
3077 $return .= img_picto('', $this->picto);
3078 $return .= '</span>';
3079 $return .= '<div class="info-box-content">';
3080 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
3081 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3082 if (!empty($arraydata['user_assignment'])) {
3083 $return .= '<br><span class="info-box-label" title="'.dol_escape_htmltag($langs->trans("AssignedTo")).'">'.$arraydata['user_assignment'].'</span>';
3084 }
3085 if (property_exists($this, 'type_code') && !empty($this->type_code)) {
3086 $return .= '<br>';
3087 $return .= '<div class="tdoverflowmax125 inline-block">'.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$this->type_code, 'c_ticket_type', 'code', 'label', $this->type_code).'</div>';
3088 }
3089 if (method_exists($this, 'getLibStatut')) {
3090 $return .= '<br><div class="info-box-status margintoponly">'.$this->getLibStatut(3).'</div>';
3091 }
3092 $return .= '</div>';
3093 $return .= '</div>';
3094 $return .= '</div>';
3095 return $return;
3096 }
3097}
3098
3099
3104{
3109 public $rowid;
3110
3114 public $id;
3115
3119 public $ref;
3120
3125
3129 public $fk_soc;
3130
3135
3140
3145
3150
3154 public $subject;
3155
3159 public $message;
3160
3165
3170
3175
3179 public $timing;
3180
3185
3190
3195
3200
3205
3210
3214 public $datec = '';
3215
3219 public $date_read = '';
3220
3224 public $date_last_msg_sent = '';
3225
3229 public $date_close = '';
3230}
$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.