dolibarr 20.0.0
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
32require_once DOL_DOCUMENT_ROOT."/core/class/commonobject.class.php";
33require_once DOL_DOCUMENT_ROOT.'/fichinter/class/fichinter.class.php';
34require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php';
35
36
40class 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
65 public $picto = 'ticket';
66
70 public $track_id;
71
75 public $fk_soc;
76 public $socid;
77
81 public $fk_project;
82
86 public $fk_contract;
87
91 public $origin_email;
92
96 public $fk_user_create;
97
101 public $fk_user_assign;
102
106 public $subject;
107
111 public $message;
112
116 public $private;
117
123 public $fk_statut;
124
128 public $status;
129
133 public $resolution;
134
138 public $progress;
139
143 public $timing;
144
148 public $type_code;
149
153 public $category_code;
154
158 public $severity_code;
159
164
169
174
179
184
189
193 public $datec;
194
198 public $date_read;
199
203 public $date_last_msg_sent;
204
208 public $date_close;
209
213 public $cache_types_tickets;
214
218 public $cache_category_tickets;
219
223 public $cache_msgs_ticket;
224
228 public $notify_tiers_at_create;
229
233 public $email_msgid;
234
238 public $email_date;
239
243 public $ip;
244
248 public $oldcopy;
249
253 public $lines;
254
255
259 public $regeximgext = '\.gif|\.jpg|\.jpeg|\.png|\.bmp|\.webp|\.xpm|\.xbm'; // See also into images.lib.php
260
264 const STATUS_NOT_READ = 0; // Draft. Not take into account yet.
265 const STATUS_READ = 1; // Ticket was read.
266 const STATUS_ASSIGNED = 2; // Ticket was just assigned to someone. Not in progress yet.
267 const STATUS_IN_PROGRESS = 3; // In progress
268 const STATUS_NEED_MORE_INFO = 5; // Waiting requester feedback
269 const STATUS_WAITING = 7; // On hold
270 const STATUS_CLOSED = 8; // Closed - Solved
271 const STATUS_CANCELED = 9; // Closed - Not solved
272
273
300 // BEGIN MODULEBUILDER PROPERTIES
301 public $fields = array(
302 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'visible' => -2, 'enabled' => 1, 'position' => 1, 'notnull' => 1, 'index' => 1, 'comment' => "Id"),
303 'entity' => array('type' => 'integer', 'label' => 'Entity', 'visible' => 0, 'enabled' => 1, 'position' => 5, 'notnull' => 1, 'index' => 1),
304 '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),
305 'track_id' => array('type' => 'varchar(255)', 'label' => 'TicketTrackId', 'visible' => -2, 'enabled' => 1, 'position' => 11, 'notnull' => -1, 'searchall' => 1, 'help' => "Help text"),
306 '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'),
307 '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'),
308 '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'),
309 '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'),
310 'subject' => array('type' => 'varchar(255)', 'label' => 'Subject', 'visible' => 1, 'enabled' => 1, 'position' => 19, 'notnull' => -1, 'searchall' => 1, 'help' => "", 'css' => 'maxwidth200 tdoverflowmax200', 'csslist'=>'tdoverflowmax250', 'autofocusoncreate' => 1),
311 'type_code' => array('type' => 'varchar(32)', 'label' => 'Type', 'visible' => 1, 'enabled' => 1, 'position' => 20, 'notnull' => -1, 'help' => "", 'csslist' => 'tdoverflowmax100'),
312 'category_code' => array('type' => 'varchar(32)', 'label' => 'TicketCategory', 'visible' => -1, 'enabled' => 1, 'position' => 21, 'notnull' => -1, 'help' => "", 'css' => 'maxwidth100 tdoverflowmax200'),
313 'severity_code' => array('type' => 'varchar(32)', 'label' => 'Severity', 'visible' => 1, 'enabled' => 1, 'position' => 22, 'notnull' => -1, 'help' => "", 'css' => 'maxwidth100'),
314 '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'),
315 'notify_tiers_at_create' => array('type' => 'integer', 'label' => 'NotifyThirdparty', 'visible' => -1, 'enabled' => 0, 'position' => 51, 'notnull' => 1, 'index' => 1),
316 '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"),
317 '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"),
318 //'timing' => array('type'=>'varchar(20)', 'label'=>'Timing', 'visible'=>-1, 'enabled'=>1, 'position'=>42, 'notnull'=>-1, 'help'=>""), // what is this ?
319 'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'visible' => 1, 'enabled' => 1, 'position' => 500, 'notnull' => 1, 'csslist' => 'nowraponall'),
320 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'visible' => -1, 'enabled' => 1, 'position' => 501, 'notnull' => 1),
321 'date_read' => array('type' => 'datetime', 'label' => 'DateReading', 'visible' => -1, 'enabled' => 1, 'position' => 505, 'notnull' => 1, 'csslist' => 'nowraponall'),
322 'date_last_msg_sent' => array('type' => 'datetime', 'label' => 'TicketLastMessageDate', 'visible' => 0, 'enabled' => 1, 'position' => 506, 'notnull' => -1),
323 'fk_user_assign' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'AssignedTo', 'visible' => 1, 'enabled' => 1, 'position' => 507, 'notnull' => 1, 'csslist' => 'tdoverflowmax100 maxwidth150onsmartphone'),
324 'date_close' => array('type' => 'datetime', 'label' => 'TicketCloseOn', 'visible' => -1, 'enabled' => 1, 'position' => 510, 'notnull' => 1),
325 'message' => array('type' => 'html', 'label' => 'Message', 'visible' => -2, 'enabled' => 1, 'position' => 540, 'notnull' => -1,),
326 'email_msgid' => array('type' => 'varchar(255)', 'label' => 'EmailMsgID', 'visible' => -2, 'enabled' => 1, 'position' => 540, 'notnull' => -1, 'help' => 'EmailMsgIDDesc', 'csslist' => 'tdoverflowmax100'),
327 'email_date' => array('type' => 'datetime', 'label' => 'EmailDate', 'visible' => -2, 'enabled' => 1, 'position' => 541),
328 'progress' => array('type' => 'integer', 'label' => 'Progression', 'visible' => -1, 'enabled' => 1, 'position' => 540, 'notnull' => -1, 'css' => 'right', 'help' => "", 'isameasure' => 2, 'csslist' => 'width50'),
329 'resolution' => array('type' => 'integer', 'label' => 'Resolution', 'visible' => -1, 'enabled' => 'getDolGlobalString("TICKET_ENABLE_RESOLUTION")', 'position' => 550, 'notnull' => 1),
330 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'PDFTemplate', 'enabled' => 1, 'visible' => 0, 'position' => 560),
331 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => -1, 'position' => 570),
332 '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')),
333 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 900),
334 );
335 // END MODULEBUILDER PROPERTIES
336
337
343 public function __construct($db)
344 {
345 $this->db = $db;
346
347 $this->ismultientitymanaged = 1;
348 $this->isextrafieldmanaged = 1;
349
350 $this->labelStatusShort = array(
351 self::STATUS_NOT_READ => 'Unread',
352 self::STATUS_READ => 'Read',
353 self::STATUS_ASSIGNED => 'Assigned',
354 self::STATUS_IN_PROGRESS => 'InProgress',
355 self::STATUS_NEED_MORE_INFO => 'NeedMoreInformationShort',
356 self::STATUS_WAITING => 'OnHold',
357 self::STATUS_CLOSED => 'SolvedClosed',
358 self::STATUS_CANCELED => 'Canceled'
359 );
360 $this->labelStatus = array(
361 self::STATUS_NOT_READ => 'Unread',
362 self::STATUS_READ => 'Read',
363 self::STATUS_ASSIGNED => 'Assigned',
364 self::STATUS_IN_PROGRESS => 'InProgress',
365 self::STATUS_NEED_MORE_INFO => 'NeedMoreInformation',
366 self::STATUS_WAITING => 'OnHold',
367 self::STATUS_CLOSED => 'SolvedClosed',
368 self::STATUS_CANCELED => 'Canceled'
369 );
370
371 if (!getDolGlobalString('TICKET_INCLUDE_SUSPENDED_STATUS')) {
372 unset($this->fields['fk_statut']['arrayofkeyval'][self::STATUS_WAITING]);
373 unset($this->labelStatusShort[self::STATUS_WAITING]);
374 unset($this->labelStatus[self::STATUS_WAITING]);
375 }
376 }
377
384 private function verify()
385 {
386 $this->errors = array();
387
388 $result = 0;
389
390 // Clean parameters
391 if (isset($this->ref)) {
392 $this->ref = trim($this->ref);
393 }
394
395 if (isset($this->track_id)) {
396 $this->track_id = trim($this->track_id);
397 }
398
399 if (isset($this->fk_soc)) {
400 $this->fk_soc = (int) $this->fk_soc;
401 }
402
403 if (isset($this->fk_project)) {
404 $this->fk_project = (int) $this->fk_project;
405 }
406
407 if (isset($this->origin_email)) {
408 $this->origin_email = trim($this->origin_email);
409 }
410
411 if (isset($this->fk_user_create)) {
412 $this->fk_user_create = (int) $this->fk_user_create;
413 }
414
415 if (isset($this->fk_user_assign)) {
416 $this->fk_user_assign = (int) $this->fk_user_assign;
417 }
418
419 if (isset($this->subject)) {
420 $this->subject = trim($this->subject);
421 }
422
423 if (isset($this->message)) {
424 $this->message = trim($this->message);
425 if (dol_strlen($this->message) > 65000) {
426 $this->errors[] = 'ErrorFieldTooLong';
427 dol_syslog(get_class($this).'::create error -1 message too long', LOG_ERR);
428 $result = -1;
429 }
430 }
431
432 if (isset($this->fk_statut)) {
433 $this->fk_statut = (int) $this->fk_statut;
434 }
435
436 if (isset($this->resolution)) {
437 $this->resolution = trim($this->resolution);
438 }
439
440 if (isset($this->progress)) {
441 $this->progress = (int) $this->progress;
442 }
443
444 if (isset($this->timing)) {
445 $this->timing = trim($this->timing);
446 }
447
448 if (isset($this->type_code)) {
449 $this->type_code = trim($this->type_code);
450 }
451
452 if (isset($this->category_code)) {
453 $this->category_code = trim($this->category_code);
454 }
455
456 if (isset($this->severity_code)) {
457 $this->severity_code = trim($this->severity_code);
458 }
459
460 if (empty($this->ref)) {
461 $this->errors[] = 'ErrorTicketRefRequired';
462 dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR);
463 $result = -1;
464 }
465
466 return $result;
467 }
468
477 public function checkExistingRef(string $action, string $getRef): bool
478 {
479 $test = new self($this->db);
480
481 if ($test->fetch('', $getRef) > 0) {
482 if (($action == 'add') || ($action == 'update' && $this->ref != $getRef)) {
483 return true;
484 }
485 }
486
487 $this->ref = $getRef;
488 return false;
489 }
490
498 public function create($user, $notrigger = 0)
499 {
500 global $conf;
501
502 $error = 0;
503
504 // Clean parameters
505 $this->datec = dol_now();
506 if (empty($this->track_id)) {
507 $this->track_id = generate_random_id(16);
508 }
509
510 // Check more parameters
511 // If error, this->errors[] is filled
512 $result = $this->verify();
513
514 if ($result >= 0) {
515 $this->entity = ((isset($this->entity) && is_numeric($this->entity)) ? $this->entity : $conf->entity);
516
517 // Insert request
518 $sql = "INSERT INTO ".MAIN_DB_PREFIX."ticket(";
519 $sql .= "ref,";
520 $sql .= "track_id,";
521 $sql .= "fk_soc,";
522 $sql .= "fk_project,";
523 $sql .= "fk_contract,";
524 $sql .= "origin_email,";
525 $sql .= "origin_replyto,";
526 $sql .= "origin_references,";
527 $sql .= "fk_user_create,";
528 $sql .= "fk_user_assign,";
529 $sql .= "email_msgid,";
530 $sql .= "email_date,";
531 $sql .= "subject,";
532 $sql .= "message,";
533 $sql .= "fk_statut,";
534 $sql .= "resolution,";
535 $sql .= "progress,";
536 $sql .= "timing,";
537 $sql .= "type_code,";
538 $sql .= "category_code,";
539 $sql .= "severity_code,";
540 $sql .= "datec,";
541 $sql .= "date_read,";
542 $sql .= "date_close,";
543 $sql .= "entity,";
544 $sql .= "notify_tiers_at_create,";
545 $sql .= "model_pdf,";
546 $sql .= "ip";
547 $sql .= ") VALUES (";
548 $sql .= " ".(!isset($this->ref) ? '' : "'".$this->db->escape($this->ref)."'").",";
549 $sql .= " ".(!isset($this->track_id) ? 'NULL' : "'".$this->db->escape($this->track_id)."'").",";
550 $sql .= " ".($this->fk_soc > 0 ? ((int) $this->fk_soc) : "null").",";
551 $sql .= " ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null").",";
552 $sql .= " ".($this->fk_contract > 0 ? ((int) $this->fk_contract) : "null").",";
553 $sql .= " ".(!isset($this->origin_email) ? 'NULL' : "'".$this->db->escape($this->origin_email)."'").",";
554 $sql .= " ".(!isset($this->origin_replyto) ? 'NULL' : "'".$this->db->escape($this->origin_replyto)."'").",";
555 $sql .= " ".(!isset($this->origin_references) ? 'NULL' : "'".$this->db->escape($this->origin_references)."'").",";
556 $sql .= " ".(!isset($this->fk_user_create) ? ($user->id > 0 ? ((int) $user->id) : 'NULL') : ($this->fk_user_create > 0 ? ((int) $this->fk_user_create) : 'NULL')).",";
557 $sql .= " ".($this->fk_user_assign > 0 ? ((int) $this->fk_user_assign) : 'NULL').",";
558 $sql .= " ".(empty($this->email_msgid) ? 'NULL' : "'".$this->db->escape($this->email_msgid)."'").",";
559 $sql .= " ".(empty($this->email_date) ? 'NULL' : "'".$this->db->idate($this->email_date)."'").",";
560 $sql .= " ".(!isset($this->subject) ? 'NULL' : "'".$this->db->escape($this->subject)."'").",";
561 $sql .= " ".(!isset($this->message) ? 'NULL' : "'".$this->db->escape($this->message)."'").",";
562 $sql .= " ".(!isset($this->status) ? '0' : ((int) $this->status)).",";
563 $sql .= " ".(!isset($this->resolution) ? 'NULL' : ((int) $this->resolution)).",";
564 $sql .= " ".(!isset($this->progress) ? '0' : ((int) $this->progress)).",";
565 $sql .= " ".(!isset($this->timing) ? 'NULL' : "'".$this->db->escape($this->timing)."'").",";
566 $sql .= " ".(!isset($this->type_code) ? 'NULL' : "'".$this->db->escape($this->type_code)."'").",";
567 $sql .= " ".(empty($this->category_code) || $this->category_code == '-1' ? 'NULL' : "'".$this->db->escape($this->category_code)."'").",";
568 $sql .= " ".(!isset($this->severity_code) ? 'NULL' : "'".$this->db->escape($this->severity_code)."'").",";
569 $sql .= " ".(!isset($this->datec) || dol_strlen($this->datec) == 0 ? 'NULL' : "'".$this->db->idate($this->datec)."'").",";
570 $sql .= " ".(!isset($this->date_read) || dol_strlen($this->date_read) == 0 ? 'NULL' : "'".$this->db->idate($this->date_read)."'").",";
571 $sql .= " ".(!isset($this->date_close) || dol_strlen($this->date_close) == 0 ? 'NULL' : "'".$this->db->idate($this->date_close)."'");
572 $sql .= ", ".((int) $this->entity);
573 $sql .= ", ".(!isset($this->notify_tiers_at_create) ? 1 : ((int) $this->notify_tiers_at_create));
574 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
575 $sql .= ", ".(!isset($this->ip) ? 'NULL' : "'".$this->db->escape($this->ip)."'");
576 $sql .= ")";
577
578 $this->db->begin();
579
580 dol_syslog(get_class($this)."::create", LOG_DEBUG);
581 $resql = $this->db->query($sql);
582 if (!$resql) {
583 $error++;
584 $this->errors[] = "Error ".$this->db->lasterror();
585 }
586
587 if (!$error) {
588 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."ticket");
589 }
590
591 if (!$error && getDolGlobalString('TICKET_ADD_AUTHOR_AS_CONTACT') && empty($this->context["createdfrompublicinterface"])) {
592 // add creator as contributor
593
594 // We first check the type of contact (internal or external)
595 if (!empty($user->socid) && !empty($user->contact_id) && getDolGlobalInt('TICKET_ADD_AUTHOR_AS_CONTACT') == 2) {
596 $contact_type = 'external';
597 $contributor_id = $user->contact_id;
598 } else {
599 $contact_type = 'internal';
600 $contributor_id = $user->id;
601 }
602
603 // We add the creator as contributor
604 if ($this->add_contact($contributor_id, 'CONTRIBUTOR', $contact_type) < 0) {
605 $error++;
606 }
607 }
608
609 if (!$error && $this->fk_user_assign > 0) {
610 if ($this->add_contact($this->fk_user_assign, 'SUPPORTTEC', 'internal') < 0) {
611 $error++;
612 }
613 }
614
615
616 //Update extrafield
617 if (!$error) {
618 $result = $this->insertExtraFields();
619 if ($result < 0) {
620 $error++;
621 }
622 }
623
624 if (!$error && !$notrigger) {
625 // Call trigger
626 $result = $this->call_trigger('TICKET_CREATE', $user);
627 if ($result < 0) {
628 $error++;
629 }
630 // End call triggers
631 }
632
633 // Commit or rollback
634 if ($error) {
635 foreach ($this->errors as $errmsg) {
636 dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
637 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
638 }
639 $this->db->rollback();
640 return -1 * $error;
641 } else {
642 $this->db->commit();
643 return $this->id;
644 }
645 } else {
646 $this->db->rollback();
647 dol_syslog(get_class($this)."::Create fails verify ".implode(',', $this->errors), LOG_WARNING);
648 return -3;
649 }
650 }
651
661 public function fetch($id = 0, $ref = '', $track_id = '', $email_msgid = '')
662 {
663 global $langs;
664
665 // Check parameters
666 if (empty($id) && empty($ref) && empty($track_id) && empty($email_msgid)) {
667 $this->error = 'ErrorWrongParameters';
668 dol_print_error(null, get_class($this)."::fetch ".$this->error);
669 return -1;
670 }
671
672 $sql = "SELECT";
673 $sql .= " t.rowid,";
674 $sql .= " t.entity,";
675 $sql .= " t.ref,";
676 $sql .= " t.track_id,";
677 $sql .= " t.fk_soc,";
678 $sql .= " t.fk_project,";
679 $sql .= " t.fk_contract,";
680 $sql .= " t.origin_email,";
681 $sql .= " t.origin_replyto,";
682 $sql .= " t.origin_references,";
683 $sql .= " t.fk_user_create,";
684 $sql .= " t.fk_user_assign,";
685 $sql .= " t.email_msgid,";
686 $sql .= " t.email_date,";
687 $sql .= " t.subject,";
688 $sql .= " t.message,";
689 $sql .= " t.fk_statut as status,";
690 $sql .= " t.resolution,";
691 $sql .= " t.progress,";
692 $sql .= " t.timing,";
693 $sql .= " t.type_code,";
694 $sql .= " t.category_code,";
695 $sql .= " t.severity_code,";
696 $sql .= " t.datec,";
697 $sql .= " t.date_read,";
698 $sql .= " t.date_last_msg_sent,";
699 $sql .= " t.date_close,";
700 $sql .= " t.tms,";
701 $sql .= " t.model_pdf,";
702 $sql .= " t.extraparams,";
703 $sql .= " t.ip,";
704 $sql .= " type.label as type_label, category.label as category_label, severity.label as severity_label";
705 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as t";
706 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_type as type ON type.code=t.type_code";
707 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_category as category ON category.code=t.category_code";
708 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_severity as severity ON severity.code=t.severity_code";
709
710 if ($id) {
711 $sql .= " WHERE t.rowid = ".((int) $id);
712 } else {
713 $sql .= " WHERE t.entity IN (".getEntity($this->element, 1).")";
714 if (!empty($ref)) {
715 $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
716 } elseif ($track_id) {
717 $sql .= " AND t.track_id = '".$this->db->escape($track_id)."'";
718 } else {
719 $sql .= " AND t.email_msgid = '".$this->db->escape($email_msgid)."'";
720 }
721 }
722
723 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
724 $resql = $this->db->query($sql);
725 if ($resql) {
726 if ($this->db->num_rows($resql)) {
727 $obj = $this->db->fetch_object($resql);
728
729 $this->id = $obj->rowid;
730 $this->entity = $obj->entity;
731 $this->ref = $obj->ref;
732 $this->track_id = $obj->track_id;
733 $this->fk_soc = $obj->fk_soc;
734 $this->socid = $obj->fk_soc; // for fetch_thirdparty() method
735 $this->fk_project = $obj->fk_project;
736 $this->fk_contract = $obj->fk_contract;
737 $this->origin_email = $obj->origin_email;
738 $this->origin_replyto = $obj->origin_replyto;
739 $this->origin_references = $obj->origin_references;
740 $this->fk_user_create = $obj->fk_user_create;
741 $this->fk_user_assign = $obj->fk_user_assign;
742 $this->email_msgid = $obj->email_msgid;
743 $this->email_date = $this->db->jdate($obj->email_date);
744 $this->subject = $obj->subject;
745 $this->message = $obj->message;
746 $this->model_pdf = $obj->model_pdf;
747 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
748 $this->ip = $obj->ip;
749
750 $this->status = $obj->status;
751 $this->fk_statut = $this->status; // For backward compatibility
752
753 $this->resolution = $obj->resolution;
754 $this->progress = $obj->progress;
755 $this->timing = $obj->timing;
756
757 $this->type_code = $obj->type_code;
758 $label_type = ($langs->trans("TicketTypeShort".$obj->type_code) != "TicketTypeShort".$obj->type_code ? $langs->trans("TicketTypeShort".$obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));
759 $this->type_label = $label_type;
760
761 $this->category_code = $obj->category_code;
762 $label_category = ($langs->trans("TicketCategoryShort".$obj->category_code) != "TicketCategoryShort".$obj->category_code ? $langs->trans("TicketCategoryShort".$obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));
763 $this->category_label = $label_category;
764
765 $this->severity_code = $obj->severity_code;
766 $label_severity = ($langs->trans("TicketSeverityShort".$obj->severity_code) != "TicketSeverityShort".$obj->severity_code ? $langs->trans("TicketSeverityShort".$obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));
767 $this->severity_label = $label_severity;
768
769 $this->datec = $this->db->jdate($obj->datec);
770 $this->date_creation = $this->db->jdate($obj->datec);
771 $this->date_read = $this->db->jdate($obj->date_read);
772 $this->date_validation = $this->db->jdate($obj->date_read);
773 $this->date_last_msg_sent = $this->db->jdate($obj->date_last_msg_sent);
774 $this->date_close = $this->db->jdate($obj->date_close);
775 $this->tms = $this->db->jdate($obj->tms);
776 $this->date_modification = $this->db->jdate($obj->tms);
777
778 $this->fetch_optionals();
779
780 $this->db->free($resql);
781 return 1;
782 } else {
783 return 0;
784 }
785 } else {
786 $this->error = "Error ".$this->db->lasterror();
787 dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
788 return -1;
789 }
790 }
791
804 public function fetchAll($user, $sortorder = 'ASC', $sortfield = 't.datec', $limit = 0, $offset = 0, $arch = 0, $filter = '')
805 {
806 global $langs, $extrafields;
807
808 // fetch optionals attributes and labels
809 $extrafields->fetch_name_optionals_label($this->table_element);
810
811 $sql = "SELECT";
812 $sql .= " t.rowid,";
813 $sql .= " t.ref,";
814 $sql .= " t.track_id,";
815 $sql .= " t.fk_soc,";
816 $sql .= " t.fk_project,";
817 $sql .= " t.fk_contract,";
818 $sql .= " t.origin_email,";
819 $sql .= " t.origin_replyto,";
820 $sql .= " t.origin_references,";
821 $sql .= " t.fk_user_create, uc.lastname as user_create_lastname, uc.firstname as user_create_firstname,";
822 $sql .= " t.fk_user_assign, ua.lastname as user_assign_lastname, ua.firstname as user_assign_firstname,";
823 $sql .= " t.subject,";
824 $sql .= " t.message,";
825 $sql .= " t.fk_statut as status,";
826 $sql .= " t.resolution,";
827 $sql .= " t.progress,";
828 $sql .= " t.timing,";
829 $sql .= " t.type_code,";
830 $sql .= " t.category_code,";
831 $sql .= " t.severity_code,";
832 $sql .= " t.datec,";
833 $sql .= " t.date_read,";
834 $sql .= " t.date_last_msg_sent,";
835 $sql .= " t.date_close,";
836 $sql .= " t.tms,";
837 $sql .= " type.label as type_label, category.label as category_label, severity.label as severity_label";
838 // Add fields for extrafields
839 if ($extrafields->attributes[$this->table_element]['count'] > 0) {
840 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
841 $sql .= ($extrafields->attributes[$this->table_element]['type'][$key] != 'separate' ? ",ef.".$key." as options_".$key : '');
842 }
843 }
844 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as t";
845 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_type as type ON type.code = t.type_code";
846 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_category as category ON category.code = t.category_code";
847 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_severity as severity ON severity.code = t.severity_code";
848 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = t.fk_soc";
849 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as uc ON uc.rowid = t.fk_user_create";
850 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as ua ON ua.rowid = t.fk_user_assign";
851 if ($extrafields->attributes[$this->table_element]['count'] > 0) {
852 if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {
853 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."ticket_extrafields as ef on (t.rowid = ef.fk_object)";
854 }
855 }
856 $sql .= " WHERE t.entity IN (".getEntity('ticket').")";
857
858 // Manage filter
859 if (is_array($filter)) {
860 foreach ($filter as $key => $value) {
861 if (strpos($key, 'date')) { // To allow $filter['YEAR(s.dated)']=>$year
862 $sql .= " AND ".$this->db->sanitize($key)." = '".$this->db->escape($value)."'";
863 } elseif (($key == 't.fk_user_assign') || ($key == 't.type_code') || ($key == 't.category_code') || ($key == 't.severity_code') || ($key == 't.fk_soc')) {
864 $sql .= " AND ".$this->db->sanitize($key)." = '".$this->db->escape($value)."'";
865 } elseif ($key == 't.fk_statut') {
866 if (is_array($value) && count($value) > 0) {
867 $sql .= " AND ".$this->db->sanitize($key)." IN (".$this->db->sanitize(implode(',', $value)).")";
868 } else {
869 $sql .= " AND ".$this->db->sanitize($key).' = '.((int) $value);
870 }
871 } elseif ($key == 't.fk_contract') {
872 $sql .= " AND ".$this->db->sanitize($key).' = '.((int) $value);
873 } else {
874 $sql .= " AND ".$this->db->sanitize($key)." LIKE '%".$this->db->escape($this->db->escapeforlike($value))."%'";
875 }
876 }
877
878 $filter = '';
879 }
880
881 // Manage filter
882 $errormessage = '';
883 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
884 if ($errormessage) {
885 $this->errors[] = $errormessage;
886 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
887 return -1;
888 }
889
890 // Case of external user
891 $socid = $user->socid ? $user->socid : 0;
892 // If the internal user must only see his customers, force searching by him
893 $search_sale = 0;
894 if (!$user->hasRight('societe', 'client', 'voir')) {
895 $search_sale = $user->id;
896 }
897 // Search on sale representative
898 if ($search_sale && $search_sale != '-1') {
899 if ($search_sale == -2) {
900 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc)";
901 } elseif ($search_sale > 0) {
902 $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).")";
903 }
904 }
905 // Search on socid
906 if ($socid) {
907 $sql .= " AND t.fk_soc = ".((int) $socid);
908 }
909
910 $sql .= $this->db->order($sortfield, $sortorder);
911 if (!empty($limit)) {
912 $sql .= $this->db->plimit($limit + 1, $offset);
913 }
914
915 dol_syslog(get_class($this)."::fetchAll", LOG_DEBUG);
916 $resql = $this->db->query($sql);
917
918 if ($resql) {
919 $this->lines = array();
920
921 $num = $this->db->num_rows($resql);
922 $i = 0;
923
924 if ($num) {
925 while ($i < $num) {
926 $obj = $this->db->fetch_object($resql);
927
928 $line = new self($this->db);
929
930 $line->id = $obj->rowid;
931 //$line->rowid = $obj->rowid;
932 $line->ref = $obj->ref;
933 $line->track_id = $obj->track_id;
934 $line->fk_soc = $obj->fk_soc;
935 $line->fk_project = $obj->fk_project;
936 $line->fk_contract = $obj->fk_contract;
937 $line->origin_email = $obj->origin_email;
938 $line->origin_replyto = $obj->origin_replyto;
939 $line->origin_references = $obj->origin_references;
940
941 $line->fk_user_create = $obj->fk_user_create;
942 $line->fk_user_assign = $obj->fk_user_assign;
943
944 $line->subject = $obj->subject;
945 $line->message = $obj->message;
946 $line->fk_statut = $obj->status;
947 $line->status = $obj->status;
948 $line->resolution = $obj->resolution;
949 $line->progress = $obj->progress;
950 $line->timing = $obj->timing;
951
952 $label_type = ($langs->trans("TicketTypeShort".$obj->type_code) != "TicketTypeShort".$obj->type_code ? $langs->trans("TicketTypeShort".$obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));
953 $line->type_label = $label_type;
954
955 $this->category_code = $obj->category_code;
956 $label_category = ($langs->trans("TicketCategoryShort".$obj->category_code) != "TicketCategoryShort".$obj->category_code ? $langs->trans("TicketCategoryShort".$obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));
957 $line->category_label = $label_category;
958
959 $this->severity_code = $obj->severity_code;
960 $label_severity = ($langs->trans("TicketSeverityShort".$obj->severity_code) != "TicketSeverityShort".$obj->severity_code ? $langs->trans("TicketSeverityShort".$obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));
961 $line->severity_label = $label_severity;
962
963 $line->datec = $this->db->jdate($obj->datec);
964 $line->date_read = $this->db->jdate($obj->date_read);
965 $line->date_last_msg_sent = $this->db->jdate($obj->date_last_msg_sent);
966 $line->date_close = $this->db->jdate($obj->date_close);
967
968 // Extra fields
969 if ($extrafields->attributes[$this->table_element]['count'] > 0) {
970 if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {
971 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
972 $tmpkey = 'options_'.$key;
973 $line->{$tmpkey} = $obj->$tmpkey;
974 }
975 }
976 }
977 $this->lines[$i] = $line;
978 $i++;
979 }
980 }
981 $this->db->free($resql);
982 return $num;
983 } else {
984 $this->error = "Error ".$this->db->lasterror();
985 dol_syslog(get_class($this)."::fetchAll ".$this->error, LOG_ERR);
986 return -1;
987 }
988 }
989
997 public function update($user, $notrigger = 0)
998 {
999 $error = 0;
1000
1001 // $this->oldcopy should have been set by the caller of update
1002 //if (empty($this->oldcopy)) {
1003 // dol_syslog("this->oldcopy should have been set by the caller of update (here properties were already modified)", LOG_WARNING);
1004 // $this->oldcopy = dol_clone($this, 2);
1005 //}
1006
1007 // Clean parameters
1008 if (isset($this->ref)) {
1009 $this->ref = trim($this->ref);
1010 }
1011
1012 if (isset($this->track_id)) {
1013 $this->track_id = trim($this->track_id);
1014 }
1015
1016 if (isset($this->fk_soc)) {
1017 $this->fk_soc = (int) $this->fk_soc;
1018 }
1019
1020 if (isset($this->fk_project)) {
1021 $this->fk_project = (int) $this->fk_project;
1022 }
1023
1024 if (isset($this->fk_contract)) {
1025 $this->fk_contract = (int) $this->fk_contract;
1026 }
1027
1028 if (isset($this->origin_email)) {
1029 $this->origin_email = trim($this->origin_email);
1030 }
1031
1032 if (isset($this->fk_user_create)) {
1033 $this->fk_user_create = (int) $this->fk_user_create;
1034 }
1035
1036 if (isset($this->fk_user_assign)) {
1037 $this->fk_user_assign = (int) $this->fk_user_assign;
1038 }
1039
1040 if (isset($this->subject)) {
1041 $this->subject = trim($this->subject);
1042 }
1043
1044 if (isset($this->message)) {
1045 $this->message = trim($this->message);
1046 if (dol_strlen($this->message) > 65000) {
1047 $this->errors[] = 'ErrorFieldTooLong';
1048 dol_syslog(get_class($this).'::update error -1 message too long', LOG_ERR);
1049 return -1;
1050 }
1051 }
1052
1053 if (isset($this->fk_statut)) {
1054 $this->fk_statut = (int) $this->fk_statut;
1055 }
1056
1057 if (isset($this->resolution)) {
1058 $this->resolution = trim($this->resolution);
1059 }
1060
1061 if (isset($this->progress)) {
1062 $this->progress = (int) $this->progress;
1063 }
1064
1065 if (isset($this->timing)) {
1066 $this->timing = trim($this->timing);
1067 }
1068
1069 if (isset($this->type_code)) {
1070 $this->timing = trim($this->type_code);
1071 }
1072
1073 if (isset($this->category_code)) {
1074 $this->timing = trim($this->category_code);
1075 }
1076
1077 if (isset($this->severity_code)) {
1078 $this->timing = trim($this->severity_code);
1079 }
1080 if (isset($this->model_pdf)) {
1081 $this->model_pdf = trim($this->model_pdf);
1082 }
1083 // Check parameters
1084 // Put here code to add a control on parameters values
1085 // Update request
1086 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket SET";
1087 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "").",";
1088 $sql .= " track_id=".(isset($this->track_id) ? "'".$this->db->escape($this->track_id)."'" : "null").",";
1089 $sql .= " fk_soc=".(isset($this->fk_soc) ? (int) $this->fk_soc : "null").",";
1090 $sql .= " fk_project=".(isset($this->fk_project) ? (int) $this->fk_project : "null").",";
1091 $sql .= " fk_contract=".(isset($this->fk_contract) ? (int) $this->fk_contract : "null").",";
1092 $sql .= " origin_email=".(isset($this->origin_email) ? "'".$this->db->escape($this->origin_email)."'" : "null").",";
1093 $sql .= " origin_replyto=".(isset($this->origin_replyto) ? "'".$this->db->escape($this->origin_replyto)."'" : "null").",";
1094 $sql .= " origin_references=".(isset($this->origin_references) ? "'".$this->db->escape($this->origin_references)."'" : "null").",";
1095 $sql .= " fk_user_create=".(isset($this->fk_user_create) ? (int) $this->fk_user_create : "null").",";
1096 $sql .= " fk_user_assign=".(isset($this->fk_user_assign) ? (int) $this->fk_user_assign : "null").",";
1097 $sql .= " subject=".(isset($this->subject) ? "'".$this->db->escape($this->subject)."'" : "null").",";
1098 $sql .= " message=".(isset($this->message) ? "'".$this->db->escape($this->message)."'" : "null").",";
1099 $sql .= " fk_statut=".(isset($this->fk_statut) ? (int) $this->fk_statut : "0").",";
1100 $sql .= " resolution=".(isset($this->resolution) ? (int) $this->resolution : "null").",";
1101 $sql .= " progress=".(isset($this->progress) ? "'".$this->db->escape($this->progress)."'" : "null").",";
1102 $sql .= " timing=".(isset($this->timing) ? "'".$this->db->escape($this->timing)."'" : "null").",";
1103 $sql .= " type_code=".(isset($this->type_code) ? "'".$this->db->escape($this->type_code)."'" : "null").",";
1104 $sql .= " category_code=".(isset($this->category_code) ? "'".$this->db->escape($this->category_code)."'" : "null").",";
1105 $sql .= " severity_code=".(isset($this->severity_code) ? "'".$this->db->escape($this->severity_code)."'" : "null").",";
1106 $sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1107 $sql .= " date_read=".(dol_strlen($this->date_read) != 0 ? "'".$this->db->idate($this->date_read)."'" : 'null').",";
1108 $sql .= " date_last_msg_sent=".(dol_strlen($this->date_last_msg_sent) != 0 ? "'".$this->db->idate($this->date_last_msg_sent)."'" : 'null').",";
1109 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1110 $sql .= " date_close=".(dol_strlen($this->date_close) != 0 ? "'".$this->db->idate($this->date_close)."'" : 'null');
1111 $sql .= " WHERE rowid=".((int) $this->id);
1112
1113 $this->db->begin();
1114
1115 $resql = $this->db->query($sql);
1116 if (!$resql) {
1117 $error++;
1118 $this->errors[] = "Error ".$this->db->lasterror();
1119 }
1120
1121 if (!$error) {
1122 // Update extrafields
1123 $result = $this->insertExtraFields();
1124 if ($result < 0) {
1125 $error++;
1126 }
1127 }
1128
1129 if (!$error && !$notrigger) {
1130 // Call trigger
1131 $result = $this->call_trigger('TICKET_MODIFY', $user);
1132 if ($result < 0) {
1133 $error++;
1134 }
1135 // End call triggers
1136 }
1137
1138 // Commit or rollback
1139 if ($error) {
1140 foreach ($this->errors as $errmsg) {
1141 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1142 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1143 }
1144 $this->db->rollback();
1145 return -1 * $error;
1146 } else {
1147 $this->db->commit();
1148 return 1;
1149 }
1150 }
1151
1159 public function delete($user, $notrigger = 0)
1160 {
1161 $error = 0;
1162
1163 $this->db->begin();
1164
1165 if (!$error) {
1166 if (!$notrigger) {
1167 // Call trigger
1168 $result = $this->call_trigger('TICKET_DELETE', $user);
1169 if ($result < 0) {
1170 $error++;
1171 }
1172 // End call triggers
1173 }
1174 }
1175
1176 if (!$error) {
1177 // Delete linked contacts
1178 $res = $this->delete_linked_contact();
1179 if ($res < 0) {
1180 dol_syslog(get_class($this)."::delete error", LOG_ERR);
1181 $error++;
1182 }
1183 }
1184
1185 if (!$error) {
1186 // Delete linked object
1187 $res = $this->deleteObjectLinked();
1188 if ($res < 0) {
1189 $error++;
1190 }
1191 }
1192
1193 // Removed extrafields
1194 if (!$error) {
1195 $result = $this->deleteExtraFields();
1196 if ($result < 0) {
1197 $error++;
1198 dol_syslog(get_class($this)."::delete error -3 ".$this->error, LOG_ERR);
1199 }
1200 }
1201
1202 // Delete all child tables
1203
1204 if (!$error) {
1205 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_ticket";
1206 $sql .= " WHERE fk_ticket = ".(int) $this->id;
1207
1208 $result = $this->db->query($sql);
1209 if (!$result) {
1210 $error++;
1211 $this->errors[] = $this->db->lasterror();
1212 }
1213 }
1214
1215 if (!$error) {
1216 $sql = "DELETE FROM ".MAIN_DB_PREFIX."ticket";
1217 $sql .= " WHERE rowid=".((int) $this->id);
1218
1219 dol_syslog(get_class($this)."::delete sql=".$sql);
1220 $resql = $this->db->query($sql);
1221 if (!$resql) {
1222 $error++;
1223 $this->errors[] = "Error ".$this->db->lasterror();
1224 } else {
1225 // we delete file with dol_delete_dir_recursive
1226 $this->deleteEcmFiles(1);
1227
1228 $dir = DOL_DATA_ROOT.'/'.$this->element.'/'.$this->ref;
1229 // For remove dir
1230 if (dol_is_dir($dir)) {
1231 if (!dol_delete_dir_recursive($dir)) {
1232 $this->errors[] = $this->error;
1233 }
1234 }
1235 }
1236 }
1237
1238 // Commit or rollback
1239 if ($error) {
1240 foreach ($this->errors as $errmsg) {
1241 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1242 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1243 }
1244 $this->db->rollback();
1245 return -1 * $error;
1246 } else {
1247 $this->db->commit();
1248 return 1;
1249 }
1250 }
1251
1259 public function createFromClone(User $user, $fromid)
1260 {
1261 $error = 0;
1262
1263 $object = new Ticket($this->db);
1264
1265 $this->db->begin();
1266
1267 // Load source object
1268 $object->fetch($fromid);
1269
1270 // Clear fields
1271 $object->id = 0;
1272 $object->statut = 0;
1273 $object->status = 0;
1274
1275 // Create clone
1276 $object->context['createfromclone'] = 'createfromclone';
1277 $result = $object->create($user);
1278
1279 // Other options
1280 if ($result < 0) {
1281 $this->error = $object->error;
1282 $error++;
1283 }
1284
1285 if (!$error) {
1286 }
1287
1288 unset($object->context['createfromclone']);
1289
1290 // End
1291 if (!$error) {
1292 $this->db->commit();
1293 return $object->id;
1294 } else {
1295 $this->db->rollback();
1296 return -1;
1297 }
1298 }
1299
1306 public function initAsSpecimen()
1307 {
1308 $this->id = 0;
1309 $this->entity = 1;
1310 $this->ref = 'TI0501-001';
1311 $this->track_id = 'XXXXaaaa';
1312 $this->origin_email = 'email@email.com';
1313 $this->fk_project = 1;
1314 $this->fk_user_create = 1;
1315 $this->fk_user_assign = 1;
1316 $this->subject = 'Subject of ticket';
1317 $this->message = 'Message of ticket';
1318 $this->status = 0;
1319 $this->resolution = '1';
1320 $this->progress = 10;
1321 //$this->timing = '30';
1322 $this->type_code = 'TYPECODE';
1323 $this->category_code = 'CATEGORYCODE';
1324 $this->severity_code = 'SEVERITYCODE';
1325 $this->datec = dol_now();
1326 $this->date_read = dol_now();
1327 $this->date_last_msg_sent = dol_now();
1328 $this->date_close = dol_now();
1329 $this->tms = dol_now();
1330
1331 return 1;
1332 }
1333
1340 public function printSelectStatus($selected = "")
1341 {
1342 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 = '');
1343 }
1344
1345
1351 public function loadCacheTypesTickets()
1352 {
1353 global $langs;
1354
1355 if (!empty($this->cache_types_tickets) && count($this->cache_types_tickets)) {
1356 return 0;
1357 }
1358 // Cache deja charge
1359
1360 $sql = "SELECT rowid, code, label, use_default, pos, description";
1361 $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_type";
1362 $sql .= " WHERE entity IN (".getEntity('c_ticket_type').")";
1363 $sql .= " AND active > 0";
1364 $sql .= " ORDER BY pos";
1365 dol_syslog(get_class($this)."::load_cache_type_tickets", LOG_DEBUG);
1366 $resql = $this->db->query($sql);
1367 if ($resql) {
1368 $num = $this->db->num_rows($resql);
1369 $i = 0;
1370 while ($i < $num) {
1371 $obj = $this->db->fetch_object($resql);
1372 $label = ($langs->trans("TicketTypeShort".$obj->code) != "TicketTypeShort".$obj->code ? $langs->trans("TicketTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1373 $this->cache_types_tickets[$obj->rowid]['code'] = $obj->code;
1374 $this->cache_types_tickets[$obj->rowid]['label'] = $label;
1375 $this->cache_types_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1376 $this->cache_types_tickets[$obj->rowid]['pos'] = $obj->pos;
1377 $i++;
1378 }
1379 return $num;
1380 } else {
1381 dol_print_error($this->db);
1382 return -1;
1383 }
1384 }
1385
1392 public function loadCacheCategoriesTickets($publicgroup = -1)
1393 {
1394 global $conf, $langs;
1395
1396 if ($publicgroup == -1 && !empty($this->cache_category_ticket) && count($this->cache_category_tickets)) {
1397 // Cache already loaded
1398 return 0;
1399 }
1400
1401 $sql = "SELECT rowid, code, label, use_default, pos, description, public, active, force_severity, fk_parent";
1402 $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_category";
1403 $sql .= " WHERE entity IN (".getEntity('c_ticket_category').")";
1404 $sql .= " AND active > 0";
1405 if ($publicgroup > -1) {
1406 $sql .= " AND public = ".((int) $publicgroup);
1407 }
1408 $sql .= " ORDER BY pos";
1409
1410 dol_syslog(get_class($this)."::load_cache_categories_tickets", LOG_DEBUG);
1411
1412 $resql = $this->db->query($sql);
1413 if ($resql) {
1414 $num = $this->db->num_rows($resql);
1415 $i = 0;
1416 while ($i < $num) {
1417 $obj = $this->db->fetch_object($resql);
1418 $this->cache_category_tickets[$obj->rowid]['code'] = $obj->code;
1419 $this->cache_category_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1420 $this->cache_category_tickets[$obj->rowid]['pos'] = $obj->pos;
1421 $this->cache_category_tickets[$obj->rowid]['public'] = $obj->public;
1422 $this->cache_category_tickets[$obj->rowid]['active'] = $obj->active;
1423 $this->cache_category_tickets[$obj->rowid]['force_severity'] = $obj->force_severity;
1424 $this->cache_category_tickets[$obj->rowid]['fk_parent'] = $obj->fk_parent;
1425
1426 // If translation exists, we use it to store already translated string.
1427 // Warning: You should not use this and recompute the translated string into caller code to get the value into expected language
1428 $label = ($langs->trans("TicketCategoryShort".$obj->code) != "TicketCategoryShort".$obj->code ? $langs->trans("TicketCategoryShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1429 $this->cache_category_tickets[$obj->rowid]['label'] = $label;
1430
1431 $i++;
1432 }
1433 return $num;
1434 } else {
1435 dol_print_error($this->db);
1436 return -1;
1437 }
1438 }
1439
1446 {
1447 global $conf, $langs;
1448
1449 if (!empty($conf->cache['severity_tickets']) && count($conf->cache['severity_tickets'])) {
1450 // Cache already loaded
1451 return 0;
1452 }
1453
1454 $sql = "SELECT rowid, code, label, use_default, pos, description";
1455 $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_severity";
1456 $sql .= " WHERE entity IN (".getEntity('c_ticket_severity').")";
1457 $sql .= " AND active > 0";
1458 $sql .= " ORDER BY pos";
1459 dol_syslog(get_class($this)."::loadCacheSeveritiesTickets", LOG_DEBUG);
1460 $resql = $this->db->query($sql);
1461 if ($resql) {
1462 $num = $this->db->num_rows($resql);
1463 $i = 0;
1464 while ($i < $num) {
1465 $obj = $this->db->fetch_object($resql);
1466
1467 $conf->cache['severity_tickets'][$obj->rowid]['code'] = $obj->code;
1468 $label = ($langs->trans("TicketSeverityShort".$obj->code) != "TicketSeverityShort".$obj->code ? $langs->trans("TicketSeverityShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1469 $conf->cache['severity_tickets'][$obj->rowid]['label'] = $label;
1470 $conf->cache['severity_tickets'][$obj->rowid]['use_default'] = $obj->use_default;
1471 $conf->cache['severity_tickets'][$obj->rowid]['pos'] = $obj->pos;
1472 $i++;
1473 }
1474 return $num;
1475 } else {
1476 dol_print_error($this->db);
1477 return -1;
1478 }
1479 }
1480
1481
1488 public function getLibStatut($mode = 0)
1489 {
1490 return $this->LibStatut($this->status, $mode, 0, $this->progress);
1491 }
1492
1493
1494 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1504 public function LibStatut($status, $mode = 0, $notooltip = 0, $progress = 0)
1505 {
1506 // phpcs:enable
1507 global $langs, $hookmanager;
1508
1509 $labelStatus = (isset($status) && !empty($this->labelStatus[$status])) ? $this->labelStatus[$status] : '';
1510 $labelStatusShort = (isset($status) && !empty($this->labelStatusShort[$status])) ? $this->labelStatusShort[$status] : '';
1511
1512 switch ($status) {
1513 case self::STATUS_NOT_READ: // Not read
1514 $statusType = 'status0';
1515 break;
1516 case self::STATUS_READ: // Read
1517 $statusType = 'status1';
1518 break;
1519 case self::STATUS_ASSIGNED: // Assigned
1520 $statusType = 'status2';
1521 break;
1522 case self::STATUS_IN_PROGRESS: // In progress
1523 $statusType = 'status4';
1524 break;
1525 case self::STATUS_WAITING: // Waiting/pending/suspended
1526 $statusType = 'status7';
1527 break;
1528 case self::STATUS_NEED_MORE_INFO: // Waiting more information from the requester
1529 $statusType = 'status3';
1530 break;
1531 case self::STATUS_CANCELED: // Canceled
1532 $statusType = 'status9';
1533 break;
1534 case self::STATUS_CLOSED: // Closed
1535 $statusType = 'status6';
1536 break;
1537 default:
1538 $labelStatus = 'Unknown';
1539 $labelStatusShort = 'Unknown';
1540 $statusType = 'status0';
1541 $mode = 0;
1542 }
1543
1544 $parameters = array(
1545 'status' => $status,
1546 'mode' => $mode,
1547 );
1548
1549 // Note that $action and $object may have been modified by hook
1550 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this);
1551
1552 if ($reshook > 0) {
1553 return $hookmanager->resPrint;
1554 }
1555
1556 $params = array();
1557 if ($notooltip) {
1558 $params = array('tooltip' => 'no');
1559 }
1560
1561 $labelStatus = $langs->transnoentitiesnoconv($labelStatus);
1562 $labelStatusShort = $langs->transnoentitiesnoconv($labelStatusShort);
1563
1564 if ($status == self::STATUS_IN_PROGRESS && $progress > 0) {
1565 $labelStatus .= ' ('.round($progress).'%)';
1566 $labelStatusShort .= ' ('.round($progress).'%)';
1567 }
1568
1569 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', $params);
1570 }
1571
1579 public function getTooltipContentArray($params)
1580 {
1581 global $langs;
1582
1583 $langs->load('ticket');
1584 $nofetch = !empty($params['nofetch']);
1585
1586 $datas = array();
1587 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Ticket").'</u>';
1588 $datas['picto'] .= ' '.$this->getLibStatut(4);
1589 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1590 $datas['track_id'] = '<br><b>'.$langs->trans('TicketTrackId').':</b> '.$this->track_id;
1591 $datas['subject'] = '<br><b>'.$langs->trans('Subject').':</b> '.$this->subject;
1592 if ($this->date_creation) {
1593 $datas['date_creation'] = '<br><b>'.$langs->trans('DateCreation').':</b> '.dol_print_date($this->date_creation, 'dayhour');
1594 }
1595 if ($this->date_modification) {
1596 $datas['date_modification'] = '<br><b>'.$langs->trans('DateModification').':</b> '.dol_print_date($this->date_modification, 'dayhour');
1597 }
1598 // show categories for this record only in ajax to not overload lists
1599 if (isModEnabled('category') && !$nofetch) {
1600 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
1601 $form = new Form($this->db);
1602 $datas['categories'] = '<br>' . $form->showCategories($this->id, Categorie::TYPE_TICKET, 1);
1603 }
1604
1605 return $datas;
1606 }
1607
1618 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1619 {
1620 global $action, $conf, $hookmanager, $langs;
1621
1622 if (!empty($conf->dol_no_mouse_hover)) {
1623 $notooltip = 1; // Force disable tooltips
1624 }
1625
1626 $result = '';
1627
1628 $params = [
1629 'id' => $this->id,
1630 'objecttype' => $this->element,
1631 'option' => $option,
1632 'nofetch' => 1,
1633 ];
1634 $classfortooltip = 'classfortooltip';
1635 $dataparams = '';
1636 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1637 $classfortooltip = 'classforajaxtooltip';
1638 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1639 $label = '';
1640 } else {
1641 $label = implode($this->getTooltipContentArray($params));
1642 }
1643
1644 $url = DOL_URL_ROOT.'/ticket/card.php?id='.$this->id;
1645
1646 if ($option != 'nolink') {
1647 // Add param to save lastsearch_values or not
1648 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1649 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1650 $add_save_lastsearch_values = 1;
1651 }
1652 if ($add_save_lastsearch_values) {
1653 $url .= '&save_lastsearch_values=1';
1654 }
1655 }
1656
1657 $linkclose = '';
1658 if (empty($notooltip)) {
1659 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1660 $label = $langs->trans("ShowTicket");
1661 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
1662 }
1663 $linkclose .= ($label ? ' title="'.dol_escape_htmltag($label, 1).'"' : ' title="tocomplete"');
1664 $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
1665 } else {
1666 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1667 }
1668
1669 $linkstart = '<a href="'.$url.'"';
1670 $linkstart .= $linkclose.'>';
1671 $linkend = '</a>';
1672
1673 $result .= $linkstart;
1674 if ($withpicto) {
1675 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1676 }
1677 if ($withpicto != 2) {
1678 $result .= $this->ref;
1679 }
1680 $result .= $linkend;
1681 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1682
1683 $hookmanager->initHooks(array('ticketdao'));
1684 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1685 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1686 if ($reshook > 0) {
1687 $result = $hookmanager->resPrint;
1688 } else {
1689 $result .= $hookmanager->resPrint;
1690 }
1691
1692 return $result;
1693 }
1694
1695
1703 public function markAsRead($user, $notrigger = 0)
1704 {
1705 global $langs;
1706
1707 $error = 0;
1708
1709 if ($this->status != self::STATUS_CANCELED) { // no closed
1710 $this->oldcopy = dol_clone($this, 2);
1711
1712 $this->db->begin();
1713
1714 $this->status = Ticket::STATUS_READ;
1715
1716 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1717 $sql .= " SET fk_statut = ".Ticket::STATUS_READ.", date_read = '".$this->db->idate(dol_now())."'";
1718 $sql .= " WHERE rowid = ".((int) $this->id);
1719
1720 dol_syslog(get_class($this)."::markAsRead");
1721 $resql = $this->db->query($sql);
1722 if ($resql) {
1723 $this->context['actionmsg'] = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1724 $this->context['actionmsg2'] = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1725
1726 if (!$notrigger) {
1727 // Call trigger
1728 $result = $this->call_trigger('TICKET_MODIFY', $user);
1729 if ($result < 0) {
1730 $error++;
1731 }
1732 // End call triggers
1733 }
1734
1735 if (!$error) {
1736 $this->db->commit();
1737 return 1;
1738 } else {
1739 $this->status = $this->oldcopy->status;
1740
1741 $this->db->rollback();
1742 $this->error = implode(',', $this->errors);
1743 dol_syslog(get_class($this)."::markAsRead ".$this->error, LOG_ERR);
1744 return -1;
1745 }
1746 } else {
1747 $this->status = $this->oldcopy->status;
1748
1749 $this->db->rollback();
1750 $this->error = $this->db->lasterror();
1751 dol_syslog(get_class($this)."::markAsRead ".$this->error, LOG_ERR);
1752 return -1;
1753 }
1754 }
1755
1756 return 0;
1757 }
1758
1767 public function assignUser($user, $id_assign_user, $notrigger = 0)
1768 {
1769 $error = 0;
1770
1771 $this->oldcopy = dol_clone($this, 2);
1772
1773 $this->db->begin();
1774
1775 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1776 if ($id_assign_user > 0) {
1777 $sql .= " SET fk_user_assign=".((int) $id_assign_user).", fk_statut = ".Ticket::STATUS_ASSIGNED;
1778 } else {
1779 $sql .= " SET fk_user_assign=null, fk_statut = ".Ticket::STATUS_READ;
1780 }
1781 $sql .= " WHERE rowid = ".((int) $this->id);
1782
1783 dol_syslog(get_class($this)."::assignUser sql=".$sql);
1784 $resql = $this->db->query($sql);
1785 if ($resql) {
1786 $this->fk_user_assign = $id_assign_user; // May be used by trigger
1787
1788 if (!$notrigger) {
1789 // Call trigger
1790 $result = $this->call_trigger('TICKET_ASSIGNED', $user);
1791 if ($result < 0) {
1792 $error++;
1793 }
1794 // End call triggers
1795 }
1796
1797 if (!$error) {
1798 $this->db->commit();
1799 return 1;
1800 } else {
1801 $this->db->rollback();
1802 $this->error = implode(',', $this->errors);
1803 dol_syslog(get_class($this)."::assignUser ".$this->error, LOG_ERR);
1804 return -1;
1805 }
1806 } else {
1807 $this->db->rollback();
1808 $this->error = $this->db->lasterror();
1809 dol_syslog(get_class($this)."::assignUser ".$this->error, LOG_ERR);
1810 return -1;
1811 }
1812 }
1813
1826 public function createTicketMessage($user, $notrigger = 0, $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array(), $send_email = false, $public_area = 0)
1827 {
1828 global $conf, $langs;
1829 $error = 0;
1830
1831 $now = dol_now();
1832
1833 // Clean parameters
1834 if (isset($this->fk_track_id)) {
1835 $this->fk_track_id = trim($this->fk_track_id);
1836 }
1837
1838 if (isset($this->message)) {
1839 $this->message = trim($this->message);
1840 }
1841
1842 $this->db->begin();
1843
1844 // Insert entry into agenda with code 'TICKET_MSG'
1845 include_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
1846 $actioncomm = new ActionComm($this->db);
1847 $actioncomm->type_code = 'AC_OTH_AUTO'; // This is not an entry that must appears into manual calendar but only into CRM calendar
1848 $actioncomm->code = 'TICKET_MSG';
1849 if ($this->private) {
1850 $actioncomm->code = 'TICKET_MSG_PRIVATE';
1851 }
1852 if ($send_email) {
1853 $actioncomm->code .= '_SENTBYMAIL';
1854 }
1855 if ((empty($user->id) || $user->id == 0) && isset($_SESSION['email_customer'])) {
1856 $actioncomm->email_from = $_SESSION['email_customer'];
1857 }
1858 $actioncomm->socid = $this->socid;
1859 $actioncomm->label = $this->subject;
1860 $actioncomm->note_private = $this->message;
1861 $actioncomm->userassigned = array($user->id => array('id' => $user->id,'transparency' => 0));
1862 $actioncomm->userownerid = $user->id;
1863 $actioncomm->datep = $now;
1864 $actioncomm->percentage = -1; // percentage is not relevant for punctual events
1865 $actioncomm->elementtype = 'ticket';
1866 $actioncomm->fk_element = $this->id;
1867 $actioncomm->fk_project = $this->fk_project;
1868
1869 // add contact id from author email on public interface
1870 if ($public_area && !empty($this->origin_email) && getDolGlobalString('TICKET_ASSIGN_CONTACT_TO_MESSAGE')) {
1871 $contacts = $this->searchContactByEmail($this->origin_email);
1872 if (!empty($contacts)) {
1873 // Ensure that contact is active and select first active contact
1874 foreach ($contacts as $contact) {
1875 if ((int) $contact->statut == 1) {
1876 $actioncomm->contact_id = $contact->id;
1877 break;
1878 }
1879 }
1880 }
1881 }
1882
1883 $attachedfiles = array();
1884 $attachedfiles['paths'] = $filename_list;
1885 $attachedfiles['names'] = $mimefilename_list;
1886 $attachedfiles['mimes'] = $mimetype_list;
1887 if (is_array($attachedfiles) && count($attachedfiles) > 0) {
1888 $actioncomm->attachedfiles = $attachedfiles;
1889 }
1890
1891 //if (!empty($mimefilename_list) && is_array($mimefilename_list)) {
1892 // $actioncomm->note_private = dol_concatdesc($actioncomm->note_private, "\n".$langs->transnoentities("AttachedFiles").': '.implode(';', $mimefilename_list));
1893 //}
1894 $actionid = $actioncomm->create($user);
1895 if ($actionid <= 0) {
1896 $error++;
1897 $this->error = $actioncomm->error;
1898 $this->errors = $actioncomm->errors;
1899 }
1900
1901 if ($actionid > 0) {
1902 if (is_array($attachedfiles) && array_key_exists('paths', $attachedfiles) && count($attachedfiles['paths']) > 0) {
1903 // If there is some files, we must now link them to the event, so we can show them per event.
1904 foreach ($attachedfiles['paths'] as $key => $filespath) {
1905 // Disabled the move into another directory, Files for a ticket should be stored into ticket directory. It generates too much troubles.
1906 $destdir = $conf->ticket->dir_output.'/'.$this->ref;
1907 //$destfile = $destdir.'/'.$attachedfiles['names'][$key];
1908 //if (dol_mkdir($destdir) >= 0) {
1909 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1910 //dol_move($filespath, $destfile); // Disabled, a file for a ticket should be stored into ticket directory. It generates big trouble.
1911 if (in_array($actioncomm->code, array('TICKET_MSG', 'TICKET_MSG_SENTBYMAIL'))) {
1912 $ecmfile = new EcmFiles($this->db);
1913 $destdir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $destdir);
1914 $destdir = preg_replace('/[\\/]$/', '', $destdir);
1915 $destdir = preg_replace('/^[\\/]/', '', $destdir);
1916
1917 $result = $ecmfile->fetch(0, '', $destdir.'/'.$attachedfiles['names'][$key]);
1918
1919 // TODO We must add a column into ecm_files table agenda_id to store the ID of event.
1920 // $ecmfile->agenda_id = $actionid;
1921
1922 // Disabled, serious security hole. A file published into the ERP should not become public for everybody.
1923 //require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
1924 //$ecmfile->share = getRandomPassword(true);
1925
1926 if ($result > 0) {
1927 $result = $ecmfile->update($user);
1928 if ($result < 0) {
1929 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
1930 }
1931 }
1932 }
1933 //}
1934 }
1935 }
1936 }
1937
1938 // Commit or rollback
1939 if ($error) {
1940 $this->db->rollback();
1941 return -1 * $error;
1942 } else {
1943 $this->db->commit();
1944 return 1;
1945 }
1946 }
1947
1953 public function loadCacheMsgsTicket()
1954 {
1955 if (!empty($this->cache_msgs_ticket) && is_array($this->cache_msgs_ticket) && count($this->cache_msgs_ticket)) {
1956 return 0;
1957 }
1958
1959 // Cache already loaded
1960
1961 $sql = "SELECT id as rowid, fk_user_author, email_from, datec, datep, label, note as message, code";
1962 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm";
1963 $sql .= " WHERE fk_element = ".(int) $this->id;
1964 $sql .= " AND elementtype = 'ticket'";
1965 $sql .= " ORDER BY datep DESC";
1966
1967 dol_syslog(get_class($this)."::load_cache_actions_ticket", LOG_DEBUG);
1968 $resql = $this->db->query($sql);
1969 if ($resql) {
1970 $num = $this->db->num_rows($resql);
1971 $i = 0;
1972 while ($i < $num) {
1973 $obj = $this->db->fetch_object($resql);
1974 $this->cache_msgs_ticket[$i]['id'] = $obj->rowid;
1975 $this->cache_msgs_ticket[$i]['fk_user_author'] = $obj->fk_user_author;
1976 if (in_array($obj->code, array('TICKET_MSG', 'AC_TICKET_CREATE')) && empty($obj->fk_user_author)) {
1977 $this->cache_msgs_ticket[$i]['fk_contact_author'] = $obj->email_from;
1978 }
1979 $this->cache_msgs_ticket[$i]['datec'] = $this->db->jdate($obj->datec);
1980 $this->cache_msgs_ticket[$i]['datep'] = $this->db->jdate($obj->datep);
1981 $this->cache_msgs_ticket[$i]['subject'] = $obj->label;
1982 $this->cache_msgs_ticket[$i]['message'] = $obj->message;
1983 $this->cache_msgs_ticket[$i]['private'] = (preg_match('/^TICKET_MSG_PRIVATE/', $obj->code) ? 1 : 0);
1984 $i++;
1985 }
1986 return $num;
1987 } else {
1988 $this->error = "Error ".$this->db->lasterror();
1989 dol_syslog(get_class($this)."::load_cache_actions_ticket ".$this->error, LOG_ERR);
1990 return -1;
1991 }
1992 }
1993
2001 public function close(User $user, $mode = 0)
2002 {
2003 global $conf;
2004
2005 if ($this->status != Ticket::STATUS_CLOSED && $this->status != Ticket::STATUS_CANCELED) { // not closed
2006 $this->db->begin();
2007
2008 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2009 $sql .= " SET fk_statut=".($mode ? Ticket::STATUS_CANCELED : Ticket::STATUS_CLOSED).", progress=100, date_close='".$this->db->idate(dol_now())."'";
2010 $sql .= " WHERE rowid = ".((int) $this->id);
2011
2012 dol_syslog(get_class($this)."::close mode=".$mode);
2013 $resql = $this->db->query($sql);
2014 if ($resql) {
2015 $error = 0;
2016
2017 // Valid and close fichinter linked
2018 if (isModEnabled('intervention') && getDolGlobalString('WORKFLOW_TICKET_CLOSE_INTERVENTION')) {
2019 dol_syslog("We have closed the ticket, so we close all linked interventions");
2020 $this->fetchObjectLinked($this->id, $this->element, null, 'fichinter');
2021 if ($this->linkedObjectsIds) {
2022 foreach ($this->linkedObjectsIds['fichinter'] as $fichinter_id) {
2023 $fichinter = new Fichinter($this->db);
2024 $fichinter->fetch($fichinter_id);
2025 if ($fichinter->statut == 0) {
2026 $result = $fichinter->setValid($user);
2027 if (!$result) {
2028 $this->errors[] = $fichinter->error;
2029 $error++;
2030 }
2031 }
2032 if ($fichinter->statut < 3) {
2033 $result = $fichinter->setStatut(3);
2034 if (!$result) {
2035 $this->errors[] = $fichinter->error;
2036 $error++;
2037 }
2038 }
2039 }
2040 }
2041 }
2042
2043 // Call trigger
2044 $result = $this->call_trigger('TICKET_CLOSE', $user);
2045 if ($result < 0) {
2046 $error++;
2047 }
2048 // End call triggers
2049
2050 if (!$error) {
2051 $this->db->commit();
2052 return 1;
2053 } else {
2054 $this->db->rollback();
2055 $this->error = implode(',', $this->errors);
2056 dol_syslog(get_class($this)."::close ".$this->error, LOG_ERR);
2057 return -1;
2058 }
2059 } else {
2060 $this->db->rollback();
2061 $this->error = $this->db->lasterror();
2062 dol_syslog(get_class($this)."::close ".$this->error, LOG_ERR);
2063 return -1;
2064 }
2065 }
2066
2067 return 0;
2068 }
2069
2079 public function searchSocidByEmail($email, $type = 0, $filters = array(), $clause = 'AND')
2080 {
2081 $thirdparties = array();
2082 $exact = 0;
2083
2084 // Generation requete recherche
2085 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."societe";
2086 $sql .= " WHERE entity IN (".getEntity('ticket', 1).")";
2087 if (!empty($type)) {
2088 if ($type == 1 || $type == 2) {
2089 $sql .= " AND client = ".((int) $type);
2090 } elseif ($type == 3) {
2091 $sql .= " AND fournisseur = 1";
2092 }
2093 }
2094 if (!empty($email)) {
2095 if (empty($exact)) {
2096 $regs = array();
2097 if (preg_match('/^([\*])?[^*]+([\*])?$/', $email, $regs) && count($regs) > 1) {
2098 $email = str_replace('*', '%', $email);
2099 } else {
2100 $email = '%'.$email.'%';
2101 }
2102 }
2103 $sql .= " AND ";
2104 if (is_array($filters) && !empty($filters)) {
2105 $sql .= "(";
2106 }
2107
2108 $sql .= "email LIKE '".$this->db->escape($email)."'";
2109 }
2110 if (is_array($filters) && !empty($filters)) {
2111 foreach ($filters as $field => $value) {
2112 $sql .= " ".$clause." ".$field." LIKE '".$this->db->escape($value)."'";
2113 }
2114 if (!empty($email)) {
2115 $sql .= ")";
2116 }
2117 }
2118
2119 $res = $this->db->query($sql);
2120 if ($res) {
2121 while ($rec = $this->db->fetch_array($res)) {
2122 $soc = new Societe($this->db);
2123 $soc->fetch($rec['rowid']);
2124 $thirdparties[] = $soc;
2125 }
2126
2127 return $thirdparties;
2128 } else {
2129 $this->error = $this->db->error().' sql='.$sql;
2130 dol_syslog(get_class($this)."::searchSocidByEmail ".$this->error, LOG_ERR);
2131 return -1;
2132 }
2133 }
2134
2143 public function searchContactByEmail($email, $socid = 0, $case = '')
2144 {
2145 $contacts = array();
2146
2147 // Forge the search SQL
2148 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."socpeople";
2149 $sql .= " WHERE entity IN (".getEntity('contact').")";
2150 if (!empty($socid)) {
2151 $sql .= " AND fk_soc = ".((int) $socid);
2152 }
2153 if (!empty($email)) {
2154 $sql .= " AND ";
2155 if (!$case) {
2156 $sql .= "email = '".$this->db->escape($email)."'";
2157 } else {
2158 $sql .= "email LIKE BINARY '".$this->db->escape($this->db->escapeforlike($email))."'";
2159 }
2160 }
2161
2162 $res = $this->db->query($sql);
2163 if ($res) {
2164 while ($rec = $this->db->fetch_object($res)) {
2165 include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
2166 $contactstatic = new Contact($this->db);
2167 $contactstatic->fetch($rec->rowid);
2168 $contacts[] = $contactstatic;
2169 }
2170
2171 return $contacts;
2172 } else {
2173 $this->error = $this->db->error().' sql='.$sql;
2174 dol_syslog(get_class($this)."::searchContactByEmail ".$this->error, LOG_ERR);
2175 return -1;
2176 }
2177 }
2178
2185 public function setCustomer($id)
2186 {
2187 if ($this->id) {
2188 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2189 $sql .= " SET fk_soc = ".($id > 0 ? $id : "null");
2190 $sql .= " WHERE rowid = ".((int) $this->id);
2191 dol_syslog(get_class($this).'::setCustomer sql='.$sql);
2192 $resql = $this->db->query($sql);
2193 if ($resql) {
2194 return 1;
2195 } else {
2196 return -1;
2197 }
2198 } else {
2199 return -1;
2200 }
2201 }
2202
2209 public function setProgression($percent)
2210 {
2211 if ($this->id) {
2212 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2213 $sql .= " SET progress = ".($percent > 0 ? $percent : "null");
2214 $sql .= " WHERE rowid = ".((int) $this->id);
2215 dol_syslog(get_class($this).'::set_progression sql='.$sql);
2216 $resql = $this->db->query($sql);
2217 if ($resql) {
2218 return 1;
2219 } else {
2220 return -1;
2221 }
2222 } else {
2223 return -1;
2224 }
2225 }
2226
2233 public function setContract($contractid)
2234 {
2235 if ($this->id) {
2236 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2237 $sql .= " SET fk_contract = ".($contractid > 0 ? $contractid : "null");
2238 $sql .= " WHERE rowid = ".((int) $this->id);
2239 dol_syslog(get_class($this).'::setContract sql='.$sql);
2240 $resql = $this->db->query($sql);
2241 if ($resql) {
2242 return 1;
2243 } else {
2244 return -1;
2245 }
2246 } else {
2247 return -1;
2248 }
2249 }
2250
2251 /* gestion des contacts d'un ticket */
2252
2259 {
2260 return $this->getIdContact('internal', 'SUPPORTTEC');
2261 }
2262
2269 public function getInfosTicketInternalContact($status = -1)
2270 {
2271 return $this->listeContact(-1, 'internal', 0, '', $status);
2272 }
2273
2280 {
2281 return $this->getIdContact('external', 'SUPPORTCLI');
2282 }
2283
2290 public function getInfosTicketExternalContact($status = -1)
2291 {
2292 return $this->listeContact(-1, 'external', 0, '', $status);
2293 }
2294
2301 {
2302 return $this->getIdContact('internal', 'CONTRIBUTOR');
2303 }
2304
2311 {
2312 return $this->getIdContact('external', 'CONTRIBUTOR');
2313 }
2314
2320 public function getTicketAllContacts()
2321 {
2322 $array_contact = array();
2323
2324 $array_contact = $this->getIdTicketInternalContact();
2325
2326 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());
2327
2328 $array_contact = array_merge($array_contact, $this->getIdTicketInternalInvolvedContact());
2329
2330 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());
2331
2332 return $array_contact;
2333 }
2334
2341 {
2342 $array_contact = array();
2343
2344 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());
2345
2346 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());
2347
2348 return $array_contact;
2349 }
2350
2351
2363 public function listeContact($statusoflink = -1, $source = 'external', $list = 0, $code = '', $status = -1)
2364 {
2365 global $langs;
2366
2367 $tab = array();
2368
2369 $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
2370 if ($source == 'internal') {
2371 $sql .= ", '-1' as socid, t.statut as statuscontact";
2372 }
2373
2374 if ($source == 'external' || $source == 'thirdparty') {
2375 $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
2376 }
2377
2378 $sql .= ", t.civility, t.lastname as lastname, t.firstname, t.email";
2379 if ($source == 'internal') {
2380 $sql .= ", t.office_phone as phone, t.user_mobile as phone_mobile";
2381 }
2382
2383 if ($source == 'external') {
2384 $sql .= ", t.phone as phone, t.phone_mobile as phone_mobile, t.phone_perso as phone_perso";
2385 }
2386
2387 $sql .= ", tc.source, tc.element, tc.code, tc.libelle as type_contact_label";
2388 $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
2389 $sql .= ", ".MAIN_DB_PREFIX."element_contact ec";
2390 if ($source == 'internal') {
2391 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
2392 }
2393
2394 if ($source == 'external' || $source == 'thirdparty') {
2395 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
2396 }
2397
2398 $sql .= " WHERE ec.element_id = ".((int) $this->id);
2399 $sql .= " AND ec.fk_c_type_contact=tc.rowid";
2400 $sql .= " AND tc.element='".$this->db->escape($this->element)."'";
2401 if ($source == 'internal') {
2402 $sql .= " AND tc.source = 'internal'";
2403 if ($status >= 0) {
2404 $sql .= " AND t.statut = ".((int) $status);
2405 }
2406 }
2407
2408 if ($source == 'external' || $source == 'thirdparty') {
2409 $sql .= " AND tc.source = 'external'";
2410 if ($status >= 0) {
2411 $sql .= " AND t.statut = ".((int) $status);
2412 }
2413 }
2414
2415 if (!empty($code)) {
2416 $sql .= " AND tc.code = '".$this->db->escape($code)."'";
2417 }
2418
2419 $sql .= " AND tc.active=1";
2420 if ($statusoflink >= 0) {
2421 $sql .= " AND ec.statut = ".((int) $statusoflink);
2422 }
2423
2424 $sql .= " ORDER BY t.lastname ASC";
2425
2426 $resql = $this->db->query($sql);
2427 if ($resql) {
2428 $num = $this->db->num_rows($resql);
2429 $i = 0;
2430 while ($i < $num) {
2431 $obj = $this->db->fetch_object($resql);
2432
2433 if (!$list) {
2434 $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
2435 $labelType = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_contact_label);
2436 $tab[$i] = array(
2437 'source' => $obj->source,
2438 'socid' => $obj->socid,
2439 'id' => $obj->id,
2440 'nom' => $obj->lastname, // For backward compatibility
2441 'civility' => $obj->civility,
2442 'lastname' => $obj->lastname,
2443 'firstname' => $obj->firstname,
2444 'email' => $obj->email,
2445 'rowid' => $obj->rowid,
2446 'code' => $obj->code,
2447 'libelle' => $labelType, // deprecated, replaced with labeltype
2448 'labeltype' => $labelType,
2449 'status' => $obj->statuslink,
2450 'statuscontact' => $obj->statuscontact,
2451 'fk_c_type_contact' => $obj->fk_c_type_contact,
2452 'phone' => $obj->phone,
2453 'phone_mobile' => $obj->phone_mobile);
2454 } else {
2455 $tab[$i] = $obj->id;
2456 }
2457
2458 $i++;
2459 }
2460
2461 return $tab;
2462 } else {
2463 $this->error = $this->db->error();
2464 dol_print_error($this->db);
2465 return -1;
2466 }
2467 }
2468
2475 public function getDefaultRef($thirdparty = null)
2476 {
2477 global $conf;
2478
2479 $defaultref = '';
2480 $modele = getDolGlobalString('TICKET_ADDON', 'mod_ticket_simple');
2481
2482 // Search template files
2483 $file = '';
2484 $classname = '';
2485 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2486 foreach ($dirmodels as $reldir) {
2487 $file = dol_buildpath($reldir."core/modules/ticket/".$modele.'.php', 0);
2488 if (file_exists($file)) {
2489 $classname = $modele;
2490 break;
2491 }
2492 }
2493
2494 if ($classname !== '') {
2495 $result = dol_include_once($reldir."core/modules/ticket/".$modele.'.php');
2496 $modTicket = new $classname();
2497
2498 $defaultref = $modTicket->getNextValue($thirdparty, $this);
2499 }
2500
2501 if (is_numeric($defaultref) && $defaultref <= 0) {
2502 $defaultref = '';
2503 }
2504
2505 return $defaultref;
2506 }
2507
2508
2509 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2516 public function is_photo_available($sdir)
2517 {
2518 // phpcs:enable
2519 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2520
2521 global $conf;
2522
2523 $dir = $sdir.'/';
2524 $nbphoto = 0;
2525
2526 $dir_osencoded = dol_osencode($dir);
2527 if (file_exists($dir_osencoded)) {
2528 $handle = opendir($dir_osencoded);
2529 if (is_resource($handle)) {
2530 while (($file = readdir($handle)) !== false) {
2531 if (!utf8_check($file)) {
2532 $file = mb_convert_encoding($file, 'UTF-8', 'ISO-8859-1'); // To be sure data is stored in UTF8 in memory
2533 }
2534 if (dol_is_file($dir.$file)) {
2535 return true;
2536 }
2537 }
2538 }
2539 }
2540 return false;
2541 }
2542
2543
2552 public function copyFilesForTicket($forcetrackid = null)
2553 {
2554 global $conf;
2555
2556 // Create form object
2557 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
2558 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2559 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
2560
2561 $maxwidthsmall = 270;
2562 $maxheightsmall = 150;
2563 $maxwidthmini = 128;
2564 $maxheightmini = 72;
2565
2566 $formmail = new FormMail($this->db);
2567 $formmail->trackid = (is_null($forcetrackid) ? 'tic'.$this->id : '');
2568 $attachedfiles = $formmail->get_attached_files();
2569
2570 $filepath = $attachedfiles['paths']; // path is for example user->dir_temp.'/'.$user->id.'/'...
2571 $filename = $attachedfiles['names'];
2572 $mimetype = $attachedfiles['mimes'];
2573
2574 // Copy files into ticket directory
2575 $destdir = $conf->ticket->dir_output.'/'.$this->ref;
2576
2577 if (!dol_is_dir($destdir)) {
2578 dol_mkdir($destdir);
2579 }
2580
2581 $listofpaths = array();
2582 $listofnames = array();
2583 foreach ($filename as $i => $val) {
2584 $destfile = $destdir.'/'.$filename[$i];
2585 // If destination file already exists, we add a suffix to avoid to overwrite
2586 if (is_file($destfile)) {
2587 $pathinfo = pathinfo($filename[$i]);
2588 $now = dol_now();
2589 $destfile = $destdir.'/'.$pathinfo['filename'].' - '.dol_print_date($now, 'dayhourlog').'.'.$pathinfo['extension'];
2590 }
2591
2592 $moreinfo = array('description' => 'File saved by copyFilesForTicket', 'src_object_type' => $this->element, 'src_object_id' => $this->id);
2593
2594 $res = dol_move($filepath[$i], $destfile, 0, 1, 0, 1, $moreinfo);
2595
2596 if (!$res) {
2597 // Move has failed
2598 $this->error = "Failed to move file ".dirbasename($filepath[$i])." into ".dirbasename($destfile);
2599 return -1;
2600 } else {
2601 // If file is an image, we create thumbs
2602 if (image_format_supported($destfile) == 1) {
2603 // Create small thumbs for image (Ratio is near 16/9)
2604 // Used on logon for example
2605 $imgThumbSmall = vignette($destfile, $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs");
2606 // Create mini thumbs for image (Ratio is near 16/9)
2607 // Used on menu or for setup page for example
2608 $imgThumbMini = vignette($destfile, $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs");
2609 }
2610 }
2611
2612 // Clear variables into session
2613 $formmail->remove_attached_files($i);
2614
2615 // Fill array with new names
2616 $listofpaths[$i] = $destfile;
2617 $listofnames[$i] = basename($destfile);
2618 }
2619
2620 return array('listofpaths' => $listofpaths, 'listofnames' => $listofnames, 'listofmimes' => $mimetype);
2621 }
2622
2633 public function setCategories($categories)
2634 {
2635 // Handle single category
2636 if (!is_array($categories)) {
2637 $categories = array($categories);
2638 }
2639
2640 // Get current categories
2641 include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2642 $c = new Categorie($this->db);
2643 $existing = $c->containing($this->id, Categorie::TYPE_TICKET, 'id');
2644
2645 // Diff
2646 if (is_array($existing)) {
2647 $to_del = array_diff($existing, $categories);
2648 $to_add = array_diff($categories, $existing);
2649 } else {
2650 $to_del = array(); // Nothing to delete
2651 $to_add = $categories;
2652 }
2653
2654 // Process
2655 foreach ($to_del as $del) {
2656 if ($c->fetch($del) > 0) {
2657 $c->del_type($this, Categorie::TYPE_TICKET);
2658 }
2659 }
2660 foreach ($to_add as $add) {
2661 if ($c->fetch($add) > 0) {
2662 $c->add_type($this, Categorie::TYPE_TICKET);
2663 }
2664 }
2665
2666 return 1;
2667 }
2668
2681 public function newMessage($user, &$action, $private = 1, $public_area = 0)
2682 {
2683 global $mysoc, $conf, $langs;
2684
2685 $error = 0;
2686
2687 $object = new Ticket($this->db);
2688
2689 $ret = $object->fetch('', '', GETPOST('track_id', 'alpha'));
2690
2691 $object->socid = $object->fk_soc;
2692 $object->fetch_thirdparty();
2693 $object->fetch_project();
2694
2695 if ($ret < 0) {
2696 $error++;
2697 array_push($this->errors, $langs->trans("ErrorTicketIsNotValid"));
2698 $action = '';
2699 }
2700
2701 if (!GETPOST("message")) {
2702 $error++;
2703 array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Message")));
2704 $action = 'add_message';
2705 }
2706
2707 if (!$error) {
2708 $object->subject = GETPOST('subject', 'alphanohtml');
2709 $object->message = GETPOST("message", "restricthtml");
2710 $object->private = GETPOST("private_message", "alpha");
2711
2712 $send_email = GETPOSTINT('send_email');
2713
2714 // Copy attached files (saved into $_SESSION) as linked files to ticket. Return array with final name used.
2715 $resarray = $object->copyFilesForTicket();
2716 if (is_numeric($resarray) && $resarray == -1) {
2717 setEventMessages($object->error, $object->errors, 'errors');
2718 return -1;
2719 }
2720
2721 $listofpaths = $resarray['listofpaths'];
2722 $listofnames = $resarray['listofnames'];
2723 $listofmimes = $resarray['listofmimes'];
2724
2725 $id = $object->createTicketMessage($user, 0, $listofpaths, $listofmimes, $listofnames, $send_email, $public_area);
2726 if ($id <= 0) {
2727 $error++;
2728 $this->error = $object->error;
2729 $this->errors = $object->errors;
2730 $action = 'add_message';
2731 }
2732
2733 if (!$error && $id > 0) {
2734 setEventMessages($langs->trans('TicketMessageSuccessfullyAdded'), null, 'mesgs');
2735
2736 if (!empty($public_area)) {
2737 /*
2738 * Message created from the Public interface
2739 *
2740 * Send emails to assigned users (public area notification)
2741 */
2742 if (getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_ENABLED')) {
2743 // Retrieve internal contact datas
2744 $internal_contacts = $object->getInfosTicketInternalContact(1);
2745
2746 $assigned_user_dont_have_email = '';
2747
2748 $sendto = array();
2749
2750 if ($this->fk_user_assign > 0) {
2751 $assigned_user = new User($this->db);
2752 $assigned_user->fetch($this->fk_user_assign);
2753 if (!empty($assigned_user->email)) {
2754 $sendto[$assigned_user->email] = $assigned_user->getFullName($langs)." <".$assigned_user->email.">";
2755 } else {
2756 $assigned_user_dont_have_email = $assigned_user->getFullName($langs);
2757 }
2758 }
2759
2760 // Build array to display recipient list
2761 foreach ($internal_contacts as $key => $info_sendto) {
2762 // Avoid duplicate notifications
2763 if ($info_sendto['id'] == $user->id) {
2764 continue;
2765 }
2766
2767 // We check if the email address is not the assignee's address to prevent notification from being sent twice
2768 if (!empty($info_sendto['email']) && $assigned_user->email != $info_sendto['email']) {
2769 $sendto[] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";
2770 }
2771 }
2772
2773 if (empty($sendto)) {
2774 if (getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL')) {
2775 $sendto[getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL')] = getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL');
2776 } elseif (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2777 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2778 }
2779 }
2780
2781 // Add global email address recipient
2782 if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') &&
2783 getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)
2784 ) {
2785 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2786 }
2787
2788 if (!empty($sendto)) {
2789 $appli = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2790
2791 $subject = '['.$appli.'- ticket #'.$object->track_id.'] '.$this->subject;
2792
2793 // Message send
2794 $message = getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO', $langs->trans('TicketMessageMailIntroText'));
2795 $message .= '<br><br>';
2796 $messagePost = GETPOST('message', 'restricthtml');
2797 if (!dol_textishtml($messagePost)) {
2798 $messagePost = dol_nl2br($messagePost);
2799 }
2800 $message .= $messagePost;
2801
2802 // Customer company infos
2803 $message .= '<br><br>';
2804 $message .= "==============================================";
2805 $message .= !empty($object->thirdparty->name) ? '<br>'.$langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';
2806 $message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';
2807 $message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';
2808
2809 // Email send to
2810 $message .= '<br><br>';
2811 if (!empty($assigned_user_dont_have_email)) {
2812 $message .= '<br>'.$langs->trans('NoEMail').' : '.$assigned_user_dont_have_email;
2813 }
2814 foreach ($sendto as $val) {
2815 $message .= '<br>'.$langs->trans('TicketNotificationRecipient').' : '.$val;
2816 }
2817
2818 // URL ticket
2819 $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;
2820 $message .= '<br><br>';
2821 $message .= $langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a>';
2822
2823 $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2824 }
2825 }
2826 } else {
2827 /*
2828 * Message send from the Backoffice / Private area
2829 *
2830 * 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)
2831 */
2832 if ($send_email > 0) {
2833 // Retrieve internal contact datas
2834 $internal_contacts = $object->getInfosTicketInternalContact(1);
2835
2836 $sendto = array();
2837 if (is_array($internal_contacts) && count($internal_contacts) > 0) {
2838 // Set default subject
2839 $appli = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2840
2841 $subject = GETPOST('subject', 'alphanohtml') ? GETPOST('subject', 'alphanohtml') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2842
2843 $message_intro = $langs->trans('TicketNotificationEmailBody', "#".$object->id);
2844 $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');
2845
2846 $message = getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO', $langs->trans('TicketMessageMailIntroText'));
2847 $message .= '<br><br>';
2848 $messagePost = GETPOST('message', 'restricthtml');
2849 if (!dol_textishtml($messagePost)) {
2850 $messagePost = dol_nl2br($messagePost);
2851 }
2852 $message .= $messagePost;
2853
2854 // Data about customer
2855 $message .= '<br><br>';
2856 $message .= "==============================================<br>";
2857 $message .= !empty($object->thirdparty->name) ? $langs->trans('Thirdparty')." : ".$object->thirdparty->name : '';
2858 $message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';
2859 $message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';
2860
2861 // Build array to display recipient list
2862 foreach ($internal_contacts as $key => $info_sendto) {
2863 // Avoid duplicate notifications
2864 if ($info_sendto['id'] == $user->id) {
2865 continue;
2866 }
2867
2868 if ($info_sendto['email'] != '') {
2869 if (!empty($info_sendto['email'])) {
2870 $sendto[$info_sendto['email']] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";
2871 }
2872
2873 // Contact type
2874 $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1').' ('.strtolower($info_sendto['libelle']).')';
2875 $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');
2876 }
2877 }
2878 $message .= '<br>';
2879 // URL ticket
2880 $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;
2881
2882 // Add html link on url
2883 $message .= '<br>'.$langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a><br>';
2884
2885 // Add global email address recipient
2886 if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {
2887 if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2888 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2889 }
2890 }
2891
2892 // don't try to send email if no recipient
2893 if (!empty($sendto)) {
2894 $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2895 }
2896 }
2897
2898 /*
2899 * Send emails for externals users if not private (linked contacts)
2900 */
2901 if (empty($object->private)) {
2902 // Retrieve email of all contacts (external)
2903 $external_contacts = $object->getInfosTicketExternalContact(1);
2904
2905 // If no contact, get email from thirdparty
2906 if (is_array($external_contacts) && count($external_contacts) === 0) {
2907 if (!empty($object->fk_soc)) {
2908 $object->fetch_thirdparty($object->fk_soc);
2909 $array_company = array(array('firstname' => '', 'lastname' => $object->thirdparty->name, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
2910 $external_contacts = array_merge($external_contacts, $array_company);
2911 } elseif (empty($object->fk_soc) && !empty($object->origin_replyto)) {
2912 $array_external = array(array('firstname' => '', 'lastname' => $object->origin_replyto, 'email' => $object->origin_replyto, 'libelle' => $langs->transnoentities('Customer'), 'socid' => 0));
2913 $external_contacts = array_merge($external_contacts, $array_external);
2914 } elseif (empty($object->fk_soc) && !empty($object->origin_email)) {
2915 $array_external = array(array('firstname' => '', 'lastname' => $object->origin_email, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
2916 $external_contacts = array_merge($external_contacts, $array_external);
2917 }
2918 }
2919
2920 $sendto = array();
2921 if (is_array($external_contacts) && count($external_contacts) > 0) {
2922 // Get default subject for email to external contacts
2923 $appli = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2924
2925 $subject = GETPOST('subject') ? GETPOST('subject') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2926
2927 $message_intro = GETPOST('mail_intro') ? GETPOST('mail_intro', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO');
2928 $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');
2929 if (!dol_textishtml($message_intro)) {
2930 $message_intro = dol_nl2br($message_intro);
2931 }
2932 if (!dol_textishtml($message_signature)) {
2933 $message_signature = dol_nl2br($message_signature);
2934 }
2935
2936 // We put intro after
2937 $messagePost = GETPOST('message', 'restricthtml');
2938 if (!dol_textishtml($messagePost)) {
2939 $messagePost = dol_nl2br($messagePost);
2940 }
2941 $message = $messagePost;
2942 $message .= '<br><br>';
2943
2944 foreach ($external_contacts as $key => $info_sendto) {
2945 // avoid duplicate emails to external contacts
2946 if ($info_sendto['id'] == $user->contact_id) {
2947 continue;
2948 }
2949
2950 if ($info_sendto['email'] != '' && $info_sendto['email'] != $object->origin_email) {
2951 if (!empty($info_sendto['email'])) {
2952 $sendto[$info_sendto['email']] = trim($info_sendto['firstname']." ".$info_sendto['lastname'])." <".$info_sendto['email'].">";
2953 }
2954
2955 $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], '-1').' ('.strtolower($info_sendto['libelle']).')';
2956 $message .= (!empty($recipient) ? $langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');
2957 }
2958 }
2959
2960 // If public interface is not enable, use link to internal page into mail
2961 $url_public_ticket = (getDolGlobalInt('TICKET_ENABLE_PUBLIC_INTERFACE') ?
2962 (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;
2963
2964 $message .= '<br>'.$langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer').' : <a href="'.$url_public_ticket.'">'.$object->track_id.'</a><br>';
2965
2966 // Build final message
2967 $message = $message_intro.'<br><br>'.$message;
2968
2969 // Add signature
2970 $message .= '<br>'.$message_signature;
2971
2972 if (!empty($object->origin_replyto)) {
2973 $sendto[$object->origin_replyto] = $object->origin_replyto;
2974 } elseif (!empty($object->origin_email)) {
2975 $sendto[$object->origin_email] = $object->origin_email;
2976 }
2977
2978 if ($object->fk_soc > 0 && !array_key_exists($object->origin_replyto, $sendto) && !array_key_exists($object->origin_email, $sendto)) {
2979 $object->socid = $object->fk_soc;
2980 $object->fetch_thirdparty();
2981 if (!empty($object->thirdparty->email)) {
2982 $sendto[$object->thirdparty->email] = $object->thirdparty->email;
2983 }
2984 }
2985
2986 // Add global email address recipient
2987 if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {
2988 if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2989 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2990 }
2991 }
2992
2993 // Don't try to send email when no recipient
2994 if (!empty($sendto)) {
2995 $result = $this->sendTicketMessageByEmail($subject, $message, '', $sendto, $listofpaths, $listofmimes, $listofnames);
2996 if ($result) {
2997 // update last_msg_sent date (for last message sent to external users)
2998 $this->date_last_msg_sent = dol_now();
2999 $this->update($user, 1); // disable trigger when updating date_last_msg_sent. sendTicketMessageByEmail already create an event in actioncomm table.
3000 }
3001 }
3002 }
3003 }
3004 }
3005 }
3006
3007 // Set status back to "In progress" if not set yet, but only if internal user and not a private message
3008 // Or set status to "In progress" if the client has answered and if the ticket has started
3009 // So we are sure to leave the STATUS_DRAFT, STATUS_NEED_INFO.
3010 if (($object->status < self::STATUS_IN_PROGRESS && !$user->socid && !$private) ||
3011 ($object->status > self::STATUS_IN_PROGRESS && $public_area)
3012 ) {
3013 $object->setStatut($object::STATUS_IN_PROGRESS);
3014 }
3015 return 1;
3016 } else {
3017 setEventMessages($object->error, $object->errors, 'errors');
3018 return -1;
3019 }
3020 } else {
3021 setEventMessages($this->error, $this->errors, 'errors');
3022 return -1;
3023 }
3024 }
3025
3026
3039 public function sendTicketMessageByEmail($subject, $message, $send_internal_cc = 0, $array_receiver = array(), $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array())
3040 {
3041 global $conf, $langs, $user;
3042
3043 if (getDolGlobalString('TICKET_DISABLE_ALL_MAILS')) {
3044 dol_syslog(get_class($this).'::sendTicketMessageByEmail: Emails are disable into ticket setup by option TICKET_DISABLE_ALL_MAILS', LOG_WARNING);
3045 return false;
3046 }
3047
3048 $langs->load("mails");
3049
3050 include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
3051 //$contactstatic = new Contact($this->db);
3052
3053 // If no receiver defined, load all ticket linked contacts
3054 if (!is_array($array_receiver) || !count($array_receiver) > 0) {
3055 $array_receiver = $this->getInfosTicketInternalContact(1);
3056 $array_receiver = array_merge($array_receiver, $this->getInfosTicketExternalContact(1));
3057 }
3058
3059 $sendtocc = '';
3060 if ($send_internal_cc) {
3061 $sendtocc = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
3062 }
3063
3064 $from = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
3065 $is_sent = false;
3066 if (is_array($array_receiver) && count($array_receiver) > 0) {
3067 foreach ($array_receiver as $key => $receiver) {
3068 $deliveryreceipt = 0;
3069 $filepath = $filename_list;
3070 $filename = $mimefilename_list;
3071 $mimetype = $mimetype_list;
3072
3073 // Send email
3074
3075 $old_MAIN_MAIL_AUTOCOPY_TO = getDolGlobalString('MAIN_MAIL_AUTOCOPY_TO');
3076 if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
3077 $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
3078 }
3079
3080 $upload_dir_tmp = $conf->user->dir_output."/".$user->id.'/temp';
3081
3082 include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
3083 $trackid = "tic".$this->id;
3084
3085 $moreinheader = 'X-Dolibarr-Info: sendTicketMessageByEmail'."\r\n";
3086 if (!empty($this->email_msgid)) {
3087 // We must also add 1 entry In-Reply-To: <$this->email_msgid> with Message-ID we respond from (See RFC5322).
3088 $moreinheader .= 'In-Reply-To: <'.$this->email_msgid.'>'."\r\n";
3089 // TODO We should now be able to give the in_reply_to as a dedicated parameter of new CMailFile() instead of into $moreinheader.
3090 }
3091
3092 // We should add here also a header 'References:'
3093 // According to RFC5322, we should add here all the References fields of the initial message concatenated with
3094 // the Message-ID of the message we respond from (but each ID must be once).
3095 $references = '';
3096 if (!empty($this->origin_references)) { // $this->origin_references should be '<'.$this->origin_references.'>'
3097 $references .= (empty($references) ? '' : ' ').$this->origin_references;
3098 }
3099 if (!empty($this->email_msgid) && !preg_match('/'.preg_quote($this->email_msgid, '/').'/', $references)) {
3100 $references .= (empty($references) ? '' : ' ').'<'.$this->email_msgid.'>';
3101 }
3102 if ($references) {
3103 $moreinheader .= 'References: '.$references."\r\n";
3104 // TODO We should now be able to give the references as a dedicated parameter of new CMailFile() instead of into $moreinheader.
3105 }
3106
3107 $mailfile = new CMailFile($subject, $receiver, $from, $message, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1, '', '', $trackid, $moreinheader, 'ticket', '', $upload_dir_tmp);
3108
3109 if ($mailfile->error) {
3110 setEventMessages($mailfile->error, null, 'errors');
3111 } else {
3112 $result = $mailfile->sendfile();
3113 if ($result) {
3114 setEventMessages($langs->trans('MailSuccessfulySent', $mailfile->getValidAddress($from, 2), $mailfile->getValidAddress($receiver, 2)), null, 'mesgs');
3115 $is_sent = true;
3116 } else {
3117 $langs->load("other");
3118 if ($mailfile->error) {
3119 setEventMessages($langs->trans('ErrorFailedToSendMail', $from, $receiver), null, 'errors');
3120 dol_syslog($langs->trans('ErrorFailedToSendMail', $from, $receiver).' : '.$mailfile->error);
3121 } else {
3122 setEventMessages('No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS', null, 'errors');
3123 }
3124 }
3125 }
3126
3127 if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
3128 $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
3129 }
3130 }
3131 } else {
3132 $langs->load("other");
3133 setEventMessages($langs->trans('ErrorMailRecipientIsEmptyForSendTicketMessage'), null, 'warnings');
3134 }
3135
3136 return $is_sent;
3137 }
3138
3139 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3147 public function load_board($user, $mode)
3148 {
3149 // phpcs:enable
3150 global $user, $langs;
3151
3152 $now = dol_now();
3153 $delay_warning = 0;
3154
3155 $clause = " WHERE";
3156
3157 $sql = "SELECT p.rowid, p.ref, p.datec as datec";
3158 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
3159 if (isModEnabled('societe') && !$user->hasRight('societe', 'client', 'voir') && !$user->socid) {
3160 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
3161 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3162 $clause = " AND";
3163 }
3164 $sql .= $clause." p.entity IN (".getEntity('ticket').")";
3165 if ($mode == 'opened') {
3166 $sql .= " AND p.fk_statut NOT IN (".Ticket::STATUS_CLOSED.", ".Ticket::STATUS_CANCELED.")";
3167 }
3168 if ($user->socid) {
3169 $sql .= " AND p.fk_soc = ".((int) $user->socid);
3170 }
3171
3172 $resql = $this->db->query($sql);
3173 if ($resql) {
3174 $label = $labelShort = '';
3175 $status = '';
3176 if ($mode == 'opened') {
3177 $status = 'openall';
3178 //$delay_warning = $conf->ticket->warning_delay;
3179 $delay_warning = 0;
3180 $label = $langs->trans("MenuListNonClosed");
3181 $labelShort = $langs->trans("MenuListNonClosed");
3182 }
3183
3184 $response = new WorkboardResponse();
3185 //$response->warning_delay = $delay_warning / 60 / 60 / 24;
3186 $response->label = $label;
3187 $response->labelShort = $labelShort;
3188 $response->url = DOL_URL_ROOT.'/ticket/list.php?search_fk_statut[]='.$status;
3189 $response->img = img_object('', "ticket");
3190
3191 // This assignment in condition is not a bug. It allows walking the results.
3192 while ($obj = $this->db->fetch_object($resql)) {
3193 $response->nbtodo++;
3194 if ($mode == 'opened') {
3195 $datelimit = $this->db->jdate($obj->datec) + $delay_warning;
3196 if ($datelimit < $now) {
3197 //$response->nbtodolate++;
3198 }
3199 }
3200 }
3201 return $response;
3202 } else {
3203 $this->error = $this->db->lasterror();
3204 return -1;
3205 }
3206 }
3207
3213 public function loadStateBoard()
3214 {
3215 global $user;
3216
3217 $this->nb = array();
3218 $clause = "WHERE";
3219
3220 $sql = "SELECT count(p.rowid) as nb";
3221 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
3222 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3223 if (!$user->hasRight('societe', 'client', 'voir')) {
3224 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3225 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3226 $clause = "AND";
3227 }
3228 $sql .= " ".$clause." p.entity IN (".getEntity('ticket').")";
3229
3230 $resql = $this->db->query($sql);
3231 if ($resql) {
3232 // This assignment in condition is not a bug. It allows walking the results.
3233 while ($obj = $this->db->fetch_object($resql)) {
3234 $this->nb["ticket"] = $obj->nb;
3235 }
3236 $this->db->free($resql);
3237 return 1;
3238 } else {
3239 dol_print_error($this->db);
3240 $this->error = $this->db->lasterror();
3241 return -1;
3242 }
3243 }
3244
3253 public static function replaceThirdparty($db, $origin_id, $dest_id)
3254 {
3255 $tables = array('ticket');
3256
3257 return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3258 }
3259
3267 public function getKanbanView($option = '', $arraydata = null)
3268 {
3269 global $langs;
3270
3271 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3272
3273 $return = '<div class="box-flex-item box-flex-grow-zero">';
3274 $return .= '<div class="info-box info-box-sm">';
3275 $return .= '<span class="info-box-icon bg-infobox-action">';
3276 $return .= img_picto('', $this->picto);
3277 $return .= '</span>';
3278 $return .= '<div class="info-box-content">';
3279 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
3280 if ($selected >= 0) {
3281 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3282 }
3283 if (!empty($arraydata['user_assignment'])) {
3284 $return .= '<br><span class="info-box-label" title="'.dol_escape_htmltag($langs->trans("AssignedTo")).'">'.$arraydata['user_assignment'].'</span>';
3285 }
3286 if (property_exists($this, 'type_code') && !empty($this->type_code)) {
3287 $return .= '<br>';
3288 $return .= '<div class="tdoverflowmax125 inline-block">'.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$this->type_code, 'c_ticket_type', 'code', 'label', $this->type_code).'</div>';
3289 }
3290 if (method_exists($this, 'getLibStatut')) {
3291 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
3292 }
3293 $return .= '</div>';
3294 $return .= '</div>';
3295 $return .= '</div>';
3296
3297 return $return;
3298 }
3299
3311 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3312 {
3313 global $langs;
3314
3315 $langs->load("ticket");
3316 $outputlangs->load("ticket");
3317
3318 if (!dol_strlen($modele)) {
3319 $modele = 'generic_ticket_odt';
3320
3321 if (!empty($this->model_pdf)) {
3322 $modele = $this->model_pdf;
3323 } elseif (getDolGlobalString('TICKET_ADDON_PDF')) {
3324 $modele = getDolGlobalString('TICKET_ADDON_PDF');
3325 }
3326 }
3327
3328 $modelpath = "core/modules/ticket/doc/";
3329
3330 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3331 }
3332}
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:637
$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.
$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.
create($user, $notrigger=0)
Create object into database.
Class to manage Dolibarr users.
dirbasename($pathfile)
Return the relative dirname (relative to DOL_DATA_ROOT) of a full path string.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_move($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=1, $moreinfo=array())
Move a file into another name.
dol_is_file($pathoffile)
Return if path is a file.
dol_is_dir($folder)
Test if filename is a directory.
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
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).
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_clone($object, $native=0)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
dol_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.
utf8_check($str)
Check if a string is in UTF8.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
vignette($file, $maxWidth=160, $maxHeight=120, $extName='_small', $quality=50, $outdir='thumbs', $targetformat=0)
Create a thumbnail from an image file (Supported extensions are gif, jpg, png and bmp).
image_format_supported($file, $acceptsvg=0)
Return if a filename is file name of a supported image format.
Class to generate the form for creating a new ticket.
generate_random_id($car=16)
Generate a random id.