dolibarr  20.0.0-alpha
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-2024 Frédéric France <frederic.france@free.fr>
5  * Copyright (C) 2020 Laurent Destailleur <eldy@users.sourceforge.net>
6  * Copyright (C) 2023 Charlene Benke <charlene@patas-monkey.com>
7  * Copyright (C) 2023 Benjamin Falière <benjamin.faliere@altairis.fr>
8  * Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
9  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program. If not, see <https://www.gnu.org/licenses/>.
23  */
24 
31 // Put here all includes required by your class file
32 require_once DOL_DOCUMENT_ROOT."/core/class/commonobject.class.php";
33 require_once DOL_DOCUMENT_ROOT.'/fichinter/class/fichinter.class.php';
34 require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php';
35 
36 
40 class Ticket extends CommonObject
41 {
45  public $db;
46 
50  public $element = 'ticket';
51 
55  public $table_element = 'ticket';
56 
60  public $fk_element = 'fk_ticket';
61 
66  public $ismultientitymanaged = 1;
67 
71  public $isextrafieldmanaged = 1;
72 
76  public $picto = 'ticket';
77 
78 
82  public $track_id;
83 
87  public $fk_soc;
88  public $socid;
89 
93  public $fk_project;
94 
98  public $fk_contract;
99 
103  public $origin_email;
104 
108  public $fk_user_create;
109 
113  public $fk_user_assign;
114 
118  public $subject;
119 
123  public $message;
124 
128  public $private;
129 
135  public $fk_statut;
136 
140  public $status;
141 
145  public $resolution;
146 
150  public $progress;
151 
155  public $timing;
156 
160  public $type_code;
161 
165  public $category_code;
166 
170  public $severity_code;
171 
175  public $type_label;
176 
181 
186 
190  public $email_from;
191 
196 
201 
205  public $datec;
206 
210  public $date_read;
211 
215  public $date_last_msg_sent;
216 
220  public $date_close;
221 
225  public $cache_types_tickets;
226 
230  public $cache_category_tickets;
231 
235  public $cache_msgs_ticket;
236 
240  public $labelStatus;
241 
245  public $labelStatusShort;
246 
250  public $notify_tiers_at_create;
251 
255  public $email_msgid;
256 
260  public $email_date;
261 
265  public $ip;
266 
270  public $oldcopy;
271 
275  public $lines;
276 
277 
281  public $regeximgext = '\.gif|\.jpg|\.jpeg|\.png|\.bmp|\.webp|\.xpm|\.xbm'; // See also into images.lib.php
282 
286  const STATUS_NOT_READ = 0; // Draft. Not take into account yet.
287  const STATUS_READ = 1; // Ticket was read.
288  const STATUS_ASSIGNED = 2; // Ticket was just assigned to someone. Not in progress yet.
289  const STATUS_IN_PROGRESS = 3; // In progress
290  const STATUS_NEED_MORE_INFO = 5; // Waiting requester feedback
291  const STATUS_WAITING = 7; // On hold
292  const STATUS_CLOSED = 8; // Closed - Solved
293  const STATUS_CANCELED = 9; // Closed - Not solved
294 
295 
322  // BEGIN MODULEBUILDER PROPERTIES
323  public $fields = array(
324  'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'visible' => -2, 'enabled' => 1, 'position' => 1, 'notnull' => 1, 'index' => 1, 'comment' => "Id"),
325  'entity' => array('type' => 'integer', 'label' => 'Entity', 'visible' => 0, 'enabled' => 1, 'position' => 5, 'notnull' => 1, 'index' => 1),
326  '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),
327  'track_id' => array('type' => 'varchar(255)', 'label' => 'TicketTrackId', 'visible' => -2, 'enabled' => 1, 'position' => 11, 'notnull' => -1, 'searchall' => 1, 'help' => "Help text"),
328  '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'),
329  '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'),
330  'origin_replyto' => array('type' => 'mail', 'label' => 'EmailReplyto', 'visible' => -2, 'enabled' => 1, 'position' => 17, 'notnull' => 1, 'index' => 1, 'searchall' => 1, 'comment' => "Email to reply to", 'csslist' => 'tdoverflowmax150'),
331  'origin_references' => array('type' => 'text', 'label' => 'EmailReferences', 'visible' => -2, 'enabled' => 1, 'position' => 18, 'notnull' => 1, 'index' => 1, 'searchall' => 1, 'comment' => "References from origin email", 'csslist' => 'tdoverflowmax150'),
332  'subject' => array('type' => 'varchar(255)', 'label' => 'Subject', 'visible' => 1, 'enabled' => 1, 'position' => 19, 'notnull' => -1, 'searchall' => 1, 'help' => "", 'css' => 'maxwidth200 tdoverflowmax200', 'autofocusoncreate' => 1),
333  'type_code' => array('type' => 'varchar(32)', 'label' => 'Type', 'visible' => 1, 'enabled' => 1, 'position' => 20, 'notnull' => -1, 'help' => "", 'csslist' => 'maxwidth125 tdoverflowmax50'),
334  'category_code' => array('type' => 'varchar(32)', 'label' => 'TicketCategory', 'visible' => -1, 'enabled' => 1, 'position' => 21, 'notnull' => -1, 'help' => "", 'css' => 'maxwidth100 tdoverflowmax200'),
335  'severity_code' => array('type' => 'varchar(32)', 'label' => 'Severity', 'visible' => 1, 'enabled' => 1, 'position' => 22, 'notnull' => -1, 'help' => "", 'css' => 'maxwidth100'),
336  'fk_soc' => array('type' => 'integer:Societe:societe/class/societe.class.php', 'label' => 'ThirdParty', 'visible' => 1, 'enabled' => 'isModEnabled("societe")', 'position' => 50, 'notnull' => -1, 'index' => 1, 'searchall' => 1, 'help' => "OrganizationEventLinkToThirdParty", 'css' => 'tdoverflowmax150 maxwidth150onsmartphone'),
337  'notify_tiers_at_create' => array('type' => 'integer', 'label' => 'NotifyThirdparty', 'visible' => -1, 'enabled' => 0, 'position' => 51, 'notnull' => 1, 'index' => 1),
338  '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"),
339  'fk_contract' => array('type' => 'integer:Contrat:contrat/class/contrat.class.php', 'label' => 'Contract', 'visible' => -1, 'enabled' => '$conf->contract->enabled', 'position' => 53, 'notnull' => -1, 'index' => 1, 'help' => "LinkToContract"),
340  //'timing' => array('type'=>'varchar(20)', 'label'=>'Timing', 'visible'=>-1, 'enabled'=>1, 'position'=>42, 'notnull'=>-1, 'help'=>""), // what is this ?
341  'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'visible' => 1, 'enabled' => 1, 'position' => 500, 'notnull' => 1, 'csslist' => 'nowraponall'),
342  'date_read' => array('type' => 'datetime', 'label' => 'TicketReadOn', 'visible' => -1, 'enabled' => 1, 'position' => 501, 'notnull' => 1, 'csslist' => 'nowraponall'),
343  'date_last_msg_sent' => array('type' => 'datetime', 'label' => 'TicketLastMessageDate', 'visible' => 0, 'enabled' => 1, 'position' => 502, 'notnull' => -1),
344  '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'),
345  'date_close' => array('type' => 'datetime', 'label' => 'TicketCloseOn', 'visible' => -1, 'enabled' => 1, 'position' => 510, 'notnull' => 1),
346  'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'visible' => -1, 'enabled' => 1, 'position' => 520, 'notnull' => 1),
347  'message' => array('type' => 'html', 'label' => 'Message', 'visible' => -2, 'enabled' => 1, 'position' => 540, 'notnull' => -1,),
348  'email_msgid' => array('type' => 'varchar(255)', 'label' => 'EmailMsgID', 'visible' => -2, 'enabled' => 1, 'position' => 540, 'notnull' => -1, 'help' => 'EmailMsgIDDesc', 'csslist' => 'tdoverflowmax100'),
349  'email_date' => array('type' => 'datetime', 'label' => 'EmailDate', 'visible' => -2, 'enabled' => 1, 'position' => 541),
350  'progress' => array('type' => 'integer', 'label' => 'Progression', 'visible' => -1, 'enabled' => 1, 'position' => 540, 'notnull' => -1, 'css' => 'right', 'help' => "", 'isameasure' => 2, 'csslist' => 'width50'),
351  'resolution' => array('type' => 'integer', 'label' => 'Resolution', 'visible' => -1, 'enabled' => 'getDolGlobalString("TICKET_ENABLE_RESOLUTION")', 'position' => 550, 'notnull' => 1),
352  'model_pdf' => array('type' => 'varchar(255)', 'label' => 'PDFTemplate', 'enabled' => 1, 'visible' => 0, 'position' => 560),
353  'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 570),
354  'fk_statut' => array('type' => 'integer', 'label' => 'Status', 'visible' => 1, 'enabled' => 1, 'position' => 600, 'notnull' => 1, 'index' => 1, 'arrayofkeyval' => array(0 => 'Unread', 1 => 'Read', 2 => 'Assigned', 3 => 'InProgress', 5 => 'NeedMoreInformation', 7 => 'OnHold', 8 => 'SolvedClosed', 9 => 'Deleted')),
355  'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 900),
356  );
357  // END MODULEBUILDER PROPERTIES
358 
359 
365  public function __construct($db)
366  {
367  $this->db = $db;
368 
369  $this->labelStatusShort = array(
370  self::STATUS_NOT_READ => 'Unread',
371  self::STATUS_READ => 'Read',
372  self::STATUS_ASSIGNED => 'Assigned',
373  self::STATUS_IN_PROGRESS => 'InProgress',
374  self::STATUS_NEED_MORE_INFO => 'NeedMoreInformationShort',
375  self::STATUS_WAITING => 'OnHold',
376  self::STATUS_CLOSED => 'SolvedClosed',
377  self::STATUS_CANCELED => 'Canceled'
378  );
379  $this->labelStatus = array(
380  self::STATUS_NOT_READ => 'Unread',
381  self::STATUS_READ => 'Read',
382  self::STATUS_ASSIGNED => 'Assigned',
383  self::STATUS_IN_PROGRESS => 'InProgress',
384  self::STATUS_NEED_MORE_INFO => 'NeedMoreInformation',
385  self::STATUS_WAITING => 'OnHold',
386  self::STATUS_CLOSED => 'SolvedClosed',
387  self::STATUS_CANCELED => 'Canceled'
388  );
389 
390  if (!getDolGlobalString('TICKET_INCLUDE_SUSPENDED_STATUS')) {
391  unset($this->fields['fk_statut']['arrayofkeyval'][self::STATUS_WAITING]);
392  unset($this->labelStatusShort[self::STATUS_WAITING]);
393  unset($this->labelStatus[self::STATUS_WAITING]);
394  }
395  }
396 
403  private function verify()
404  {
405  $this->errors = array();
406 
407  $result = 0;
408 
409  // Clean parameters
410  if (isset($this->ref)) {
411  $this->ref = trim($this->ref);
412  }
413 
414  if (isset($this->track_id)) {
415  $this->track_id = trim($this->track_id);
416  }
417 
418  if (isset($this->fk_soc)) {
419  $this->fk_soc = (int) $this->fk_soc;
420  }
421 
422  if (isset($this->fk_project)) {
423  $this->fk_project = (int) $this->fk_project;
424  }
425 
426  if (isset($this->origin_email)) {
427  $this->origin_email = trim($this->origin_email);
428  }
429 
430  if (isset($this->fk_user_create)) {
431  $this->fk_user_create = (int) $this->fk_user_create;
432  }
433 
434  if (isset($this->fk_user_assign)) {
435  $this->fk_user_assign = (int) $this->fk_user_assign;
436  }
437 
438  if (isset($this->subject)) {
439  $this->subject = trim($this->subject);
440  }
441 
442  if (isset($this->message)) {
443  $this->message = trim($this->message);
444  if (dol_strlen($this->message) > 65000) {
445  $this->errors[] = 'ErrorFieldTooLong';
446  dol_syslog(get_class($this).'::create error -1 message too long', LOG_ERR);
447  $result = -1;
448  }
449  }
450 
451  if (isset($this->fk_statut)) {
452  $this->fk_statut = (int) $this->fk_statut;
453  }
454 
455  if (isset($this->resolution)) {
456  $this->resolution = trim($this->resolution);
457  }
458 
459  if (isset($this->progress)) {
460  $this->progress = (int) $this->progress;
461  }
462 
463  if (isset($this->timing)) {
464  $this->timing = trim($this->timing);
465  }
466 
467  if (isset($this->type_code)) {
468  $this->type_code = trim($this->type_code);
469  }
470 
471  if (isset($this->category_code)) {
472  $this->category_code = trim($this->category_code);
473  }
474 
475  if (isset($this->severity_code)) {
476  $this->severity_code = trim($this->severity_code);
477  }
478 
479  if (empty($this->ref)) {
480  $this->errors[] = 'ErrorTicketRefRequired';
481  dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR);
482  $result = -1;
483  }
484 
485  return $result;
486  }
487 
496  public function checkExistingRef(string $action, string $getRef): bool
497  {
498  $test = new self($this->db);
499 
500  if ($test->fetch('', $getRef) > 0) {
501  if (($action == 'add') || ($action == 'update' && $this->ref != $getRef)) {
502  return true;
503  }
504  }
505 
506  $this->ref = $getRef;
507  return false;
508  }
509 
517  public function create($user, $notrigger = 0)
518  {
519  global $conf;
520 
521  $error = 0;
522 
523  // Clean parameters
524  $this->datec = dol_now();
525  if (empty($this->track_id)) {
526  $this->track_id = generate_random_id(16);
527  }
528 
529  // Check more parameters
530  // If error, this->errors[] is filled
531  $result = $this->verify();
532 
533  if ($result >= 0) {
534  $this->entity = ((isset($this->entity) && is_numeric($this->entity)) ? $this->entity : $conf->entity);
535 
536  // Insert request
537  $sql = "INSERT INTO ".MAIN_DB_PREFIX."ticket(";
538  $sql .= "ref,";
539  $sql .= "track_id,";
540  $sql .= "fk_soc,";
541  $sql .= "fk_project,";
542  $sql .= "fk_contract,";
543  $sql .= "origin_email,";
544  $sql .= "origin_replyto,";
545  $sql .= "origin_references,";
546  $sql .= "fk_user_create,";
547  $sql .= "fk_user_assign,";
548  $sql .= "email_msgid,";
549  $sql .= "email_date,";
550  $sql .= "subject,";
551  $sql .= "message,";
552  $sql .= "fk_statut,";
553  $sql .= "resolution,";
554  $sql .= "progress,";
555  $sql .= "timing,";
556  $sql .= "type_code,";
557  $sql .= "category_code,";
558  $sql .= "severity_code,";
559  $sql .= "datec,";
560  $sql .= "date_read,";
561  $sql .= "date_close,";
562  $sql .= "entity,";
563  $sql .= "notify_tiers_at_create,";
564  $sql .= "model_pdf,";
565  $sql .= "ip";
566  $sql .= ") VALUES (";
567  $sql .= " ".(!isset($this->ref) ? '' : "'".$this->db->escape($this->ref)."'").",";
568  $sql .= " ".(!isset($this->track_id) ? 'NULL' : "'".$this->db->escape($this->track_id)."'").",";
569  $sql .= " ".($this->fk_soc > 0 ? ((int) $this->fk_soc) : "null").",";
570  $sql .= " ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null").",";
571  $sql .= " ".($this->fk_contract > 0 ? ((int) $this->fk_contract) : "null").",";
572  $sql .= " ".(!isset($this->origin_email) ? 'NULL' : "'".$this->db->escape($this->origin_email)."'").",";
573  $sql .= " ".(!isset($this->origin_replyto) ? 'NULL' : "'".$this->db->escape($this->origin_replyto)."'").",";
574  $sql .= " ".(!isset($this->origin_references) ? 'NULL' : "'".$this->db->escape($this->origin_references)."'").",";
575  $sql .= " ".(!isset($this->fk_user_create) ? ($user->id > 0 ? ((int) $user->id) : 'NULL') : ($this->fk_user_create > 0 ? ((int) $this->fk_user_create) : 'NULL')).",";
576  $sql .= " ".($this->fk_user_assign > 0 ? ((int) $this->fk_user_assign) : 'NULL').",";
577  $sql .= " ".(empty($this->email_msgid) ? 'NULL' : "'".$this->db->escape($this->email_msgid)."'").",";
578  $sql .= " ".(empty($this->email_date) ? 'NULL' : "'".$this->db->idate($this->email_date)."'").",";
579  $sql .= " ".(!isset($this->subject) ? 'NULL' : "'".$this->db->escape($this->subject)."'").",";
580  $sql .= " ".(!isset($this->message) ? 'NULL' : "'".$this->db->escape($this->message)."'").",";
581  $sql .= " ".(!isset($this->status) ? '0' : ((int) $this->status)).",";
582  $sql .= " ".(!isset($this->resolution) ? 'NULL' : ((int) $this->resolution)).",";
583  $sql .= " ".(!isset($this->progress) ? '0' : ((int) $this->progress)).",";
584  $sql .= " ".(!isset($this->timing) ? 'NULL' : "'".$this->db->escape($this->timing)."'").",";
585  $sql .= " ".(!isset($this->type_code) ? 'NULL' : "'".$this->db->escape($this->type_code)."'").",";
586  $sql .= " ".(empty($this->category_code) || $this->category_code == '-1' ? 'NULL' : "'".$this->db->escape($this->category_code)."'").",";
587  $sql .= " ".(!isset($this->severity_code) ? 'NULL' : "'".$this->db->escape($this->severity_code)."'").",";
588  $sql .= " ".(!isset($this->datec) || dol_strlen($this->datec) == 0 ? 'NULL' : "'".$this->db->idate($this->datec)."'").",";
589  $sql .= " ".(!isset($this->date_read) || dol_strlen($this->date_read) == 0 ? 'NULL' : "'".$this->db->idate($this->date_read)."'").",";
590  $sql .= " ".(!isset($this->date_close) || dol_strlen($this->date_close) == 0 ? 'NULL' : "'".$this->db->idate($this->date_close)."'");
591  $sql .= ", ".((int) $this->entity);
592  $sql .= ", ".(!isset($this->notify_tiers_at_create) ? 1 : ((int) $this->notify_tiers_at_create));
593  $sql .= ", '".$this->db->escape($this->model_pdf)."'";
594  $sql .= ", ".(!isset($this->ip) ? 'NULL' : "'".$this->db->escape($this->ip)."'");
595  $sql .= ")";
596 
597  $this->db->begin();
598 
599  dol_syslog(get_class($this)."::create", LOG_DEBUG);
600  $resql = $this->db->query($sql);
601  if (!$resql) {
602  $error++;
603  $this->errors[] = "Error ".$this->db->lasterror();
604  }
605 
606  if (!$error) {
607  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."ticket");
608  }
609 
610  if (!$error && getDolGlobalString('TICKET_ADD_AUTHOR_AS_CONTACT') && empty($this->context["createdfrompublicinterface"])) {
611  // add creator as contributor
612 
613  // We first check the type of contact (internal or external)
614  if (!empty($user->socid) && !empty($user->contact_id) && getDolGlobalInt('TICKET_ADD_AUTHOR_AS_CONTACT') == 2) {
615  $contact_type = 'external';
616  $contributor_id = $user->contact_id;
617  } else {
618  $contact_type = 'internal';
619  $contributor_id = $user->id;
620  }
621 
622  // We add the creator as contributor
623  if ($this->add_contact($contributor_id, 'CONTRIBUTOR', $contact_type) < 0) {
624  $error++;
625  }
626  }
627 
628  if (!$error && $this->fk_user_assign > 0) {
629  if ($this->add_contact($this->fk_user_assign, 'SUPPORTTEC', 'internal') < 0) {
630  $error++;
631  }
632  }
633 
634 
635  //Update extrafield
636  if (!$error) {
637  $result = $this->insertExtraFields();
638  if ($result < 0) {
639  $error++;
640  }
641  }
642 
643  if (!$error && !$notrigger) {
644  // Call trigger
645  $result = $this->call_trigger('TICKET_CREATE', $user);
646  if ($result < 0) {
647  $error++;
648  }
649  // End call triggers
650  }
651 
652  // Commit or rollback
653  if ($error) {
654  foreach ($this->errors as $errmsg) {
655  dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
656  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
657  }
658  $this->db->rollback();
659  return -1 * $error;
660  } else {
661  $this->db->commit();
662  return $this->id;
663  }
664  } else {
665  $this->db->rollback();
666  dol_syslog(get_class($this)."::Create fails verify ".implode(',', $this->errors), LOG_WARNING);
667  return -3;
668  }
669  }
670 
680  public function fetch($id = 0, $ref = '', $track_id = '', $email_msgid = '')
681  {
682  global $langs;
683 
684  // Check parameters
685  if (empty($id) && empty($ref) && empty($track_id) && empty($email_msgid)) {
686  $this->error = 'ErrorWrongParameters';
687  dol_print_error(null, get_class($this)."::fetch ".$this->error);
688  return -1;
689  }
690 
691  $sql = "SELECT";
692  $sql .= " t.rowid,";
693  $sql .= " t.entity,";
694  $sql .= " t.ref,";
695  $sql .= " t.track_id,";
696  $sql .= " t.fk_soc,";
697  $sql .= " t.fk_project,";
698  $sql .= " t.fk_contract,";
699  $sql .= " t.origin_email,";
700  $sql .= " t.origin_replyto,";
701  $sql .= " t.origin_references,";
702  $sql .= " t.fk_user_create,";
703  $sql .= " t.fk_user_assign,";
704  $sql .= " t.email_msgid,";
705  $sql .= " t.email_date,";
706  $sql .= " t.subject,";
707  $sql .= " t.message,";
708  $sql .= " t.fk_statut as status,";
709  $sql .= " t.resolution,";
710  $sql .= " t.progress,";
711  $sql .= " t.timing,";
712  $sql .= " t.type_code,";
713  $sql .= " t.category_code,";
714  $sql .= " t.severity_code,";
715  $sql .= " t.datec,";
716  $sql .= " t.date_read,";
717  $sql .= " t.date_last_msg_sent,";
718  $sql .= " t.date_close,";
719  $sql .= " t.tms,";
720  $sql .= " t.model_pdf,";
721  $sql .= " t.extraparams,";
722  $sql .= " t.ip,";
723  $sql .= " type.label as type_label, category.label as category_label, severity.label as severity_label";
724  $sql .= " FROM ".MAIN_DB_PREFIX."ticket as t";
725  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_type as type ON type.code=t.type_code";
726  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_category as category ON category.code=t.category_code";
727  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_severity as severity ON severity.code=t.severity_code";
728 
729  if ($id) {
730  $sql .= " WHERE t.rowid = ".((int) $id);
731  } else {
732  $sql .= " WHERE t.entity IN (".getEntity($this->element, 1).")";
733  if (!empty($ref)) {
734  $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
735  } elseif ($track_id) {
736  $sql .= " AND t.track_id = '".$this->db->escape($track_id)."'";
737  } else {
738  $sql .= " AND t.email_msgid = '".$this->db->escape($email_msgid)."'";
739  }
740  }
741 
742  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
743  $resql = $this->db->query($sql);
744  if ($resql) {
745  if ($this->db->num_rows($resql)) {
746  $obj = $this->db->fetch_object($resql);
747 
748  $this->id = $obj->rowid;
749  $this->entity = $obj->entity;
750  $this->ref = $obj->ref;
751  $this->track_id = $obj->track_id;
752  $this->fk_soc = $obj->fk_soc;
753  $this->socid = $obj->fk_soc; // for fetch_thirdparty() method
754  $this->fk_project = $obj->fk_project;
755  $this->fk_contract = $obj->fk_contract;
756  $this->origin_email = $obj->origin_email;
757  $this->origin_replyto = $obj->origin_replyto;
758  $this->origin_references = $obj->origin_references;
759  $this->fk_user_create = $obj->fk_user_create;
760  $this->fk_user_assign = $obj->fk_user_assign;
761  $this->email_msgid = $obj->email_msgid;
762  $this->email_date = $this->db->jdate($obj->email_date);
763  $this->subject = $obj->subject;
764  $this->message = $obj->message;
765  $this->model_pdf = $obj->model_pdf;
766  $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
767  $this->ip = $obj->ip;
768 
769  $this->status = $obj->status;
770  $this->fk_statut = $this->status; // For backward compatibility
771 
772  $this->resolution = $obj->resolution;
773  $this->progress = $obj->progress;
774  $this->timing = $obj->timing;
775 
776  $this->type_code = $obj->type_code;
777  $label_type = ($langs->trans("TicketTypeShort".$obj->type_code) != "TicketTypeShort".$obj->type_code ? $langs->trans("TicketTypeShort".$obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));
778  $this->type_label = $label_type;
779 
780  $this->category_code = $obj->category_code;
781  $label_category = ($langs->trans("TicketCategoryShort".$obj->category_code) != "TicketCategoryShort".$obj->category_code ? $langs->trans("TicketCategoryShort".$obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));
782  $this->category_label = $label_category;
783 
784  $this->severity_code = $obj->severity_code;
785  $label_severity = ($langs->trans("TicketSeverityShort".$obj->severity_code) != "TicketSeverityShort".$obj->severity_code ? $langs->trans("TicketSeverityShort".$obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));
786  $this->severity_label = $label_severity;
787 
788  $this->datec = $this->db->jdate($obj->datec);
789  $this->date_creation = $this->db->jdate($obj->datec);
790  $this->date_read = $this->db->jdate($obj->date_read);
791  $this->date_validation = $this->db->jdate($obj->date_read);
792  $this->date_last_msg_sent = $this->db->jdate($obj->date_last_msg_sent);
793  $this->date_close = $this->db->jdate($obj->date_close);
794  $this->tms = $this->db->jdate($obj->tms);
795  $this->date_modification = $this->db->jdate($obj->tms);
796 
797  $this->fetch_optionals();
798 
799  $this->db->free($resql);
800  return 1;
801  } else {
802  return 0;
803  }
804  } else {
805  $this->error = "Error ".$this->db->lasterror();
806  dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
807  return -1;
808  }
809  }
810 
823  public function fetchAll($user, $sortorder = 'ASC', $sortfield = 't.datec', $limit = 0, $offset = 0, $arch = 0, $filter = '')
824  {
825  global $langs, $extrafields;
826 
827  // fetch optionals attributes and labels
828  $extrafields->fetch_name_optionals_label($this->table_element);
829 
830  $sql = "SELECT";
831  $sql .= " t.rowid,";
832  $sql .= " t.ref,";
833  $sql .= " t.track_id,";
834  $sql .= " t.fk_soc,";
835  $sql .= " t.fk_project,";
836  $sql .= " t.fk_contract,";
837  $sql .= " t.origin_email,";
838  $sql .= " t.origin_replyto,";
839  $sql .= " t.origin_references,";
840  $sql .= " t.fk_user_create, uc.lastname as user_create_lastname, uc.firstname as user_create_firstname,";
841  $sql .= " t.fk_user_assign, ua.lastname as user_assign_lastname, ua.firstname as user_assign_firstname,";
842  $sql .= " t.subject,";
843  $sql .= " t.message,";
844  $sql .= " t.fk_statut as status,";
845  $sql .= " t.resolution,";
846  $sql .= " t.progress,";
847  $sql .= " t.timing,";
848  $sql .= " t.type_code,";
849  $sql .= " t.category_code,";
850  $sql .= " t.severity_code,";
851  $sql .= " t.datec,";
852  $sql .= " t.date_read,";
853  $sql .= " t.date_last_msg_sent,";
854  $sql .= " t.date_close,";
855  $sql .= " t.tms,";
856  $sql .= " type.label as type_label, category.label as category_label, severity.label as severity_label";
857  // Add fields for extrafields
858  if ($extrafields->attributes[$this->table_element]['count'] > 0) {
859  foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
860  $sql .= ($extrafields->attributes[$this->table_element]['type'][$key] != 'separate' ? ",ef.".$key." as options_".$key : '');
861  }
862  }
863  $sql .= " FROM ".MAIN_DB_PREFIX."ticket as t";
864  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_type as type ON type.code = t.type_code";
865  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_category as category ON category.code = t.category_code";
866  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_severity as severity ON severity.code = t.severity_code";
867  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = t.fk_soc";
868  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as uc ON uc.rowid = t.fk_user_create";
869  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as ua ON ua.rowid = t.fk_user_assign";
870  if ($extrafields->attributes[$this->table_element]['count'] > 0) {
871  if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {
872  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."ticket_extrafields as ef on (t.rowid = ef.fk_object)";
873  }
874  }
875  $sql .= " WHERE t.entity IN (".getEntity('ticket').")";
876 
877  // Manage filter
878  if (is_array($filter)) {
879  foreach ($filter as $key => $value) {
880  if (strpos($key, 'date')) { // To allow $filter['YEAR(s.dated)']=>$year
881  $sql .= " AND ".$this->db->sanitize($key)." = '".$this->db->escape($value)."'";
882  } elseif (($key == 't.fk_user_assign') || ($key == 't.type_code') || ($key == 't.category_code') || ($key == 't.severity_code') || ($key == 't.fk_soc')) {
883  $sql .= " AND ".$this->db->sanitize($key)." = '".$this->db->escape($value)."'";
884  } elseif ($key == 't.fk_statut') {
885  if (is_array($value) && count($value) > 0) {
886  $sql .= " AND ".$this->db->sanitize($key)." IN (".$this->db->sanitize(implode(',', $value)).")";
887  } else {
888  $sql .= " AND ".$this->db->sanitize($key).' = '.((int) $value);
889  }
890  } elseif ($key == 't.fk_contract') {
891  $sql .= " AND ".$this->db->sanitize($key).' = '.((int) $value);
892  } else {
893  $sql .= " AND ".$this->db->sanitize($key)." LIKE '%".$this->db->escape($this->db->escapeforlike($value))."%'";
894  }
895  }
896 
897  $filter = '';
898  }
899 
900  // Manage filter
901  $errormessage = '';
902  $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
903  if ($errormessage) {
904  $this->errors[] = $errormessage;
905  dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
906  return -1;
907  }
908 
909  // Case of external user
910  $socid = $user->socid ? $user->socid : 0;
911  // If the internal user must only see his customers, force searching by him
912  $search_sale = 0;
913  if (!$user->hasRight('societe', 'client', 'voir')) {
914  $search_sale = $user->id;
915  }
916  // Search on sale representative
917  if ($search_sale && $search_sale != '-1') {
918  if ($search_sale == -2) {
919  $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc)";
920  } elseif ($search_sale > 0) {
921  $sql .= " AND EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc AND sc.fk_user = ".((int) $search_sale).")";
922  }
923  }
924  // Search on socid
925  if ($socid) {
926  $sql .= " AND t.fk_soc = ".((int) $socid);
927  }
928 
929  $sql .= $this->db->order($sortfield, $sortorder);
930  if (!empty($limit)) {
931  $sql .= $this->db->plimit($limit + 1, $offset);
932  }
933 
934  dol_syslog(get_class($this)."::fetchAll", LOG_DEBUG);
935  $resql = $this->db->query($sql);
936 
937  if ($resql) {
938  $this->lines = array();
939 
940  $num = $this->db->num_rows($resql);
941  $i = 0;
942 
943  if ($num) {
944  while ($i < $num) {
945  $obj = $this->db->fetch_object($resql);
946 
947  $line = new self($this->db);
948 
949  $line->id = $obj->rowid;
950  //$line->rowid = $obj->rowid;
951  $line->ref = $obj->ref;
952  $line->track_id = $obj->track_id;
953  $line->fk_soc = $obj->fk_soc;
954  $line->fk_project = $obj->fk_project;
955  $line->fk_contract = $obj->fk_contract;
956  $line->origin_email = $obj->origin_email;
957  $line->origin_replyto = $obj->origin_replyto;
958  $line->origin_references = $obj->origin_references;
959 
960  $line->fk_user_create = $obj->fk_user_create;
961  $line->fk_user_assign = $obj->fk_user_assign;
962 
963  $line->subject = $obj->subject;
964  $line->message = $obj->message;
965  $line->fk_statut = $obj->status;
966  $line->status = $obj->status;
967  $line->resolution = $obj->resolution;
968  $line->progress = $obj->progress;
969  $line->timing = $obj->timing;
970 
971  $label_type = ($langs->trans("TicketTypeShort".$obj->type_code) != "TicketTypeShort".$obj->type_code ? $langs->trans("TicketTypeShort".$obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));
972  $line->type_label = $label_type;
973 
974  $this->category_code = $obj->category_code;
975  $label_category = ($langs->trans("TicketCategoryShort".$obj->category_code) != "TicketCategoryShort".$obj->category_code ? $langs->trans("TicketCategoryShort".$obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));
976  $line->category_label = $label_category;
977 
978  $this->severity_code = $obj->severity_code;
979  $label_severity = ($langs->trans("TicketSeverityShort".$obj->severity_code) != "TicketSeverityShort".$obj->severity_code ? $langs->trans("TicketSeverityShort".$obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));
980  $line->severity_label = $label_severity;
981 
982  $line->datec = $this->db->jdate($obj->datec);
983  $line->date_read = $this->db->jdate($obj->date_read);
984  $line->date_last_msg_sent = $this->db->jdate($obj->date_last_msg_sent);
985  $line->date_close = $this->db->jdate($obj->date_close);
986 
987  // Extra fields
988  if ($extrafields->attributes[$this->table_element]['count'] > 0) {
989  if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {
990  foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
991  $tmpkey = 'options_'.$key;
992  $line->{$tmpkey} = $obj->$tmpkey;
993  }
994  }
995  }
996  $this->lines[$i] = $line;
997  $i++;
998  }
999  }
1000  $this->db->free($resql);
1001  return $num;
1002  } else {
1003  $this->error = "Error ".$this->db->lasterror();
1004  dol_syslog(get_class($this)."::fetchAll ".$this->error, LOG_ERR);
1005  return -1;
1006  }
1007  }
1008 
1016  public function update($user, $notrigger = 0)
1017  {
1018  $error = 0;
1019 
1020  // $this->oldcopy should have been set by the caller of update
1021  //if (empty($this->oldcopy)) {
1022  // dol_syslog("this->oldcopy should have been set by the caller of update (here properties were already modified)", LOG_WARNING);
1023  // $this->oldcopy = dol_clone($this, 2);
1024  //}
1025 
1026  // Clean parameters
1027  if (isset($this->ref)) {
1028  $this->ref = trim($this->ref);
1029  }
1030 
1031  if (isset($this->track_id)) {
1032  $this->track_id = trim($this->track_id);
1033  }
1034 
1035  if (isset($this->fk_soc)) {
1036  $this->fk_soc = (int) $this->fk_soc;
1037  }
1038 
1039  if (isset($this->fk_project)) {
1040  $this->fk_project = (int) $this->fk_project;
1041  }
1042 
1043  if (isset($this->fk_contract)) {
1044  $this->fk_contract = (int) $this->fk_contract;
1045  }
1046 
1047  if (isset($this->origin_email)) {
1048  $this->origin_email = trim($this->origin_email);
1049  }
1050 
1051  if (isset($this->fk_user_create)) {
1052  $this->fk_user_create = (int) $this->fk_user_create;
1053  }
1054 
1055  if (isset($this->fk_user_assign)) {
1056  $this->fk_user_assign = (int) $this->fk_user_assign;
1057  }
1058 
1059  if (isset($this->subject)) {
1060  $this->subject = trim($this->subject);
1061  }
1062 
1063  if (isset($this->message)) {
1064  $this->message = trim($this->message);
1065  if (dol_strlen($this->message) > 65000) {
1066  $this->errors[] = 'ErrorFieldTooLong';
1067  dol_syslog(get_class($this).'::update error -1 message too long', LOG_ERR);
1068  return -1;
1069  }
1070  }
1071 
1072  if (isset($this->fk_statut)) {
1073  $this->fk_statut = (int) $this->fk_statut;
1074  }
1075 
1076  if (isset($this->resolution)) {
1077  $this->resolution = trim($this->resolution);
1078  }
1079 
1080  if (isset($this->progress)) {
1081  $this->progress = (int) $this->progress;
1082  }
1083 
1084  if (isset($this->timing)) {
1085  $this->timing = trim($this->timing);
1086  }
1087 
1088  if (isset($this->type_code)) {
1089  $this->timing = trim($this->type_code);
1090  }
1091 
1092  if (isset($this->category_code)) {
1093  $this->timing = trim($this->category_code);
1094  }
1095 
1096  if (isset($this->severity_code)) {
1097  $this->timing = trim($this->severity_code);
1098  }
1099  if (isset($this->model_pdf)) {
1100  $this->model_pdf = trim($this->model_pdf);
1101  }
1102  // Check parameters
1103  // Put here code to add a control on parameters values
1104  // Update request
1105  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket SET";
1106  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "").",";
1107  $sql .= " track_id=".(isset($this->track_id) ? "'".$this->db->escape($this->track_id)."'" : "null").",";
1108  $sql .= " fk_soc=".(isset($this->fk_soc) ? (int) $this->fk_soc : "null").",";
1109  $sql .= " fk_project=".(isset($this->fk_project) ? (int) $this->fk_project : "null").",";
1110  $sql .= " fk_contract=".(isset($this->fk_contract) ? (int) $this->fk_contract : "null").",";
1111  $sql .= " origin_email=".(isset($this->origin_email) ? "'".$this->db->escape($this->origin_email)."'" : "null").",";
1112  $sql .= " origin_replyto=".(isset($this->origin_replyto) ? "'".$this->db->escape($this->origin_replyto)."'" : "null").",";
1113  $sql .= " origin_references=".(isset($this->origin_references) ? "'".$this->db->escape($this->origin_references)."'" : "null").",";
1114  $sql .= " fk_user_create=".(isset($this->fk_user_create) ? (int) $this->fk_user_create : "null").",";
1115  $sql .= " fk_user_assign=".(isset($this->fk_user_assign) ? (int) $this->fk_user_assign : "null").",";
1116  $sql .= " subject=".(isset($this->subject) ? "'".$this->db->escape($this->subject)."'" : "null").",";
1117  $sql .= " message=".(isset($this->message) ? "'".$this->db->escape($this->message)."'" : "null").",";
1118  $sql .= " fk_statut=".(isset($this->fk_statut) ? (int) $this->fk_statut : "0").",";
1119  $sql .= " resolution=".(isset($this->resolution) ? (int) $this->resolution : "null").",";
1120  $sql .= " progress=".(isset($this->progress) ? "'".$this->db->escape($this->progress)."'" : "null").",";
1121  $sql .= " timing=".(isset($this->timing) ? "'".$this->db->escape($this->timing)."'" : "null").",";
1122  $sql .= " type_code=".(isset($this->type_code) ? "'".$this->db->escape($this->type_code)."'" : "null").",";
1123  $sql .= " category_code=".(isset($this->category_code) ? "'".$this->db->escape($this->category_code)."'" : "null").",";
1124  $sql .= " severity_code=".(isset($this->severity_code) ? "'".$this->db->escape($this->severity_code)."'" : "null").",";
1125  $sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1126  $sql .= " date_read=".(dol_strlen($this->date_read) != 0 ? "'".$this->db->idate($this->date_read)."'" : 'null').",";
1127  $sql .= " date_last_msg_sent=".(dol_strlen($this->date_last_msg_sent) != 0 ? "'".$this->db->idate($this->date_last_msg_sent)."'" : 'null').",";
1128  $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1129  $sql .= " date_close=".(dol_strlen($this->date_close) != 0 ? "'".$this->db->idate($this->date_close)."'" : 'null');
1130  $sql .= " WHERE rowid=".((int) $this->id);
1131 
1132  $this->db->begin();
1133 
1134  $resql = $this->db->query($sql);
1135  if (!$resql) {
1136  $error++;
1137  $this->errors[] = "Error ".$this->db->lasterror();
1138  }
1139 
1140  if (!$error) {
1141  // Update extrafields
1142  $result = $this->insertExtraFields();
1143  if ($result < 0) {
1144  $error++;
1145  }
1146  }
1147 
1148  if (!$error && !$notrigger) {
1149  // Call trigger
1150  $result = $this->call_trigger('TICKET_MODIFY', $user);
1151  if ($result < 0) {
1152  $error++;
1153  }
1154  // End call triggers
1155  }
1156 
1157  // Commit or rollback
1158  if ($error) {
1159  foreach ($this->errors as $errmsg) {
1160  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1161  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1162  }
1163  $this->db->rollback();
1164  return -1 * $error;
1165  } else {
1166  $this->db->commit();
1167  return 1;
1168  }
1169  }
1170 
1178  public function delete($user, $notrigger = 0)
1179  {
1180  $error = 0;
1181 
1182  $this->db->begin();
1183 
1184  if (!$error) {
1185  if (!$notrigger) {
1186  // Call trigger
1187  $result = $this->call_trigger('TICKET_DELETE', $user);
1188  if ($result < 0) {
1189  $error++;
1190  }
1191  // End call triggers
1192  }
1193  }
1194 
1195  if (!$error) {
1196  // Delete linked contacts
1197  $res = $this->delete_linked_contact();
1198  if ($res < 0) {
1199  dol_syslog(get_class($this)."::delete error", LOG_ERR);
1200  $error++;
1201  }
1202  }
1203 
1204  if (!$error) {
1205  // Delete linked object
1206  $res = $this->deleteObjectLinked();
1207  if ($res < 0) {
1208  $error++;
1209  }
1210  }
1211 
1212  // Removed extrafields
1213  if (!$error) {
1214  $result = $this->deleteExtraFields();
1215  if ($result < 0) {
1216  $error++;
1217  dol_syslog(get_class($this)."::delete error -3 ".$this->error, LOG_ERR);
1218  }
1219  }
1220 
1221  // Delete all child tables
1222 
1223  if (!$error) {
1224  $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_ticket";
1225  $sql .= " WHERE fk_ticket = ".(int) $this->id;
1226 
1227  $result = $this->db->query($sql);
1228  if (!$result) {
1229  $error++;
1230  $this->errors[] = $this->db->lasterror();
1231  }
1232  }
1233 
1234  if (!$error) {
1235  $sql = "DELETE FROM ".MAIN_DB_PREFIX."ticket";
1236  $sql .= " WHERE rowid=".((int) $this->id);
1237 
1238  dol_syslog(get_class($this)."::delete sql=".$sql);
1239  $resql = $this->db->query($sql);
1240  if (!$resql) {
1241  $error++;
1242  $this->errors[] = "Error ".$this->db->lasterror();
1243  }
1244  }
1245 
1246  // Commit or rollback
1247  if ($error) {
1248  foreach ($this->errors as $errmsg) {
1249  dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1250  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1251  }
1252  $this->db->rollback();
1253  return -1 * $error;
1254  } else {
1255  $this->db->commit();
1256  return 1;
1257  }
1258  }
1259 
1267  public function createFromClone(User $user, $fromid)
1268  {
1269  $error = 0;
1270 
1271  $object = new Ticket($this->db);
1272 
1273  $this->db->begin();
1274 
1275  // Load source object
1276  $object->fetch($fromid);
1277 
1278  // Clear fields
1279  $object->id = 0;
1280  $object->statut = 0;
1281  $object->status = 0;
1282 
1283  // Create clone
1284  $object->context['createfromclone'] = 'createfromclone';
1285  $result = $object->create($user);
1286 
1287  // Other options
1288  if ($result < 0) {
1289  $this->error = $object->error;
1290  $error++;
1291  }
1292 
1293  if (!$error) {
1294  }
1295 
1296  unset($object->context['createfromclone']);
1297 
1298  // End
1299  if (!$error) {
1300  $this->db->commit();
1301  return $object->id;
1302  } else {
1303  $this->db->rollback();
1304  return -1;
1305  }
1306  }
1307 
1314  public function initAsSpecimen()
1315  {
1316  $this->id = 0;
1317  $this->entity = 1;
1318  $this->ref = 'TI0501-001';
1319  $this->track_id = 'XXXXaaaa';
1320  $this->origin_email = 'email@email.com';
1321  $this->fk_project = 1;
1322  $this->fk_user_create = 1;
1323  $this->fk_user_assign = 1;
1324  $this->subject = 'Subject of ticket';
1325  $this->message = 'Message of ticket';
1326  $this->status = 0;
1327  $this->resolution = '1';
1328  $this->progress = 10;
1329  //$this->timing = '30';
1330  $this->type_code = 'TYPECODE';
1331  $this->category_code = 'CATEGORYCODE';
1332  $this->severity_code = 'SEVERITYCODE';
1333  $this->datec = dol_now();
1334  $this->date_read = dol_now();
1335  $this->date_last_msg_sent = dol_now();
1336  $this->date_close = dol_now();
1337  $this->tms = dol_now();
1338 
1339  return 1;
1340  }
1341 
1348  public function printSelectStatus($selected = "")
1349  {
1350  print Form::selectarray('search_fk_statut', $this->labelStatusShort, $selected, $show_empty = 1, $key_in_label = 0, $value_as_key = 0, $option = '', $translate = 1, $maxlen = 0, $disabled = 0, $sort = '', $morecss = '');
1351  }
1352 
1353 
1359  public function loadCacheTypesTickets()
1360  {
1361  global $langs;
1362 
1363  if (!empty($this->cache_types_tickets) && count($this->cache_types_tickets)) {
1364  return 0;
1365  }
1366  // Cache deja charge
1367 
1368  $sql = "SELECT rowid, code, label, use_default, pos, description";
1369  $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_type";
1370  $sql .= " WHERE entity IN (".getEntity('c_ticket_type').")";
1371  $sql .= " AND active > 0";
1372  $sql .= " ORDER BY pos";
1373  dol_syslog(get_class($this)."::load_cache_type_tickets", LOG_DEBUG);
1374  $resql = $this->db->query($sql);
1375  if ($resql) {
1376  $num = $this->db->num_rows($resql);
1377  $i = 0;
1378  while ($i < $num) {
1379  $obj = $this->db->fetch_object($resql);
1380  $label = ($langs->trans("TicketTypeShort".$obj->code) != "TicketTypeShort".$obj->code ? $langs->trans("TicketTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1381  $this->cache_types_tickets[$obj->rowid]['code'] = $obj->code;
1382  $this->cache_types_tickets[$obj->rowid]['label'] = $label;
1383  $this->cache_types_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1384  $this->cache_types_tickets[$obj->rowid]['pos'] = $obj->pos;
1385  $i++;
1386  }
1387  return $num;
1388  } else {
1389  dol_print_error($this->db);
1390  return -1;
1391  }
1392  }
1393 
1400  public function loadCacheCategoriesTickets($publicgroup = -1)
1401  {
1402  global $conf, $langs;
1403 
1404  if ($publicgroup == -1 && !empty($this->cache_category_ticket) && count($this->cache_category_tickets)) {
1405  // Cache already loaded
1406  return 0;
1407  }
1408 
1409  $sql = "SELECT rowid, code, label, use_default, pos, description, public, active, force_severity, fk_parent";
1410  $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_category";
1411  $sql .= " WHERE entity IN (".getEntity('c_ticket_category').")";
1412  $sql .= " AND active > 0";
1413  if ($publicgroup > -1) {
1414  $sql .= " AND public = ".((int) $publicgroup);
1415  }
1416  $sql .= " ORDER BY pos";
1417 
1418  dol_syslog(get_class($this)."::load_cache_categories_tickets", LOG_DEBUG);
1419 
1420  $resql = $this->db->query($sql);
1421  if ($resql) {
1422  $num = $this->db->num_rows($resql);
1423  $i = 0;
1424  while ($i < $num) {
1425  $obj = $this->db->fetch_object($resql);
1426  $this->cache_category_tickets[$obj->rowid]['code'] = $obj->code;
1427  $this->cache_category_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1428  $this->cache_category_tickets[$obj->rowid]['pos'] = $obj->pos;
1429  $this->cache_category_tickets[$obj->rowid]['public'] = $obj->public;
1430  $this->cache_category_tickets[$obj->rowid]['active'] = $obj->active;
1431  $this->cache_category_tickets[$obj->rowid]['force_severity'] = $obj->force_severity;
1432  $this->cache_category_tickets[$obj->rowid]['fk_parent'] = $obj->fk_parent;
1433 
1434  // If translation exists, we use it to store already translated string.
1435  // Warning: You should not use this and recompute the translated string into caller code to get the value into expected language
1436  $label = ($langs->trans("TicketCategoryShort".$obj->code) != "TicketCategoryShort".$obj->code ? $langs->trans("TicketCategoryShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1437  $this->cache_category_tickets[$obj->rowid]['label'] = $label;
1438 
1439  $i++;
1440  }
1441  return $num;
1442  } else {
1443  dol_print_error($this->db);
1444  return -1;
1445  }
1446  }
1447 
1453  public function loadCacheSeveritiesTickets()
1454  {
1455  global $conf, $langs;
1456 
1457  if (!empty($conf->cache['severity_tickets']) && count($conf->cache['severity_tickets'])) {
1458  // Cache already loaded
1459  return 0;
1460  }
1461 
1462  $sql = "SELECT rowid, code, label, use_default, pos, description";
1463  $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_severity";
1464  $sql .= " WHERE entity IN (".getEntity('c_ticket_severity').")";
1465  $sql .= " AND active > 0";
1466  $sql .= " ORDER BY pos";
1467  dol_syslog(get_class($this)."::loadCacheSeveritiesTickets", LOG_DEBUG);
1468  $resql = $this->db->query($sql);
1469  if ($resql) {
1470  $num = $this->db->num_rows($resql);
1471  $i = 0;
1472  while ($i < $num) {
1473  $obj = $this->db->fetch_object($resql);
1474 
1475  $conf->cache['severity_tickets'][$obj->rowid]['code'] = $obj->code;
1476  $label = ($langs->trans("TicketSeverityShort".$obj->code) != "TicketSeverityShort".$obj->code ? $langs->trans("TicketSeverityShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1477  $conf->cache['severity_tickets'][$obj->rowid]['label'] = $label;
1478  $conf->cache['severity_tickets'][$obj->rowid]['use_default'] = $obj->use_default;
1479  $conf->cache['severity_tickets'][$obj->rowid]['pos'] = $obj->pos;
1480  $i++;
1481  }
1482  return $num;
1483  } else {
1484  dol_print_error($this->db);
1485  return -1;
1486  }
1487  }
1488 
1489 
1496  public function getLibStatut($mode = 0)
1497  {
1498  return $this->LibStatut($this->status, $mode, 0, $this->progress);
1499  }
1500 
1501 
1502  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1512  public function LibStatut($status, $mode = 0, $notooltip = 0, $progress = 0)
1513  {
1514  // phpcs:enable
1515  global $langs, $hookmanager;
1516 
1517  $labelStatus = (isset($status) && !empty($this->labelStatus[$status])) ? $this->labelStatus[$status] : '';
1518  $labelStatusShort = (isset($status) && !empty($this->labelStatusShort[$status])) ? $this->labelStatusShort[$status] : '';
1519 
1520  switch ($status) {
1521  case self::STATUS_NOT_READ: // Not read
1522  $statusType = 'status0';
1523  break;
1524  case self::STATUS_READ: // Read
1525  $statusType = 'status1';
1526  break;
1527  case self::STATUS_ASSIGNED: // Assigned
1528  $statusType = 'status2';
1529  break;
1530  case self::STATUS_IN_PROGRESS: // In progress
1531  $statusType = 'status4';
1532  break;
1533  case self::STATUS_WAITING: // Waiting/pending/suspended
1534  $statusType = 'status7';
1535  break;
1536  case self::STATUS_NEED_MORE_INFO: // Waiting more information from the requester
1537  $statusType = 'status3';
1538  break;
1539  case self::STATUS_CANCELED: // Canceled
1540  $statusType = 'status9';
1541  break;
1542  case self::STATUS_CLOSED: // Closed
1543  $statusType = 'status6';
1544  break;
1545  default:
1546  $labelStatus = 'Unknown';
1547  $labelStatusShort = 'Unknown';
1548  $statusType = 'status0';
1549  $mode = 0;
1550  }
1551 
1552  $parameters = array(
1553  'status' => $status,
1554  'mode' => $mode,
1555  );
1556 
1557  // Note that $action and $object may have been modified by hook
1558  $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this);
1559 
1560  if ($reshook > 0) {
1561  return $hookmanager->resPrint;
1562  }
1563 
1564  $params = array();
1565  if ($notooltip) {
1566  $params = array('tooltip' => 'no');
1567  }
1568 
1569  $labelStatus = $langs->transnoentitiesnoconv($labelStatus);
1570  $labelStatusShort = $langs->transnoentitiesnoconv($labelStatusShort);
1571 
1572  if ($status == self::STATUS_IN_PROGRESS && $progress > 0) {
1573  $labelStatus .= ' ('.round($progress).'%)';
1574  $labelStatusShort .= ' ('.round($progress).'%)';
1575  }
1576 
1577  return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', $params);
1578  }
1579 
1587  public function getTooltipContentArray($params)
1588  {
1589  global $langs;
1590 
1591  $langs->load('ticket');
1592  $nofetch = !empty($params['nofetch']);
1593 
1594  $datas = array();
1595  $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Ticket").'</u>';
1596  $datas['picto'] .= ' '.$this->getLibStatut(4);
1597  $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1598  $datas['track_id'] = '<br><b>'.$langs->trans('TicketTrackId').':</b> '.$this->track_id;
1599  $datas['subject'] = '<br><b>'.$langs->trans('Subject').':</b> '.$this->subject;
1600  if ($this->date_creation) {
1601  $datas['date_creation'] = '<br><b>'.$langs->trans('DateCreation').':</b> '.dol_print_date($this->date_creation, 'dayhour');
1602  }
1603  if ($this->date_modification) {
1604  $datas['date_modification'] = '<br><b>'.$langs->trans('DateModification').':</b> '.dol_print_date($this->date_modification, 'dayhour');
1605  }
1606  // show categories for this record only in ajax to not overload lists
1607  if (isModEnabled('category') && !$nofetch) {
1608  require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
1609  $form = new Form($this->db);
1610  $datas['categories'] = '<br>' . $form->showCategories($this->id, Categorie::TYPE_TICKET, 1);
1611  }
1612 
1613  return $datas;
1614  }
1615 
1626  public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1627  {
1628  global $conf, $langs;
1629 
1630  if (!empty($conf->dol_no_mouse_hover)) {
1631  $notooltip = 1; // Force disable tooltips
1632  }
1633 
1634  $result = '';
1635 
1636  $params = [
1637  'id' => $this->id,
1638  'objecttype' => $this->element,
1639  'option' => $option,
1640  'nofetch' => 1,
1641  ];
1642  $classfortooltip = 'classfortooltip';
1643  $dataparams = '';
1644  if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1645  $classfortooltip = 'classforajaxtooltip';
1646  $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1647  $label = '';
1648  } else {
1649  $label = implode($this->getTooltipContentArray($params));
1650  }
1651 
1652  $url = DOL_URL_ROOT.'/ticket/card.php?id='.$this->id;
1653 
1654  if ($option != 'nolink') {
1655  // Add param to save lastsearch_values or not
1656  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1657  if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1658  $add_save_lastsearch_values = 1;
1659  }
1660  if ($add_save_lastsearch_values) {
1661  $url .= '&save_lastsearch_values=1';
1662  }
1663  }
1664 
1665  $linkclose = '';
1666  if (empty($notooltip)) {
1667  if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1668  $label = $langs->trans("ShowTicket");
1669  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1670  }
1671  $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1672  $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
1673  } else {
1674  $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1675  }
1676 
1677  $linkstart = '<a href="'.$url.'"';
1678  $linkstart .= $linkclose.'>';
1679  $linkend = '</a>';
1680 
1681  $result .= $linkstart;
1682  if ($withpicto) {
1683  $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1684  }
1685  if ($withpicto != 2) {
1686  $result .= $this->ref;
1687  }
1688  $result .= $linkend;
1689  //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1690 
1691  return $result;
1692  }
1693 
1694 
1702  public function markAsRead($user, $notrigger = 0)
1703  {
1704  global $langs;
1705 
1706  $error = 0;
1707 
1708  if ($this->status != self::STATUS_CANCELED) { // no closed
1709  $this->oldcopy = dol_clone($this, 2);
1710 
1711  $this->db->begin();
1712 
1713  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1714  $sql .= " SET fk_statut = ".Ticket::STATUS_READ.", date_read = '".$this->db->idate(dol_now())."'";
1715  $sql .= " WHERE rowid = ".((int) $this->id);
1716 
1717  dol_syslog(get_class($this)."::markAsRead");
1718  $resql = $this->db->query($sql);
1719  if ($resql) {
1720  $this->context['actionmsg'] = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1721  $this->context['actionmsg2'] = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1722 
1723  if (!$error && !$notrigger) {
1724  // Call trigger
1725  $result = $this->call_trigger('TICKET_MODIFY', $user);
1726  if ($result < 0) {
1727  $error++;
1728  }
1729  // End call triggers
1730  }
1731 
1732  if (!$error) {
1733  $this->db->commit();
1734  return 1;
1735  } else {
1736  $this->db->rollback();
1737  $this->error = implode(',', $this->errors);
1738  dol_syslog(get_class($this)."::markAsRead ".$this->error, LOG_ERR);
1739  return -1;
1740  }
1741  } else {
1742  $this->db->rollback();
1743  $this->error = $this->db->lasterror();
1744  dol_syslog(get_class($this)."::markAsRead ".$this->error, LOG_ERR);
1745  return -1;
1746  }
1747  }
1748 
1749  return 0;
1750  }
1751 
1760  public function assignUser($user, $id_assign_user, $notrigger = 0)
1761  {
1762  $error = 0;
1763 
1764  $this->oldcopy = dol_clone($this, 2);
1765 
1766  $this->db->begin();
1767 
1768  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1769  if ($id_assign_user > 0) {
1770  $sql .= " SET fk_user_assign=".((int) $id_assign_user).", fk_statut = ".Ticket::STATUS_ASSIGNED;
1771  } else {
1772  $sql .= " SET fk_user_assign=null, fk_statut = ".Ticket::STATUS_READ;
1773  }
1774  $sql .= " WHERE rowid = ".((int) $this->id);
1775 
1776  dol_syslog(get_class($this)."::assignUser sql=".$sql);
1777  $resql = $this->db->query($sql);
1778  if ($resql) {
1779  $this->fk_user_assign = $id_assign_user; // May be used by trigger
1780 
1781  if (!$notrigger) {
1782  // Call trigger
1783  $result = $this->call_trigger('TICKET_ASSIGNED', $user);
1784  if ($result < 0) {
1785  $error++;
1786  }
1787  // End call triggers
1788  }
1789 
1790  if (!$error) {
1791  $this->db->commit();
1792  return 1;
1793  } else {
1794  $this->db->rollback();
1795  $this->error = implode(',', $this->errors);
1796  dol_syslog(get_class($this)."::assignUser ".$this->error, LOG_ERR);
1797  return -1;
1798  }
1799  } else {
1800  $this->db->rollback();
1801  $this->error = $this->db->lasterror();
1802  dol_syslog(get_class($this)."::assignUser ".$this->error, LOG_ERR);
1803  return -1;
1804  }
1805  }
1806 
1819  public function createTicketMessage($user, $notrigger = 0, $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array(), $send_email = false, $public_area = 0)
1820  {
1821  global $conf, $langs;
1822  $error = 0;
1823 
1824  $now = dol_now();
1825 
1826  // Clean parameters
1827  if (isset($this->fk_track_id)) {
1828  $this->fk_track_id = trim($this->fk_track_id);
1829  }
1830 
1831  if (isset($this->message)) {
1832  $this->message = trim($this->message);
1833  }
1834 
1835  $this->db->begin();
1836 
1837  // Insert entry into agenda with code 'TICKET_MSG'
1838  include_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
1839  $actioncomm = new ActionComm($this->db);
1840  $actioncomm->type_code = 'AC_OTH_AUTO'; // This is not an entry that must appears into manual calendar but only into CRM calendar
1841  $actioncomm->code = 'TICKET_MSG';
1842  if ($this->private) {
1843  $actioncomm->code = 'TICKET_MSG_PRIVATE';
1844  }
1845  if ($send_email) {
1846  $actioncomm->code .= '_SENTBYMAIL';
1847  }
1848  if ((empty($user->id) || $user->id == 0) && isset($_SESSION['email_customer'])) {
1849  $actioncomm->email_from = $_SESSION['email_customer'];
1850  }
1851  $actioncomm->socid = $this->socid;
1852  $actioncomm->label = $this->subject;
1853  $actioncomm->note_private = $this->message;
1854  $actioncomm->userassigned = array($user->id => array('id' => $user->id,'transparency' => 0));
1855  $actioncomm->userownerid = $user->id;
1856  $actioncomm->datep = $now;
1857  $actioncomm->percentage = -1; // percentage is not relevant for punctual events
1858  $actioncomm->elementtype = 'ticket';
1859  $actioncomm->fk_element = $this->id;
1860  $actioncomm->fk_project = $this->fk_project;
1861 
1862  // add contact id from author email on public interface
1863  if ($public_area && !empty($this->origin_email) && getDolGlobalString('TICKET_ASSIGN_CONTACT_TO_MESSAGE')) {
1864  $contacts = $this->searchContactByEmail($this->origin_email);
1865  if (!empty($contacts)) {
1866  // Ensure that contact is active and select first active contact
1867  foreach ($contacts as $contact) {
1868  if ((int) $contact->statut == 1) {
1869  $actioncomm->contact_id = $contact->id;
1870  break;
1871  }
1872  }
1873  }
1874  }
1875 
1876  $attachedfiles = array();
1877  $attachedfiles['paths'] = $filename_list;
1878  $attachedfiles['names'] = $mimefilename_list;
1879  $attachedfiles['mimes'] = $mimetype_list;
1880  if (is_array($attachedfiles) && count($attachedfiles) > 0) {
1881  $actioncomm->attachedfiles = $attachedfiles;
1882  }
1883 
1884  //if (!empty($mimefilename_list) && is_array($mimefilename_list)) {
1885  // $actioncomm->note_private = dol_concatdesc($actioncomm->note_private, "\n".$langs->transnoentities("AttachedFiles").': '.implode(';', $mimefilename_list));
1886  //}
1887  $actionid = $actioncomm->create($user);
1888  if ($actionid <= 0) {
1889  $error++;
1890  $this->error = $actioncomm->error;
1891  $this->errors = $actioncomm->errors;
1892  }
1893 
1894  if ($actionid > 0) {
1895  if (is_array($attachedfiles) && array_key_exists('paths', $attachedfiles) && count($attachedfiles['paths']) > 0) {
1896  foreach ($attachedfiles['paths'] as $key => $filespath) {
1897  $destdir = $conf->agenda->dir_output.'/'.$actionid;
1898  $destfile = $destdir.'/'.$attachedfiles['names'][$key];
1899  if (dol_mkdir($destdir) >= 0) {
1900  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1901  dol_move($filespath, $destfile);
1902  if ($actioncomm->code == "TICKET_MSG") {
1903  $ecmfile = new EcmFiles($this->db);
1904  $destdir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $destdir);
1905  $destdir = preg_replace('/[\\/]$/', '', $destdir);
1906  $destdir = preg_replace('/^[\\/]/', '', $destdir);
1907  $ecmfile->fetch(0, '', $destdir.'/'.$attachedfiles['names'][$key]);
1908  require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
1909  $ecmfile->share = getRandomPassword(true);
1910  $result = $ecmfile->update($user);
1911  if ($result < 0) {
1912  setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1913  }
1914  }
1915  }
1916  }
1917  }
1918  }
1919 
1920  // Commit or rollback
1921  if ($error) {
1922  $this->db->rollback();
1923  return -1 * $error;
1924  } else {
1925  $this->db->commit();
1926  return 1;
1927  }
1928  }
1929 
1935  public function loadCacheMsgsTicket()
1936  {
1937  if (!empty($this->cache_msgs_ticket) && is_array($this->cache_msgs_ticket) && count($this->cache_msgs_ticket)) {
1938  return 0;
1939  }
1940 
1941  // Cache already loaded
1942 
1943  $sql = "SELECT id as rowid, fk_user_author, email_from, datec, datep, label, note as message, code";
1944  $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm";
1945  $sql .= " WHERE fk_element = ".(int) $this->id;
1946  $sql .= " AND elementtype = 'ticket'";
1947  $sql .= " ORDER BY datep DESC";
1948 
1949  dol_syslog(get_class($this)."::load_cache_actions_ticket", LOG_DEBUG);
1950  $resql = $this->db->query($sql);
1951  if ($resql) {
1952  $num = $this->db->num_rows($resql);
1953  $i = 0;
1954  while ($i < $num) {
1955  $obj = $this->db->fetch_object($resql);
1956  $this->cache_msgs_ticket[$i]['id'] = $obj->rowid;
1957  $this->cache_msgs_ticket[$i]['fk_user_author'] = $obj->fk_user_author;
1958  if ($obj->code == 'TICKET_MSG' && empty($obj->fk_user_author)) {
1959  $this->cache_msgs_ticket[$i]['fk_contact_author'] = $obj->email_from;
1960  }
1961  $this->cache_msgs_ticket[$i]['datec'] = $this->db->jdate($obj->datec);
1962  $this->cache_msgs_ticket[$i]['datep'] = $this->db->jdate($obj->datep);
1963  $this->cache_msgs_ticket[$i]['subject'] = $obj->label;
1964  $this->cache_msgs_ticket[$i]['message'] = $obj->message;
1965  $this->cache_msgs_ticket[$i]['private'] = (preg_match('/^TICKET_MSG_PRIVATE/', $obj->code) ? 1 : 0);
1966  $i++;
1967  }
1968  return $num;
1969  } else {
1970  $this->error = "Error ".$this->db->lasterror();
1971  dol_syslog(get_class($this)."::load_cache_actions_ticket ".$this->error, LOG_ERR);
1972  return -1;
1973  }
1974  }
1975 
1983  public function close(User $user, $mode = 0)
1984  {
1985  global $conf;
1986 
1987  if ($this->status != Ticket::STATUS_CLOSED && $this->status != Ticket::STATUS_CANCELED) { // not closed
1988  $this->db->begin();
1989 
1990  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1991  $sql .= " SET fk_statut=".($mode ? Ticket::STATUS_CANCELED : Ticket::STATUS_CLOSED).", progress=100, date_close='".$this->db->idate(dol_now())."'";
1992  $sql .= " WHERE rowid = ".((int) $this->id);
1993 
1994  dol_syslog(get_class($this)."::close mode=".$mode);
1995  $resql = $this->db->query($sql);
1996  if ($resql) {
1997  $error = 0;
1998 
1999  // Valid and close fichinter linked
2000  if (isModEnabled('intervention') && getDolGlobalString('WORKFLOW_TICKET_CLOSE_INTERVENTION')) {
2001  dol_syslog("We have closed the ticket, so we close all linked interventions");
2002  $this->fetchObjectLinked($this->id, $this->element, null, 'fichinter');
2003  if ($this->linkedObjectsIds) {
2004  foreach ($this->linkedObjectsIds['fichinter'] as $fichinter_id) {
2005  $fichinter = new Fichinter($this->db);
2006  $fichinter->fetch($fichinter_id);
2007  if ($fichinter->statut == 0) {
2008  $result = $fichinter->setValid($user);
2009  if (!$result) {
2010  $this->errors[] = $fichinter->error;
2011  $error++;
2012  }
2013  }
2014  if ($fichinter->statut < 3) {
2015  $result = $fichinter->setStatut(3);
2016  if (!$result) {
2017  $this->errors[] = $fichinter->error;
2018  $error++;
2019  }
2020  }
2021  }
2022  }
2023  }
2024 
2025  // Call trigger
2026  $result = $this->call_trigger('TICKET_CLOSE', $user);
2027  if ($result < 0) {
2028  $error++;
2029  }
2030  // End call triggers
2031 
2032  if (!$error) {
2033  $this->db->commit();
2034  return 1;
2035  } else {
2036  $this->db->rollback();
2037  $this->error = implode(',', $this->errors);
2038  dol_syslog(get_class($this)."::close ".$this->error, LOG_ERR);
2039  return -1;
2040  }
2041  } else {
2042  $this->db->rollback();
2043  $this->error = $this->db->lasterror();
2044  dol_syslog(get_class($this)."::close ".$this->error, LOG_ERR);
2045  return -1;
2046  }
2047  }
2048 
2049  return 0;
2050  }
2051 
2061  public function searchSocidByEmail($email, $type = 0, $filters = array(), $clause = 'AND')
2062  {
2063  $thirdparties = array();
2064  $exact = 0;
2065 
2066  // Generation requete recherche
2067  $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."societe";
2068  $sql .= " WHERE entity IN (".getEntity('ticket', 1).")";
2069  if (!empty($type)) {
2070  if ($type == 1 || $type == 2) {
2071  $sql .= " AND client = ".((int) $type);
2072  } elseif ($type == 3) {
2073  $sql .= " AND fournisseur = 1";
2074  }
2075  }
2076  if (!empty($email)) {
2077  if (empty($exact)) {
2078  $regs = array();
2079  if (preg_match('/^([\*])?[^*]+([\*])?$/', $email, $regs) && count($regs) > 1) {
2080  $email = str_replace('*', '%', $email);
2081  } else {
2082  $email = '%'.$email.'%';
2083  }
2084  }
2085  $sql .= " AND ";
2086  if (is_array($filters) && !empty($filters)) {
2087  $sql .= "(";
2088  }
2089 
2090  $sql .= "email LIKE '".$this->db->escape($email)."'";
2091  }
2092  if (is_array($filters) && !empty($filters)) {
2093  foreach ($filters as $field => $value) {
2094  $sql .= " ".$clause." ".$field." LIKE '".$this->db->escape($value)."'";
2095  }
2096  if (!empty($email)) {
2097  $sql .= ")";
2098  }
2099  }
2100 
2101  $res = $this->db->query($sql);
2102  if ($res) {
2103  while ($rec = $this->db->fetch_array($res)) {
2104  $soc = new Societe($this->db);
2105  $soc->fetch($rec['rowid']);
2106  $thirdparties[] = $soc;
2107  }
2108 
2109  return $thirdparties;
2110  } else {
2111  $this->error = $this->db->error().' sql='.$sql;
2112  dol_syslog(get_class($this)."::searchSocidByEmail ".$this->error, LOG_ERR);
2113  return -1;
2114  }
2115  }
2116 
2125  public function searchContactByEmail($email, $socid = 0, $case = '')
2126  {
2127  $contacts = array();
2128 
2129  // Forge the search SQL
2130  $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."socpeople";
2131  $sql .= " WHERE entity IN (".getEntity('contact').")";
2132  if (!empty($socid)) {
2133  $sql .= " AND fk_soc = ".((int) $socid);
2134  }
2135  if (!empty($email)) {
2136  $sql .= " AND ";
2137  if (!$case) {
2138  $sql .= "email = '".$this->db->escape($email)."'";
2139  } else {
2140  $sql .= "email LIKE BINARY '".$this->db->escape($this->db->escapeforlike($email))."'";
2141  }
2142  }
2143 
2144  $res = $this->db->query($sql);
2145  if ($res) {
2146  while ($rec = $this->db->fetch_object($res)) {
2147  include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
2148  $contactstatic = new Contact($this->db);
2149  $contactstatic->fetch($rec->rowid);
2150  $contacts[] = $contactstatic;
2151  }
2152 
2153  return $contacts;
2154  } else {
2155  $this->error = $this->db->error().' sql='.$sql;
2156  dol_syslog(get_class($this)."::searchContactByEmail ".$this->error, LOG_ERR);
2157  return -1;
2158  }
2159  }
2160 
2167  public function setCustomer($id)
2168  {
2169  if ($this->id) {
2170  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2171  $sql .= " SET fk_soc = ".($id > 0 ? $id : "null");
2172  $sql .= " WHERE rowid = ".((int) $this->id);
2173  dol_syslog(get_class($this).'::setCustomer sql='.$sql);
2174  $resql = $this->db->query($sql);
2175  if ($resql) {
2176  return 1;
2177  } else {
2178  return -1;
2179  }
2180  } else {
2181  return -1;
2182  }
2183  }
2184 
2191  public function setProgression($percent)
2192  {
2193  if ($this->id) {
2194  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2195  $sql .= " SET progress = ".($percent > 0 ? $percent : "null");
2196  $sql .= " WHERE rowid = ".((int) $this->id);
2197  dol_syslog(get_class($this).'::set_progression sql='.$sql);
2198  $resql = $this->db->query($sql);
2199  if ($resql) {
2200  return 1;
2201  } else {
2202  return -1;
2203  }
2204  } else {
2205  return -1;
2206  }
2207  }
2208 
2215  public function setContract($contractid)
2216  {
2217  if ($this->id) {
2218  $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2219  $sql .= " SET fk_contract = ".($contractid > 0 ? $contractid : "null");
2220  $sql .= " WHERE rowid = ".((int) $this->id);
2221  dol_syslog(get_class($this).'::setContract sql='.$sql);
2222  $resql = $this->db->query($sql);
2223  if ($resql) {
2224  return 1;
2225  } else {
2226  return -1;
2227  }
2228  } else {
2229  return -1;
2230  }
2231  }
2232 
2233  /* gestion des contacts d'un ticket */
2234 
2240  public function getIdTicketInternalContact()
2241  {
2242  return $this->getIdContact('internal', 'SUPPORTTEC');
2243  }
2244 
2251  public function getInfosTicketInternalContact($status = -1)
2252  {
2253  return $this->listeContact(-1, 'internal', 0, '', $status);
2254  }
2255 
2261  public function getIdTicketCustomerContact()
2262  {
2263  return $this->getIdContact('external', 'SUPPORTCLI');
2264  }
2265 
2272  public function getInfosTicketExternalContact($status = -1)
2273  {
2274  return $this->listeContact(-1, 'external', 0, '', $status);
2275  }
2276 
2283  {
2284  return $this->getIdContact('internal', 'CONTRIBUTOR');
2285  }
2286 
2293  {
2294  return $this->getIdContact('external', 'CONTRIBUTOR');
2295  }
2296 
2302  public function getTicketAllContacts()
2303  {
2304  $array_contact = array();
2305 
2306  $array_contact = $this->getIdTicketInternalContact();
2307 
2308  $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());
2309 
2310  $array_contact = array_merge($array_contact, $this->getIdTicketInternalInvolvedContact());
2311 
2312  $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());
2313 
2314  return $array_contact;
2315  }
2316 
2323  {
2324  $array_contact = array();
2325 
2326  $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());
2327 
2328  $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());
2329 
2330  return $array_contact;
2331  }
2332 
2333 
2345  public function listeContact($statusoflink = -1, $source = 'external', $list = 0, $code = '', $status = -1)
2346  {
2347  global $langs;
2348 
2349  $tab = array();
2350 
2351  $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
2352  if ($source == 'internal') {
2353  $sql .= ", '-1' as socid, t.statut as statuscontact";
2354  }
2355 
2356  if ($source == 'external' || $source == 'thirdparty') {
2357  $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
2358  }
2359 
2360  $sql .= ", t.civility, t.lastname as lastname, t.firstname, t.email";
2361  if ($source == 'internal') {
2362  $sql .= ", t.office_phone as phone, t.user_mobile as phone_mobile";
2363  }
2364 
2365  if ($source == 'external') {
2366  $sql .= ", t.phone as phone, t.phone_mobile as phone_mobile, t.phone_perso as phone_perso";
2367  }
2368 
2369  $sql .= ", tc.source, tc.element, tc.code, tc.libelle as type_contact_label";
2370  $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
2371  $sql .= ", ".MAIN_DB_PREFIX."element_contact ec";
2372  if ($source == 'internal') {
2373  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
2374  }
2375 
2376  if ($source == 'external' || $source == 'thirdparty') {
2377  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
2378  }
2379 
2380  $sql .= " WHERE ec.element_id = ".((int) $this->id);
2381  $sql .= " AND ec.fk_c_type_contact=tc.rowid";
2382  $sql .= " AND tc.element='".$this->db->escape($this->element)."'";
2383  if ($source == 'internal') {
2384  $sql .= " AND tc.source = 'internal'";
2385  if ($status >= 0) {
2386  $sql .= " AND t.statut = ".((int) $status);
2387  }
2388  }
2389 
2390  if ($source == 'external' || $source == 'thirdparty') {
2391  $sql .= " AND tc.source = 'external'";
2392  if ($status >= 0) {
2393  $sql .= " AND t.statut = ".((int) $status);
2394  }
2395  }
2396 
2397  if (!empty($code)) {
2398  $sql .= " AND tc.code = '".$this->db->escape($code)."'";
2399  }
2400 
2401  $sql .= " AND tc.active=1";
2402  if ($statusoflink >= 0) {
2403  $sql .= " AND ec.statut = ".((int) $statusoflink);
2404  }
2405 
2406  $sql .= " ORDER BY t.lastname ASC";
2407 
2408  $resql = $this->db->query($sql);
2409  if ($resql) {
2410  $num = $this->db->num_rows($resql);
2411  $i = 0;
2412  while ($i < $num) {
2413  $obj = $this->db->fetch_object($resql);
2414 
2415  if (!$list) {
2416  $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
2417  $labelType = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_contact_label);
2418  $tab[$i] = array(
2419  'source' => $obj->source,
2420  'socid' => $obj->socid,
2421  'id' => $obj->id,
2422  'nom' => $obj->lastname, // For backward compatibility
2423  'civility' => $obj->civility,
2424  'lastname' => $obj->lastname,
2425  'firstname' => $obj->firstname,
2426  'email' => $obj->email,
2427  'rowid' => $obj->rowid,
2428  'code' => $obj->code,
2429  'libelle' => $labelType, // deprecated, replaced with labeltype
2430  'labeltype' => $labelType,
2431  'status' => $obj->statuslink,
2432  'statuscontact' => $obj->statuscontact,
2433  'fk_c_type_contact' => $obj->fk_c_type_contact,
2434  'phone' => $obj->phone,
2435  'phone_mobile' => $obj->phone_mobile);
2436  } else {
2437  $tab[$i] = $obj->id;
2438  }
2439 
2440  $i++;
2441  }
2442 
2443  return $tab;
2444  } else {
2445  $this->error = $this->db->error();
2446  dol_print_error($this->db);
2447  return -1;
2448  }
2449  }
2450 
2457  public function getDefaultRef($thirdparty = null)
2458  {
2459  global $conf;
2460 
2461  $defaultref = '';
2462  $modele = getDolGlobalString('TICKET_ADDON', 'mod_ticket_simple');
2463 
2464  // Search template files
2465  $file = '';
2466  $classname = '';
2467  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2468  foreach ($dirmodels as $reldir) {
2469  $file = dol_buildpath($reldir."core/modules/ticket/".$modele.'.php', 0);
2470  if (file_exists($file)) {
2471  $classname = $modele;
2472  break;
2473  }
2474  }
2475 
2476  if ($classname !== '') {
2477  $result = dol_include_once($reldir."core/modules/ticket/".$modele.'.php');
2478  $modTicket = new $classname();
2479 
2480  $defaultref = $modTicket->getNextValue($thirdparty, $this);
2481  }
2482 
2483  if (is_numeric($defaultref) && $defaultref <= 0) {
2484  $defaultref = '';
2485  }
2486 
2487  return $defaultref;
2488  }
2489 
2490 
2491  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2498  public function is_photo_available($sdir)
2499  {
2500  // phpcs:enable
2501  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2502 
2503  global $conf;
2504 
2505  $dir = $sdir.'/';
2506  $nbphoto = 0;
2507 
2508  $dir_osencoded = dol_osencode($dir);
2509  if (file_exists($dir_osencoded)) {
2510  $handle = opendir($dir_osencoded);
2511  if (is_resource($handle)) {
2512  while (($file = readdir($handle)) !== false) {
2513  if (!utf8_check($file)) {
2514  $file = mb_convert_encoding($file, 'UTF-8', 'ISO-8859-1'); // To be sure data is stored in UTF8 in memory
2515  }
2516  if (dol_is_file($dir.$file)) {
2517  return true;
2518  }
2519  }
2520  }
2521  }
2522  return false;
2523  }
2524 
2525 
2534  public function copyFilesForTicket($forcetrackid = null)
2535  {
2536  global $conf;
2537 
2538  // Create form object
2539  include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
2540  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2541  include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
2542 
2543  $maxwidthsmall = 270;
2544  $maxheightsmall = 150;
2545  $maxwidthmini = 128;
2546  $maxheightmini = 72;
2547 
2548  $formmail = new FormMail($this->db);
2549  $formmail->trackid = (is_null($forcetrackid) ? 'tic'.$this->id : '');
2550  $attachedfiles = $formmail->get_attached_files();
2551 
2552  $filepath = $attachedfiles['paths']; // path is for example user->dir_temp.'/'.$user->id.'/'...
2553  $filename = $attachedfiles['names'];
2554  $mimetype = $attachedfiles['mimes'];
2555 
2556  // Copy files into ticket directory
2557  $destdir = $conf->ticket->dir_output.'/'.$this->ref;
2558 
2559  if (!dol_is_dir($destdir)) {
2560  dol_mkdir($destdir);
2561  }
2562 
2563  $listofpaths = array();
2564  $listofnames = array();
2565  foreach ($filename as $i => $val) {
2566  $destfile = $destdir.'/'.$filename[$i];
2567  // If destination file already exists, we add a suffix to avoid to overwrite
2568  if (is_file($destfile)) {
2569  $pathinfo = pathinfo($filename[$i]);
2570  $now = dol_now();
2571  $destfile = $destdir.'/'.$pathinfo['filename'].' - '.dol_print_date($now, 'dayhourlog').'.'.$pathinfo['extension'];
2572  }
2573 
2574  $moreinfo = array('description' => 'File saved by copyFilesForTicket', 'src_object_type' => $this->element, 'src_object_id' => $this->id);
2575  $res = dol_move($filepath[$i], $destfile, 0, 1, 0, 1, $moreinfo);
2576  if (!$res) {
2577  // Move has failed
2578  $this->error = "Failed to move file ".dirbasename($filepath[$i])." into ".dirbasename($destfile);
2579  return -1;
2580  } else {
2581  // If file is an image, we create thumbs
2582  if (image_format_supported($destfile) == 1) {
2583  // Create small thumbs for image (Ratio is near 16/9)
2584  // Used on logon for example
2585  $imgThumbSmall = vignette($destfile, $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs");
2586  // Create mini thumbs for image (Ratio is near 16/9)
2587  // Used on menu or for setup page for example
2588  $imgThumbMini = vignette($destfile, $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs");
2589  }
2590  }
2591 
2592  // Clear variables into session
2593  $formmail->remove_attached_files($i);
2594 
2595  // Fill array with new names
2596  $listofpaths[$i] = $destfile;
2597  $listofnames[$i] = basename($destfile);
2598  }
2599 
2600  return array('listofpaths' => $listofpaths, 'listofnames' => $listofnames, 'listofmimes' => $mimetype);
2601  }
2602 
2613  public function setCategories($categories)
2614  {
2615  // Handle single category
2616  if (!is_array($categories)) {
2617  $categories = array($categories);
2618  }
2619 
2620  // Get current categories
2621  include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2622  $c = new Categorie($this->db);
2623  $existing = $c->containing($this->id, Categorie::TYPE_TICKET, 'id');
2624 
2625  // Diff
2626  if (is_array($existing)) {
2627  $to_del = array_diff($existing, $categories);
2628  $to_add = array_diff($categories, $existing);
2629  } else {
2630  $to_del = array(); // Nothing to delete
2631  $to_add = $categories;
2632  }
2633 
2634  // Process
2635  foreach ($to_del as $del) {
2636  if ($c->fetch($del) > 0) {
2637  $c->del_type($this, Categorie::TYPE_TICKET);
2638  }
2639  }
2640  foreach ($to_add as $add) {
2641  if ($c->fetch($add) > 0) {
2642  $c->add_type($this, Categorie::TYPE_TICKET);
2643  }
2644  }
2645 
2646  return 1;
2647  }
2648 
2661  public function newMessage($user, &$action, $private = 1, $public_area = 0)
2662  {
2663  global $mysoc, $conf, $langs;
2664 
2665  $error = 0;
2666 
2667  $object = new Ticket($this->db);
2668 
2669  $ret = $object->fetch('', '', GETPOST('track_id', 'alpha'));
2670 
2671  $object->socid = $object->fk_soc;
2672  $object->fetch_thirdparty();
2673  $object->fetch_project();
2674 
2675  if ($ret < 0) {
2676  $error++;
2677  array_push($this->errors, $langs->trans("ErrorTicketIsNotValid"));
2678  $action = '';
2679  }
2680 
2681  if (!GETPOST("message")) {
2682  $error++;
2683  array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Message")));
2684  $action = 'add_message';
2685  }
2686 
2687  if (!$error) {
2688  $object->subject = GETPOST('subject', 'alphanohtml');
2689  $object->message = GETPOST("message", "restricthtml");
2690  $object->private = GETPOST("private_message", "alpha");
2691 
2692  $send_email = GETPOSTINT('send_email');
2693 
2694  // Copy attached files (saved into $_SESSION) as linked files to ticket. Return array with final name used.
2695  $resarray = $object->copyFilesForTicket();
2696  if (is_numeric($resarray) && $resarray == -1) {
2697  setEventMessages($object->error, $object->errors, 'errors');
2698  return -1;
2699  }
2700 
2701  $listofpaths = $resarray['listofpaths'];
2702  $listofnames = $resarray['listofnames'];
2703  $listofmimes = $resarray['listofmimes'];
2704 
2705  $id = $object->createTicketMessage($user, 0, $listofpaths, $listofmimes, $listofnames, $send_email, $public_area);
2706  if ($id <= 0) {
2707  $error++;
2708  $this->error = $object->error;
2709  $this->errors = $object->errors;
2710  $action = 'add_message';
2711  }
2712 
2713  if (!$error && $id > 0) {
2714  setEventMessages($langs->trans('TicketMessageSuccessfullyAdded'), null, 'mesgs');
2715 
2716  if (!empty($public_area)) {
2717  /*
2718  * Message created from the Public interface
2719  *
2720  * Send emails to assigned users (public area notification)
2721  */
2722  if (getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_ENABLED')) {
2723  // Retrieve internal contact datas
2724  $internal_contacts = $object->getInfosTicketInternalContact(1);
2725 
2726  $assigned_user_dont_have_email = '';
2727 
2728  $sendto = array();
2729 
2730  if ($this->fk_user_assign > 0) {
2731  $assigned_user = new User($this->db);
2732  $assigned_user->fetch($this->fk_user_assign);
2733  if (!empty($assigned_user->email)) {
2734  $sendto[$assigned_user->email] = $assigned_user->getFullName($langs)." <".$assigned_user->email.">";
2735  } else {
2736  $assigned_user_dont_have_email = $assigned_user->getFullName($langs);
2737  }
2738  }
2739 
2740  // Build array to display recipient list
2741  foreach ($internal_contacts as $key => $info_sendto) {
2742  // Avoid duplicate notifications
2743  if ($info_sendto['id'] == $user->id) {
2744  continue;
2745  }
2746 
2747  // We check if the email address is not the assignee's address to prevent notification from being sent twice
2748  if (!empty($info_sendto['email']) && $assigned_user->email != $info_sendto['email']) {
2749  $sendto[] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";
2750  }
2751  }
2752 
2753  if (empty($sendto)) {
2754  if (getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL')) {
2755  $sendto[getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL')] = getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL');
2756  } elseif (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2757  $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2758  }
2759  }
2760 
2761  // Add global email address recipient
2762  if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') &&
2763  getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)
2764  ) {
2765  $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2766  }
2767 
2768  if (!empty($sendto)) {
2769  $appli = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2770 
2771  $subject = '['.$appli.'- ticket #'.$object->track_id.'] '.$this->subject;
2772 
2773  // Message send
2774  $message = $langs->trans('TicketMessageMailIntroText');
2775  $message .= '<br><br>';
2776  $messagePost = GETPOST('message', 'restricthtml');
2777  if (!dol_textishtml($messagePost)) {
2778  $messagePost = dol_nl2br($messagePost);
2779  }
2780  $message .= $messagePost;
2781 
2782  // Customer company infos
2783  $message .= '<br><br>';
2784  $message .= "==============================================";
2785  $message .= !empty($object->thirdparty->name) ? '<br>'.$langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';
2786  $message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';
2787  $message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';
2788 
2789  // Email send to
2790  $message .= '<br><br>';
2791  if (!empty($assigned_user_dont_have_email)) {
2792  $message .= '<br>'.$langs->trans('NoEMail').' : '.$assigned_user_dont_have_email;
2793  }
2794  foreach ($sendto as $val) {
2795  $message .= '<br>'.$langs->trans('TicketNotificationRecipient').' : '.$val;
2796  }
2797 
2798  // URL ticket
2799  $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;
2800  $message .= '<br><br>';
2801  $message .= $langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a>';
2802 
2803  $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2804  }
2805  }
2806  } else {
2807  /*
2808  * Message send from the Backoffice / Private area
2809  *
2810  * 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)
2811  */
2812  if ($send_email > 0) {
2813  // Retrieve internal contact datas
2814  $internal_contacts = $object->getInfosTicketInternalContact(1);
2815 
2816  $sendto = array();
2817  if (is_array($internal_contacts) && count($internal_contacts) > 0) {
2818  // Set default subject
2819  $appli = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2820 
2821  $subject = GETPOST('subject', 'alphanohtml') ? GETPOST('subject', 'alphanohtml') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2822 
2823  $message_intro = $langs->trans('TicketNotificationEmailBody', "#".$object->id);
2824  $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');
2825 
2826  $message = $langs->trans('TicketMessageMailIntroText');
2827  $message .= '<br><br>';
2828  $messagePost = GETPOST('message', 'restricthtml');
2829  if (!dol_textishtml($messagePost)) {
2830  $messagePost = dol_nl2br($messagePost);
2831  }
2832  $message .= $messagePost;
2833 
2834  // Data about customer
2835  $message .= '<br><br>';
2836  $message .= "==============================================<br>";
2837  $message .= !empty($object->thirdparty->name) ? $langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';
2838  $message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';
2839  $message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';
2840 
2841  // Build array to display recipient list
2842  foreach ($internal_contacts as $key => $info_sendto) {
2843  // Avoid duplicate notifications
2844  if ($info_sendto['id'] == $user->id) {
2845  continue;
2846  }
2847 
2848  if ($info_sendto['email'] != '') {
2849  if (!empty($info_sendto['email'])) {
2850  $sendto[$info_sendto['email']] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";
2851  }
2852 
2853  // Contact type
2854  $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1').' ('.strtolower($info_sendto['libelle']).')';
2855  $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');
2856  }
2857  }
2858  $message .= '<br>';
2859  // URL ticket
2860  $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;
2861 
2862  // Add html link on url
2863  $message .= '<br>'.$langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a><br>';
2864 
2865  // Add global email address recipient
2866  if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {
2867  if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2868  $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2869  }
2870  }
2871 
2872  // don't try to send email if no recipient
2873  if (!empty($sendto)) {
2874  $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2875  }
2876  }
2877 
2878  /*
2879  * Send emails for externals users if not private (linked contacts)
2880  */
2881  if (empty($object->private)) {
2882  // Retrieve email of all contacts (external)
2883  $external_contacts = $object->getInfosTicketExternalContact(1);
2884 
2885  // If no contact, get email from thirdparty
2886  if (is_array($external_contacts) && count($external_contacts) === 0) {
2887  if (!empty($object->fk_soc)) {
2888  $object->fetch_thirdparty($object->fk_soc);
2889  $array_company = array(array('firstname' => '', 'lastname' => $object->thirdparty->name, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
2890  $external_contacts = array_merge($external_contacts, $array_company);
2891  } elseif (empty($object->fk_soc) && !empty($object->origin_replyto)) {
2892  $array_external = array(array('firstname' => '', 'lastname' => $object->origin_replyto, 'email' => $object->origin_replyto, 'libelle' => $langs->transnoentities('Customer'), 'socid' => 0));
2893  $external_contacts = array_merge($external_contacts, $array_external);
2894  } elseif (empty($object->fk_soc) && !empty($object->origin_email)) {
2895  $array_external = array(array('firstname' => '', 'lastname' => $object->origin_email, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
2896  $external_contacts = array_merge($external_contacts, $array_external);
2897  }
2898  }
2899 
2900  $sendto = array();
2901  if (is_array($external_contacts) && count($external_contacts) > 0) {
2902  // Get default subject for email to external contacts
2903  $appli = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2904 
2905  $subject = GETPOST('subject') ? GETPOST('subject') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2906 
2907  $message_intro = GETPOST('mail_intro') ? GETPOST('mail_intro', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO');
2908  $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');
2909  if (!dol_textishtml($message_intro)) {
2910  $message_intro = dol_nl2br($message_intro);
2911  }
2912  if (!dol_textishtml($message_signature)) {
2913  $message_signature = dol_nl2br($message_signature);
2914  }
2915 
2916  // We put intro after
2917  $messagePost = GETPOST('message', 'restricthtml');
2918  if (!dol_textishtml($messagePost)) {
2919  $messagePost = dol_nl2br($messagePost);
2920  }
2921  $message = $messagePost;
2922  $message .= '<br><br>';
2923 
2924  foreach ($external_contacts as $key => $info_sendto) {
2925  // avoid duplicate emails to external contacts
2926  if ($info_sendto['id'] == $user->contact_id) {
2927  continue;
2928  }
2929 
2930  if ($info_sendto['email'] != '' && $info_sendto['email'] != $object->origin_email) {
2931  if (!empty($info_sendto['email'])) {
2932  $sendto[$info_sendto['email']] = trim($info_sendto['firstname']." ".$info_sendto['lastname'])." <".$info_sendto['email'].">";
2933  }
2934 
2935  $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1').' ('.strtolower($info_sendto['libelle']).')';
2936  $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');
2937  }
2938  }
2939 
2940  // If public interface is not enable, use link to internal page into mail
2941  $url_public_ticket = (getDolGlobalInt('TICKET_ENABLE_PUBLIC_INTERFACE') ?
2942  (getDolGlobalString('TICKET_URL_PUBLIC_INTERFACE') !== '' ? getDolGlobalString('TICKET_URL_PUBLIC_INTERFACE') . '/view.php' : dol_buildpath('/public/ticket/view.php', 2)) : dol_buildpath('/ticket/card.php', 2)).'?track_id='.$object->track_id;
2943 
2944  $message .= '<br>'.$langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer').' : <a href="'.$url_public_ticket.'">'.$object->track_id.'</a><br>';
2945 
2946  // Build final message
2947  $message = $message_intro.'<br><br>'.$message;
2948 
2949  // Add signature
2950  $message .= '<br>'.$message_signature;
2951 
2952  if (!empty($object->origin_email)) {
2953  $sendto[$object->origin_email] = $object->origin_email;
2954  }
2955 
2956  if ($object->fk_soc > 0 && !array_key_exists($object->origin_email, $sendto)) {
2957  $object->socid = $object->fk_soc;
2958  $object->fetch_thirdparty();
2959  if (!empty($object->thirdparty->email)) {
2960  $sendto[$object->thirdparty->email] = $object->thirdparty->email;
2961  }
2962  }
2963 
2964  // Add global email address recipient
2965  if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {
2966  if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2967  $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2968  }
2969  }
2970 
2971  // Don't try to send email when no recipient
2972  if (!empty($sendto)) {
2973  $result = $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2974  if ($result) {
2975  // update last_msg_sent date (for last message sent to external users)
2976  $this->date_last_msg_sent = dol_now();
2977  $this->update($user, 1); // disable trigger when updating date_last_msg_sent. sendTicketMessageByEmail already create an event in actioncomm table.
2978  }
2979  }
2980  }
2981  }
2982  }
2983  }
2984 
2985  // Set status back to "In progress" if not set yet, but only if internal user and not a private message
2986  // Or set status to "In progress" if the client has answered and if the ticket has started
2987  // So we are sure to leave the STATUS_DRAFT, STATUS_NEED_INFO.
2988  if (($object->status < self::STATUS_IN_PROGRESS && !$user->socid && !$private) ||
2989  ($object->status > self::STATUS_IN_PROGRESS && $public_area)
2990  ) {
2991  $object->setStatut($object::STATUS_IN_PROGRESS);
2992  }
2993  return 1;
2994  } else {
2995  setEventMessages($object->error, $object->errors, 'errors');
2996  return -1;
2997  }
2998  } else {
2999  setEventMessages($this->error, $this->errors, 'errors');
3000  return -1;
3001  }
3002  }
3003 
3004 
3017  public function sendTicketMessageByEmail($subject, $message, $send_internal_cc = 0, $array_receiver = array(), $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array())
3018  {
3019  global $conf, $langs, $user;
3020 
3021  if (getDolGlobalString('TICKET_DISABLE_ALL_MAILS')) {
3022  dol_syslog(get_class($this).'::sendTicketMessageByEmail: Emails are disable into ticket setup by option TICKET_DISABLE_ALL_MAILS', LOG_WARNING);
3023  return false;
3024  }
3025 
3026  $langs->load("mails");
3027 
3028  include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
3029  //$contactstatic = new Contact($this->db);
3030 
3031  // If no receiver defined, load all ticket linked contacts
3032  if (!is_array($array_receiver) || !count($array_receiver) > 0) {
3033  $array_receiver = $this->getInfosTicketInternalContact(1);
3034  $array_receiver = array_merge($array_receiver, $this->getInfosTicketExternalContact(1));
3035  }
3036 
3037  $sendtocc = '';
3038  if ($send_internal_cc) {
3039  $sendtocc = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
3040  }
3041 
3042  $from = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
3043  $is_sent = false;
3044  if (is_array($array_receiver) && count($array_receiver) > 0) {
3045  foreach ($array_receiver as $key => $receiver) {
3046  $deliveryreceipt = 0;
3047  $filepath = $filename_list;
3048  $filename = $mimefilename_list;
3049  $mimetype = $mimetype_list;
3050 
3051  // Send email
3052 
3053  $old_MAIN_MAIL_AUTOCOPY_TO = getDolGlobalString('MAIN_MAIL_AUTOCOPY_TO');
3054  if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
3055  $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
3056  }
3057 
3058  $upload_dir_tmp = $conf->user->dir_output."/".$user->id.'/temp';
3059 
3060  include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
3061  $trackid = "tic".$this->id;
3062 
3063  $moreinheader = 'X-Dolibarr-Info: sendTicketMessageByEmail'."\r\n";
3064  if (!empty($this->email_msgid)) {
3065  // We must also add 1 entry In-Reply-To: <$this->email_msgid> with Message-ID we respond from (See RFC5322).
3066  $moreinheader .= 'In-Reply-To: <'.$this->email_msgid.'>'."\r\n";
3067  // TODO We should now be able to give the in_reply_to as a dedicated parameter of new CMailFile() instead of into $moreinheader.
3068  }
3069 
3070  // We should add here also a header 'References:'
3071  // According to RFC5322, we should add here all the References fields of the initial message concatenated with
3072  // the Message-ID of the message we respond from (but each ID must be once).
3073  $references = '';
3074  if (!empty($this->origin_references)) { // $this->origin_references should be '<'.$this->origin_references.'>'
3075  $references .= (empty($references) ? '' : ' ').$this->origin_references;
3076  }
3077  if (!empty($this->email_msgid) && !preg_match('/'.preg_quote($this->email_msgid, '/').'/', $references)) {
3078  $references .= (empty($references) ? '' : ' ').'<'.$this->email_msgid.'>';
3079  }
3080  if ($references) {
3081  $moreinheader .= 'References: '.$references."\r\n";
3082  // TODO We should now be able to give the references as a dedicated parameter of new CMailFile() instead of into $moreinheader.
3083  }
3084 
3085  $mailfile = new CMailFile($subject, $receiver, $from, $message, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1, '', '', $trackid, $moreinheader, 'ticket', '', $upload_dir_tmp);
3086 
3087  if ($mailfile->error) {
3088  setEventMessages($mailfile->error, null, 'errors');
3089  } else {
3090  $result = $mailfile->sendfile();
3091  if ($result) {
3092  setEventMessages($langs->trans('MailSuccessfulySent', $mailfile->getValidAddress($from, 2), $mailfile->getValidAddress($receiver, 2)), null, 'mesgs');
3093  $is_sent = true;
3094  } else {
3095  $langs->load("other");
3096  if ($mailfile->error) {
3097  setEventMessages($langs->trans('ErrorFailedToSendMail', $from, $receiver), null, 'errors');
3098  dol_syslog($langs->trans('ErrorFailedToSendMail', $from, $receiver).' : '.$mailfile->error);
3099  } else {
3100  setEventMessages('No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS', null, 'errors');
3101  }
3102  }
3103  }
3104 
3105  if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
3106  $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
3107  }
3108  }
3109  } else {
3110  $langs->load("other");
3111  setEventMessages($langs->trans('ErrorMailRecipientIsEmptyForSendTicketMessage'), null, 'warnings');
3112  }
3113 
3114  return $is_sent;
3115  }
3116 
3117  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3125  public function load_board($user, $mode)
3126  {
3127  // phpcs:enable
3128  global $user, $langs;
3129 
3130  $now = dol_now();
3131  $delay_warning = 0;
3132 
3133  $clause = " WHERE";
3134 
3135  $sql = "SELECT p.rowid, p.ref, p.datec as datec";
3136  $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
3137  if (isModEnabled('societe') && !$user->hasRight('societe', 'client', 'voir') && !$user->socid) {
3138  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
3139  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3140  $clause = " AND";
3141  }
3142  $sql .= $clause." p.entity IN (".getEntity('ticket').")";
3143  if ($mode == 'opened') {
3144  $sql .= " AND p.fk_statut NOT IN (".Ticket::STATUS_CLOSED.", ".Ticket::STATUS_CANCELED.")";
3145  }
3146  if ($user->socid) {
3147  $sql .= " AND p.fk_soc = ".((int) $user->socid);
3148  }
3149 
3150  $resql = $this->db->query($sql);
3151  if ($resql) {
3152  $label = $labelShort = '';
3153  $status = '';
3154  if ($mode == 'opened') {
3155  $status = 'openall';
3156  //$delay_warning = $conf->ticket->warning_delay;
3157  $delay_warning = 0; // @phan-suppress-current-line PhanPluginRedundantAssignment
3158  $label = $langs->trans("MenuListNonClosed");
3159  $labelShort = $langs->trans("MenuListNonClosed");
3160  }
3161 
3162  $response = new WorkboardResponse();
3163  //$response->warning_delay = $delay_warning / 60 / 60 / 24;
3164  $response->label = $label;
3165  $response->labelShort = $labelShort;
3166  $response->url = DOL_URL_ROOT.'/ticket/list.php?search_fk_statut[]='.$status;
3167  $response->img = img_object('', "ticket");
3168 
3169  // This assignment in condition is not a bug. It allows walking the results.
3170  while ($obj = $this->db->fetch_object($resql)) {
3171  $response->nbtodo++;
3172  if ($mode == 'opened') {
3173  $datelimit = $this->db->jdate($obj->datec) + $delay_warning;
3174  if ($datelimit < $now) {
3175  //$response->nbtodolate++;
3176  }
3177  }
3178  }
3179  return $response;
3180  } else {
3181  $this->error = $this->db->lasterror();
3182  return -1;
3183  }
3184  }
3185 
3191  public function loadStateBoard()
3192  {
3193  global $user;
3194 
3195  $this->nb = array();
3196  $clause = "WHERE";
3197 
3198  $sql = "SELECT count(p.rowid) as nb";
3199  $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
3200  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3201  if (!$user->hasRight('societe', 'client', 'voir')) {
3202  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3203  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3204  $clause = "AND";
3205  }
3206  $sql .= " ".$clause." p.entity IN (".getEntity('ticket').")";
3207 
3208  $resql = $this->db->query($sql);
3209  if ($resql) {
3210  // This assignment in condition is not a bug. It allows walking the results.
3211  while ($obj = $this->db->fetch_object($resql)) {
3212  $this->nb["ticket"] = $obj->nb;
3213  }
3214  $this->db->free($resql);
3215  return 1;
3216  } else {
3217  dol_print_error($this->db);
3218  $this->error = $this->db->lasterror();
3219  return -1;
3220  }
3221  }
3222 
3231  public static function replaceThirdparty($db, $origin_id, $dest_id)
3232  {
3233  $tables = array('ticket');
3234 
3235  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3236  }
3237 
3245  public function getKanbanView($option = '', $arraydata = null)
3246  {
3247  global $langs;
3248 
3249  $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3250 
3251  $return = '<div class="box-flex-item box-flex-grow-zero">';
3252  $return .= '<div class="info-box info-box-sm">';
3253  $return .= '<span class="info-box-icon bg-infobox-action">';
3254  $return .= img_picto('', $this->picto);
3255  $return .= '</span>';
3256  $return .= '<div class="info-box-content">';
3257  $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
3258  if ($selected >= 0) {
3259  $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3260  }
3261  if (!empty($arraydata['user_assignment'])) {
3262  $return .= '<br><span class="info-box-label" title="'.dol_escape_htmltag($langs->trans("AssignedTo")).'">'.$arraydata['user_assignment'].'</span>';
3263  }
3264  if (property_exists($this, 'type_code') && !empty($this->type_code)) {
3265  $return .= '<br>';
3266  $return .= '<div class="tdoverflowmax125 inline-block">'.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$this->type_code, 'c_ticket_type', 'code', 'label', $this->type_code).'</div>';
3267  }
3268  if (method_exists($this, 'getLibStatut')) {
3269  $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
3270  }
3271  $return .= '</div>';
3272  $return .= '</div>';
3273  $return .= '</div>';
3274 
3275  return $return;
3276  }
3277 
3289  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3290  {
3291  global $langs;
3292 
3293  $langs->load("ticket");
3294  $outputlangs->load("ticket");
3295 
3296  if (!dol_strlen($modele)) {
3297  $modele = 'generic_ticket_odt';
3298 
3299  if (!empty($this->model_pdf)) {
3300  $modele = $this->model_pdf;
3301  } elseif (getDolGlobalString('TICKET_ADDON_PDF')) {
3302  $modele = getDolGlobalString('TICKET_ADDON_PDF');
3303  }
3304  }
3305 
3306  $modelpath = "core/modules/ticket/doc/";
3307 
3308  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3309  }
3310 }
if($user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition: card.php:58
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition: security.php:604
$object ref
Definition: info.php:79
Class to manage agenda events (actions)
Class to send emails (with attachments or not) Usage: $mailfile = new CMailFile($subject,...
Class to manage categories.
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
call_trigger($triggerName, $user)
Call trigger based on this instance.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
Class to manage contact/addresses.
Class to manage ECM files.
Class to manage interventions.
Class to manage generation of HTML components Only common components must be here.
static selectarray($htmlname, $array, $id='', $show_empty=0, $key_in_label=0, $value_as_key=0, $moreparam='', $translate=0, $maxlen=0, $disabled=0, $sort='', $morecss='minwidth75', $addjscombo=1, $moreparamonempty='', $disablebademail=0, $nohtmlescape=0)
Return a HTML select string, built from an array of key+value.
Class permettant la generation du formulaire html d'envoi de mail unitaire Usage: $formail = new Form...
Class to manage third parties objects (customers, suppliers, prospects...)
fetch($id=0, $ref='', $track_id='', $email_msgid='')
Load object in memory from the database.
fetchAll($user, $sortorder='ASC', $sortfield='t.datec', $limit=0, $offset=0, $arch=0, $filter='')
Load all objects in memory from database.
$email_from
Email from user.
static replaceThirdparty($db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
listeContact($statusoflink=-1, $source='external', $list=0, $code='', $status=-1)
Get array of all contacts for a ticket Override method of file commonobject.class....
loadCacheMsgsTicket()
Load the list of event on ticket into ->cache_msgs_ticket.
setProgression($percent)
Define progression of current ticket.
searchSocidByEmail($email, $type=0, $filters=array(), $clause='AND')
Search and fetch thirparties by email.
loadCacheSeveritiesTickets()
Charge dans cache la liste des sévérité de tickets (paramétrable dans dictionnaire)
$fields
'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]',...
getInfosTicketExternalContact($status=-1)
Retrieve information about external contacts.
searchContactByEmail($email, $socid=0, $case='')
Search and fetch contacts by email.
getIdTicketCustomerInvolvedContact()
Return id des contacts clients des intervenants.
LibStatut($status, $mode=0, $notooltip=0, $progress=0)
Return status label of object.
$type_label
Type label.
setContract($contractid)
Link element with a contract.
getTicketAllContacts()
Return id of all contacts for ticket.
createFromClone(User $user, $fromid)
Load an object from its id and create a new one in database.
assignUser($user, $id_assign_user, $notrigger=0)
Set an assigned user to a ticket.
loadStateBoard()
Load indicator this->nb of global stats widget.
getTooltipContentArray($params)
getTooltipContentArray
getKanbanView($option='', $arraydata=null)
Return clicable link of object (with eventually picto)
printSelectStatus($selected="")
Print selected status.
is_photo_available($sdir)
Return if at least one photo is available.
getIdTicketInternalContact()
Return id des contacts interne de suivi.
update($user, $notrigger=0)
Update object into 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=null)
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.
$origin_references
References from origin email.
create($user, $notrigger=0)
Create object into database.
$severity_label
Severity label.
createTicketMessage($user, $notrigger=0, $filename_list=array(), $mimetype_list=array(), $mimefilename_list=array(), $send_email=false, $public_area=0)
Add message into database.
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.
checkExistingRef(string $action, string $getRef)
Check if ref exists or not.
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.
$origin_replyto
Email to reply to.
getIdTicketInternalInvolvedContact()
Return id des contacts clients des intervenants.
copyFilesForTicket($forcetrackid=null)
Copy files defined into $_SESSION array into the ticket directory of attached files.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
getNomUrl($withpicto=0, $option='', $notooltip=0, $morecss='', $save_lastsearch_value=-1)
Return a link to the object card (with optionally the picto)
load_board($user, $mode)
Load indicators for dashboard (this->nbtodo and this->nbtodolate)
getInfosTicketInternalContact($status=-1)
Retrieve information about internal contacts.
Class to manage Dolibarr users.
Definition: user.class.php:50
if(isModEnabled('invoice') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&!getDolGlobalString('MAIN_USE_NEW_SUPPLIERMOD') && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') && $user->hasRight('tax', 'charges', 'lire')) if(isModEnabled('invoice') &&isModEnabled('order') && $user->hasRight("commande", "lire") &&!getDolGlobalString('WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER')) $sql
Social contributions to pay.
Definition: index.php:744
if($cancel &&! $id) if($action=='add' &&! $cancel) if($action=='delete') if($id) $form
Actions.
Definition: card.php:143
dirbasename($pathfile)
Return the relative dirname (relative to DOL_DATA_ROOT) of a full path string.
Definition: files.lib.php:3481
dol_move($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=1, $moreinfo=array())
Move a file into another name.
Definition: files.lib.php:991
dol_is_file($pathoffile)
Return if path is a file.
Definition: files.lib.php:519
dol_is_dir($folder)
Test if filename is a directory.
Definition: files.lib.php:489
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
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.
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
dol_textishtml($msg, $option=0)
Return if a text is a html content.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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:513
image_format_supported($file, $acceptsvg=0)
Return if a filename is file name of a supported image format.
Definition: images.lib.php:84
Class to generate the form for creating a new ticket.
getRandomPassword($generic=false, $replaceambiguouschars=null, $length=32)
Return a generated password using default module.
generate_random_id($car=16)
Generate a random id.
Definition: ticket.lib.php:205