dolibarr  18.0.6
ticket.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2013-2018 Jean-François Ferry <hello@librethic.io>
3  * Copyright (C) 2016 Christophe Battarel <christophe@altairis.fr>
4  * Copyright (C) 2019-2023 Frédéric France <frederic.france@netlogic.fr>
5  * Copyright (C) 2020 Laurent Destailleur <eldy@users.sourceforge.net>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <https://www.gnu.org/licenses/>.
19  */
20 
27 // Put here all includes required by your class file
28 require_once DOL_DOCUMENT_ROOT."/core/class/commonobject.class.php";
29 require_once DOL_DOCUMENT_ROOT.'/fichinter/class/fichinter.class.php';
30 require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php';
31 
32 
36 class 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 
154  public $type_label;
155 
160 
165 
169  public $email_from;
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 
260  const STATUS_NOT_READ = 0;
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 
1328  public function loadCacheSeveritiesTickets()
1329  {
1330  global $langs;
1331 
1332  if (!empty($this->cache_severity_tickets) && count($this->cache_severity_tickets)) {
1333  return 0;
1334  }
1335  // Cache deja charge
1336 
1337  $sql = "SELECT rowid, code, label, use_default, pos, description";
1338  $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_severity";
1339  $sql .= " WHERE entity IN (".getEntity('c_ticket_severity').")";
1340  $sql .= " AND active > 0";
1341  $sql .= " ORDER BY pos";
1342  dol_syslog(get_class($this)."::loadCacheSeveritiesTickets", LOG_DEBUG);
1343  $resql = $this->db->query($sql);
1344  if ($resql) {
1345  $num = $this->db->num_rows($resql);
1346  $i = 0;
1347  while ($i < $num) {
1348  $obj = $this->db->fetch_object($resql);
1349 
1350  $this->cache_severity_tickets[$obj->rowid]['code'] = $obj->code;
1351  $label = ($langs->trans("TicketSeverityShort".$obj->code) != ("TicketSeverityShort".$obj->code) ? $langs->trans("TicketSeverityShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1352  $this->cache_severity_tickets[$obj->rowid]['label'] = $label;
1353  $this->cache_severity_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1354  $this->cache_severity_tickets[$obj->rowid]['pos'] = $obj->pos;
1355  $i++;
1356  }
1357  return $num;
1358  } else {
1359  dol_print_error($this->db);
1360  return -1;
1361  }
1362  }
1363 
1364 
1371  public function getLibStatut($mode = 0)
1372  {
1373  return $this->libStatut($this->fk_statut, $mode, 0, $this->progress);
1374  }
1375 
1376 
1377  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1387  public function LibStatut($status, $mode = 0, $notooltip = 0, $progress = 0)
1388  {
1389  // phpcs:enable
1390  global $langs, $hookmanager;
1391 
1392  $labelStatus = $this->statuts[$status];
1393  $labelStatusShort = $this->statuts_short[$status];
1394 
1395  if ($status == self::STATUS_NOT_READ) {
1396  $statusType = 'status0';
1397  } elseif ($status == self::STATUS_READ) {
1398  $statusType = 'status1';
1399  } elseif ($status == self::STATUS_ASSIGNED) {
1400  $statusType = 'status2';
1401  } elseif ($status == self::STATUS_IN_PROGRESS) {
1402  $statusType = 'status4';
1403  } elseif ($status == self::STATUS_WAITING) {
1404  $statusType = 'status7';
1405  } elseif ($status == self::STATUS_NEED_MORE_INFO) {
1406  $statusType = 'status3';
1407  } elseif ($status == self::STATUS_CANCELED) {
1408  $statusType = 'status9';
1409  } elseif ($status == self::STATUS_CLOSED) {
1410  $statusType = 'status6';
1411  } else {
1412  $labelStatus = 'Unknown';
1413  $labelStatusShort = 'Unknown';
1414  $statusType = 'status0';
1415  $mode = 0;
1416  }
1417 
1418  $parameters = array(
1419  'status' => $status,
1420  'mode' => $mode,
1421  );
1422 
1423  // Note that $action and $object may have been modified by hook
1424  $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this);
1425 
1426  if ($reshook > 0) {
1427  return $hookmanager->resPrint;
1428  }
1429 
1430  $params = array();
1431  if ($notooltip) {
1432  $params = array('tooltip' => 'no');
1433  }
1434 
1435  $labelStatus = $langs->transnoentitiesnoconv($labelStatus);
1436  $labelStatusShort = $langs->transnoentitiesnoconv($labelStatusShort);
1437 
1438  if ($status == self::STATUS_IN_PROGRESS && $progress > 0) {
1439  $labelStatus .= ' ('.round($progress).'%)';
1440  $labelStatusShort .= ' ('.round($progress).'%)';
1441  }
1442 
1443  return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', $params);
1444  }
1445 
1453  public function getTooltipContentArray($params)
1454  {
1455  global $langs;
1456 
1457  $langs->load('ticket');
1458  $nofetch = !empty($params['nofetch']);
1459 
1460  $datas = array();
1461  $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Ticket").'</u>';
1462  $datas['picto'] .= ' '.$this->getLibStatut(4);
1463  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1464  $datas['track_id'] = '<br><b>'.$langs->trans('TicketTrackId').':</b> '.$this->track_id;
1465  $datas['subject'] = '<br><b>'.$langs->trans('Subject').':</b> '.$this->subject;
1466  if ($this->date_creation) {
1467  $datas['date_creation'] = '<br><b>'.$langs->trans('DateCreation').':</b> '.dol_print_date($this->date_creation, 'dayhour');
1468  }
1469  if ($this->date_modification) {
1470  $datas['date_modification'] = '<br><b>'.$langs->trans('DateModification').':</b> '.dol_print_date($this->date_modification, 'dayhour');
1471  }
1472  // show categories for this record only in ajax to not overload lists
1473  if (isModEnabled('categorie') && !$nofetch) {
1474  require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
1475  $form = new Form($this->db);
1476  $datas['categories'] = '<br>' . $form->showCategories($this->id, Categorie::TYPE_TICKET, 1);
1477  }
1478 
1479  return $datas;
1480  }
1481 
1492  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1493  {
1494  global $conf, $langs;
1495 
1496  if (!empty($conf->dol_no_mouse_hover)) {
1497  $notooltip = 1; // Force disable tooltips
1498  }
1499 
1500  $result = '';
1501 
1502  $params = [
1503  'id' => $this->id,
1504  'objecttype' => $this->element,
1505  'option' => $option,
1506  'nofetch' => 1,
1507  ];
1508  $classfortooltip = 'classfortooltip';
1509  $dataparams = '';
1510  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1511  $classfortooltip = 'classforajaxtooltip';
1512  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1513  $label = '';
1514  } else {
1515  $label = implode($this->getTooltipContentArray($params));
1516  }
1517 
1518  $url = DOL_URL_ROOT.'/ticket/card.php?id='.$this->id;
1519 
1520  if ($option != 'nolink') {
1521  // Add param to save lastsearch_values or not
1522  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1523  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1524  $add_save_lastsearch_values = 1;
1525  }
1526  if ($add_save_lastsearch_values) {
1527  $url .= '&save_lastsearch_values=1';
1528  }
1529  }
1530 
1531  $linkclose = '';
1532  if (empty($notooltip)) {
1533  if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1534  $label = $langs->trans("ShowTicket");
1535  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1536  }
1537  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1538  $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
1539  } else {
1540  $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1541  }
1542 
1543  $linkstart = '<a href="'.$url.'"';
1544  $linkstart .= $linkclose.'>';
1545  $linkend = '</a>';
1546 
1547  $result .= $linkstart;
1548  if ($withpicto) {
1549  $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1550  }
1551  if ($withpicto != 2) {
1552  $result .= $this->ref;
1553  }
1554  $result .= $linkend;
1555  //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1556 
1557  return $result;
1558  }
1559 
1560 
1568  public function markAsRead($user, $notrigger = 0)
1569  {
1570  global $langs;
1571 
1572  $error = 0;
1573 
1574  if ($this->statut != self::STATUS_CANCELED) { // no closed
1575  $this->oldcopy = dol_clone($this);
1576 
1577  $this->db->begin();
1578 
1579  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1580  $sql .= " SET fk_statut = ".Ticket::STATUS_READ.", date_read = '".$this->db->idate(dol_now())."'";
1581  $sql .= " WHERE rowid = ".((int) $this->id);
1582 
1583  dol_syslog(get_class($this)."::markAsRead");
1584  $resql = $this->db->query($sql);
1585  if ($resql) {
1586  $this->actionmsg = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1587  $this->actionmsg2 = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1588 
1589  if (!$error && !$notrigger) {
1590  // Call trigger
1591  $result = $this->call_trigger('TICKET_MODIFY', $user);
1592  if ($result < 0) {
1593  $error++;
1594  }
1595  // End call triggers
1596  }
1597 
1598  if (!$error) {
1599  $this->db->commit();
1600  return 1;
1601  } else {
1602  $this->db->rollback();
1603  $this->error = join(',', $this->errors);
1604  dol_syslog(get_class($this)."::markAsRead ".$this->error, LOG_ERR);
1605  return -1;
1606  }
1607  } else {
1608  $this->db->rollback();
1609  $this->error = $this->db->lasterror();
1610  dol_syslog(get_class($this)."::markAsRead ".$this->error, LOG_ERR);
1611  return -1;
1612  }
1613  }
1614 
1615  return 0;
1616  }
1617 
1626  public function assignUser($user, $id_assign_user, $notrigger = 0)
1627  {
1628  global $conf, $langs;
1629 
1630  $error = 0;
1631 
1632  $this->oldcopy = dol_clone($this);
1633 
1634  $this->db->begin();
1635 
1636  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1637  if ($id_assign_user > 0) {
1638  $sql .= " SET fk_user_assign=".((int) $id_assign_user).", fk_statut = ".Ticket::STATUS_ASSIGNED;
1639  } else {
1640  $sql .= " SET fk_user_assign=null, fk_statut = ".Ticket::STATUS_READ;
1641  }
1642  $sql .= " WHERE rowid = ".((int) $this->id);
1643 
1644  dol_syslog(get_class($this)."::assignUser sql=".$sql);
1645  $resql = $this->db->query($sql);
1646  if ($resql) {
1647  $this->fk_user_assign = $id_assign_user; // May be used by trigger
1648 
1649  if (!$notrigger) {
1650  // Call trigger
1651  $result = $this->call_trigger('TICKET_ASSIGNED', $user);
1652  if ($result < 0) {
1653  $error++;
1654  }
1655  // End call triggers
1656  }
1657 
1658  if (!$error) {
1659  $this->db->commit();
1660  return 1;
1661  } else {
1662  $this->db->rollback();
1663  $this->error = join(',', $this->errors);
1664  dol_syslog(get_class($this)."::assignUser ".$this->error, LOG_ERR);
1665  return -1;
1666  }
1667  } else {
1668  $this->db->rollback();
1669  $this->error = $this->db->lasterror();
1670  dol_syslog(get_class($this)."::assignUser ".$this->error, LOG_ERR);
1671  return -1;
1672  }
1673  }
1674 
1686  public function createTicketMessage($user, $notrigger = 0, $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array(), $send_email = false)
1687  {
1688  global $conf, $langs;
1689  $error = 0;
1690 
1691  $now = dol_now();
1692 
1693  // Clean parameters
1694  if (isset($this->fk_track_id)) {
1695  $this->fk_track_id = trim($this->fk_track_id);
1696  }
1697 
1698  if (isset($this->message)) {
1699  $this->message = trim($this->message);
1700  }
1701 
1702  $this->db->begin();
1703 
1704  // Insert entry into agenda with code 'TICKET_MSG'
1705  include_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
1706  $actioncomm = new ActionComm($this->db);
1707  $actioncomm->type_code = 'AC_OTH_AUTO'; // This is not an entry that must appears into manual calendar but only into CRM calendar
1708  $actioncomm->code = 'TICKET_MSG';
1709  if ($this->private) {
1710  $actioncomm->code = 'TICKET_MSG_PRIVATE';
1711  }
1712  if ($send_email) {
1713  $actioncomm->code .= '_SENTBYMAIL';
1714  }
1715  if ((empty($user->id) || $user->id == 0) && isset($_SESSION['email_customer'])) {
1716  $actioncomm->email_from = $_SESSION['email_customer'];
1717  }
1718  $actioncomm->socid = $this->socid;
1719  $actioncomm->label = $this->subject;
1720  $actioncomm->note_private = $this->message;
1721  $actioncomm->userassigned = array($user->id);
1722  $actioncomm->userownerid = $user->id;
1723  $actioncomm->datep = $now;
1724  $actioncomm->percentage = -1; // percentage is not relevant for punctual events
1725  $actioncomm->elementtype = 'ticket';
1726  $actioncomm->fk_element = $this->id;
1727  $actioncomm->fk_project = $this->fk_project;
1728 
1729  $attachedfiles = array();
1730  $attachedfiles['paths'] = $filename_list;
1731  $attachedfiles['names'] = $mimefilename_list;
1732  $attachedfiles['mimes'] = $mimetype_list;
1733  if (is_array($attachedfiles) && count($attachedfiles) > 0) {
1734  $actioncomm->attachedfiles = $attachedfiles;
1735  }
1736 
1737  if (!empty($mimefilename_list) && is_array($mimefilename_list)) {
1738  $actioncomm->note_private = dol_concatdesc($actioncomm->note_private, "\n".$langs->transnoentities("AttachedFiles").': '.join(';', $mimefilename_list));
1739  }
1740 
1741  $actionid = $actioncomm->create($user);
1742  if ($actionid <= 0) {
1743  $error++;
1744  $this->error = $actioncomm->error;
1745  $this->errors = $actioncomm->errors;
1746  }
1747 
1748  // Commit or rollback
1749  if ($error) {
1750  $this->db->rollback();
1751  return -1 * $error;
1752  } else {
1753  $this->db->commit();
1754  return 1;
1755  }
1756  }
1757 
1763  public function loadCacheMsgsTicket()
1764  {
1765  if (!empty($this->cache_msgs_ticket) && is_array($this->cache_msgs_ticket) && count($this->cache_msgs_ticket)) {
1766  return 0;
1767  }
1768 
1769  // Cache already loaded
1770 
1771  $sql = "SELECT id as rowid, fk_user_author, email_from, datec, datep, label, note as message, code";
1772  $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm";
1773  $sql .= " WHERE fk_element = ".(int) $this->id;
1774  $sql .= " AND elementtype = 'ticket'";
1775  $sql .= " ORDER BY datep DESC";
1776 
1777  dol_syslog(get_class($this)."::load_cache_actions_ticket", LOG_DEBUG);
1778  $resql = $this->db->query($sql);
1779  if ($resql) {
1780  $num = $this->db->num_rows($resql);
1781  $i = 0;
1782  while ($i < $num) {
1783  $obj = $this->db->fetch_object($resql);
1784  $this->cache_msgs_ticket[$i]['id'] = $obj->rowid;
1785  $this->cache_msgs_ticket[$i]['fk_user_author'] = $obj->fk_user_author;
1786  if ($obj->code == 'TICKET_MSG' && empty($obj->fk_user_author)) {
1787  $this->cache_msgs_ticket[$i]['fk_contact_author'] = $obj->email_from;
1788  }
1789  $this->cache_msgs_ticket[$i]['datec'] = $this->db->jdate($obj->datec);
1790  $this->cache_msgs_ticket[$i]['datep'] = $this->db->jdate($obj->datep);
1791  $this->cache_msgs_ticket[$i]['subject'] = $obj->label;
1792  $this->cache_msgs_ticket[$i]['message'] = $obj->message;
1793  $this->cache_msgs_ticket[$i]['private'] = (preg_match('/^TICKET_MSG_PRIVATE/', $obj->code) ? 1 : 0);
1794  $i++;
1795  }
1796  return $num;
1797  } else {
1798  $this->error = "Error ".$this->db->lasterror();
1799  dol_syslog(get_class($this)."::load_cache_actions_ticket ".$this->error, LOG_ERR);
1800  return -1;
1801  }
1802  }
1803 
1811  public function close(User $user, $mode = 0)
1812  {
1813  global $conf;
1814 
1815  if ($this->status != Ticket::STATUS_CLOSED && $this->status != Ticket::STATUS_CANCELED) { // not closed
1816  $this->db->begin();
1817 
1818  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1819  $sql .= " SET fk_statut=".($mode ? Ticket::STATUS_CANCELED : Ticket::STATUS_CLOSED).", progress=100, date_close='".$this->db->idate(dol_now())."'";
1820  $sql .= " WHERE rowid = ".((int) $this->id);
1821 
1822  dol_syslog(get_class($this)."::close mode=".$mode);
1823  $resql = $this->db->query($sql);
1824  if ($resql) {
1825  $error = 0;
1826 
1827  // Valid and close fichinter linked
1828  if (isModEnabled('ficheinter') && getDolGlobalString('WORKFLOW_TICKET_CLOSE_INTERVENTION')) {
1829  dol_syslog("We have closed the ticket, so we close all linked interventions");
1830  $this->fetchObjectLinked($this->id, $this->element, null, 'fichinter');
1831  if ($this->linkedObjectsIds) {
1832  foreach ($this->linkedObjectsIds['fichinter'] as $fichinter_id) {
1833  $fichinter = new Fichinter($this->db);
1834  $fichinter->fetch($fichinter_id);
1835  if ($fichinter->statut == 0) {
1836  $result = $fichinter->setValid($user);
1837  if (!$result) {
1838  $this->errors[] = $fichinter->error;
1839  $error++;
1840  }
1841  }
1842  if ($fichinter->statut < 3) {
1843  $result = $fichinter->setStatut(3);
1844  if (!$result) {
1845  $this->errors[] = $fichinter->error;
1846  $error++;
1847  }
1848  }
1849  }
1850  }
1851  }
1852 
1853  // Call trigger
1854  $result = $this->call_trigger('TICKET_CLOSE', $user);
1855  if ($result < 0) {
1856  $error++;
1857  }
1858  // End call triggers
1859 
1860  if (!$error) {
1861  $this->db->commit();
1862  return 1;
1863  } else {
1864  $this->db->rollback();
1865  $this->error = join(',', $this->errors);
1866  dol_syslog(get_class($this)."::close ".$this->error, LOG_ERR);
1867  return -1;
1868  }
1869  } else {
1870  $this->db->rollback();
1871  $this->error = $this->db->lasterror();
1872  dol_syslog(get_class($this)."::close ".$this->error, LOG_ERR);
1873  return -1;
1874  }
1875  }
1876 
1877  return 0;
1878  }
1879 
1889  public function searchSocidByEmail($email, $type = '0', $filters = array(), $clause = 'AND')
1890  {
1891  $thirdparties = array();
1892  $exact = 0;
1893 
1894  // Generation requete recherche
1895  $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."societe";
1896  $sql .= " WHERE entity IN (".getEntity('ticket', 1).")";
1897  if (!empty($type)) {
1898  if ($type == 1 || $type == 2) {
1899  $sql .= " AND client = ".((int) $type);
1900  } elseif ($type == 3) {
1901  $sql .= " AND fournisseur = 1";
1902  }
1903  }
1904  if (!empty($email)) {
1905  if (empty($exact)) {
1906  $regs = array();
1907  if (preg_match('/^([\*])?[^*]+([\*])?$/', $email, $regs) && count($regs) > 1) {
1908  $email = str_replace('*', '%', $email);
1909  } else {
1910  $email = '%'.$email.'%';
1911  }
1912  }
1913  $sql .= " AND ";
1914  if (is_array($filters) && !empty($filters)) {
1915  $sql .= "(";
1916  }
1917 
1918  $sql .= "email LIKE '".$this->db->escape($email)."'";
1919  }
1920  if (is_array($filters) && !empty($filters)) {
1921  foreach ($filters as $field => $value) {
1922  $sql .= " ".$clause." ".$field." LIKE '".$this->db->escape($value)."'";
1923  }
1924  if (!empty($email)) {
1925  $sql .= ")";
1926  }
1927  }
1928 
1929  $res = $this->db->query($sql);
1930  if ($res) {
1931  while ($rec = $this->db->fetch_array($res)) {
1932  $soc = new Societe($this->db);
1933  $soc->fetch($rec['rowid']);
1934  $thirdparties[] = $soc;
1935  }
1936 
1937  return $thirdparties;
1938  } else {
1939  $this->error = $this->db->error().' sql='.$sql;
1940  dol_syslog(get_class($this)."::searchSocidByEmail ".$this->error, LOG_ERR);
1941  return -1;
1942  }
1943  }
1944 
1953  public function searchContactByEmail($email, $socid = '', $case = '')
1954  {
1955  $contacts = array();
1956 
1957  // Forge the search SQL
1958  $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."socpeople";
1959  $sql .= " WHERE entity IN (".getEntity('contact').")";
1960  if (!empty($socid)) {
1961  $sql .= " AND fk_soc = ".((int) $socid);
1962  }
1963  if (!empty($email)) {
1964  $sql .= " AND ";
1965  if (!$case) {
1966  $sql .= "email = '".$this->db->escape($email)."'";
1967  } else {
1968  $sql .= "email LIKE BINARY '".$this->db->escape($this->db->escapeforlike($email))."'";
1969  }
1970  }
1971 
1972  $res = $this->db->query($sql);
1973  if ($res) {
1974  while ($rec = $this->db->fetch_object($res)) {
1975  include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1976  $contactstatic = new Contact($this->db);
1977  $contactstatic->fetch($rec->rowid);
1978  $contacts[] = $contactstatic;
1979  }
1980 
1981  return $contacts;
1982  } else {
1983  $this->error = $this->db->error().' sql='.$sql;
1984  dol_syslog(get_class($this)."::searchContactByEmail ".$this->error, LOG_ERR);
1985  return -1;
1986  }
1987  }
1988 
1995  public function setCustomer($id)
1996  {
1997  if ($this->id) {
1998  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1999  $sql .= " SET fk_soc = ".($id > 0 ? $id : "null");
2000  $sql .= " WHERE rowid = ".((int) $this->id);
2001  dol_syslog(get_class($this).'::setCustomer sql='.$sql);
2002  $resql = $this->db->query($sql);
2003  if ($resql) {
2004  return 1;
2005  } else {
2006  return -1;
2007  }
2008  } else {
2009  return -1;
2010  }
2011  }
2012 
2019  public function setProgression($percent)
2020  {
2021  if ($this->id) {
2022  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2023  $sql .= " SET progress = ".($percent > 0 ? $percent : "null");
2024  $sql .= " WHERE rowid = ".((int) $this->id);
2025  dol_syslog(get_class($this).'::set_progression sql='.$sql);
2026  $resql = $this->db->query($sql);
2027  if ($resql) {
2028  return 1;
2029  } else {
2030  return -1;
2031  }
2032  } else {
2033  return -1;
2034  }
2035  }
2036 
2043  public function setContract($contractid)
2044  {
2045  if (!$this->table_element) {
2046  dol_syslog(get_class($this)."::setContract was called on objet with property table_element not defined", LOG_ERR);
2047  return -1;
2048  }
2049 
2050  $result = $this->add_object_linked('contrat', $contractid);
2051  if ($result) {
2052  $this->fk_contract = $contractid;
2053  return 1;
2054  } else {
2055  dol_print_error($this->db);
2056  return -1;
2057  }
2058  }
2059 
2060  /* gestion des contacts d'un ticket */
2061 
2067  public function getIdTicketInternalContact()
2068  {
2069  return $this->getIdContact('internal', 'SUPPORTTEC');
2070  }
2071 
2078  public function getInfosTicketInternalContact($status = -1)
2079  {
2080  return $this->listeContact(-1, 'internal', 0, '', $status);
2081  }
2082 
2088  public function getIdTicketCustomerContact()
2089  {
2090  return $this->getIdContact('external', 'SUPPORTCLI');
2091  }
2092 
2099  public function getInfosTicketExternalContact($status = -1)
2100  {
2101  return $this->listeContact(-1, 'external', 0, '', $status);
2102  }
2103 
2110  {
2111  return $this->getIdContact('internal', 'CONTRIBUTOR');
2112  }
2113 
2120  {
2121  return $this->getIdContact('external', 'CONTRIBUTOR');
2122  }
2123 
2129  public function getTicketAllContacts()
2130  {
2131  $array_contact = array();
2132 
2133  $array_contact = $this->getIdTicketInternalContact();
2134 
2135  $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());
2136 
2137  $array_contact = array_merge($array_contact, $this->getIdTicketInternalInvolvedContact());
2138 
2139  $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());
2140 
2141  return $array_contact;
2142  }
2143 
2150  {
2151  $array_contact = array();
2152 
2153  $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());
2154 
2155  $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());
2156 
2157  return $array_contact;
2158  }
2159 
2160 
2172  public function listeContact($statusoflink = -1, $source = 'external', $list = 0, $code = '', $status = -1)
2173  {
2174  global $langs;
2175 
2176  $tab = array();
2177 
2178  $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
2179  if ($source == 'internal') {
2180  $sql .= ", '-1' as socid, t.statut as statuscontact";
2181  }
2182 
2183  if ($source == 'external' || $source == 'thirdparty') {
2184  $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
2185  }
2186 
2187  $sql .= ", t.civility, t.lastname as lastname, t.firstname, t.email";
2188  if ($source == 'internal') {
2189  $sql .= ", t.office_phone as phone, t.user_mobile as phone_mobile";
2190  }
2191 
2192  if ($source == 'external') {
2193  $sql .= ", t.phone as phone, t.phone_mobile as phone_mobile, t.phone_perso as phone_perso";
2194  }
2195 
2196  $sql .= ", tc.source, tc.element, tc.code, tc.libelle as type_contact_label";
2197  $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
2198  $sql .= ", ".MAIN_DB_PREFIX."element_contact ec";
2199  if ($source == 'internal') {
2200  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
2201  }
2202 
2203  if ($source == 'external' || $source == 'thirdparty') {
2204  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
2205  }
2206 
2207  $sql .= " WHERE ec.element_id = ".((int) $this->id);
2208  $sql .= " AND ec.fk_c_type_contact=tc.rowid";
2209  $sql .= " AND tc.element='".$this->db->escape($this->element)."'";
2210  if ($source == 'internal') {
2211  $sql .= " AND tc.source = 'internal'";
2212  if ($status >= 0) {
2213  $sql .= " AND t.statut = ".((int) $status);
2214  }
2215  }
2216 
2217  if ($source == 'external' || $source == 'thirdparty') {
2218  $sql .= " AND tc.source = 'external'";
2219  if ($status >= 0) {
2220  $sql .= " AND t.statut = ".((int) $status);
2221  }
2222  }
2223 
2224  if (!empty($code)) {
2225  $sql .= " AND tc.code = '".$this->db->escape($code)."'";
2226  }
2227 
2228  $sql .= " AND tc.active=1";
2229  if ($statusoflink >= 0) {
2230  $sql .= " AND ec.statut = ".((int) $statusoflink);
2231  }
2232 
2233  $sql .= " ORDER BY t.lastname ASC";
2234 
2235  $resql = $this->db->query($sql);
2236  if ($resql) {
2237  $num = $this->db->num_rows($resql);
2238  $i = 0;
2239  while ($i < $num) {
2240  $obj = $this->db->fetch_object($resql);
2241 
2242  if (!$list) {
2243  $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
2244  $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_contact_label);
2245  $tab[$i] = array(
2246  'source' => $obj->source,
2247  'socid' => $obj->socid,
2248  'id' => $obj->id,
2249  'nom' => $obj->lastname, // For backward compatibility
2250  'civility' => $obj->civility,
2251  'lastname' => $obj->lastname,
2252  'firstname' => $obj->firstname,
2253  'email' => $obj->email,
2254  'rowid' => $obj->rowid,
2255  'code' => $obj->code,
2256  'libelle' => $libelle_type,
2257  'status' => $obj->statuslink,
2258  'statuscontact'=>$obj->statuscontact,
2259  'fk_c_type_contact' => $obj->fk_c_type_contact,
2260  'phone' => $obj->phone,
2261  'phone_mobile' => $obj->phone_mobile);
2262  } else {
2263  $tab[$i] = $obj->id;
2264  }
2265 
2266  $i++;
2267  }
2268 
2269  return $tab;
2270  } else {
2271  $this->error = $this->db->error();
2272  dol_print_error($this->db);
2273  return -1;
2274  }
2275  }
2276 
2283  public function getDefaultRef($thirdparty = '')
2284  {
2285  global $conf;
2286 
2287  $defaultref = '';
2288  $modele = getDolGlobalString('TICKET_ADDON', 'mod_ticket_simple');
2289 
2290  // Search template files
2291  $file = '';
2292  $classname = '';
2293  $filefound = 0;
2294  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2295  foreach ($dirmodels as $reldir) {
2296  $file = dol_buildpath($reldir."core/modules/ticket/".$modele.'.php', 0);
2297  if (file_exists($file)) {
2298  $filefound = 1;
2299  $classname = $modele;
2300  break;
2301  }
2302  }
2303 
2304  if ($filefound) {
2305  $result = dol_include_once($reldir."core/modules/ticket/".$modele.'.php');
2306  $modTicket = new $classname;
2307 
2308  $defaultref = $modTicket->getNextValue($thirdparty, $this);
2309  }
2310 
2311  if (is_numeric($defaultref) && $defaultref <= 0) {
2312  $defaultref = '';
2313  }
2314 
2315  return $defaultref;
2316  }
2317 
2318 
2319  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2326  public function is_photo_available($sdir)
2327  {
2328  // phpcs:enable
2329  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2330 
2331  global $conf;
2332 
2333  $dir = $sdir.'/';
2334  $nbphoto = 0;
2335 
2336  $dir_osencoded = dol_osencode($dir);
2337  if (file_exists($dir_osencoded)) {
2338  $handle = opendir($dir_osencoded);
2339  if (is_resource($handle)) {
2340  while (($file = readdir($handle)) !== false) {
2341  if (!utf8_check($file)) {
2342  $file = utf8_encode($file); // To be sure data is stored in UTF8 in memory
2343  }
2344  if (dol_is_file($dir.$file)) {
2345  return true;
2346  }
2347  }
2348  }
2349  }
2350  return false;
2351  }
2352 
2353 
2362  public function copyFilesForTicket($forcetrackid = null)
2363  {
2364  global $conf;
2365 
2366  // Create form object
2367  include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
2368  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2369  include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
2370 
2371  $maxwidthsmall = 270;
2372  $maxheightsmall = 150;
2373  $maxwidthmini = 128;
2374  $maxheightmini = 72;
2375 
2376  $formmail = new FormMail($this->db);
2377  $formmail->trackid = (is_null($forcetrackid) ? 'tic'.$this->id : '');
2378  $attachedfiles = $formmail->get_attached_files();
2379 
2380  $filepath = $attachedfiles['paths']; // path is for example user->dir_temp.'/'.$user->id.'/'...
2381  $filename = $attachedfiles['names'];
2382  $mimetype = $attachedfiles['mimes'];
2383 
2384  // Copy files into ticket directory
2385  $destdir = $conf->ticket->dir_output.'/'.$this->ref;
2386 
2387  if (!dol_is_dir($destdir)) {
2388  dol_mkdir($destdir);
2389  }
2390 
2391  $listofpaths = array();
2392  $listofnames = array();
2393  foreach ($filename as $i => $val) {
2394  $destfile = $destdir.'/'.$filename[$i];
2395  // If destination file already exists, we add a suffix to avoid to overwrite
2396  if (is_file($destfile)) {
2397  $pathinfo = pathinfo($filename[$i]);
2398  $now = dol_now();
2399  $destfile = $destdir.'/'.$pathinfo['filename'].' - '.dol_print_date($now, 'dayhourlog').'.'.$pathinfo['extension'];
2400  }
2401 
2402  $res = dol_move($filepath[$i], $destfile, 0, 1, 0, 1);
2403  if (!$res) {
2404  // Move has failed
2405  $this->error = "Failed to move file ".dirbasename($filepath[$i])." into ".dirbasename($destfile);
2406  return -1;
2407  } else {
2408  // If file is an image, we create thumbs
2409  if (image_format_supported($destfile) == 1) {
2410  // Create small thumbs for image (Ratio is near 16/9)
2411  // Used on logon for example
2412  $imgThumbSmall = vignette($destfile, $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs");
2413  // Create mini thumbs for image (Ratio is near 16/9)
2414  // Used on menu or for setup page for example
2415  $imgThumbMini = vignette($destfile, $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs");
2416  }
2417  }
2418 
2419  // Clear variables into session
2420  $formmail->remove_attached_files($i);
2421 
2422  // Fill array with new names
2423  $listofpaths[$i] = $destfile;
2424  $listofnames[$i] = basename($destfile);
2425  }
2426 
2427  return array('listofpaths'=>$listofpaths, 'listofnames'=>$listofnames, 'listofmimes'=>$mimetype);
2428  }
2429 
2440  public function setCategories($categories)
2441  {
2442  // Handle single category
2443  if (!is_array($categories)) {
2444  $categories = array($categories);
2445  }
2446 
2447  // Get current categories
2448  include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2449  $c = new Categorie($this->db);
2450  $existing = $c->containing($this->id, Categorie::TYPE_TICKET, 'id');
2451 
2452  // Diff
2453  if (is_array($existing)) {
2454  $to_del = array_diff($existing, $categories);
2455  $to_add = array_diff($categories, $existing);
2456  } else {
2457  $to_del = array(); // Nothing to delete
2458  $to_add = $categories;
2459  }
2460 
2461  // Process
2462  foreach ($to_del as $del) {
2463  if ($c->fetch($del) > 0) {
2464  $c->del_type($this, Categorie::TYPE_TICKET);
2465  }
2466  }
2467  foreach ($to_add as $add) {
2468  if ($c->fetch($add) > 0) {
2469  $c->add_type($this, Categorie::TYPE_TICKET);
2470  }
2471  }
2472 
2473  return 1;
2474  }
2475 
2486  public function newMessage($user, &$action, $private = 1, $public_area = 0)
2487  {
2488  global $mysoc, $conf, $langs;
2489 
2490  $error = 0;
2491 
2492  $object = new Ticket($this->db);
2493 
2494  $ret = $object->fetch('', '', GETPOST('track_id', 'alpha'));
2495 
2496  $object->socid = $object->fk_soc;
2497  $object->fetch_thirdparty();
2498  $object->fetch_project();
2499 
2500  if ($ret < 0) {
2501  $error++;
2502  array_push($this->errors, $langs->trans("ErrorTicketIsNotValid"));
2503  $action = '';
2504  }
2505 
2506  if (!GETPOST("message")) {
2507  $error++;
2508  array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("message")));
2509  $action = 'add_message';
2510  }
2511 
2512  if (!$error) {
2513  $object->subject = GETPOST('subject', 'alphanohtml');
2514  $object->message = GETPOST("message", "restricthtml");
2515  $object->private = GETPOST("private_message", "alpha");
2516 
2517  $send_email = GETPOST('send_email', 'int');
2518 
2519  // Copy attached files (saved into $_SESSION) as linked files to ticket. Return array with final name used.
2520  $resarray = $object->copyFilesForTicket();
2521  if (is_numeric($resarray) && $resarray == -1) {
2522  setEventMessages($object->error, $object->errors, 'errors');
2523  return -1;
2524  }
2525 
2526  $listofpaths = $resarray['listofpaths'];
2527  $listofnames = $resarray['listofnames'];
2528  $listofmimes = $resarray['listofmimes'];
2529 
2530  $id = $object->createTicketMessage($user, 0, $listofpaths, $listofmimes, $listofnames, $send_email);
2531  if ($id <= 0) {
2532  $error++;
2533  $this->error = $object->error;
2534  $this->errors = $object->errors;
2535  $action = 'add_message';
2536  }
2537 
2538  if (!$error && $id > 0) {
2539  setEventMessages($langs->trans('TicketMessageSuccessfullyAdded'), null, 'mesgs');
2540 
2541  //var_dump($_SESSION);
2542  //var_dump($listofpaths);exit;
2543 
2544  if (!empty($public_area)) {
2545  /*
2546  * Message created from the Public interface
2547  *
2548  * Send emails to assigned users (public area notification)
2549  */
2550  if (getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_ENABLED')) {
2551  // Retrieve internal contact datas
2552  $internal_contacts = $object->getInfosTicketInternalContact(1);
2553 
2554  $assigned_user_dont_have_email = '';
2555 
2556  $sendto = array();
2557 
2558  if ($this->fk_user_assign > 0) {
2559  $assigned_user = new User($this->db);
2560  $assigned_user->fetch($this->fk_user_assign);
2561  if (!empty($assigned_user->email)) {
2562  $sendto[$assigned_user->email] = $assigned_user->getFullName($langs)." <".$assigned_user->email.">";
2563  } else {
2564  $assigned_user_dont_have_email = $assigned_user->getFullName($langs);
2565  }
2566  }
2567 
2568  // Build array to display recipient list
2569  foreach ($internal_contacts as $key => $info_sendto) {
2570  // Avoid duplicate notifications
2571  if ($info_sendto['id'] == $user->id) {
2572  continue;
2573  }
2574 
2575  // We check if the email address is not the assignee's address to prevent notification from being sent twice
2576  if (!empty($info_sendto['email']) && $assigned_user->email != $info_sendto['email']) {
2577  $sendto[] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";
2578  }
2579  }
2580 
2581  if (empty($sendto)) {
2582  if (getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL')) {
2583  $sendto[getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL')] = getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL');
2584  } elseif (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2585  $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2586  }
2587  }
2588 
2589  // Add global email address recipient
2590  if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') &&
2591  getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)
2592  ) {
2593  $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2594  }
2595 
2596  if (!empty($sendto)) {
2597  $label_title = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2598  $subject = '['.$label_title.'- ticket #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2599 
2600  // Message send
2601  $message = $langs->trans('TicketMessageMailIntroText');
2602  $message .= '<br><br>';
2603  $messagePost = GETPOST('message', 'restricthtml');
2604  if (!dol_textishtml($messagePost)) {
2605  $messagePost = dol_nl2br($messagePost);
2606  }
2607  $message .= $messagePost;
2608 
2609  // Customer company infos
2610  $message .= '<br><br>';
2611  $message .= "==============================================";
2612  $message .= !empty($object->thirdparty->name) ? '<br>'.$langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';
2613  $message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';
2614  $message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';
2615 
2616  // Email send to
2617  $message .= '<br><br>';
2618  if (!empty($assigned_user_dont_have_email)) {
2619  $message .= '<br>'.$langs->trans('NoEMail').' : '.$assigned_user_dont_have_email;
2620  }
2621  foreach ($sendto as $val) {
2622  $message .= '<br>'.$langs->trans('TicketNotificationRecipient').' : '.$val;
2623  }
2624 
2625  // URL ticket
2626  $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;
2627  $message .= '<br><br>';
2628  $message .= $langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a>';
2629 
2630  $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2631  }
2632  }
2633  } else {
2634  /*
2635  * Message send from the Backoffice / Private area
2636  *
2637  * 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)
2638  */
2639  if ($send_email > 0) {
2640  // Retrieve internal contact datas
2641  $internal_contacts = $object->getInfosTicketInternalContact(1);
2642 
2643  $sendto = array();
2644  if (is_array($internal_contacts) && count($internal_contacts) > 0) {
2645  // Set default subject
2646  $label_title = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2647  $appli = $label_title;
2648  $subject = GETPOST('subject', 'alphanohtml') ? GETPOST('subject', 'alphanohtml') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2649 
2650  $message_intro = $langs->trans('TicketNotificationEmailBody', "#".$object->id);
2651  $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');
2652 
2653  $message = $langs->trans('TicketMessageMailIntroText');
2654  $message .= '<br><br>';
2655  $messagePost = GETPOST('message', 'restricthtml');
2656  if (!dol_textishtml($messagePost)) {
2657  $messagePost = dol_nl2br($messagePost);
2658  }
2659  $message .= $messagePost;
2660 
2661  // Data about customer
2662  $message .= '<br><br>';
2663  $message .= "==============================================<br>";
2664  $message .= !empty($object->thirdparty->name) ? $langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';
2665  $message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';
2666  $message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';
2667 
2668  // Build array to display recipient list
2669  foreach ($internal_contacts as $key => $info_sendto) {
2670  // Avoid duplicate notifications
2671  if ($info_sendto['id'] == $user->id) {
2672  continue;
2673  }
2674 
2675  if ($info_sendto['email'] != '') {
2676  if (!empty($info_sendto['email'])) {
2677  $sendto[$info_sendto['email']] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";
2678  }
2679 
2680  // Contact type
2681  $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1').' ('.strtolower($info_sendto['libelle']).')';
2682  $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');
2683  }
2684  }
2685  $message .= '<br>';
2686  // URL ticket
2687  $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;
2688 
2689  // Add html link on url
2690  $message .= '<br>'.$langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a><br>';
2691 
2692  // Add global email address recipient
2693  if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {
2694  if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2695  $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2696  }
2697  }
2698 
2699  // dont try to send email if no recipient
2700  if (!empty($sendto)) {
2701  $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2702  }
2703  }
2704 
2705  /*
2706  * Send emails for externals users if not private (linked contacts)
2707  */
2708  if (empty($object->private)) {
2709  // Retrieve email of all contacts (external)
2710  $external_contacts = $object->getInfosTicketExternalContact(1);
2711 
2712  // If no contact, get email from thirdparty
2713  if (is_array($external_contacts) && count($external_contacts) === 0) {
2714  if (!empty($object->fk_soc)) {
2715  $object->fetch_thirdparty($object->fk_soc);
2716  $array_company = array(array('firstname' => '', 'lastname' => $object->thirdparty->name, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
2717  $external_contacts = array_merge($external_contacts, $array_company);
2718  } elseif (empty($object->fk_soc) && !empty($object->origin_email)) {
2719  $array_external = array(array('firstname' => '', 'lastname' => $object->origin_email, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
2720  $external_contacts = array_merge($external_contacts, $array_external);
2721  }
2722  }
2723 
2724  $sendto = array();
2725  if (is_array($external_contacts) && count($external_contacts) > 0) {
2726  // Get default subject for email to external contacts
2727  $label_title = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2728  $appli = $mysoc->name;
2729  $subject = GETPOST('subject') ? GETPOST('subject') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2730 
2731  $message_intro = GETPOST('mail_intro') ? GETPOST('mail_intro', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO');
2732  $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');
2733  if (!dol_textishtml($message_intro)) {
2734  $message_intro = dol_nl2br($message_intro);
2735  }
2736  if (!dol_textishtml($message_signature)) {
2737  $message_signature = dol_nl2br($message_signature);
2738  }
2739 
2740  // We put intro after
2741  $messagePost = GETPOST('message', 'restricthtml');
2742  if (!dol_textishtml($messagePost)) {
2743  $messagePost = dol_nl2br($messagePost);
2744  }
2745  $message = $messagePost;
2746  $message .= '<br><br>';
2747 
2748  foreach ($external_contacts as $key => $info_sendto) {
2749  // altairis: avoid duplicate emails to external contacts
2750  if ($info_sendto['id'] == $user->contact_id) {
2751  continue;
2752  }
2753 
2754  if ($info_sendto['email'] != '' && $info_sendto['email'] != $object->origin_email) {
2755  if (!empty($info_sendto['email'])) {
2756  $sendto[$info_sendto['email']] = trim($info_sendto['firstname']." ".$info_sendto['lastname'])." <".$info_sendto['email'].">";
2757  }
2758 
2759  $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1').' ('.strtolower($info_sendto['libelle']).')';
2760  $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');
2761  }
2762  }
2763 
2764  // If public interface is not enable, use link to internal page into mail
2765  $url_public_ticket = getDolGlobalString('TICKET_URL_PUBLIC_INTERFACE', dol_buildpath('/public/ticket/view.php', 2)) . '/view.php?track_id='.$object->track_id;
2766  $message .= '<br>'.$langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer').' : <a href="'.$url_public_ticket.'">'.$object->track_id.'</a><br>';
2767 
2768  // Build final message
2769  $message = $message_intro.'<br><br>'.$message;
2770 
2771  // Add signature
2772  $message .= '<br>'.$message_signature;
2773 
2774  if (!empty($object->origin_email)) {
2775  $sendto[$object->origin_email] = $object->origin_email;
2776  }
2777 
2778  if ($object->fk_soc > 0 && !array_key_exists($object->origin_email, $sendto)) {
2779  $object->socid = $object->fk_soc;
2780  $object->fetch_thirdparty();
2781  if (!empty($object->thirdparty->email)) {
2782  $sendto[$object->thirdparty->email] = $object->thirdparty->email;
2783  }
2784  }
2785 
2786  // Add global email address recipient
2787  if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {
2788  if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2789  $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2790  }
2791  }
2792 
2793  // Dont try to send email when no recipient
2794  if (!empty($sendto)) {
2795  $result = $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2796  if ($result) {
2797  // update last_msg_sent date (for last message sent to external users)
2798  $this->date_last_msg_sent = dol_now();
2799  $this->update($user, 1); // disable trigger when updating date_last_msg_sent. sendTicketMessageByEmail already create an event in actioncomm table.
2800  }
2801  }
2802  }
2803  }
2804  }
2805  }
2806 
2807  // Set status to "answered" if not set yet, but only if internal user and not private message
2808  // Or set status to "answered" if the client has answered and if the ticket has started
2809  if (($object->status < self::STATUS_IN_PROGRESS && !$user->socid && !$private) ||
2810  ($object->status > self::STATUS_IN_PROGRESS && $public_area)
2811  ) {
2812  $object->setStatut(3);
2813  }
2814  return 1;
2815  } else {
2816  setEventMessages($object->error, $object->errors, 'errors');
2817  return -1;
2818  }
2819  } else {
2820  setEventMessages($this->error, $this->errors, 'errors');
2821  return -1;
2822  }
2823  }
2824 
2825 
2838  public function sendTicketMessageByEmail($subject, $message, $send_internal_cc = 0, $array_receiver = array(), $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array())
2839  {
2840  global $conf, $langs;
2841 
2842  if (getDolGlobalString('TICKET_DISABLE_ALL_MAILS')) {
2843  dol_syslog(get_class($this).'::sendTicketMessageByEmail: Emails are disable into ticket setup by option TICKET_DISABLE_ALL_MAILS', LOG_WARNING);
2844  return false;
2845  }
2846 
2847  $langs->load("mails");
2848 
2849  include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
2850  //$contactstatic = new Contact($this->db);
2851 
2852  // If no receiver defined, load all ticket linked contacts
2853  if (!is_array($array_receiver) || !count($array_receiver) > 0) {
2854  $array_receiver = $this->getInfosTicketInternalContact(1);
2855  $array_receiver = array_merge($array_receiver, $this->getInfosTicketExternalContact(1));
2856  }
2857 
2858  $sendtocc = "";
2859  if ($send_internal_cc) {
2860  $sendtocc = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
2861  }
2862 
2863  $from = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
2864  $is_sent = false;
2865  if (is_array($array_receiver) && count($array_receiver) > 0) {
2866  foreach ($array_receiver as $key => $receiver) {
2867  $deliveryreceipt = 0;
2868  $filepath = $filename_list;
2869  $filename = $mimefilename_list;
2870  $mimetype = $mimetype_list;
2871 
2872  // Send email
2873 
2874  $old_MAIN_MAIL_AUTOCOPY_TO = getDolGlobalString('MAIN_MAIL_AUTOCOPY_TO');
2875 
2876  if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
2877  $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
2878  }
2879 
2880  $upload_dir_tmp = $conf->user->dir_output."/".$user->id.'/temp';
2881 
2882  include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
2883  $trackid = "tic".$this->id;
2884 
2885  $moreinheader = 'X-Dolibarr-Info: sendTicketMessageByEmail'."\r\n";
2886  if (!empty($this->email_msgid)) {
2887  // We must also add 1 entry In-Reply-To: <$this->email_msgid> with Message-ID we respond from (See RFC5322).
2888  $moreinheader .= 'In-Reply-To: <'.$this->email_msgid.'>'."\r\n";
2889  }
2890 
2891  $mailfile = new CMailFile($subject, $receiver, $from, $message, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1, '', '', $trackid, $moreinheader, 'ticket', '', $upload_dir_tmp);
2892 
2893  if ($mailfile->error) {
2894  setEventMessages($mailfile->error, null, 'errors');
2895  } else {
2896  $result = $mailfile->sendfile();
2897  if ($result) {
2898  setEventMessages($langs->trans('MailSuccessfulySent', $mailfile->getValidAddress($from, 2), $mailfile->getValidAddress($receiver, 2)), null, 'mesgs');
2899  $is_sent = true;
2900  } else {
2901  $langs->load("other");
2902  if ($mailfile->error) {
2903  setEventMessages($langs->trans('ErrorFailedToSendMail', $from, $receiver), null, 'errors');
2904  dol_syslog($langs->trans('ErrorFailedToSendMail', $from, $receiver).' : '.$mailfile->error);
2905  } else {
2906  setEventMessages('No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS', null, 'errors');
2907  }
2908  }
2909  }
2910 
2911  if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
2912  $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
2913  }
2914  }
2915  } else {
2916  $langs->load("other");
2917  setEventMessages($langs->trans('ErrorMailRecipientIsEmptyForSendTicketMessage'), null, 'warnings');
2918  }
2919 
2920  return $is_sent;
2921  }
2922 
2923  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2931  public function load_board($user, $mode)
2932  {
2933  // phpcs:enable
2934  global $conf, $user, $langs;
2935 
2936  $now = dol_now();
2937  $delay_warning = 0;
2938 
2939  $this->nbtodo = $this->nbtodolate = 0;
2940  $clause = " WHERE";
2941 
2942  $sql = "SELECT p.rowid, p.ref, p.datec as datec";
2943  $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
2944  if (isModEnabled('societe') && empty($user->rights->societe->client->voir) && !$user->socid) {
2945  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2946  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2947  $clause = " AND";
2948  }
2949  $sql .= $clause." p.entity IN (".getEntity('ticket').")";
2950  if ($mode == 'opened') {
2951  $sql .= " AND p.fk_statut NOT IN (".Ticket::STATUS_CLOSED.", ".Ticket::STATUS_CANCELED.")";
2952  }
2953  if ($user->socid) {
2954  $sql .= " AND p.fk_soc = ".((int) $user->socid);
2955  }
2956 
2957  $resql = $this->db->query($sql);
2958  if ($resql) {
2959  $label = $labelShort = '';
2960  $status = '';
2961  if ($mode == 'opened') {
2962  $status = 'openall';
2963  //$delay_warning = $conf->ticket->warning_delay;
2964  $delay_warning = 0;
2965  $label = $langs->trans("MenuListNonClosed");
2966  $labelShort = $langs->trans("MenuListNonClosed");
2967  }
2968 
2969  $response = new WorkboardResponse();
2970  //$response->warning_delay = $delay_warning / 60 / 60 / 24;
2971  $response->label = $label;
2972  $response->labelShort = $labelShort;
2973  $response->url = DOL_URL_ROOT.'/ticket/list.php?search_fk_statut[]='.$status;
2974  $response->img = img_object('', "ticket");
2975 
2976  // This assignment in condition is not a bug. It allows walking the results.
2977  while ($obj = $this->db->fetch_object($resql)) {
2978  $response->nbtodo++;
2979  if ($mode == 'opened') {
2980  $datelimit = $this->db->jdate($obj->datec) + $delay_warning;
2981  if ($datelimit < $now) {
2982  //$response->nbtodolate++;
2983  }
2984  }
2985  }
2986  return $response;
2987  } else {
2988  $this->error = $this->db->lasterror();
2989  return -1;
2990  }
2991  }
2992 
2993  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2999  public function load_state_board()
3000  {
3001  // phpcs:enable
3002  global $conf, $user;
3003 
3004  $this->nb = array();
3005  $clause = "WHERE";
3006 
3007  $sql = "SELECT count(p.rowid) as nb";
3008  $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
3009  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3010  if (empty($user->rights->societe->client->voir) && !$user->socid) {
3011  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3012  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3013  $clause = "AND";
3014  }
3015  $sql .= " ".$clause." p.entity IN (".getEntity('ticket').")";
3016 
3017  $resql = $this->db->query($sql);
3018  if ($resql) {
3019  // This assignment in condition is not a bug. It allows walking the results.
3020  while ($obj = $this->db->fetch_object($resql)) {
3021  $this->nb["ticket"] = $obj->nb;
3022  }
3023  $this->db->free($resql);
3024  return 1;
3025  } else {
3026  dol_print_error($this->db);
3027  $this->error = $this->db->lasterror();
3028  return -1;
3029  }
3030  }
3031 
3040  public static function replaceThirdparty($db, $origin_id, $dest_id)
3041  {
3042  $tables = array('ticket');
3043 
3044  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3045  }
3046 
3054  public function getKanbanView($option = '', $arraydata = null)
3055  {
3056  global $langs;
3057 
3058  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3059 
3060  $return = '<div class="box-flex-item box-flex-grow-zero">';
3061  $return .= '<div class="info-box info-box-sm">';
3062  $return .= '<span class="info-box-icon bg-infobox-action">';
3063  $return .= img_picto('', $this->picto);
3064  $return .= '</span>';
3065  $return .= '<div class="info-box-content">';
3066  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
3067  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3068  if (!empty($arraydata['user_assignment'])) {
3069  $return .= '<br><span class="info-box-label" title="'.dol_escape_htmltag($langs->trans("AssignedTo")).'">'.$arraydata['user_assignment'].'</span>';
3070  }
3071  if (property_exists($this, 'type_code') && !empty($this->type_code)) {
3072  $return .= '<br>';
3073  $return .= '<div class="tdoverflowmax125 inline-block">'.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$this->type_code, 'c_ticket_type', 'code', 'label', $this->type_code).'</div>';
3074  }
3075  if (method_exists($this, 'getLibStatut')) {
3076  $return .= '<br><div class="info-box-status margintoponly">'.$this->getLibStatut(3).'</div>';
3077  }
3078  $return .= '</div>';
3079  $return .= '</div>';
3080  $return .= '</div>';
3081  return $return;
3082  }
3083 }
3084 
3085 
3090 {
3095  public $rowid;
3096 
3100  public $id;
3101 
3105  public $ref;
3106 
3110  public $track_id;
3111 
3115  public $fk_soc;
3116 
3120  public $fk_project;
3121 
3126 
3131 
3136 
3140  public $subject;
3141 
3145  public $message;
3146 
3150  public $fk_statut;
3151 
3155  public $resolution;
3156 
3160  public $progress;
3161 
3165  public $timing;
3166 
3170  public $type_code;
3171 
3176 
3181 
3185  public $type_label;
3186 
3191 
3196 
3200  public $datec = '';
3201 
3205  public $date_read = '';
3206 
3210  public $date_last_msg_sent = '';
3211 
3215  public $date_close = '';
3216 }
$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.
create($user, $notrigger=0)
Create object into database.
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.
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.
Definition: user.class.php:48
if(isModEnabled('facture') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $sql
Social contributions to pay.
Definition: index.php:746
if($cancel &&! $id) if($action=='add' &&! $cancel) if($action=='delete') if($id) $form
Actions.
Definition: card.php:143
dirbasename($pathfile)
Return the relative dirname (relative to DOL_DATA_ROOT) of a full path string.
Definition: files.lib.php:3387
dol_move($srcfile, $destfile, $newmask=0, $overwriteifexists=1, $testvirus=0, $indexdatabase=1, $moreinfo=array())
Move a file into another name.
Definition: files.lib.php:948
dol_is_file($pathoffile)
Return if path is a file.
Definition: files.lib.php:483
dol_is_dir($folder)
Test if filename is a directory.
Definition: files.lib.php:453
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).
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
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)
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...
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
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.
isModEnabled($module)
Is Dolibarr module enabled.
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).
Definition: images.lib.php:509
image_format_supported($file, $acceptsvg=0)
Return if a filename is file name of a supported image format.
Definition: images.lib.php:80
Class to generate the form for creating a new ticket.
generate_random_id($car=16)
Generate a random id.
Definition: ticket.lib.php:194