dolibarr  17.0.4
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-2022 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 $notify_tiers_at_create;
205 
209  public $email_msgid;
210 
214  public $email_date;
215 
219  public $ip;
220 
224  public $oldcopy;
225 
229  public $lines;
230 
231 
235  public $regeximgext = '\.gif|\.jpg|\.jpeg|\.png|\.bmp|\.webp|\.xpm|\.xbm'; // See also into images.lib.php
236 
240  const STATUS_NOT_READ = 0;
241  const STATUS_READ = 1;
242  const STATUS_ASSIGNED = 2;
243  const STATUS_IN_PROGRESS = 3;
244  const STATUS_NEED_MORE_INFO = 5; // waiting requester feedback
245  const STATUS_WAITING = 7; // on hold
246  const STATUS_CLOSED = 8; // Closed - Solved
247  const STATUS_CANCELED = 9; // Closed - Not solved
248 
249 
276  // BEGIN MODULEBUILDER PROPERTIES
277  public $fields = array(
278  'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'visible'=>-2, 'enabled'=>1, 'position'=>1, 'notnull'=>1, 'index'=>1, 'comment'=>"Id"),
279  'entity' => array('type'=>'integer', 'label'=>'Entity', 'visible'=>0, 'enabled'=>1, 'position'=>5, 'notnull'=>1, 'index'=>1),
280  '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),
281  'track_id' => array('type'=>'varchar(255)', 'label'=>'TicketTrackId', 'visible'=>-2, 'enabled'=>1, 'position'=>11, 'notnull'=>-1, 'searchall'=>1, 'help'=>"Help text"),
282  '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'),
283  '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'),
284  'subject' => array('type'=>'varchar(255)', 'label'=>'Subject', 'visible'=>1, 'enabled'=>1, 'position'=>18, 'notnull'=>-1, 'searchall'=>1, 'help'=>"", 'css'=>'maxwidth200 tdoverflowmax200', 'autofocusoncreate'=>1),
285  'type_code' => array('type'=>'varchar(32)', 'label'=>'Type', 'visible'=>1, 'enabled'=>1, 'position'=>20, 'notnull'=>-1, 'help'=>"", 'csslist'=>'maxwidth125 tdoverflowmax50'),
286  'category_code' => array('type'=>'varchar(32)', 'label'=>'TicketCategory', 'visible'=>-1, 'enabled'=>1, 'position'=>21, 'notnull'=>-1, 'help'=>"", 'css'=>'maxwidth100 tdoverflowmax200'),
287  'severity_code' => array('type'=>'varchar(32)', 'label'=>'Severity', 'visible'=>1, 'enabled'=>1, 'position'=>22, 'notnull'=>-1, 'help'=>"", 'css'=>'maxwidth100'),
288  'fk_soc' => array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'visible'=>1, 'enabled'=>'$conf->societe->enabled', 'position'=>50, 'notnull'=>-1, 'index'=>1, 'searchall'=>1, 'help'=>"OrganizationEventLinkToThirdParty", 'css'=>'tdoverflowmax150 maxwidth150onsmartphone'),
289  'notify_tiers_at_create' => array('type'=>'integer', 'label'=>'NotifyThirdparty', 'visible'=>-1, 'enabled'=>0, 'position'=>51, 'notnull'=>1, 'index'=>1),
290  '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"),
291  //'timing' => array('type'=>'varchar(20)', 'label'=>'Timing', 'visible'=>-1, 'enabled'=>1, 'position'=>42, 'notnull'=>-1, 'help'=>""), // what is this ?
292  'datec' => array('type'=>'datetime', 'label'=>'DateCreation', 'visible'=>1, 'enabled'=>1, 'position'=>500, 'notnull'=>1, 'csslist'=>'nowraponall'),
293  'date_read' => array('type'=>'datetime', 'label'=>'TicketReadOn', 'visible'=>-1, 'enabled'=>1, 'position'=>501, 'notnull'=>1),
294  'date_last_msg_sent' => array('type'=>'datetime', 'label'=>'TicketLastMessageDate', 'visible'=>0, 'enabled'=>1, 'position'=>502, 'notnull'=>-1),
295  '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'),
296  'date_close' => array('type'=>'datetime', 'label'=>'TicketCloseOn', 'visible'=>-1, 'enabled'=>1, 'position'=>510, 'notnull'=>1),
297  'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'visible'=>-1, 'enabled'=>1, 'position'=>520, 'notnull'=>1),
298  'message' => array('type'=>'text', 'label'=>'Message', 'visible'=>-2, 'enabled'=>1, 'position'=>540, 'notnull'=>-1,),
299  'email_msgid' => array('type'=>'varchar(255)', 'label'=>'EmailMsgID', 'visible'=>-2, 'enabled'=>1, 'position'=>540, 'notnull'=>-1, 'help'=>'EmailMsgIDDesc'),
300  'email_date' => array('type'=>'datetime', 'label'=>'EmailDate', 'visible'=>-2, 'enabled'=>1, 'position'=>541),
301  'progress' => array('type'=>'integer', 'label'=>'Progression', 'visible'=>-1, 'enabled'=>1, 'position'=>540, 'notnull'=>-1, 'css'=>'right', 'help'=>"", 'isameasure'=>2, 'csslist'=>'width50'),
302  'resolution' => array('type'=>'integer', 'label'=>'Resolution', 'visible'=>-1, 'enabled'=>'getDolGlobalString("TICKET_ENABLE_RESOLUTION")', 'position'=>550, 'notnull'=>1),
303  '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')),
304  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>900),
305  );
306  // END MODULEBUILDER PROPERTIES
307 
308 
314  public function __construct($db)
315  {
316  global $conf;
317 
318  $this->db = $db;
319 
320  $this->statuts_short = array(
321  self::STATUS_NOT_READ => 'Unread',
322  self::STATUS_READ => 'Read',
323  self::STATUS_ASSIGNED => 'Assigned',
324  self::STATUS_IN_PROGRESS => 'InProgress',
325  self::STATUS_WAITING => 'OnHold',
326  self::STATUS_NEED_MORE_INFO => 'NeedMoreInformationShort',
327  self::STATUS_CLOSED => 'SolvedClosed',
328  self::STATUS_CANCELED => 'Canceled'
329  );
330  $this->statuts = array(
331  self::STATUS_NOT_READ => 'Unread',
332  self::STATUS_READ => 'Read',
333  self::STATUS_ASSIGNED => 'Assigned',
334  self::STATUS_IN_PROGRESS => 'InProgress',
335  self::STATUS_WAITING => 'OnHold',
336  self::STATUS_NEED_MORE_INFO => 'NeedMoreInformation',
337  self::STATUS_CLOSED => 'SolvedClosed',
338  self::STATUS_CANCELED => 'Canceled'
339  );
340  }
341 
348  private function verify()
349  {
350  $this->errors = array();
351 
352  $result = 0;
353 
354  // Clean parameters
355  if (isset($this->ref)) {
356  $this->ref = trim($this->ref);
357  }
358 
359  if (isset($this->track_id)) {
360  $this->track_id = trim($this->track_id);
361  }
362 
363  if (isset($this->fk_soc)) {
364  $this->fk_soc = (int) $this->fk_soc;
365  }
366 
367  if (isset($this->fk_project)) {
368  $this->fk_project = (int) $this->fk_project;
369  }
370 
371  if (isset($this->origin_email)) {
372  $this->origin_email = trim($this->origin_email);
373  }
374 
375  if (isset($this->fk_user_create)) {
376  $this->fk_user_create = (int) $this->fk_user_create;
377  }
378 
379  if (isset($this->fk_user_assign)) {
380  $this->fk_user_assign = (int) $this->fk_user_assign;
381  }
382 
383  if (isset($this->subject)) {
384  $this->subject = trim($this->subject);
385  }
386 
387  if (isset($this->message)) {
388  $this->message = trim($this->message);
389  }
390 
391  if (isset($this->fk_statut)) {
392  $this->fk_statut = (int) $this->fk_statut;
393  }
394 
395  if (isset($this->resolution)) {
396  $this->resolution = trim($this->resolution);
397  }
398 
399  if (isset($this->progress)) {
400  $this->progress = trim($this->progress);
401  }
402 
403  if (isset($this->timing)) {
404  $this->timing = trim($this->timing);
405  }
406 
407  if (isset($this->type_code)) {
408  $this->type_code = trim($this->type_code);
409  }
410 
411  if (isset($this->category_code)) {
412  $this->category_code = trim($this->category_code);
413  }
414 
415  if (isset($this->severity_code)) {
416  $this->severity_code = trim($this->severity_code);
417  }
418 
419  if (empty($this->ref)) {
420  $this->errors[] = 'ErrorTicketRefRequired';
421  dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR);
422  $result = -1;
423  }
424 
425  return $result;
426  }
427 
435  public function create($user, $notrigger = 0)
436  {
437  global $conf, $langs;
438  $error = 0;
439 
440  // Clean parameters
441  $this->datec = dol_now();
442  if (empty($this->track_id)) {
443  $this->track_id = generate_random_id(16);
444  }
445 
446  // Check more parameters
447  // If error, this->errors[] is filled
448  $result = $this->verify();
449 
450  if ($result >= 0) {
451  $this->entity = ((isset($this->entity) && is_numeric($this->entity)) ? $this->entity : $conf->entity);
452 
453  // Insert request
454  $sql = "INSERT INTO ".MAIN_DB_PREFIX."ticket(";
455  $sql .= "ref,";
456  $sql .= "track_id,";
457  $sql .= "fk_soc,";
458  $sql .= "fk_project,";
459  $sql .= "origin_email,";
460  $sql .= "fk_user_create,";
461  $sql .= "fk_user_assign,";
462  $sql .= "email_msgid,";
463  $sql .= "email_date,";
464  $sql .= "subject,";
465  $sql .= "message,";
466  $sql .= "fk_statut,";
467  $sql .= "resolution,";
468  $sql .= "progress,";
469  $sql .= "timing,";
470  $sql .= "type_code,";
471  $sql .= "category_code,";
472  $sql .= "severity_code,";
473  $sql .= "datec,";
474  $sql .= "date_read,";
475  $sql .= "date_close,";
476  $sql .= "entity,";
477  $sql .= "notify_tiers_at_create,";
478  $sql .= "ip";
479  $sql .= ") VALUES (";
480  $sql .= " ".(!isset($this->ref) ? '' : "'".$this->db->escape($this->ref)."'").",";
481  $sql .= " ".(!isset($this->track_id) ? 'NULL' : "'".$this->db->escape($this->track_id)."'").",";
482  $sql .= " ".($this->fk_soc > 0 ? $this->db->escape($this->fk_soc) : "null").",";
483  $sql .= " ".($this->fk_project > 0 ? $this->db->escape($this->fk_project) : "null").",";
484  $sql .= " ".(!isset($this->origin_email) ? 'NULL' : "'".$this->db->escape($this->origin_email)."'").",";
485  $sql .= " ".($this->fk_user_create > 0 ? $this->fk_user_create : ($user->id > 0 ? $user->id : 'NULL')).",";
486  $sql .= " ".($this->fk_user_assign > 0 ? $this->fk_user_assign : 'NULL').",";
487  $sql .= " ".(empty($this->email_msgid) ? 'NULL' : "'".$this->db->escape($this->email_msgid)."'").",";
488  $sql .= " ".(empty($this->email_date) ? 'NULL' : "'".$this->db->idate($this->email_date)."'").",";
489  $sql .= " ".(!isset($this->subject) ? 'NULL' : "'".$this->db->escape($this->subject)."'").",";
490  $sql .= " ".(!isset($this->message) ? 'NULL' : "'".$this->db->escape($this->message)."'").",";
491  $sql .= " ".(!isset($this->fk_statut) ? '0' : "'".$this->db->escape($this->fk_statut)."'").",";
492  $sql .= " ".(!isset($this->resolution) ? 'NULL' : "'".$this->db->escape($this->resolution)."'").",";
493  $sql .= " ".(!isset($this->progress) ? '0' : "'".$this->db->escape($this->progress)."'").",";
494  $sql .= " ".(!isset($this->timing) ? 'NULL' : "'".$this->db->escape($this->timing)."'").",";
495  $sql .= " ".(!isset($this->type_code) ? 'NULL' : "'".$this->db->escape($this->type_code)."'").",";
496  $sql .= " ".(empty($this->category_code) || $this->category_code == '-1' ? 'NULL' : "'".$this->db->escape($this->category_code)."'").",";
497  $sql .= " ".(!isset($this->severity_code) ? 'NULL' : "'".$this->db->escape($this->severity_code)."'").",";
498  $sql .= " ".(!isset($this->datec) || dol_strlen($this->datec) == 0 ? 'NULL' : "'".$this->db->idate($this->datec)."'").",";
499  $sql .= " ".(!isset($this->date_read) || dol_strlen($this->date_read) == 0 ? 'NULL' : "'".$this->db->idate($this->date_read)."'").",";
500  $sql .= " ".(!isset($this->date_close) || dol_strlen($this->date_close) == 0 ? 'NULL' : "'".$this->db->idate($this->date_close)."'")."";
501  $sql .= ", ".((int) $this->entity);
502  $sql .= ", ".(!isset($this->notify_tiers_at_create) ? '1' : "'".$this->db->escape($this->notify_tiers_at_create)."'");
503  $sql .= ", ".(!isset($this->ip) ? 'NULL' : "'".$this->db->escape($this->ip)."'");
504  $sql .= ")";
505 
506  $this->db->begin();
507 
508  dol_syslog(get_class($this)."::create", LOG_DEBUG);
509  $resql = $this->db->query($sql);
510  if (!$resql) {
511  $error++;
512  $this->errors[] = "Error ".$this->db->lasterror();
513  }
514 
515  if (!$error) {
516  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."ticket");
517  }
518 
519  if (!$error && !empty($conf->global->TICKET_ADD_AUTHOR_AS_CONTACT)) {
520  // add creator as contributor
521  if ($this->add_contact($user->id, 'CONTRIBUTOR', 'internal') < 0) {
522  $error++;
523  }
524  }
525 
526  if (!$error && $this->fk_user_assign > 0) {
527  if ($this->add_contact($this->fk_user_assign, 'SUPPORTTEC', 'internal') < 0) {
528  $error++;
529  }
530  }
531 
532 
533  //Update extrafield
534  if (!$error) {
535  $result = $this->insertExtraFields();
536  if ($result < 0) {
537  $error++;
538  }
539  }
540 
541  if (!$error && !$notrigger) {
542  // Call trigger
543  $result = $this->call_trigger('TICKET_CREATE', $user);
544  if ($result < 0) {
545  $error++;
546  }
547  // End call triggers
548  }
549 
550  // Commit or rollback
551  if ($error) {
552  foreach ($this->errors as $errmsg) {
553  dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
554  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
555  }
556  $this->db->rollback();
557  return -1 * $error;
558  } else {
559  $this->db->commit();
560  return $this->id;
561  }
562  } else {
563  $this->db->rollback();
564  dol_syslog(get_class($this)."::Create fails verify ".join(',', $this->errors), LOG_WARNING);
565  return -3;
566  }
567  }
568 
578  public function fetch($id = '', $ref = '', $track_id = '', $email_msgid = '')
579  {
580  global $langs;
581 
582  // Check parameters
583  if (empty($id) && empty($ref) && empty($track_id) && empty($email_msgid)) {
584  $this->error = 'ErrorWrongParameters';
585  dol_print_error('', get_class($this)."::fetch ".$this->error);
586  return -1;
587  }
588 
589  $sql = "SELECT";
590  $sql .= " t.rowid,";
591  $sql .= " t.entity,";
592  $sql .= " t.ref,";
593  $sql .= " t.track_id,";
594  $sql .= " t.fk_soc,";
595  $sql .= " t.fk_project,";
596  $sql .= " t.origin_email,";
597  $sql .= " t.fk_user_create,";
598  $sql .= " t.fk_user_assign,";
599  $sql .= " t.email_msgid,";
600  $sql .= " t.email_date,";
601  $sql .= " t.subject,";
602  $sql .= " t.message,";
603  $sql .= " t.fk_statut as status,";
604  $sql .= " t.resolution,";
605  $sql .= " t.progress,";
606  $sql .= " t.timing,";
607  $sql .= " t.type_code,";
608  $sql .= " t.category_code,";
609  $sql .= " t.severity_code,";
610  $sql .= " t.datec,";
611  $sql .= " t.date_read,";
612  $sql .= " t.date_last_msg_sent,";
613  $sql .= " t.date_close,";
614  $sql .= " t.tms,";
615  $sql .= " t.ip,";
616  $sql .= " type.label as type_label, category.label as category_label, severity.label as severity_label";
617  $sql .= " FROM ".MAIN_DB_PREFIX."ticket as t";
618  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_type as type ON type.code=t.type_code";
619  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_category as category ON category.code=t.category_code";
620  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_severity as severity ON severity.code=t.severity_code";
621 
622  if ($id) {
623  $sql .= " WHERE t.rowid = ".((int) $id);
624  } else {
625  $sql .= " WHERE t.entity IN (".getEntity($this->element, 1).")";
626  if (!empty($ref)) {
627  $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
628  } elseif ($track_id) {
629  $sql .= " AND t.track_id = '".$this->db->escape($track_id)."'";
630  } else {
631  $sql .= " AND t.email_msgid = '".$this->db->escape($email_msgid)."'";
632  }
633  }
634 
635  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
636  $resql = $this->db->query($sql);
637  if ($resql) {
638  if ($this->db->num_rows($resql)) {
639  $obj = $this->db->fetch_object($resql);
640 
641  $this->id = $obj->rowid;
642  $this->entity = $obj->entity;
643  $this->ref = $obj->ref;
644  $this->track_id = $obj->track_id;
645  $this->fk_soc = $obj->fk_soc;
646  $this->socid = $obj->fk_soc; // for fetch_thirdparty() method
647  $this->fk_project = $obj->fk_project;
648  $this->origin_email = $obj->origin_email;
649  $this->fk_user_create = $obj->fk_user_create;
650  $this->fk_user_assign = $obj->fk_user_assign;
651  $this->email_msgid = $obj->email_msgid;
652  $this->email_date = $this->db->jdate($obj->email_date);
653  $this->subject = $obj->subject;
654  $this->message = $obj->message;
655  $this->ip = $obj->ip;
656 
657  $this->status = $obj->status;
658  $this->fk_statut = $this->status; // For backward compatibility
659 
660  $this->resolution = $obj->resolution;
661  $this->progress = $obj->progress;
662  $this->timing = $obj->timing;
663 
664  $this->type_code = $obj->type_code;
665  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
666  $label_type = ($langs->trans("TicketTypeShort".$obj->type_code) != ("TicketTypeShort".$obj->type_code) ? $langs->trans("TicketTypeShort".$obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));
667  $this->type_label = $label_type;
668 
669  $this->category_code = $obj->category_code;
670  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
671  $label_category = ($langs->trans("TicketCategoryShort".$obj->category_code) != ("TicketCategoryShort".$obj->category_code) ? $langs->trans("TicketCategoryShort".$obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));
672  $this->category_label = $label_category;
673 
674  $this->severity_code = $obj->severity_code;
675  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
676  $label_severity = ($langs->trans("TicketSeverityShort".$obj->severity_code) != ("TicketSeverityShort".$obj->severity_code) ? $langs->trans("TicketSeverityShort".$obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));
677  $this->severity_label = $label_severity;
678 
679  $this->datec = $this->db->jdate($obj->datec);
680  $this->date_creation = $this->db->jdate($obj->datec);
681  $this->date_read = $this->db->jdate($obj->date_read);
682  $this->date_validation = $this->db->jdate($obj->date_read);
683  $this->date_last_msg_sent = $this->db->jdate($obj->date_last_msg_sent);
684  $this->date_close = $this->db->jdate($obj->date_close);
685  $this->tms = $this->db->jdate($obj->tms);
686  $this->date_modification = $this->db->jdate($obj->tms);
687 
688  $this->fetch_optionals();
689 
690  $this->db->free($resql);
691  return 1;
692  } else {
693  return 0;
694  }
695  } else {
696  $this->error = "Error ".$this->db->lasterror();
697  dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
698  return -1;
699  }
700  }
701 
715  public function fetchAll($user, $sortorder = 'ASC', $sortfield = 't.datec', $limit = '', $offset = 0, $arch = '', $filter = '')
716  {
717  global $langs;
718 
719  $extrafields = new ExtraFields($this->db);
720 
721  // fetch optionals attributes and labels
722  $extrafields->fetch_name_optionals_label($this->table_element);
723 
724  $sql = "SELECT";
725  $sql .= " t.rowid,";
726  $sql .= " t.ref,";
727  $sql .= " t.track_id,";
728  $sql .= " t.fk_soc,";
729  $sql .= " t.fk_project,";
730  $sql .= " t.origin_email,";
731  $sql .= " t.fk_user_create, uc.lastname as user_create_lastname, uc.firstname as user_create_firstname,";
732  $sql .= " t.fk_user_assign, ua.lastname as user_assign_lastname, ua.firstname as user_assign_firstname,";
733  $sql .= " t.subject,";
734  $sql .= " t.message,";
735  $sql .= " t.fk_statut,";
736  $sql .= " t.resolution,";
737  $sql .= " t.progress,";
738  $sql .= " t.timing,";
739  $sql .= " t.type_code,";
740  $sql .= " t.category_code,";
741  $sql .= " t.severity_code,";
742  $sql .= " t.datec,";
743  $sql .= " t.date_read,";
744  $sql .= " t.date_last_msg_sent,";
745  $sql .= " t.date_close,";
746  $sql .= " t.tms";
747  $sql .= ", type.label as type_label, category.label as category_label, severity.label as severity_label";
748  // Add fields for extrafields
749  foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
750  $sql .= ($extrafields->attributes[$this->table_element]['type'][$key] != 'separate' ? ",ef.".$key." as options_".$key : '');
751  }
752  $sql .= " FROM ".MAIN_DB_PREFIX."ticket as t";
753  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_type as type ON type.code=t.type_code";
754  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_category as category ON category.code=t.category_code";
755  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_severity as severity ON severity.code=t.severity_code";
756  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid=t.fk_soc";
757  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as uc ON uc.rowid=t.fk_user_create";
758  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as ua ON ua.rowid=t.fk_user_assign";
759  if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {
760  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."ticket_extrafields as ef on (t.rowid = ef.fk_object)";
761  }
762  if (empty($user->rights->societe->client->voir) && !$user->socid) {
763  $sql .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
764  }
765 
766  $sql .= " WHERE t.entity IN (".getEntity('ticket').")";
767 
768  // Manage filter
769  if (!empty($filter)) {
770  foreach ($filter as $key => $value) {
771  if (strpos($key, 'date')) { // To allow $filter['YEAR(s.dated)']=>$year
772  $sql .= " AND ".$key." = '".$this->db->escape($value)."'";
773  } elseif (($key == 't.fk_user_assign') || ($key == 't.type_code') || ($key == 't.category_code') || ($key == 't.severity_code') || ($key == 't.fk_soc')) {
774  $sql .= " AND ".$key." = '".$this->db->escape($value)."'";
775  } elseif ($key == 't.fk_statut') {
776  if (is_array($value) && count($value) > 0) {
777  $sql .= " AND ".$key." IN (".$this->db->sanitize(implode(',', $value)).")";
778  } else {
779  $sql .= " AND ".$key.' = '.((int) $value);
780  }
781  } else {
782  $sql .= " AND ".$key." LIKE '%".$this->db->escape($value)."%'";
783  }
784  }
785  }
786  if (empty($user->rights->societe->client->voir) && !$user->socid) {
787  $sql .= " AND t.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
788  } elseif ($user->socid) {
789  $sql .= " AND t.fk_soc = ".((int) $user->socid);
790  }
791 
792  $sql .= $this->db->order($sortfield, $sortorder);
793  if (!empty($limit)) {
794  $sql .= $this->db->plimit($limit + 1, $offset);
795  }
796 
797  dol_syslog(get_class($this)."::fetchAll", LOG_DEBUG);
798  $resql = $this->db->query($sql);
799 
800  if ($resql) {
801  $this->lines = array();
802 
803  $num = $this->db->num_rows($resql);
804  $i = 0;
805 
806  if ($num) {
807  while ($i < $num) {
808  $obj = $this->db->fetch_object($resql);
809 
810  $line = new TicketsLine();
811 
812  $line->id = $obj->rowid;
813  $line->rowid = $obj->rowid;
814  $line->ref = $obj->ref;
815  $line->track_id = $obj->track_id;
816  $line->fk_soc = $obj->fk_soc;
817  $line->fk_project = $obj->fk_project;
818  $line->origin_email = $obj->origin_email;
819 
820  $line->fk_user_create = $obj->fk_user_create;
821  $line->user_create_lastname = $obj->user_create_lastname;
822  $line->user_create_firstname = $obj->user_create_firstname;
823 
824  $line->fk_user_assign = $obj->fk_user_assign;
825  $line->user_assign_lastname = $obj->user_assign_lastname;
826  $line->user_assign_firstname = $obj->user_assign_firstname;
827 
828  $line->subject = $obj->subject;
829  $line->message = $obj->message;
830  $line->fk_statut = $obj->fk_statut;
831  $line->resolution = $obj->resolution;
832  $line->progress = $obj->progress;
833  $line->timing = $obj->timing;
834 
835  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
836  $label_type = ($langs->trans("TicketTypeShort".$obj->type_code) != ("TicketTypeShort".$obj->type_code) ? $langs->trans("TicketTypeShort".$obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));
837  $line->type_label = $label_type;
838 
839  $this->category_code = $obj->category_code;
840  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
841  $label_category = ($langs->trans("TicketCategoryShort".$obj->category_code) != ("TicketCategoryShort".$obj->category_code) ? $langs->trans("TicketCategoryShort".$obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));
842  $line->category_label = $label_category;
843 
844  $this->severity_code = $obj->severity_code;
845  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
846  $label_severity = ($langs->trans("TicketSeverityShort".$obj->severity_code) != ("TicketSeverityShort".$obj->severity_code) ? $langs->trans("TicketSeverityShort".$obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));
847  $line->severity_label = $label_severity;
848 
849  $line->datec = $this->db->jdate($obj->datec);
850  $line->date_read = $this->db->jdate($obj->date_read);
851  $line->date_last_msg_sent = $this->db->jdate($obj->date_last_msg_sent);
852  $line->date_close = $this->db->jdate($obj->date_close);
853 
854  // Extra fields
855  if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {
856  foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
857  $tmpkey = 'options_'.$key;
858  $line->{$tmpkey} = $obj->$tmpkey;
859  }
860  }
861 
862  $this->lines[$i] = $line;
863  $i++;
864  }
865  }
866  $this->db->free($resql);
867  return $num;
868  } else {
869  $this->error = "Error ".$this->db->lasterror();
870  dol_syslog(get_class($this)."::fetchAll ".$this->error, LOG_ERR);
871  return -1;
872  }
873  }
874 
882  public function update($user = 0, $notrigger = 0)
883  {
884  global $conf, $langs, $hookmanager;
885  $error = 0;
886 
887  // $this->oldcopy should have been set by the caller of update (here properties were already modified)
888  //if (empty($this->oldcopy)) {
889  // $this->oldcopy = dol_clone($this);
890  //}
891 
892  // Clean parameters
893  if (isset($this->ref)) {
894  $this->ref = trim($this->ref);
895  }
896 
897  if (isset($this->track_id)) {
898  $this->track_id = trim($this->track_id);
899  }
900 
901  if (isset($this->fk_soc)) {
902  $this->fk_soc = (int) $this->fk_soc;
903  }
904 
905  if (isset($this->fk_project)) {
906  $this->fk_project = (int) $this->fk_project;
907  }
908 
909  if (isset($this->origin_email)) {
910  $this->origin_email = trim($this->origin_email);
911  }
912 
913  if (isset($this->fk_user_create)) {
914  $this->fk_user_create = (int) $this->fk_user_create;
915  }
916 
917  if (isset($this->fk_user_assign)) {
918  $this->fk_user_assign = (int) $this->fk_user_assign;
919  }
920 
921  if (isset($this->subject)) {
922  $this->subject = trim($this->subject);
923  }
924 
925  if (isset($this->message)) {
926  $this->message = trim($this->message);
927  }
928 
929  if (isset($this->fk_statut)) {
930  $this->fk_statut = (int) $this->fk_statut;
931  }
932 
933  if (isset($this->resolution)) {
934  $this->resolution = trim($this->resolution);
935  }
936 
937  if (isset($this->progress)) {
938  $this->progress = trim($this->progress);
939  }
940 
941  if (isset($this->timing)) {
942  $this->timing = trim($this->timing);
943  }
944 
945  if (isset($this->type_code)) {
946  $this->timing = trim($this->type_code);
947  }
948 
949  if (isset($this->category_code)) {
950  $this->timing = trim($this->category_code);
951  }
952 
953  if (isset($this->severity_code)) {
954  $this->timing = trim($this->severity_code);
955  }
956 
957  // Check parameters
958  // Put here code to add a control on parameters values
959  // Update request
960  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket SET";
961  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "").",";
962  $sql .= " track_id=".(isset($this->track_id) ? "'".$this->db->escape($this->track_id)."'" : "null").",";
963  $sql .= " fk_soc=".(isset($this->fk_soc) ? "'".$this->db->escape($this->fk_soc)."'" : "null").",";
964  $sql .= " fk_project=".(isset($this->fk_project) ? "'".$this->db->escape($this->fk_project)."'" : "null").",";
965  $sql .= " origin_email=".(isset($this->origin_email) ? "'".$this->db->escape($this->origin_email)."'" : "null").",";
966  $sql .= " fk_user_create=".(isset($this->fk_user_create) ? $this->fk_user_create : "null").",";
967  $sql .= " fk_user_assign=".(isset($this->fk_user_assign) ? $this->fk_user_assign : "null").",";
968  $sql .= " subject=".(isset($this->subject) ? "'".$this->db->escape($this->subject)."'" : "null").",";
969  $sql .= " message=".(isset($this->message) ? "'".$this->db->escape($this->message)."'" : "null").",";
970  $sql .= " fk_statut=".(isset($this->fk_statut) ? $this->fk_statut : "null").",";
971  $sql .= " resolution=".(isset($this->resolution) ? $this->resolution : "null").",";
972  $sql .= " progress=".(isset($this->progress) ? "'".$this->db->escape($this->progress)."'" : "null").",";
973  $sql .= " timing=".(isset($this->timing) ? "'".$this->db->escape($this->timing)."'" : "null").",";
974  $sql .= " type_code=".(isset($this->type_code) ? "'".$this->db->escape($this->type_code)."'" : "null").",";
975  $sql .= " category_code=".(isset($this->category_code) ? "'".$this->db->escape($this->category_code)."'" : "null").",";
976  $sql .= " severity_code=".(isset($this->severity_code) ? "'".$this->db->escape($this->severity_code)."'" : "null").",";
977  $sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
978  $sql .= " date_read=".(dol_strlen($this->date_read) != 0 ? "'".$this->db->idate($this->date_read)."'" : 'null').",";
979  $sql .= " date_last_msg_sent=".(dol_strlen($this->date_last_msg_sent) != 0 ? "'".$this->db->idate($this->date_last_msg_sent)."'" : 'null').",";
980  $sql .= " date_close=".(dol_strlen($this->date_close) != 0 ? "'".$this->db->idate($this->date_close)."'" : 'null')."";
981  $sql .= " WHERE rowid=".((int) $this->id);
982 
983  $this->db->begin();
984 
985  $resql = $this->db->query($sql);
986  if (!$resql) {
987  $error++;
988  $this->errors[] = "Error ".$this->db->lasterror();
989  }
990 
991  if (!$error) {
992  // Update extrafields
993  $result = $this->insertExtraFields();
994  if ($result < 0) {
995  $error++;
996  }
997  }
998 
999  if (!$error && !$notrigger) {
1000  // Call trigger
1001  $result = $this->call_trigger('TICKET_MODIFY', $user);
1002  if ($result < 0) {
1003  $error++;
1004  }
1005  // End call triggers
1006  }
1007 
1008  // Commit or rollback
1009  if ($error) {
1010  foreach ($this->errors as $errmsg) {
1011  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1012  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1013  }
1014  $this->db->rollback();
1015  return -1 * $error;
1016  } else {
1017  $this->db->commit();
1018  return 1;
1019  }
1020  }
1021 
1029  public function delete($user, $notrigger = 0)
1030  {
1031  global $conf, $langs;
1032  $error = 0;
1033 
1034  $this->db->begin();
1035 
1036  if (!$error) {
1037  if (!$notrigger) {
1038  // Call trigger
1039  $result = $this->call_trigger('TICKET_DELETE', $user);
1040  if ($result < 0) {
1041  $error++;
1042  }
1043  // End call triggers
1044  }
1045  }
1046 
1047  if (!$error) {
1048  // Delete linked contacts
1049  $res = $this->delete_linked_contact();
1050  if ($res < 0) {
1051  dol_syslog(get_class($this)."::delete error", LOG_ERR);
1052  $error++;
1053  }
1054  }
1055 
1056  if (!$error) {
1057  // Delete linked object
1058  $res = $this->deleteObjectLinked();
1059  if ($res < 0) {
1060  $error++;
1061  }
1062  }
1063 
1064  // Removed extrafields
1065  if (!$error) {
1066  $result = $this->deleteExtraFields();
1067  if ($result < 0) {
1068  $error++;
1069  dol_syslog(get_class($this)."::delete error -3 ".$this->error, LOG_ERR);
1070  }
1071  }
1072 
1073  // Delete all child tables
1074 
1075  if (!$error) {
1076  $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_ticket";
1077  $sql .= " WHERE fk_ticket = ".(int) $this->id;
1078 
1079  $result = $this->db->query($sql);
1080  if (!$result) {
1081  $error++;
1082  $this->errors[] = $this->db->lasterror();
1083  }
1084  }
1085 
1086  if (!$error) {
1087  $sql = "DELETE FROM ".MAIN_DB_PREFIX."ticket";
1088  $sql .= " WHERE rowid=".((int) $this->id);
1089 
1090  dol_syslog(get_class($this)."::delete sql=".$sql);
1091  $resql = $this->db->query($sql);
1092  if (!$resql) {
1093  $error++;
1094  $this->errors[] = "Error ".$this->db->lasterror();
1095  }
1096  }
1097 
1098  // Commit or rollback
1099  if ($error) {
1100  foreach ($this->errors as $errmsg) {
1101  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1102  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1103  }
1104  $this->db->rollback();
1105  return -1 * $error;
1106  } else {
1107  $this->db->commit();
1108  return 1;
1109  }
1110  }
1111 
1119  public function createFromClone(User $user, $fromid)
1120  {
1121  $error = 0;
1122 
1123  $object = new Ticket($this->db);
1124 
1125  $this->db->begin();
1126 
1127  // Load source object
1128  $object->fetch($fromid);
1129  $object->id = 0;
1130  $object->statut = 0;
1131 
1132  // Clear fields
1133  // ...
1134  // Create clone
1135  $object->context['createfromclone'] = 'createfromclone';
1136  $result = $object->create($user);
1137 
1138  // Other options
1139  if ($result < 0) {
1140  $this->error = $object->error;
1141  $error++;
1142  }
1143 
1144  if (!$error) {
1145  }
1146 
1147  unset($object->context['createfromclone']);
1148 
1149  // End
1150  if (!$error) {
1151  $this->db->commit();
1152  return $object->id;
1153  } else {
1154  $this->db->rollback();
1155  return -1;
1156  }
1157  }
1158 
1165  public function initAsSpecimen()
1166  {
1167  $this->id = 0;
1168  $this->entity = 1;
1169  $this->ref = 'TI0501-001';
1170  $this->track_id = 'XXXXaaaa';
1171  $this->origin_email = 'email@email.com';
1172  $this->fk_project = 1;
1173  $this->fk_user_create = 1;
1174  $this->fk_user_assign = 1;
1175  $this->subject = 'Subject of ticket';
1176  $this->message = 'Message of ticket';
1177  $this->status = 0;
1178  $this->resolution = '1';
1179  $this->progress = '10';
1180  //$this->timing = '30';
1181  $this->type_code = 'TYPECODE';
1182  $this->category_code = 'CATEGORYCODE';
1183  $this->severity_code = 'SEVERITYCODE';
1184  $this->datec = '';
1185  $this->date_read = '';
1186  $this->date_last_msg_sent = '';
1187  $this->date_close = '';
1188  $this->tms = '';
1189  return 1;
1190  }
1191 
1198  public function printSelectStatus($selected = "")
1199  {
1200  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 = '');
1201  }
1202 
1203 
1209  public function loadCacheTypesTickets()
1210  {
1211  global $langs;
1212 
1213  if (!empty($this->cache_types_tickets) && count($this->cache_types_tickets)) {
1214  return 0;
1215  }
1216  // Cache deja charge
1217 
1218  $sql = "SELECT rowid, code, label, use_default, pos, description";
1219  $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_type";
1220  $sql .= " WHERE active > 0";
1221  $sql .= " ORDER BY pos";
1222  dol_syslog(get_class($this)."::load_cache_type_tickets", LOG_DEBUG);
1223  $resql = $this->db->query($sql);
1224  if ($resql) {
1225  $num = $this->db->num_rows($resql);
1226  $i = 0;
1227  while ($i < $num) {
1228  $obj = $this->db->fetch_object($resql);
1229  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
1230  $label = ($langs->trans("TicketTypeShort".$obj->code) != ("TicketTypeShort".$obj->code) ? $langs->trans("TicketTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1231  $this->cache_types_tickets[$obj->rowid]['code'] = $obj->code;
1232  $this->cache_types_tickets[$obj->rowid]['label'] = $label;
1233  $this->cache_types_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1234  $this->cache_types_tickets[$obj->rowid]['pos'] = $obj->pos;
1235  $i++;
1236  }
1237  return $num;
1238  } else {
1239  dol_print_error($this->db);
1240  return -1;
1241  }
1242  }
1243 
1250  public function loadCacheCategoriesTickets($publicgroup = -1)
1251  {
1252  global $conf, $langs;
1253 
1254  if ($publicgroup == -1 && !empty($this->cache_category_ticket) && count($this->cache_category_tickets)) {
1255  // Cache already loaded
1256  return 0;
1257  }
1258 
1259  $sql = "SELECT rowid, code, label, use_default, pos, description, public, active, force_severity, fk_parent";
1260  $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_category";
1261  $sql .= " WHERE active > 0 AND entity = ".((int) $conf->entity);
1262  if ($publicgroup > -1) {
1263  $sql .= " AND public = ".((int) $publicgroup);
1264  }
1265  $sql .= " ORDER BY pos";
1266 
1267  dol_syslog(get_class($this)."::load_cache_categories_tickets", LOG_DEBUG);
1268 
1269  $resql = $this->db->query($sql);
1270  if ($resql) {
1271  $num = $this->db->num_rows($resql);
1272  $i = 0;
1273  while ($i < $num) {
1274  $obj = $this->db->fetch_object($resql);
1275  $this->cache_category_tickets[$obj->rowid]['code'] = $obj->code;
1276  $this->cache_category_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1277  $this->cache_category_tickets[$obj->rowid]['pos'] = $obj->pos;
1278  $this->cache_category_tickets[$obj->rowid]['public'] = $obj->public;
1279  $this->cache_category_tickets[$obj->rowid]['active'] = $obj->active;
1280  $this->cache_category_tickets[$obj->rowid]['force_severity'] = $obj->force_severity;
1281  $this->cache_category_tickets[$obj->rowid]['fk_parent'] = $obj->fk_parent;
1282 
1283  // If translation exists, we use it to store already translated string.
1284  // Warning: You should not use this and recompute the translated string into caller code to get the value into expected language
1285  $label = ($langs->trans("TicketCategoryShort".$obj->code) != ("TicketCategoryShort".$obj->code) ? $langs->trans("TicketCategoryShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1286  $this->cache_category_tickets[$obj->rowid]['label'] = $label;
1287 
1288  $i++;
1289  }
1290  return $num;
1291  } else {
1292  dol_print_error($this->db);
1293  return -1;
1294  }
1295  }
1296 
1302  public function loadCacheSeveritiesTickets()
1303  {
1304  global $langs;
1305 
1306  if (!empty($this->cache_severity_tickets) && count($this->cache_severity_tickets)) {
1307  return 0;
1308  }
1309  // Cache deja charge
1310 
1311  $sql = "SELECT rowid, code, label, use_default, pos, description";
1312  $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_severity";
1313  $sql .= " WHERE active > 0";
1314  $sql .= " ORDER BY pos";
1315  dol_syslog(get_class($this)."::loadCacheSeveritiesTickets", LOG_DEBUG);
1316  $resql = $this->db->query($sql);
1317  if ($resql) {
1318  $num = $this->db->num_rows($resql);
1319  $i = 0;
1320  while ($i < $num) {
1321  $obj = $this->db->fetch_object($resql);
1322 
1323  $this->cache_severity_tickets[$obj->rowid]['code'] = $obj->code;
1324  // Si traduction existe, on l'utilise, sinon on prend le libelle par defaut
1325  $label = ($langs->trans("TicketSeverityShort".$obj->code) != ("TicketSeverityShort".$obj->code) ? $langs->trans("TicketSeverityShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1326  $this->cache_severity_tickets[$obj->rowid]['label'] = $label;
1327  $this->cache_severity_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1328  $this->cache_severity_tickets[$obj->rowid]['pos'] = $obj->pos;
1329  $i++;
1330  }
1331  return $num;
1332  } else {
1333  dol_print_error($this->db);
1334  return -1;
1335  }
1336  }
1337 
1338 
1345  public function getLibStatut($mode = 0)
1346  {
1347  return $this->libStatut($this->fk_statut, $mode, 0, $this->progress);
1348  }
1349 
1350 
1351  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1361  public function LibStatut($status, $mode = 0, $notooltip = 0, $progress = 0)
1362  {
1363  // phpcs:enable
1364  global $langs, $hookmanager;
1365 
1366  $labelStatus = $this->statuts[$status];
1367  $labelStatusShort = $this->statuts_short[$status];
1368 
1369  if ($status == self::STATUS_NOT_READ) {
1370  $statusType = 'status0';
1371  } elseif ($status == self::STATUS_READ) {
1372  $statusType = 'status1';
1373  } elseif ($status == self::STATUS_ASSIGNED) {
1374  $statusType = 'status2';
1375  } elseif ($status == self::STATUS_IN_PROGRESS) {
1376  $statusType = 'status4';
1377  } elseif ($status == self::STATUS_WAITING) {
1378  $statusType = 'status7';
1379  } elseif ($status == self::STATUS_NEED_MORE_INFO) {
1380  $statusType = 'status3';
1381  } elseif ($status == self::STATUS_CANCELED) {
1382  $statusType = 'status9';
1383  } elseif ($status == self::STATUS_CLOSED) {
1384  $statusType = 'status6';
1385  } else {
1386  $labelStatus = 'Unknown';
1387  $labelStatusShort = 'Unknown';
1388  $statusType = 'status0';
1389  $mode = 0;
1390  }
1391 
1392  $parameters = array(
1393  'status' => $status,
1394  'mode' => $mode,
1395  );
1396 
1397  // Note that $action and $object may have been modified by hook
1398  $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this);
1399 
1400  if ($reshook > 0) {
1401  return $hookmanager->resPrint;
1402  }
1403 
1404  $params = array();
1405  if ($notooltip) {
1406  $params = array('tooltip' => 'no');
1407  }
1408 
1409  $labelStatus = $langs->transnoentitiesnoconv($labelStatus);
1410  $labelStatusShort = $langs->transnoentitiesnoconv($labelStatusShort);
1411 
1412  if ($status == self::STATUS_IN_PROGRESS && $progress > 0) {
1413  $labelStatus .= ' ('.round($progress).'%)';
1414  $labelStatusShort .= ' ('.round($progress).'%)';
1415  }
1416 
1417  return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', $params);
1418  }
1419 
1420 
1431  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1432  {
1433  global $db, $conf, $langs;
1434  global $dolibarr_main_authentication, $dolibarr_main_demo;
1435  global $menumanager;
1436 
1437  if (!empty($conf->dol_no_mouse_hover)) {
1438  $notooltip = 1; // Force disable tooltips
1439  }
1440 
1441  $result = '';
1442 
1443  $label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Ticket").'</u>';
1444  $label .= ' '.$this->getLibStatut(4);
1445  $label .= '<br>';
1446  $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref.'<br>';
1447  $label .= '<b>'.$langs->trans('TicketTrackId').':</b> '.$this->track_id.'<br>';
1448  $label .= '<b>'.$langs->trans('Subject').':</b> '.$this->subject;
1449  if ($this->date_creation) {
1450  $label .= '<br><b>'.$langs->trans('DateCreation').':</b> '.$this->date_creation;
1451  }
1452  if ($this->date_modification) {
1453  $label .= '<br><b>'.$langs->trans('DateModification').':</b> '.$this->date_modification;
1454  }
1455  $url = DOL_URL_ROOT.'/ticket/card.php?id='.$this->id;
1456 
1457  if ($option != 'nolink') {
1458  // Add param to save lastsearch_values or not
1459  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1460  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1461  $add_save_lastsearch_values = 1;
1462  }
1463  if ($add_save_lastsearch_values) {
1464  $url .= '&save_lastsearch_values=1';
1465  }
1466  }
1467 
1468  $linkclose = '';
1469  if (empty($notooltip)) {
1470  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
1471  $label = $langs->trans("ShowTicket");
1472  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1473  }
1474  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
1475  $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"';
1476  } else {
1477  $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1478  }
1479 
1480  $linkstart = '<a href="'.$url.'"';
1481  $linkstart .= $linkclose.'>';
1482  $linkend = '</a>';
1483 
1484  $result .= $linkstart;
1485  if ($withpicto) {
1486  $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
1487  }
1488  if ($withpicto != 2) {
1489  $result .= $this->ref;
1490  }
1491  $result .= $linkend;
1492  //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1493 
1494  return $result;
1495  }
1496 
1497 
1505  public function markAsRead($user, $notrigger = 0)
1506  {
1507  global $conf, $langs;
1508 
1509  $error = 0;
1510 
1511  if ($this->statut != self::STATUS_CANCELED) { // no closed
1512  $this->oldcopy = dol_clone($this);
1513 
1514  $this->db->begin();
1515 
1516  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1517  $sql .= " SET fk_statut = ".Ticket::STATUS_READ.", date_read = '".$this->db->idate(dol_now())."'";
1518  $sql .= " WHERE rowid = ".((int) $this->id);
1519 
1520  dol_syslog(get_class($this)."::markAsRead");
1521  $resql = $this->db->query($sql);
1522  if ($resql) {
1523  $this->actionmsg = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1524  $this->actionmsg2 = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1525 
1526  if (!$error && !$notrigger) {
1527  // Call trigger
1528  $result = $this->call_trigger('TICKET_MODIFY', $user);
1529  if ($result < 0) {
1530  $error++;
1531  }
1532  // End call triggers
1533  }
1534 
1535  if (!$error) {
1536  $this->db->commit();
1537  return 1;
1538  } else {
1539  $this->db->rollback();
1540  $this->error = join(',', $this->errors);
1541  dol_syslog(get_class($this)."::markAsRead ".$this->error, LOG_ERR);
1542  return -1;
1543  }
1544  } else {
1545  $this->db->rollback();
1546  $this->error = $this->db->lasterror();
1547  dol_syslog(get_class($this)."::markAsRead ".$this->error, LOG_ERR);
1548  return -1;
1549  }
1550  }
1551  }
1552 
1561  public function assignUser($user, $id_assign_user, $notrigger = 0)
1562  {
1563  global $conf, $langs;
1564 
1565  $error = 0;
1566 
1567  $this->oldcopy = dol_clone($this);
1568 
1569  $this->db->begin();
1570 
1571  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1572  if ($id_assign_user > 0) {
1573  $sql .= " SET fk_user_assign=".((int) $id_assign_user).", fk_statut = ".Ticket::STATUS_ASSIGNED;
1574  } else {
1575  $sql .= " SET fk_user_assign=null, fk_statut = ".Ticket::STATUS_READ;
1576  }
1577  $sql .= " WHERE rowid = ".((int) $this->id);
1578 
1579  dol_syslog(get_class($this)."::assignUser sql=".$sql);
1580  $resql = $this->db->query($sql);
1581  if ($resql) {
1582  $this->fk_user_assign = $id_assign_user; // May be used by trigger
1583 
1584  if (!$notrigger) {
1585  // Call trigger
1586  $result = $this->call_trigger('TICKET_ASSIGNED', $user);
1587  if ($result < 0) {
1588  $error++;
1589  }
1590  // End call triggers
1591  }
1592 
1593  if (!$error) {
1594  $this->db->commit();
1595  return 1;
1596  } else {
1597  $this->db->rollback();
1598  $this->error = join(',', $this->errors);
1599  dol_syslog(get_class($this)."::assignUser ".$this->error, LOG_ERR);
1600  return -1;
1601  }
1602  } else {
1603  $this->db->rollback();
1604  $this->error = $this->db->lasterror();
1605  dol_syslog(get_class($this)."::assignUser ".$this->error, LOG_ERR);
1606  return -1;
1607  }
1608  }
1609 
1621  public function createTicketMessage($user, $notrigger = 0, $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array(), $send_email = false)
1622  {
1623  global $conf, $langs;
1624  $error = 0;
1625 
1626  $now = dol_now();
1627 
1628  // Clean parameters
1629  if (isset($this->fk_track_id)) {
1630  $this->fk_track_id = trim($this->fk_track_id);
1631  }
1632 
1633  if (isset($this->message)) {
1634  $this->message = trim($this->message);
1635  }
1636 
1637  $this->db->begin();
1638 
1639  // Insert entry into agenda with code 'TICKET_MSG'
1640  include_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
1641  $actioncomm = new ActionComm($this->db);
1642  $actioncomm->type_code = 'AC_OTH_AUTO'; // This is not an entry that must appears into manual calendar but only into CRM calendar
1643  $actioncomm->code = 'TICKET_MSG';
1644  if ($this->private) {
1645  $actioncomm->code = 'TICKET_MSG_PRIVATE';
1646  }
1647  if ($send_email) {
1648  $actioncomm->code .= '_SENTBYMAIL';
1649  }
1650  $actioncomm->socid = $this->socid;
1651  $actioncomm->label = $this->subject;
1652  $actioncomm->note_private = $this->message;
1653  $actioncomm->userassigned = array($user->id);
1654  $actioncomm->userownerid = $user->id;
1655  $actioncomm->datep = $now;
1656  $actioncomm->percentage = -1; // percentage is not relevant for punctual events
1657  $actioncomm->elementtype = 'ticket';
1658  $actioncomm->fk_element = $this->id;
1659  $actioncomm->fk_project = $this->fk_project;
1660 
1661  $attachedfiles = array();
1662  $attachedfiles['paths'] = $filename_list;
1663  $attachedfiles['names'] = $mimefilename_list;
1664  $attachedfiles['mimes'] = $mimetype_list;
1665  if (is_array($attachedfiles) && count($attachedfiles) > 0) {
1666  $actioncomm->attachedfiles = $attachedfiles;
1667  }
1668 
1669  if (!empty($mimefilename_list) && is_array($mimefilename_list)) {
1670  $actioncomm->note_private = dol_concatdesc($actioncomm->note_private, "\n".$langs->transnoentities("AttachedFiles").': '.join(';', $mimefilename_list));
1671  }
1672 
1673  $actionid = $actioncomm->create($user);
1674  if ($actionid <= 0) {
1675  $error++;
1676  $this->error = $actioncomm->error;
1677  $this->errors = $actioncomm->errors;
1678  }
1679 
1680  // Commit or rollback
1681  if ($error) {
1682  $this->db->rollback();
1683  return -1 * $error;
1684  } else {
1685  $this->db->commit();
1686  return 1;
1687  }
1688  }
1689 
1695  public function loadCacheMsgsTicket()
1696  {
1697  if (!empty($this->cache_msgs_ticket) && is_array($this->cache_msgs_ticket) && count($this->cache_msgs_ticket)) {
1698  return 0;
1699  }
1700 
1701  // Cache already loaded
1702 
1703  $sql = "SELECT id as rowid, fk_user_author, datec, datep, label, note as message, code";
1704  $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm";
1705  $sql .= " WHERE fk_element = ".(int) $this->id;
1706  $sql .= " AND elementtype = 'ticket'";
1707  $sql .= " ORDER BY datep DESC";
1708 
1709  dol_syslog(get_class($this)."::load_cache_actions_ticket", LOG_DEBUG);
1710  $resql = $this->db->query($sql);
1711  if ($resql) {
1712  $num = $this->db->num_rows($resql);
1713  $i = 0;
1714  while ($i < $num) {
1715  $obj = $this->db->fetch_object($resql);
1716  $this->cache_msgs_ticket[$i]['id'] = $obj->rowid;
1717  $this->cache_msgs_ticket[$i]['fk_user_author'] = $obj->fk_user_author;
1718  $this->cache_msgs_ticket[$i]['datec'] = $this->db->jdate($obj->datec);
1719  $this->cache_msgs_ticket[$i]['datep'] = $this->db->jdate($obj->datep);
1720  $this->cache_msgs_ticket[$i]['subject'] = $obj->label;
1721  $this->cache_msgs_ticket[$i]['message'] = $obj->message;
1722  $this->cache_msgs_ticket[$i]['private'] = (preg_match('/^TICKET_MSG_PRIVATE/', $obj->code) ? 1 : 0);
1723  $i++;
1724  }
1725  return $num;
1726  } else {
1727  $this->error = "Error ".$this->db->lasterror();
1728  dol_syslog(get_class($this)."::load_cache_actions_ticket ".$this->error, LOG_ERR);
1729  return -1;
1730  }
1731  }
1732 
1740  public function close(User $user, $mode = 0)
1741  {
1742  global $conf, $langs;
1743 
1744  if ($this->fk_statut != Ticket::STATUS_CLOSED && $this->fk_statut != Ticket::STATUS_CANCELED) { // not closed
1745  $this->db->begin();
1746 
1747  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1748  $sql .= " SET fk_statut=".($mode ? Ticket::STATUS_CANCELED : Ticket::STATUS_CLOSED).", progress=100, date_close='".$this->db->idate(dol_now())."'";
1749  $sql .= " WHERE rowid = ".((int) $this->id);
1750 
1751  dol_syslog(get_class($this)."::close mode=".$mode);
1752  $resql = $this->db->query($sql);
1753  if ($resql) {
1754  $error = 0;
1755 
1756  // Valid and close fichinter linked
1757  if (isModEnabled('ficheinter') && !empty($conf->global->WORKFLOW_TICKET_CLOSE_INTERVENTION)) {
1758  dol_syslog("We have closed the ticket, so we close all linked interventions");
1759  $this->fetchObjectLinked($this->id, $this->element, null, 'fichinter');
1760  if ($this->linkedObjectsIds) {
1761  foreach ($this->linkedObjectsIds['fichinter'] as $fichinter_id) {
1762  $fichinter = new Fichinter($this->db);
1763  $fichinter->fetch($fichinter_id);
1764  if ($fichinter->statut == 0) {
1765  $result = $fichinter->setValid($user);
1766  if (!$result) {
1767  $this->errors[] = $fichinter->error;
1768  $error++;
1769  }
1770  }
1771  if ($fichinter->statut < 3) {
1772  $result = $fichinter->setStatut(3);
1773  if (!$result) {
1774  $this->errors[] = $fichinter->error;
1775  $error++;
1776  }
1777  }
1778  }
1779  }
1780  }
1781 
1782  // Call trigger
1783  $result = $this->call_trigger('TICKET_CLOSE', $user);
1784  if ($result < 0) {
1785  $error++;
1786  }
1787  // End call triggers
1788 
1789  if (!$error) {
1790  $this->db->commit();
1791  return 1;
1792  } else {
1793  $this->db->rollback();
1794  $this->error = join(',', $this->errors);
1795  dol_syslog(get_class($this)."::close ".$this->error, LOG_ERR);
1796  return -1;
1797  }
1798  } else {
1799  $this->db->rollback();
1800  $this->error = $this->db->lasterror();
1801  dol_syslog(get_class($this)."::close ".$this->error, LOG_ERR);
1802  return -1;
1803  }
1804  }
1805  }
1806 
1816  public function searchSocidByEmail($email, $type = '0', $filters = array(), $clause = 'AND')
1817  {
1818  $thirdparties = array();
1819  $exact = 0;
1820 
1821  // Generation requete recherche
1822  $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."societe";
1823  $sql .= " WHERE entity IN (".getEntity('ticket', 1).")";
1824  if (!empty($type)) {
1825  if ($type == 1 || $type == 2) {
1826  $sql .= " AND client = ".((int) $type);
1827  } elseif ($type == 3) {
1828  $sql .= " AND fournisseur = 1";
1829  }
1830  }
1831  if (!empty($email)) {
1832  if (empty($exact)) {
1833  $regs = array();
1834  if (preg_match('/^([\*])?[^*]+([\*])?$/', $email, $regs) && count($regs) > 1) {
1835  $email = str_replace('*', '%', $email);
1836  } else {
1837  $email = '%'.$email.'%';
1838  }
1839  }
1840  $sql .= " AND ";
1841  if (is_array($filters) && !empty($filters)) {
1842  $sql .= "(";
1843  }
1844 
1845  $sql .= "email LIKE '".$this->db->escape($email)."'";
1846  }
1847  if (is_array($filters) && !empty($filters)) {
1848  foreach ($filters as $field => $value) {
1849  $sql .= " ".$clause." ".$field." LIKE '".$this->db->escape($value)."'";
1850  }
1851  if (!empty($email)) {
1852  $sql .= ")";
1853  }
1854  }
1855 
1856  $res = $this->db->query($sql);
1857  if ($res) {
1858  while ($rec = $this->db->fetch_array($res)) {
1859  $soc = new Societe($this->db);
1860  $soc->fetch($rec['rowid']);
1861  $thirdparties[] = $soc;
1862  }
1863 
1864  return $thirdparties;
1865  } else {
1866  $this->error = $this->db->error().' sql='.$sql;
1867  dol_syslog(get_class($this)."::searchSocidByEmail ".$this->error, LOG_ERR);
1868  return -1;
1869  }
1870  }
1871 
1880  public function searchContactByEmail($email, $socid = '', $case = '')
1881  {
1882  $contacts = array();
1883 
1884  // Forge the search SQL
1885  $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."socpeople";
1886  $sql .= " WHERE entity IN (".getEntity('contact').")";
1887  if (!empty($socid)) {
1888  $sql .= " AND fk_soc = ".((int) $socid);
1889  }
1890  if (!empty($email)) {
1891  $sql .= " AND ";
1892  if (!$case) {
1893  $sql .= "email = '".$this->db->escape($email)."'";
1894  } else {
1895  $sql .= "email LIKE BINARY '".$this->db->escape($this->db->escapeforlike($email))."'";
1896  }
1897  }
1898 
1899  $res = $this->db->query($sql);
1900  if ($res) {
1901  while ($rec = $this->db->fetch_object($res)) {
1902  include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1903  $contactstatic = new Contact($this->db);
1904  $contactstatic->fetch($rec->rowid);
1905  $contacts[] = $contactstatic;
1906  }
1907 
1908  return $contacts;
1909  } else {
1910  $this->error = $this->db->error().' sql='.$sql;
1911  dol_syslog(get_class($this)."::searchContactByEmail ".$this->error, LOG_ERR);
1912  return -1;
1913  }
1914  }
1915 
1922  public function setCustomer($id)
1923  {
1924  if ($this->id) {
1925  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1926  $sql .= " SET fk_soc = ".($id > 0 ? $id : "null");
1927  $sql .= " WHERE rowid = ".((int) $this->id);
1928  dol_syslog(get_class($this).'::setCustomer sql='.$sql);
1929  $resql = $this->db->query($sql);
1930  if ($resql) {
1931  return 1;
1932  } else {
1933  return -1;
1934  }
1935  } else {
1936  return -1;
1937  }
1938  }
1939 
1946  public function setProgression($percent)
1947  {
1948  if ($this->id) {
1949  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1950  $sql .= " SET progress = ".($percent > 0 ? $percent : "null");
1951  $sql .= " WHERE rowid = ".((int) $this->id);
1952  dol_syslog(get_class($this).'::set_progression sql='.$sql);
1953  $resql = $this->db->query($sql);
1954  if ($resql) {
1955  return 1;
1956  } else {
1957  return -1;
1958  }
1959  } else {
1960  return -1;
1961  }
1962  }
1963 
1970  public function setContract($contractid)
1971  {
1972  if (!$this->table_element) {
1973  dol_syslog(get_class($this)."::setContract was called on objet with property table_element not defined", LOG_ERR);
1974  return -1;
1975  }
1976 
1977  $result = $this->add_object_linked('contrat', $contractid);
1978  if ($result) {
1979  $this->fk_contract = $contractid;
1980  return 1;
1981  } else {
1982  dol_print_error($this->db);
1983  return -1;
1984  }
1985  }
1986 
1987  /* gestion des contacts d'un ticket */
1988 
1994  public function getIdTicketInternalContact()
1995  {
1996  return $this->getIdContact('internal', 'SUPPORTTEC');
1997  }
1998 
2005  {
2006  return $this->listeContact(-1, 'internal');
2007  }
2008 
2014  public function getIdTicketCustomerContact()
2015  {
2016  return $this->getIdContact('external', 'SUPPORTCLI');
2017  }
2018 
2025  {
2026  return $this->listeContact(-1, 'external');
2027  }
2028 
2035  {
2036  return $this->getIdContact('internal', 'CONTRIBUTOR');
2037  }
2038 
2045  {
2046  return $this->getIdContact('external', 'CONTRIBUTOR');
2047  }
2048 
2054  public function getTicketAllContacts()
2055  {
2056  $array_contact = array();
2057 
2058  $array_contact = $this->getIdTicketInternalContact();
2059 
2060  $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());
2061 
2062  $array_contact = array_merge($array_contact, $this->getIdTicketInternalInvolvedContact());
2063 
2064  $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());
2065 
2066  return $array_contact;
2067  }
2068 
2075  {
2076  $array_contact = array();
2077 
2078  $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());
2079 
2080  $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());
2081 
2082  return $array_contact;
2083  }
2084 
2085 
2096  public function listeContact($status = -1, $source = 'external', $list = 0, $code = '')
2097  {
2098  global $langs;
2099 
2100  $tab = array();
2101 
2102  $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
2103  if ($source == 'internal') {
2104  $sql .= ", '-1' as socid, t.statut as statuscontact";
2105  }
2106 
2107  if ($source == 'external' || $source == 'thirdparty') {
2108  $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
2109  }
2110 
2111  $sql .= ", t.civility, t.lastname as lastname, t.firstname, t.email";
2112  if ($source == 'internal') {
2113  $sql .= ", t.office_phone as phone, t.user_mobile as phone_mobile";
2114  }
2115 
2116  if ($source == 'external') {
2117  $sql .= ", t.phone as phone, t.phone_mobile as phone_mobile, t.phone_perso as phone_perso";
2118  }
2119 
2120  $sql .= ", tc.source, tc.element, tc.code, tc.libelle as type_contact_label";
2121  $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
2122  $sql .= ", ".MAIN_DB_PREFIX."element_contact ec";
2123  if ($source == 'internal') {
2124  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
2125  }
2126 
2127  if ($source == 'external' || $source == 'thirdparty') {
2128  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
2129  }
2130 
2131  $sql .= " WHERE ec.element_id = ".((int) $this->id);
2132  $sql .= " AND ec.fk_c_type_contact=tc.rowid";
2133  $sql .= " AND tc.element='".$this->db->escape($this->element)."'";
2134  if ($source == 'internal') {
2135  $sql .= " AND tc.source = 'internal'";
2136  }
2137 
2138  if ($source == 'external' || $source == 'thirdparty') {
2139  $sql .= " AND tc.source = 'external'";
2140  }
2141 
2142  if (!empty($code)) {
2143  $sql .= " AND tc.code = '".$this->db->escape($code)."'";
2144  }
2145 
2146  $sql .= " AND tc.active=1";
2147  if ($status >= 0) {
2148  $sql .= " AND ec.statut = ".((int) $status);
2149  }
2150 
2151  $sql .= " ORDER BY t.lastname ASC";
2152 
2153  $resql = $this->db->query($sql);
2154  if ($resql) {
2155  $num = $this->db->num_rows($resql);
2156  $i = 0;
2157  while ($i < $num) {
2158  $obj = $this->db->fetch_object($resql);
2159 
2160  if (!$list) {
2161  $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
2162  $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_contact_label);
2163  $tab[$i] = array(
2164  'source' => $obj->source,
2165  'socid' => $obj->socid,
2166  'id' => $obj->id,
2167  'nom' => $obj->lastname, // For backward compatibility
2168  'civility' => $obj->civility,
2169  'lastname' => $obj->lastname,
2170  'firstname' => $obj->firstname,
2171  'email' => $obj->email,
2172  'rowid' => $obj->rowid,
2173  'code' => $obj->code,
2174  'libelle' => $libelle_type,
2175  'status' => $obj->statuslink,
2176  'statuscontact'=>$obj->statuscontact,
2177  'fk_c_type_contact' => $obj->fk_c_type_contact,
2178  'phone' => $obj->phone,
2179  'phone_mobile' => $obj->phone_mobile);
2180  } else {
2181  $tab[$i] = $obj->id;
2182  }
2183 
2184  $i++;
2185  }
2186 
2187  return $tab;
2188  } else {
2189  $this->error = $this->db->error();
2190  dol_print_error($this->db);
2191  return -1;
2192  }
2193  }
2194 
2201  public function getDefaultRef($thirdparty = '')
2202  {
2203  global $conf;
2204 
2205  $defaultref = '';
2206  $modele = empty($conf->global->TICKET_ADDON) ? 'mod_ticket_simple' : $conf->global->TICKET_ADDON;
2207 
2208  // Search template files
2209  $file = '';
2210  $classname = '';
2211  $filefound = 0;
2212  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2213  foreach ($dirmodels as $reldir) {
2214  $file = dol_buildpath($reldir."core/modules/ticket/".$modele.'.php', 0);
2215  if (file_exists($file)) {
2216  $filefound = 1;
2217  $classname = $modele;
2218  break;
2219  }
2220  }
2221 
2222  if ($filefound) {
2223  $result = dol_include_once($reldir."core/modules/ticket/".$modele.'.php');
2224  $modTicket = new $classname;
2225 
2226  $defaultref = $modTicket->getNextValue($thirdparty, $this);
2227  }
2228 
2229  if (is_numeric($defaultref) && $defaultref <= 0) {
2230  $defaultref = '';
2231  }
2232 
2233  return $defaultref;
2234  }
2235 
2236 
2237  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2244  public function is_photo_available($sdir)
2245  {
2246  // phpcs:enable
2247  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2248 
2249  global $conf;
2250 
2251  $dir = $sdir.'/';
2252  $nbphoto = 0;
2253 
2254  $dir_osencoded = dol_osencode($dir);
2255  if (file_exists($dir_osencoded)) {
2256  $handle = opendir($dir_osencoded);
2257  if (is_resource($handle)) {
2258  while (($file = readdir($handle)) !== false) {
2259  if (!utf8_check($file)) {
2260  $file = utf8_encode($file); // To be sure data is stored in UTF8 in memory
2261  }
2262  if (dol_is_file($dir.$file)) {
2263  return true;
2264  }
2265  }
2266  }
2267  }
2268  return false;
2269  }
2270 
2271 
2280  public function copyFilesForTicket($forcetrackid = null)
2281  {
2282  global $conf;
2283 
2284  // Create form object
2285  include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
2286  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2287  include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
2288 
2289  $maxwidthsmall = 270;
2290  $maxheightsmall = 150;
2291  $maxwidthmini = 128;
2292  $maxheightmini = 72;
2293 
2294  $formmail = new FormMail($this->db);
2295  $formmail->trackid = (is_null($forcetrackid) ? 'tic'.$this->id : '');
2296  $attachedfiles = $formmail->get_attached_files();
2297 
2298  $filepath = $attachedfiles['paths']; // path is for example user->dir_temp.'/'.$user->id.'/'...
2299  $filename = $attachedfiles['names'];
2300  $mimetype = $attachedfiles['mimes'];
2301 
2302  // Copy files into ticket directory
2303  $destdir = $conf->ticket->dir_output.'/'.$this->ref;
2304 
2305  if (!dol_is_dir($destdir)) {
2306  dol_mkdir($destdir);
2307  }
2308 
2309  $listofpaths = array();
2310  $listofnames = array();
2311  foreach ($filename as $i => $val) {
2312  $destfile = $destdir.'/'.$filename[$i];
2313  // If destination file already exists, we add a suffix to avoid to overwrite
2314  if (is_file($destfile)) {
2315  $pathinfo = pathinfo($filename[$i]);
2316  $now = dol_now();
2317  $destfile = $destdir.'/'.$pathinfo['filename'].' - '.dol_print_date($now, 'dayhourlog').'.'.$pathinfo['extension'];
2318  }
2319 
2320  $res = dol_move($filepath[$i], $destfile, 0, 1, 0, 1);
2321  if (!$res) {
2322  // Move has failed
2323  $this->error = "Failed to move file ".dirbasename($filepath[$i])." into ".dirbasename($destfile);
2324  return -1;
2325  } else {
2326  // If file is an image, we create thumbs
2327  if (image_format_supported($destfile) == 1) {
2328  // Create small thumbs for image (Ratio is near 16/9)
2329  // Used on logon for example
2330  $imgThumbSmall = vignette($destfile, $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs");
2331  // Create mini thumbs for image (Ratio is near 16/9)
2332  // Used on menu or for setup page for example
2333  $imgThumbMini = vignette($destfile, $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs");
2334  }
2335  }
2336 
2337  // Clear variables into session
2338  $formmail->remove_attached_files($i);
2339 
2340  // Fill array with new names
2341  $listofpaths[$i] = $destfile;
2342  $listofnames[$i] = basename($destfile);
2343  }
2344 
2345  return array('listofpaths'=>$listofpaths, 'listofnames'=>$listofnames, 'listofmimes'=>$mimetype);
2346  }
2347 
2358  public function setCategories($categories)
2359  {
2360  // Handle single category
2361  if (!is_array($categories)) {
2362  $categories = array($categories);
2363  }
2364 
2365  // Get current categories
2366  include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2367  $c = new Categorie($this->db);
2368  $existing = $c->containing($this->id, Categorie::TYPE_TICKET, 'id');
2369 
2370  // Diff
2371  if (is_array($existing)) {
2372  $to_del = array_diff($existing, $categories);
2373  $to_add = array_diff($categories, $existing);
2374  } else {
2375  $to_del = array(); // Nothing to delete
2376  $to_add = $categories;
2377  }
2378 
2379  // Process
2380  foreach ($to_del as $del) {
2381  if ($c->fetch($del) > 0) {
2382  $c->del_type($this, Categorie::TYPE_TICKET);
2383  }
2384  }
2385  foreach ($to_add as $add) {
2386  if ($c->fetch($add) > 0) {
2387  $c->add_type($this, Categorie::TYPE_TICKET);
2388  }
2389  }
2390 
2391  return;
2392  }
2393 
2404  public function newMessage($user, &$action, $private = 1, $public_area = 0)
2405  {
2406  global $mysoc, $conf, $langs;
2407 
2408  $error = 0;
2409 
2410  $object = new Ticket($this->db);
2411 
2412  $ret = $object->fetch('', '', GETPOST('track_id', 'alpha'));
2413 
2414  $object->socid = $object->fk_soc;
2415  $object->fetch_thirdparty();
2416  $object->fetch_project();
2417 
2418  if ($ret < 0) {
2419  $error++;
2420  array_push($this->errors, $langs->trans("ErrorTicketIsNotValid"));
2421  $action = '';
2422  }
2423 
2424  if (!GETPOST("message")) {
2425  $error++;
2426  array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("message")));
2427  $action = 'add_message';
2428  }
2429 
2430  if (!$error) {
2431  $object->subject = GETPOST('subject', 'alphanohtml');
2432  $object->message = GETPOST("message", "restricthtml");
2433  $object->private = GETPOST("private_message", "alpha");
2434 
2435  $send_email = GETPOST('send_email', 'int');
2436 
2437  // Copy attached files (saved into $_SESSION) as linked files to ticket. Return array with final name used.
2438  $resarray = $object->copyFilesForTicket();
2439  if (is_numeric($resarray) && $resarray == -1) {
2440  setEventMessages($object->error, $object->errors, 'errors');
2441  return -1;
2442  }
2443 
2444  $listofpaths = $resarray['listofpaths'];
2445  $listofnames = $resarray['listofnames'];
2446  $listofmimes = $resarray['listofmimes'];
2447 
2448  $id = $object->createTicketMessage($user, 0, $listofpaths, $listofmimes, $listofnames, $send_email);
2449  if ($id <= 0) {
2450  $error++;
2451  $this->error = $object->error;
2452  $this->errors = $object->errors;
2453  $action = 'add_message';
2454  }
2455 
2456  if (!$error && $id > 0) {
2457  setEventMessages($langs->trans('TicketMessageSuccessfullyAdded'), null, 'mesgs');
2458 
2459  //var_dump($_SESSION);
2460  //var_dump($listofpaths);exit;
2461 
2462  if (!empty($public_area)) {
2463  /*
2464  * Message created from the Public interface
2465  *
2466  * Send emails to assigned users (public area notification)
2467  */
2468  if (!empty($conf->global->TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_ENABLED)) {
2469  $assigned_user_dont_have_email = '';
2470  $sendto = array();
2471  if ($this->fk_user_assign > 0) {
2472  $assigned_user = new User($this->db);
2473  $assigned_user->fetch($this->fk_user_assign);
2474  if (!empty($assigned_user->email)) {
2475  $sendto[$assigned_user->email] = $assigned_user->getFullName($langs)." <".$assigned_user->email.">";
2476  } else {
2477  $assigned_user_dont_have_email = $assigned_user->getFullName($langs);
2478  }
2479  }
2480  if (empty($sendto)) {
2481  if (!empty($conf->global->TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL)) {
2482  $sendto[$conf->global->TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL] = $conf->global->TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL;
2483  } elseif (!empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO)) {
2484  $sendto[$conf->global->TICKET_NOTIFICATION_EMAIL_TO] = $conf->global->TICKET_NOTIFICATION_EMAIL_TO;
2485  }
2486  }
2487 
2488  // Add global email address recipient
2489  if (!empty($conf->global->TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS) &&
2490  !empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO) && !array_key_exists($conf->global->TICKET_NOTIFICATION_EMAIL_TO, $sendto)
2491  ) {
2492  $sendto[$conf->global->TICKET_NOTIFICATION_EMAIL_TO] = $conf->global->TICKET_NOTIFICATION_EMAIL_TO;
2493  }
2494 
2495  if (!empty($sendto)) {
2496  $label_title = empty($conf->global->MAIN_APPLICATION_TITLE) ? $mysoc->name : $conf->global->MAIN_APPLICATION_TITLE;
2497  $subject = '['.$label_title.'- ticket #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2498 
2499  // Message send
2500  $message = $langs->trans('TicketMessageMailIntroText');
2501  $message .= '<br><br>';
2502  $messagePost = GETPOST('message', 'restricthtml');
2503  if (!dol_textishtml($messagePost)) {
2504  $messagePost = dol_nl2br($messagePost);
2505  }
2506  $message .= $messagePost;
2507 
2508  // Customer company infos
2509  $message .= '<br><br>';
2510  $message .= "==============================================";
2511  $message .= !empty($object->thirdparty->name) ? '<br>'.$langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';
2512  $message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';
2513  $message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';
2514 
2515  // Email send to
2516  $message .= '<br><br>';
2517  if (!empty($assigned_user_dont_have_email)) {
2518  $message .= '<br>'.$langs->trans('NoEMail').' : '.$assigned_user_dont_have_email;
2519  }
2520  foreach ($sendto as $val) {
2521  $message .= '<br>'.$langs->trans('TicketNotificationRecipient').' : '.$val;
2522  }
2523 
2524  // URL ticket
2525  $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;
2526  $message .= '<br><br>';
2527  $message .= $langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a>';
2528 
2529  $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2530  }
2531  }
2532  } else {
2533  /*
2534  * Message send from the Backoffice / Private area
2535  *
2536  * 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)
2537  */
2538  if ($send_email > 0) {
2539  // Retrieve internal contact datas
2540  $internal_contacts = $object->getInfosTicketInternalContact();
2541 
2542  $sendto = array();
2543  if (is_array($internal_contacts) && count($internal_contacts) > 0) {
2544  // Set default subject
2545  $label_title = empty($conf->global->MAIN_APPLICATION_TITLE) ? $mysoc->name : $conf->global->MAIN_APPLICATION_TITLE;
2546  $appli = $label_title;
2547  $subject = GETPOST('subject', 'alphanohtml') ? GETPOST('subject', 'alphanohtml') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2548 
2549  $message_intro = $langs->trans('TicketNotificationEmailBody', "#".$object->id);
2550  $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');
2551 
2552  $message = $langs->trans('TicketMessageMailIntroText');
2553  $message .= '<br><br>';
2554  $messagePost = GETPOST('message', 'restricthtml');
2555  if (!dol_textishtml($messagePost)) {
2556  $messagePost = dol_nl2br($messagePost);
2557  }
2558  $message .= $messagePost;
2559 
2560  // Data about customer
2561  $message .= '<br><br>';
2562  $message .= "==============================================<br>";
2563  $message .= !empty($object->thirdparty->name) ? $langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';
2564  $message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';
2565  $message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';
2566 
2567  // Build array to display recipient list
2568  foreach ($internal_contacts as $key => $info_sendto) {
2569  // Avoid duplicate notifications
2570  if ($info_sendto['id'] == $user->id) {
2571  continue;
2572  }
2573 
2574  if ($info_sendto['email'] != '') {
2575  if (!empty($info_sendto['email'])) {
2576  $sendto[$info_sendto['email']] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";
2577  }
2578 
2579  // Contact type
2580  $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1').' ('.strtolower($info_sendto['libelle']).')';
2581  $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');
2582  }
2583  }
2584  $message .= '<br>';
2585  // URL ticket
2586  $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;
2587 
2588  // Add html link on url
2589  $message .= '<br>'.$langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a><br>';
2590 
2591  // Add global email address recipient
2592  if ($conf->global->TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS && !array_key_exists($conf->global->TICKET_NOTIFICATION_EMAIL_TO, $sendto)) {
2593  if (!empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO)) {
2594  $sendto[$conf->global->TICKET_NOTIFICATION_EMAIL_TO] = $conf->global->TICKET_NOTIFICATION_EMAIL_TO;
2595  }
2596  }
2597 
2598  // dont try to send email if no recipient
2599  if (!empty($sendto)) {
2600  $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2601  }
2602  }
2603 
2604  /*
2605  * Send emails for externals users if not private (linked contacts)
2606  */
2607  if (empty($object->private)) {
2608  // Retrieve email of all contacts (external)
2609  $external_contacts = $object->getInfosTicketExternalContact();
2610 
2611  // If no contact, get email from thirdparty
2612  if (is_array($external_contacts) && count($external_contacts) === 0) {
2613  if (!empty($object->fk_soc)) {
2614  $object->fetch_thirdparty($object->fk_soc);
2615  $array_company = array(array('firstname' => '', 'lastname' => $object->thirdparty->name, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
2616  $external_contacts = array_merge($external_contacts, $array_company);
2617  } elseif (empty($object->fk_soc) && !empty($object->origin_email)) {
2618  $array_external = array(array('firstname' => '', 'lastname' => $object->origin_email, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
2619  $external_contacts = array_merge($external_contacts, $array_external);
2620  }
2621  }
2622 
2623  $sendto = array();
2624  if (is_array($external_contacts) && count($external_contacts) > 0) {
2625  // Get default subject for email to external contacts
2626  $label_title = empty($conf->global->MAIN_APPLICATION_TITLE) ? $mysoc->name : $conf->global->MAIN_APPLICATION_TITLE;
2627  $appli = $mysoc->name;
2628  $subject = GETPOST('subject') ? GETPOST('subject') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2629 
2630  $message_intro = GETPOST('mail_intro') ? GETPOST('mail_intro', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO');
2631  $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');
2632  if (!dol_textishtml($message_intro)) {
2633  $message_intro = dol_nl2br($message_intro);
2634  }
2635  if (!dol_textishtml($message_signature)) {
2636  $message_signature = dol_nl2br($message_signature);
2637  }
2638 
2639  // We put intro after
2640  $messagePost = GETPOST('message', 'restricthtml');
2641  if (!dol_textishtml($messagePost)) {
2642  $messagePost = dol_nl2br($messagePost);
2643  }
2644  $message = $messagePost;
2645  $message .= '<br><br>';
2646 
2647  foreach ($external_contacts as $key => $info_sendto) {
2648  // altairis: avoid duplicate emails to external contacts
2649  if ($info_sendto['id'] == $user->contact_id) {
2650  continue;
2651  }
2652 
2653  if ($info_sendto['email'] != '' && $info_sendto['email'] != $object->origin_email) {
2654  if (!empty($info_sendto['email'])) {
2655  $sendto[$info_sendto['email']] = trim($info_sendto['firstname']." ".$info_sendto['lastname'])." <".$info_sendto['email'].">";
2656  }
2657 
2658  $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1').' ('.strtolower($info_sendto['libelle']).')';
2659  $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');
2660  }
2661  }
2662 
2663  // If public interface is not enable, use link to internal page into mail
2664  $url_public_ticket = (!empty($conf->global->TICKET_ENABLE_PUBLIC_INTERFACE) ?
2665  (!empty($conf->global->TICKET_URL_PUBLIC_INTERFACE) ? $conf->global->TICKET_URL_PUBLIC_INTERFACE.'/view.php' : dol_buildpath('/public/ticket/view.php', 2)) : dol_buildpath('/ticket/card.php', 2)).'?track_id='.$object->track_id;
2666  $message .= '<br>'.$langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer').' : <a href="'.$url_public_ticket.'">'.$object->track_id.'</a><br>';
2667 
2668  // Build final message
2669  $message = $message_intro.'<br><br>'.$message;
2670 
2671  // Add signature
2672  $message .= '<br>'.$message_signature;
2673 
2674  if (!empty($object->origin_email)) {
2675  $sendto[$object->origin_email] = $object->origin_email;
2676  }
2677 
2678  if ($object->fk_soc > 0 && !array_key_exists($object->origin_email, $sendto)) {
2679  $object->socid = $object->fk_soc;
2680  $object->fetch_thirdparty();
2681  if (!empty($object->thirdparty->email)) {
2682  $sendto[$object->thirdparty->email] = $object->thirdparty->email;
2683  }
2684  }
2685 
2686  // Add global email address recipient
2687  if ($conf->global->TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS && !array_key_exists($conf->global->TICKET_NOTIFICATION_EMAIL_TO, $sendto)) {
2688  if (!empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO)) {
2689  $sendto[$conf->global->TICKET_NOTIFICATION_EMAIL_TO] = $conf->global->TICKET_NOTIFICATION_EMAIL_TO;
2690  }
2691  }
2692 
2693  // Dont try to send email when no recipient
2694  if (!empty($sendto)) {
2695  $result = $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2696  if ($result) {
2697  // update last_msg_sent date (for last message sent to external users)
2698  $this->date_last_msg_sent = dol_now();
2699  $this->update($user, 1); // disable trigger when updating date_last_msg_sent. sendTicketMessageByEmail already create an event in actioncomm table.
2700  }
2701  }
2702  }
2703  }
2704  }
2705  }
2706 
2707  // Set status to "answered" if not set yet, but only if internal user and not private message
2708  // Or set status to "answered" if the client has answered and if the ticket has started
2709  if (($object->status < self::STATUS_IN_PROGRESS && !$user->socid && !$private) ||
2710  ($object->status > self::STATUS_IN_PROGRESS && $public_area)
2711  ) {
2712  $object->setStatut(3);
2713  }
2714  return 1;
2715  } else {
2716  setEventMessages($object->error, $object->errors, 'errors');
2717  return -1;
2718  }
2719  } else {
2720  setEventMessages($this->error, $this->errors, 'errors');
2721  return -1;
2722  }
2723  }
2724 
2725 
2738  public function sendTicketMessageByEmail($subject, $message, $send_internal_cc = 0, $array_receiver = array(), $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array())
2739  {
2740  global $conf, $langs;
2741 
2742  if ($conf->global->TICKET_DISABLE_ALL_MAILS) {
2743  dol_syslog(get_class($this).'::sendTicketMessageByEmail: Emails are disable into ticket setup by option TICKET_DISABLE_ALL_MAILS', LOG_WARNING);
2744  return false;
2745  }
2746 
2747  $langs->load("mails");
2748 
2749  include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
2750  //$contactstatic = new Contact($this->db);
2751 
2752  // If no receiver defined, load all ticket linked contacts
2753  if (!is_array($array_receiver) || !count($array_receiver) > 0) {
2754  $array_receiver = $this->getInfosTicketInternalContact();
2755  $array_receiver = array_merge($array_receiver, $this->getInfosTicketExternalContact());
2756  }
2757 
2758  if ($send_internal_cc) {
2759  $sendtocc = $conf->global->TICKET_NOTIFICATION_EMAIL_FROM;
2760  }
2761 
2762  $from = $conf->global->TICKET_NOTIFICATION_EMAIL_FROM;
2763  $is_sent = false;
2764  if (is_array($array_receiver) && count($array_receiver) > 0) {
2765  foreach ($array_receiver as $key => $receiver) {
2766  $deliveryreceipt = 0;
2767  $filepath = $filename_list;
2768  $filename = $mimefilename_list;
2769  $mimetype = $mimetype_list;
2770 
2771  // Send email
2772 
2773  $old_MAIN_MAIL_AUTOCOPY_TO = getDolGlobalString('MAIN_MAIL_AUTOCOPY_TO');
2774 
2775  if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) {
2776  $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
2777  }
2778 
2779  $upload_dir_tmp = $conf->user->dir_output."/".$user->id.'/temp';
2780 
2781  include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
2782  $trackid = "tic".$this->id;
2783 
2784  $moreinheader = 'X-Dolibarr-Info: sendTicketMessageByEmail'."\r\n";
2785  if (!empty($this->email_msgid)) {
2786  $moreinheader .= 'References <'.$this->email_msgid.'>'."\r\n";
2787  }
2788 
2789  $mailfile = new CMailFile($subject, $receiver, $from, $message, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1, '', '', $trackid, $moreinheader, 'ticket', '', $upload_dir_tmp);
2790  if ($mailfile->error) {
2791  setEventMessages($mailfile->error, null, 'errors');
2792  } else {
2793  $result = $mailfile->sendfile();
2794  if ($result) {
2795  setEventMessages($langs->trans('MailSuccessfulySent', $mailfile->getValidAddress($from, 2), $mailfile->getValidAddress($receiver, 2)), null, 'mesgs');
2796  $is_sent = true;
2797  } else {
2798  $langs->load("other");
2799  if ($mailfile->error) {
2800  setEventMessages($langs->trans('ErrorFailedToSendMail', $from, $receiver), null, 'errors');
2801  dol_syslog($langs->trans('ErrorFailedToSendMail', $from, $receiver).' : '.$mailfile->error);
2802  } else {
2803  setEventMessages('No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS', null, 'errors');
2804  }
2805  }
2806  }
2807 
2808  if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) {
2809  $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
2810  }
2811  }
2812  } else {
2813  $langs->load("other");
2814  setEventMessages($langs->trans('ErrorMailRecipientIsEmptyForSendTicketMessage'), null, 'warnings');
2815  }
2816 
2817  return $is_sent;
2818  }
2819 
2820  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2828  public function load_board($user, $mode)
2829  {
2830  // phpcs:enable
2831  global $conf, $user, $langs;
2832 
2833  $now = dol_now();
2834  $delay_warning = 0;
2835 
2836  $this->nbtodo = $this->nbtodolate = 0;
2837  $clause = " WHERE";
2838 
2839  $sql = "SELECT p.rowid, p.ref, p.datec as datec";
2840  $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
2841  if (isModEnabled('societe') && empty($user->rights->societe->client->voir) && !$user->socid) {
2842  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
2843  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2844  $clause = " AND";
2845  }
2846  $sql .= $clause." p.entity IN (".getEntity('ticket').")";
2847  if ($mode == 'opened') {
2848  $sql .= " AND p.fk_statut NOT IN (".Ticket::STATUS_CLOSED.", ".Ticket::STATUS_CANCELED.")";
2849  }
2850  if ($user->socid) {
2851  $sql .= " AND p.fk_soc = ".((int) $user->socid);
2852  }
2853 
2854  $resql = $this->db->query($sql);
2855  if ($resql) {
2856  $label = $labelShort = '';
2857  $status = '';
2858  if ($mode == 'opened') {
2859  $status = 'openall';
2860  //$delay_warning = $conf->ticket->warning_delay;
2861  $delay_warning = 0;
2862  $label = $langs->trans("MenuListNonClosed");
2863  $labelShort = $langs->trans("MenuListNonClosed");
2864  }
2865 
2866  $response = new WorkboardResponse();
2867  //$response->warning_delay = $delay_warning / 60 / 60 / 24;
2868  $response->label = $label;
2869  $response->labelShort = $labelShort;
2870  $response->url = DOL_URL_ROOT.'/ticket/list.php?search_fk_statut[]='.$status;
2871  $response->img = img_object('', "ticket");
2872 
2873  // This assignment in condition is not a bug. It allows walking the results.
2874  while ($obj = $this->db->fetch_object($resql)) {
2875  $response->nbtodo++;
2876  if ($mode == 'opened') {
2877  $datelimit = $this->db->jdate($obj->datec) + $delay_warning;
2878  if ($datelimit < $now) {
2879  //$response->nbtodolate++;
2880  }
2881  }
2882  }
2883  return $response;
2884  } else {
2885  $this->error = $this->db->lasterror();
2886  return -1;
2887  }
2888  }
2889 
2890  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2896  public function load_state_board()
2897  {
2898  // phpcs:enable
2899  global $conf, $user;
2900 
2901  $this->nb = array();
2902  $clause = "WHERE";
2903 
2904  $sql = "SELECT count(p.rowid) as nb";
2905  $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
2906  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
2907  if (empty($user->rights->societe->client->voir) && !$user->socid) {
2908  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2909  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2910  $clause = "AND";
2911  }
2912  $sql .= " ".$clause." p.entity IN (".getEntity('ticket').")";
2913 
2914  $resql = $this->db->query($sql);
2915  if ($resql) {
2916  // This assignment in condition is not a bug. It allows walking the results.
2917  while ($obj = $this->db->fetch_object($resql)) {
2918  $this->nb["ticket"] = $obj->nb;
2919  }
2920  $this->db->free($resql);
2921  return 1;
2922  } else {
2923  dol_print_error($this->db);
2924  $this->error = $this->db->lasterror();
2925  return -1;
2926  }
2927  }
2928 
2937  public static function replaceThirdparty($db, $origin_id, $dest_id)
2938  {
2939  $tables = array('ticket');
2940 
2941  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
2942  }
2943 }
2944 
2945 
2950 {
2955  public $rowid;
2956 
2960  public $id;
2961 
2965  public $ref;
2966 
2970  public $track_id;
2971 
2975  public $fk_soc;
2976 
2980  public $fk_project;
2981 
2986 
2991 
2996 
3000  public $subject;
3001 
3005  public $message;
3006 
3010  public $fk_statut;
3011 
3015  public $resolution;
3016 
3020  public $progress;
3021 
3025  public $timing;
3026 
3030  public $type_code;
3031 
3036 
3041 
3045  public $type_label;
3046 
3051 
3056 
3060  public $datec = '';
3061 
3065  public $date_read = '';
3066 
3070  public $date_last_msg_sent = '';
3071 
3075  public $date_close = '';
3076 }
$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 $db, $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.
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.
loadCacheMsgsTicket()
Load the list of event on ticket into ->cache_msgs_ticket.
setProgression($percent)
Define progression of current ticket.
listeContact($status=-1, $source='external', $list=0, $code='')
Get array of all contacts for a ticket Override method of file commonobject.class....
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]]',...
searchContactByEmail($email, $socid='', $case='')
Search and fetch contacts by email.
getIdTicketCustomerInvolvedContact()
Return id des contacts clients des intervenants.
getInfosTicketInternalContact()
Retrieve informations about internal contacts.
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.
load_state_board()
Load indicator this->nb of global stats widget.
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.
getInfosTicketExternalContact()
Retrieve informations about external contacts.
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)
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:47
if(isModEnabled('facture') &&!empty($user->rights->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') &&!empty($user->rights->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)) $resql
Social contributions to pay.
Definition: index.php:745
dirbasename($pathfile)
Return the relative dirname (relative to DOL_DATA_ROOT) of a full path string.
Definition: files.lib.php:3238
dol_is_file($pathoffile)
Return if path is a file.
Definition: files.lib.php:481
dol_is_dir($folder)
Test if filename is a directory.
Definition: files.lib.php:451
dol_move($srcfile, $destfile, $newmask=0, $overwriteifexists=1, $testvirus=0, $indexdatabase=1)
Move a file into another name.
Definition: files.lib.php:875
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.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='')
Set event messages in dol_events session object.
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.
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.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
if(!function_exists('utf8_encode')) if(!function_exists('utf8_decode')) 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:511
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.
$conf db
API class for accounts.
Definition: inc.php:41
generate_random_id($car=16)
Generate a random id.
Definition: ticket.lib.php:194