dolibarr 24.0.1
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-2026 Frédéric France <frederic.france@free.fr>
5 * Copyright (C) 2020 Laurent Destailleur <eldy@users.sourceforge.net>
6 * Copyright (C) 2023-2025 Charlene Benke <charlene@patas-monkey.com>
7 * Copyright (C) 2023-2024 Benjamin Falière <benjamin.faliere@altairis.fr>
8 * Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
9 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
10 * Copyright (C) 2026 Jon Bendtsen <jon.bendtsen.github@jonb.dk>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 3 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program. If not, see <https://www.gnu.org/licenses/>.
24 */
25
32// Put here all includes required by your class file
33require_once DOL_DOCUMENT_ROOT."/core/class/commonobject.class.php";
34require_once DOL_DOCUMENT_ROOT.'/fichinter/class/fichinter.class.php';
35require_once DOL_DOCUMENT_ROOT.'/core/lib/ticket.lib.php';
36
37
41class Ticket extends CommonObject
42{
46 public $db;
47
51 public $element = 'ticket';
52
56 public $table_element = 'ticket';
57
61 public $fk_element = 'fk_ticket';
62
66 public $picto = 'ticket';
67
71 public $track_id;
72
76 public $fk_soc;
77
81 public $socid;
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
163 public $type_label;
164
168 public $category_label;
169
173 public $severity_label;
174
178 public $email_from;
179
183 public $origin_replyto;
184
188 public $origin_references;
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_msgs_ticket;
219
223 public $notify_tiers_at_create;
224
228 public $email_msgid;
229
233 public $email_date;
234
238 public $ip;
239
243 public $lines;
244
248 public $regeximgext = '\.gif|\.jpg|\.jpeg|\.png|\.bmp|\.webp|\.xpm|\.xbm'; // See also into images.lib.php
249
253 const STATUS_NOT_READ = 0; // Draft. Not take into account yet.
254 const STATUS_READ = 1; // Ticket was read.
255 const STATUS_ASSIGNED = 2; // Ticket was just assigned to someone. Not in progress yet.
256 const STATUS_IN_PROGRESS = 3; // In progress
257 const STATUS_NEED_MORE_INFO = 5; // Waiting requester feedback
258 const STATUS_WAITING = 7; // On hold
259 const STATUS_CLOSED = 8; // Closed - Solved
260 const STATUS_CANCELED = 9; // Closed - Not solved
261
262
289 // BEGIN MODULEBUILDER PROPERTIES
290 public $fields = array(
291 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'visible' => -2, 'enabled' => 1, 'position' => 1, 'notnull' => 1, 'index' => 1, 'comment' => "Id"),
292 'entity' => array('type' => 'integer', 'label' => 'Entity', 'visible' => 0, 'enabled' => 1, 'position' => 5, 'notnull' => 1, 'index' => 1),
293 '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),
294 'track_id' => array('type' => 'varchar(255)', 'label' => 'TicketTrackId', 'visible' => -2, 'enabled' => 1, 'position' => 11, 'notnull' => -1, 'searchall' => 1, 'help' => "Help text"),
295 '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'),
296 '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'),
297 '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'),
298 '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'),
299 '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),
300 'type_code' => array('type' => 'varchar(32)', 'label' => 'Type', 'visible' => 1, 'enabled' => 1, 'position' => 20, 'notnull' => -1, 'help' => "", 'csslist' => 'tdoverflowmax100'),
301 'category_code' => array('type' => 'varchar(32)', 'label' => 'TicketCategory', 'visible' => -1, 'enabled' => 1, 'position' => 21, 'notnull' => -1, 'help' => "", 'css' => 'maxwidth100 tdoverflowmax200'),
302 'severity_code' => array('type' => 'varchar(32)', 'label' => 'Severity', 'visible' => 1, 'enabled' => 1, 'position' => 22, 'notnull' => -1, 'help' => "", 'css' => 'maxwidth100 tdoverflowmax100'),
303 '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'),
304 'notify_tiers_at_create' => array('type' => 'integer', 'label' => 'NotifyThirdparty', 'visible' => -1, 'enabled' => 0, 'position' => 51, 'notnull' => 1, 'index' => 1),
305 'fk_project' => array('type' => 'integer:Project:projet/class/project.class.php', 'label' => 'Project', 'visible' => -1, 'enabled' => 'isModEnabled("project")', 'position' => 52, 'notnull' => -1, 'index' => 1, 'help' => "LinkToProject"),
306 'fk_contract' => array('type' => 'integer:Contrat:contrat/class/contrat.class.php', 'label' => 'Contract', 'visible' => -1, 'enabled' => 'isModEnabled("contract")', 'position' => 53, 'notnull' => -1, 'index' => 1, 'help' => "LinkToContract"),
307 //'timing' => array('type'=>'varchar(20)', 'label'=>'Timing', 'visible'=>-1, 'enabled'=>1, 'position'=>42, 'notnull'=>-1, 'help'=>""), // what is this ?
308 'datec' => array('type' => 'datetime', 'label' => 'DateCreation', 'visible' => 1, 'enabled' => 1, 'position' => 500, 'notnull' => 1, 'csslist' => 'nowraponall'),
309 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'visible' => -1, 'enabled' => 1, 'position' => 501, 'notnull' => 1),
310 'date_read' => array('type' => 'datetime', 'label' => 'DateReading', 'visible' => -1, 'enabled' => 1, 'position' => 505, 'notnull' => 1, 'csslist' => 'nowraponall'),
311 'date_last_msg_sent' => array('type' => 'datetime', 'label' => 'TicketLastMessageDate', 'visible' => 0, 'enabled' => 1, 'position' => 506, 'notnull' => -1),
312 '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'),
313 'date_close' => array('type' => 'datetime', 'label' => 'TicketCloseOn', 'visible' => -1, 'enabled' => 1, 'position' => 510, 'notnull' => 1),
314 'message' => array('type' => 'html', 'label' => 'Message', 'visible' => -2, 'enabled' => 1, 'position' => 540, 'notnull' => -1,),
315 'note_private' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 110),
316 'note_public' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 115),
317 'email_msgid' => array('type' => 'varchar(255)', 'label' => 'EmailMsgID', 'visible' => -2, 'enabled' => 1, 'position' => 540, 'notnull' => -1, 'help' => 'EmailMsgIDDesc', 'csslist' => 'tdoverflowmax100'),
318 'email_date' => array('type' => 'datetime', 'label' => 'EmailDate', 'visible' => -2, 'enabled' => 1, 'position' => 541),
319 'progress' => array('type' => 'integer', 'label' => 'Progression', 'visible' => -1, 'enabled' => 1, 'position' => 540, 'notnull' => -1, 'css' => 'right', 'help' => "", 'isameasure' => 1, 'csslist' => 'width50'),
320 'resolution' => array('type' => 'integer', 'label' => 'Resolution', 'visible' => -1, 'enabled' => 'getDolGlobalString("TICKET_ENABLE_RESOLUTION")', 'position' => 550, 'notnull' => 1),
321 'model_pdf' => array('type' => 'varchar(255)', 'label' => 'PDFTemplate', 'enabled' => 1, 'visible' => 0, 'position' => 560),
322 'extraparams' => array('type' => 'varchar(255)', 'label' => 'Extraparams', 'enabled' => 1, 'visible' => 0, 'position' => 570),
323 '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')),
324 'import_key' => array('type' => 'varchar(14)', 'label' => 'ImportId', 'enabled' => 1, 'visible' => -2, 'position' => 900),
325 );
326 // END MODULEBUILDER PROPERTIES
327
328
334 public function __construct(DoliDB $db)
335 {
336 $this->db = $db;
337
338 $this->ismultientitymanaged = 1;
339 $this->isextrafieldmanaged = 1;
340
341 $this->labelStatusShort = array(
342 self::STATUS_NOT_READ => 'Unread',
343 self::STATUS_READ => 'Read',
344 self::STATUS_ASSIGNED => 'Assigned',
345 self::STATUS_IN_PROGRESS => 'InProgress',
346 self::STATUS_NEED_MORE_INFO => 'NeedMoreInformationShort',
347 self::STATUS_WAITING => 'OnHold',
348 self::STATUS_CLOSED => 'SolvedClosed',
349 self::STATUS_CANCELED => 'Canceled'
350 );
351 $this->labelStatus = array(
352 self::STATUS_NOT_READ => 'Unread',
353 self::STATUS_READ => 'Read',
354 self::STATUS_ASSIGNED => 'Assigned',
355 self::STATUS_IN_PROGRESS => 'InProgress',
356 self::STATUS_NEED_MORE_INFO => 'NeedMoreInformation',
357 self::STATUS_WAITING => 'OnHold',
358 self::STATUS_CLOSED => 'SolvedClosed',
359 self::STATUS_CANCELED => 'Canceled'
360 );
361
362 if (!getDolGlobalString('TICKET_INCLUDE_SUSPENDED_STATUS')) {
363 unset($this->fields['fk_statut']['arrayofkeyval'][self::STATUS_WAITING]);
364 unset($this->labelStatusShort[self::STATUS_WAITING]);
365 unset($this->labelStatus[self::STATUS_WAITING]);
366 }
367 }
368
375 private function verify()
376 {
377 $this->errors = array();
378
379 $result = 0;
380
381 // Clean parameters
382 if (isset($this->ref)) {
383 $this->ref = trim($this->ref);
384 }
385
386 if (isset($this->track_id)) {
387 $this->track_id = trim($this->track_id);
388 }
389
390 if (isset($this->fk_soc)) {
391 $this->fk_soc = (int) $this->fk_soc;
392 }
393
394 if (isset($this->fk_project)) {
395 $this->fk_project = (int) $this->fk_project;
396 }
397
398 if (isset($this->origin_email)) {
399 $this->origin_email = trim($this->origin_email);
400 }
401
402 if (isset($this->fk_user_create)) {
403 $this->fk_user_create = (int) $this->fk_user_create;
404 }
405
406 if (isset($this->fk_user_assign)) {
407 $this->fk_user_assign = (int) $this->fk_user_assign;
408 }
409
410 if (isset($this->subject)) {
411 $this->subject = trim($this->subject);
412 }
413
414 if (isset($this->message)) {
415 $this->message = trim($this->message);
416 if (dol_strlen($this->message) > 65000) {
417 global $langs;
418 $langs->loadLangs(array('errors', 'ticket'));
419 $this->errors[] = $langs->trans('ErrorFieldTooLong', $langs->transnoentitiesnoconv('InitialMessage'));
420 dol_syslog(get_class($this).'::create error -1 message too long', LOG_ERR);
421 $result = -1;
422 }
423 }
424
425 if (isset($this->status)) {
426 $this->status = (int) $this->status;
427 }
428
429 if (isset($this->resolution)) {
430 $this->resolution = (int) $this->resolution;
431 }
432
433 if (isset($this->progress)) {
434 $this->progress = (int) $this->progress;
435 }
436
437 if (isset($this->timing)) {
438 $this->timing = trim($this->timing);
439 }
440
441 if (isset($this->type_code)) {
442 $this->type_code = trim($this->type_code);
443 }
444
445 if (isset($this->category_code)) {
446 $this->category_code = trim($this->category_code);
447 }
448
449 if (isset($this->severity_code)) {
450 $this->severity_code = trim($this->severity_code);
451 }
452
453 if (empty($this->ref)) {
454 $this->errors[] = 'ErrorTicketRefRequired';
455 dol_syslog(get_class($this)."::create error -1 ref null", LOG_ERR);
456 $result = -1;
457 }
458
459 return $result;
460 }
461
469 public function checkExistingRef(string $action, string $getRef)
470 {
471 $test = new self($this->db);
472
473 if ($test->fetch(0, $getRef) > 0) {
474 if (($action == 'add') || ($action == 'update' && $this->ref != $getRef)) {
475 return true;
476 }
477 }
478
479 $this->ref = $getRef;
480 return false;
481 }
482
490 public function create($user, $notrigger = 0)
491 {
492 global $conf;
493
494 $error = 0;
495
496 // Clean parameters
497 if (empty($this->datec)) {
498 $this->datec = dol_now();
499 }
500 if (empty($this->track_id)) {
501 $this->track_id = generate_random_id(16);
502 }
503
504 // Check more parameters
505 // If error, this->errors[] is filled
506 $result = $this->verify();
507
508 if ($result >= 0) {
509 // setEntity will set entity with the right value if empty or change it for the right value if multicompany module is active
510 $this->entity = setEntity($this);
511
512 // Insert request
513 $sql = "INSERT INTO ".MAIN_DB_PREFIX."ticket(";
514 $sql .= "ref,";
515 $sql .= "track_id,";
516 $sql .= "fk_soc,";
517 $sql .= "fk_project,";
518 $sql .= "fk_contract,";
519 $sql .= "origin_email,";
520 $sql .= "origin_replyto,";
521 $sql .= "origin_references,";
522 $sql .= "fk_user_create,";
523 $sql .= "fk_user_assign,";
524 $sql .= "email_msgid,";
525 $sql .= "email_date,";
526 $sql .= "subject,";
527 $sql .= "message,";
528 $sql .= "note_private,";
529 $sql .= "note_public,";
530 $sql .= "fk_statut,";
531 $sql .= "resolution,";
532 $sql .= "progress,";
533 $sql .= "timing,";
534 $sql .= "type_code,";
535 $sql .= "category_code,";
536 $sql .= "severity_code,";
537 $sql .= "datec,";
538 $sql .= "date_read,";
539 $sql .= "date_close,";
540 $sql .= "entity,";
541 $sql .= "notify_tiers_at_create,";
542 $sql .= "model_pdf,";
543 $sql .= "ip";
544 $sql .= ") VALUES (";
545 $sql .= " ".(!isset($this->ref) ? '' : "'".$this->db->escape($this->ref)."'").",";
546 $sql .= " ".(!isset($this->track_id) ? 'NULL' : "'".$this->db->escape($this->track_id)."'").",";
547 $sql .= " ".($this->fk_soc > 0 ? ((int) $this->fk_soc) : "null").",";
548 $sql .= " ".($this->fk_project > 0 ? ((int) $this->fk_project) : "null").",";
549 $sql .= " ".($this->fk_contract > 0 ? ((int) $this->fk_contract) : "null").",";
550 $sql .= " ".(!isset($this->origin_email) ? 'NULL' : "'".$this->db->escape($this->origin_email)."'").",";
551 $sql .= " ".(!isset($this->origin_replyto) ? 'NULL' : "'".$this->db->escape($this->origin_replyto)."'").",";
552 $sql .= " ".(!isset($this->origin_references) ? 'NULL' : "'".$this->db->escape($this->origin_references)."'").",";
553 $sql .= " ".(!isset($this->fk_user_create) ? ($user->id > 0 ? ((int) $user->id) : 'NULL') : ($this->fk_user_create > 0 ? ((int) $this->fk_user_create) : 'NULL')).",";
554 $sql .= " ".($this->fk_user_assign > 0 ? ((int) $this->fk_user_assign) : 'NULL').",";
555 $sql .= " ".(empty($this->email_msgid) ? 'NULL' : "'".$this->db->escape($this->email_msgid)."'").",";
556 $sql .= " ".(!isDolTms($this->email_date) ? 'NULL' : "'".$this->db->idate($this->email_date)."'").",";
557 $sql .= " ".(!isset($this->subject) ? 'NULL' : "'".$this->db->escape($this->subject)."'").",";
558 $sql .= " ".(!isset($this->message) ? 'NULL' : "'".$this->db->escape($this->message)."'").",";
559 $sql .= " ".(!isset($this->note_private) ? 'NULL' : "'".$this->db->escape($this->note_private)."'").",";
560 $sql .= " ".(!isset($this->note_public) ? 'NULL' : "'".$this->db->escape($this->note_public)."'").",";
561 $sql .= " ".(!isset($this->status) ? '0' : ((int) $this->status)).",";
562 $sql .= " ".(!isset($this->resolution) ? 'NULL' : ((int) $this->resolution)).",";
563 $sql .= " ".(!isset($this->progress) ? '0' : ((int) $this->progress)).",";
564 $sql .= " ".(!isset($this->timing) ? 'NULL' : "'".$this->db->escape($this->timing)."'").",";
565 $sql .= " ".(!isset($this->type_code) ? 'NULL' : "'".$this->db->escape($this->type_code)."'").",";
566 $sql .= " ".(empty($this->category_code) || $this->category_code == '-1' ? 'NULL' : "'".$this->db->escape($this->category_code)."'").",";
567 $sql .= " ".(!isset($this->severity_code) ? 'NULL' : "'".$this->db->escape($this->severity_code)."'").",";
568 $sql .= " ".(!isDolTms($this->datec) ? 'NULL' : "'".$this->db->idate($this->datec)."'").",";
569 $sql .= " ".(!isset($this->date_read) || dol_strlen((string) $this->date_read) == 0 ? 'NULL' : "'".$this->db->idate($this->date_read)."'").",";
570 $sql .= " ".(!isset($this->date_close) || dol_strlen((string) $this->date_close) == 0 ? 'NULL' : "'".$this->db->idate($this->date_close)."'");
571 $sql .= ", ".((int) $this->entity);
572 $sql .= ", ".(!isset($this->notify_tiers_at_create) ? 1 : ((int) $this->notify_tiers_at_create));
573 $sql .= ", '".$this->db->escape($this->model_pdf)."'";
574 $sql .= ", ".(!isset($this->ip) ? 'NULL' : "'".$this->db->escape($this->ip)."'");
575 $sql .= ")";
576
577 $this->db->begin();
578
579 dol_syslog(get_class($this)."::create", LOG_DEBUG);
580 $resql = $this->db->query($sql);
581 if (!$resql) {
582 $error++;
583 $this->errors[] = "Error ".$this->db->lasterror();
584 }
585
586 if (!$error) {
587 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."ticket");
588 }
589
590 if (!$error && getDolGlobalString('TICKET_ADD_AUTHOR_AS_CONTACT') && empty($this->context["createdfrompublicinterface"])) {
591 // add creator as contributor
592
593 // We first check the type of contact (internal or external)
594 if (!empty($user->socid) && !empty($user->contact_id) && getDolGlobalInt('TICKET_ADD_AUTHOR_AS_CONTACT') == 2) {
595 $contact_type = 'external';
596 $contributor_id = $user->contact_id;
597 } else {
598 $contact_type = 'internal';
599 $contributor_id = $user->id;
600 }
601
602 // We add the creator as contributor
603 if ($this->add_contact($contributor_id, 'CONTRIBUTOR', $contact_type) < 0) {
604 $error++;
605 }
606 }
607
608 if (!$error && $this->fk_user_assign > 0) {
609 if ($this->add_contact($this->fk_user_assign, 'SUPPORTTEC', 'internal') < 0) {
610 $error++;
611 }
612 }
613
614
615 //Update extrafield
616 if (!$error) {
617 $result = $this->insertExtraFields();
618 if ($result < 0) {
619 $error++;
620 }
621 }
622
623 if (!$error && !$notrigger) {
624 // Call trigger
625 $result = $this->call_trigger('TICKET_CREATE', $user);
626 if ($result < 0) {
627 $error++;
628 }
629 // End call triggers
630 }
631
632 // Commit or rollback
633 if ($error) {
634 foreach ($this->errors as $errmsg) {
635 dol_syslog(get_class($this)."::create ".$errmsg, LOG_ERR);
636 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
637 }
638 $this->db->rollback();
639 return -1 * $error;
640 } else {
641 $this->db->commit();
642 return $this->id;
643 }
644 } else {
645 $this->db->rollback();
646 dol_syslog(get_class($this)."::Create fails verify ".implode(',', $this->errors), LOG_WARNING);
647 return -3;
648 }
649 }
650
660 public function fetch($id = 0, $ref = '', $track_id = '', $email_msgid = '')
661 {
662 global $langs;
663
664 // Check parameters
665 if (empty($id) && empty($ref) && empty($track_id) && empty($email_msgid)) {
666 $this->error = 'ErrorWrongParameters';
667 dol_print_error(null, get_class($this)."::fetch ".$this->error);
668 return -1;
669 }
670
671 $sql = "SELECT";
672 $sql .= " t.rowid,";
673 $sql .= " t.entity,";
674 $sql .= " t.ref,";
675 $sql .= " t.track_id,";
676 $sql .= " t.fk_soc,";
677 $sql .= " t.fk_project,";
678 $sql .= " t.fk_contract,";
679 $sql .= " t.origin_email,";
680 $sql .= " t.origin_replyto,";
681 $sql .= " t.origin_references,";
682 $sql .= " t.fk_user_create,";
683 $sql .= " t.fk_user_assign,";
684 $sql .= " t.email_msgid,";
685 $sql .= " t.email_date,";
686 $sql .= " t.subject,";
687 $sql .= " t.message,";
688 $sql .= " t.note_private,";
689 $sql .= " t.note_public,";
690 $sql .= " t.fk_statut as status,";
691 $sql .= " t.resolution,";
692 $sql .= " t.progress,";
693 $sql .= " t.timing,";
694 $sql .= " t.type_code,";
695 $sql .= " t.category_code,";
696 $sql .= " t.severity_code,";
697 $sql .= " t.datec,";
698 $sql .= " t.date_read,";
699 $sql .= " t.date_last_msg_sent,";
700 $sql .= " t.date_close,";
701 $sql .= " t.tms,";
702 $sql .= " t.model_pdf,";
703 $sql .= " t.extraparams,";
704 $sql .= " t.ip,";
705 $sql .= " type.label as type_label, category.label as category_label, severity.label as severity_label";
706 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as t";
707 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_type as type ON type.code=t.type_code";
708 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_category as category ON category.code=t.category_code";
709 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_severity as severity ON severity.code=t.severity_code";
710
711 if ($id) {
712 $sql .= " WHERE t.rowid = ".((int) $id);
713 } else {
714 $sql .= " WHERE t.entity IN (".getEntity($this->element, 1).")";
715 if (!empty($ref)) {
716 $sql .= " AND t.ref = '".$this->db->escape($ref)."'";
717 } elseif ($track_id) {
718 $sql .= " AND t.track_id = '".$this->db->escape($track_id)."'";
719 } else {
720 $sql .= " AND t.email_msgid = '".$this->db->escape($email_msgid)."'";
721 }
722 }
723
724 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
725 $resql = $this->db->query($sql);
726 if ($resql) {
727 if ($this->db->num_rows($resql)) {
728 $obj = $this->db->fetch_object($resql);
729
730 $this->id = $obj->rowid;
731 $this->entity = $obj->entity;
732 $this->ref = $obj->ref;
733 $this->track_id = $obj->track_id;
734 $this->fk_soc = $obj->fk_soc;
735 $this->socid = $obj->fk_soc; // for fetch_thirdparty() method
736 $this->fk_project = $obj->fk_project;
737 $this->fk_contract = $obj->fk_contract;
738 $this->origin_email = $obj->origin_email;
739 $this->origin_replyto = $obj->origin_replyto;
740 $this->origin_references = $obj->origin_references;
741 $this->fk_user_create = $obj->fk_user_create;
742 $this->fk_user_assign = $obj->fk_user_assign;
743 $this->email_msgid = $obj->email_msgid;
744 $this->email_date = $this->db->jdate($obj->email_date);
745 $this->subject = $obj->subject;
746 $this->message = $obj->message;
747 $this->note_private = $obj->note_private;
748 $this->note_public = $obj->note_public;
749 $this->model_pdf = $obj->model_pdf;
750 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
751 $this->ip = $obj->ip;
752
753 $this->status = $obj->status;
754 $this->fk_statut = $this->status; // For backward compatibility
755
756 $this->resolution = $obj->resolution;
757 $this->progress = $obj->progress;
758 $this->timing = $obj->timing;
759
760 $this->type_code = $obj->type_code;
761 $label_type = ($langs->trans("TicketTypeShort".$obj->type_code) != "TicketTypeShort".$obj->type_code ? $langs->trans("TicketTypeShort".$obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));
762 $this->type_label = $label_type;
763
764 $this->category_code = $obj->category_code;
765 $label_category = ($langs->trans("TicketCategoryShort".$obj->category_code) != "TicketCategoryShort".$obj->category_code ? $langs->trans("TicketCategoryShort".$obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));
766 $this->category_label = $label_category;
767
768 $this->severity_code = $obj->severity_code;
769 $label_severity = ($langs->trans("TicketSeverityShort".$obj->severity_code) != "TicketSeverityShort".$obj->severity_code ? $langs->trans("TicketSeverityShort".$obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));
770 $this->severity_label = $label_severity;
771
772 $this->datec = $this->db->jdate($obj->datec);
773 $this->date_creation = $this->db->jdate($obj->datec);
774 $this->date_read = $this->db->jdate($obj->date_read);
775 $this->date_validation = $this->db->jdate($obj->date_read);
776 $this->date_last_msg_sent = $this->db->jdate($obj->date_last_msg_sent);
777 $this->date_close = $this->db->jdate($obj->date_close);
778 $this->tms = $this->db->jdate($obj->tms);
779 $this->date_modification = $this->db->jdate($obj->tms);
780
781 $this->fetch_optionals();
782
783 $this->db->free($resql);
784 return 1;
785 } else {
786 return 0;
787 }
788 } else {
789 $this->error = "Error ".$this->db->lasterror();
790 dol_syslog(get_class($this)."::fetch ".$this->error, LOG_ERR);
791 return -1;
792 }
793 }
794
807 public function fetchAll($user, $sortorder = 'ASC', $sortfield = 't.datec', $limit = 0, $offset = 0, $arch = 0, $filter = '')
808 {
809 global $langs, $extrafields;
810
811 // fetch optionals attributes and labels
812 $extrafields->fetch_name_optionals_label($this->table_element);
813
814 $sql = "SELECT";
815 $sql .= " t.rowid,";
816 $sql .= " t.ref,";
817 $sql .= " t.track_id,";
818 $sql .= " t.fk_soc,";
819 $sql .= " t.fk_project,";
820 $sql .= " t.fk_contract,";
821 $sql .= " t.origin_email,";
822 $sql .= " t.origin_replyto,";
823 $sql .= " t.origin_references,";
824 $sql .= " t.fk_user_create, uc.lastname as user_create_lastname, uc.firstname as user_create_firstname,";
825 $sql .= " t.fk_user_assign, ua.lastname as user_assign_lastname, ua.firstname as user_assign_firstname,";
826 $sql .= " t.subject,";
827 $sql .= " t.message,";
828 $sql .= " t.note_private,";
829 $sql .= " t.note_public,";
830 $sql .= " t.fk_statut as status,";
831 $sql .= " t.resolution,";
832 $sql .= " t.progress,";
833 $sql .= " t.timing,";
834 $sql .= " t.type_code,";
835 $sql .= " t.category_code,";
836 $sql .= " t.severity_code,";
837 $sql .= " t.datec,";
838 $sql .= " t.date_read,";
839 $sql .= " t.date_last_msg_sent,";
840 $sql .= " t.date_close,";
841 $sql .= " t.tms,";
842 $sql .= " type.label as type_label, category.label as category_label, severity.label as severity_label";
843 // Add fields for extrafields
844 if ($extrafields->attributes[$this->table_element]['count'] > 0) {
845 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
846 $sql .= ($extrafields->attributes[$this->table_element]['type'][$key] != 'separate' ? ",ef.".$key." as options_".$key : '');
847 }
848 }
849 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as t";
850 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_type as type ON type.code = t.type_code";
851 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_category as category ON category.code = t.category_code";
852 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_ticket_severity as severity ON severity.code = t.severity_code";
853 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = t.fk_soc";
854 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as uc ON uc.rowid = t.fk_user_create";
855 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user as ua ON ua.rowid = t.fk_user_assign";
856 if ($extrafields->attributes[$this->table_element]['count'] > 0) {
857 if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {
858 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."ticket_extrafields as ef on (t.rowid = ef.fk_object)";
859 }
860 }
861 $sql .= " WHERE t.entity IN (".getEntity('ticket').")";
862
863 // Manage filter
864 if (is_array($filter)) {
865 foreach ($filter as $key => $value) {
866 if (strpos($key, 'date')) { // To allow $filter['YEAR(s.dated)']=>$year
867 $sql .= " AND ".$this->db->sanitize($key)." = '".$this->db->escape((string) $value)."'";
868 } elseif (($key == 't.fk_user_assign') || ($key == 't.type_code') || ($key == 't.category_code') || ($key == 't.severity_code') || ($key == 't.fk_soc')) {
869 $sql .= " AND ".$this->db->sanitize($key)." = '".$this->db->escape((string) $value)."'";
870 } elseif ($key == 't.fk_statut') {
871 if (is_array($value) && count($value) > 0) {
872 $sql .= " AND ".$this->db->sanitize($key)." IN (".$this->db->sanitize(implode(',', $value)).")";
873 } else {
874 $sql .= " AND ".$this->db->sanitize($key).' = '.((int) $value);
875 }
876 } elseif ($key == 't.fk_contract') {
877 $sql .= " AND ".$this->db->sanitize($key).' = '.((int) $value);
878 } else {
879 $sql .= " AND ".$this->db->sanitize($key)." LIKE '%".$this->db->escape($this->db->escapeforlike((string) $value))."%'";
880 }
881 }
882
883 $filter = '';
884 }
885
886 // Manage filter
887 $errormessage = '';
888 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
889 if ($errormessage) {
890 $this->errors[] = $errormessage;
891 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
892 return -1;
893 }
894
895 // Case of external user
896 $socid = $user->socid ?: 0;
897 // If the internal user must only see his customers, force searching by him
898 $search_sale = 0;
899 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
900 $search_sale = $user->id;
901 }
902 // Search on sale representative
903 if ($search_sale && $search_sale != '-1') {
904 if ($search_sale == -2) {
905 $sql .= " AND NOT EXISTS (SELECT sc.fk_soc FROM ".MAIN_DB_PREFIX."societe_commerciaux as sc WHERE sc.fk_soc = t.fk_soc)";
906 } elseif ($search_sale > 0) {
907 $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).")";
908 }
909 }
910 // Search on socid
911 if ($socid) {
912 $sql .= " AND t.fk_soc = ".((int) $socid);
913 }
914
915 $sql .= $this->db->order($sortfield, $sortorder);
916 if (!empty($limit)) {
917 $sql .= $this->db->plimit($limit + 1, $offset);
918 }
919
920 dol_syslog(get_class($this)."::fetchAll", LOG_DEBUG);
921 $resql = $this->db->query($sql);
922
923 if ($resql) {
924 $this->lines = array();
925
926 $num = $this->db->num_rows($resql);
927 $i = 0;
928
929 if ($num) {
930 while ($i < $num) {
931 $obj = $this->db->fetch_object($resql);
932
933 $line = new self($this->db);
934
935 $line->id = $obj->rowid;
936 //$line->rowid = $obj->rowid;
937 $line->ref = $obj->ref;
938 $line->track_id = $obj->track_id;
939 $line->fk_soc = $obj->fk_soc;
940 $line->fk_project = $obj->fk_project;
941 $line->fk_contract = $obj->fk_contract;
942 $line->origin_email = $obj->origin_email;
943 $line->origin_replyto = $obj->origin_replyto;
944 $line->origin_references = $obj->origin_references;
945
946 $line->fk_user_create = $obj->fk_user_create;
947 $line->fk_user_assign = $obj->fk_user_assign;
948
949 $line->subject = $obj->subject;
950 $line->message = $obj->message;
951 $line->note_private = $obj->note_private;
952 $line->note_public = $obj->note_public;
953 $line->fk_statut = $obj->status;
954 $line->status = $obj->status;
955 $line->resolution = $obj->resolution;
956 $line->progress = $obj->progress;
957 $line->timing = $obj->timing;
958
959 $label_type = ($langs->trans("TicketTypeShort".$obj->type_code) != "TicketTypeShort".$obj->type_code ? $langs->trans("TicketTypeShort".$obj->type_code) : ($obj->type_label != '-' ? $obj->type_label : ''));
960 $line->type_label = $label_type;
961
962 $this->category_code = $obj->category_code;
963 $label_category = ($langs->trans("TicketCategoryShort".$obj->category_code) != "TicketCategoryShort".$obj->category_code ? $langs->trans("TicketCategoryShort".$obj->category_code) : ($obj->category_label != '-' ? $obj->category_label : ''));
964 $line->category_label = $label_category;
965
966 $this->severity_code = $obj->severity_code;
967 $label_severity = ($langs->trans("TicketSeverityShort".$obj->severity_code) != "TicketSeverityShort".$obj->severity_code ? $langs->trans("TicketSeverityShort".$obj->severity_code) : ($obj->severity_label != '-' ? $obj->severity_label : ''));
968 $line->severity_label = $label_severity;
969
970 $line->datec = $this->db->jdate($obj->datec);
971 $line->date_read = $this->db->jdate($obj->date_read);
972 $line->date_last_msg_sent = $this->db->jdate($obj->date_last_msg_sent);
973 $line->date_close = $this->db->jdate($obj->date_close);
974
975 // Extra fields
976 if ($extrafields->attributes[$this->table_element]['count'] > 0) {
977 if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label'])) {
978 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
979 $tmpkey = 'options_'.$key;
980 $line->{$tmpkey} = $obj->$tmpkey;
981 }
982 }
983 }
984 $this->lines[$i] = $line;
985 $i++;
986 }
987 }
988 $this->db->free($resql);
989 return $num;
990 } else {
991 $this->error = "Error ".$this->db->lasterror();
992 dol_syslog(get_class($this)."::fetchAll ".$this->error, LOG_ERR);
993 return -1;
994 }
995 }
996
1004 public function update($user, $notrigger = 0)
1005 {
1006 $error = 0;
1007
1008 // $this->oldcopy should have been set by the caller of update
1009 //if (empty($this->oldcopy)) {
1010 // dol_syslog("this->oldcopy should have been set by the caller of update (here properties were already modified)", LOG_WARNING);
1011 // $this->oldcopy = dol_clone($this, 2);
1012 //}
1013
1014 // Clean parameters
1015 if (isset($this->ref)) {
1016 $this->ref = trim($this->ref);
1017 }
1018
1019 if (isset($this->track_id)) {
1020 $this->track_id = trim($this->track_id);
1021 }
1022
1023 if (isset($this->fk_soc)) {
1024 $this->fk_soc = (int) $this->fk_soc;
1025 }
1026
1027 if (isset($this->fk_project)) {
1028 $this->fk_project = (int) $this->fk_project;
1029 }
1030
1031 if (isset($this->fk_contract)) {
1032 $this->fk_contract = (int) $this->fk_contract;
1033 }
1034
1035 if (isset($this->origin_email)) {
1036 $this->origin_email = trim($this->origin_email);
1037 }
1038
1039 if (isset($this->fk_user_create)) {
1040 $this->fk_user_create = (int) $this->fk_user_create;
1041 }
1042
1043 if (isset($this->fk_user_assign)) {
1044 $this->fk_user_assign = (int) $this->fk_user_assign;
1045 }
1046
1047 if (isset($this->subject)) {
1048 $this->subject = trim($this->subject);
1049 }
1050
1051 if (isset($this->message)) {
1052 $this->message = trim($this->message);
1053 if (dol_strlen($this->message) > 65000) {
1054 global $langs;
1055 $langs->loadLangs(array('errors', 'ticket'));
1056 $this->errors[] = $langs->trans('ErrorFieldTooLong', $langs->transnoentitiesnoconv('InitialMessage'));
1057 dol_syslog(get_class($this).'::update error -1 message too long', LOG_ERR);
1058 return -1;
1059 }
1060 }
1061
1062 if (isset($this->status)) {
1063 $this->status = (int) $this->status;
1064 }
1065
1066 if (isset($this->resolution)) {
1067 $this->resolution = (int) $this->resolution;
1068 }
1069
1070 if (isset($this->progress)) {
1071 $this->progress = (int) $this->progress;
1072 }
1073
1074 if (isset($this->timing)) {
1075 $this->timing = trim($this->timing);
1076 }
1077
1078 if (isset($this->type_code)) {
1079 $this->type_code = trim($this->type_code);
1080 }
1081
1082 if (isset($this->category_code)) {
1083 $this->category_code = trim($this->category_code);
1084 }
1085
1086 if (isset($this->severity_code)) {
1087 $this->severity_code = trim($this->severity_code);
1088 }
1089 if (isset($this->model_pdf)) {
1090 $this->model_pdf = trim($this->model_pdf);
1091 }
1092 // Check parameters
1093 // Put here code to add a control on parameters values
1094 // Update request
1095 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket SET";
1096 $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "").",";
1097 $sql .= " track_id=".(isset($this->track_id) ? "'".$this->db->escape($this->track_id)."'" : "null").",";
1098 $sql .= " fk_soc=".(isset($this->fk_soc) ? (int) $this->fk_soc : "null").",";
1099 $sql .= " fk_project=".(isset($this->fk_project) ? (int) $this->fk_project : "null").",";
1100 $sql .= " fk_contract=".(isset($this->fk_contract) ? (int) $this->fk_contract : "null").",";
1101 $sql .= " origin_email=".(isset($this->origin_email) ? "'".$this->db->escape($this->origin_email)."'" : "null").",";
1102 $sql .= " origin_replyto=".(isset($this->origin_replyto) ? "'".$this->db->escape($this->origin_replyto)."'" : "null").",";
1103 $sql .= " origin_references=".(isset($this->origin_references) ? "'".$this->db->escape($this->origin_references)."'" : "null").",";
1104 $sql .= " fk_user_create=".(isset($this->fk_user_create) ? (int) $this->fk_user_create : "null").",";
1105 $sql .= " fk_user_assign=".(isset($this->fk_user_assign) ? (int) $this->fk_user_assign : "null").",";
1106 $sql .= " subject=".(isset($this->subject) ? "'".$this->db->escape($this->subject)."'" : "null").",";
1107 $sql .= " message=".(isset($this->message) ? "'".$this->db->escape($this->message)."'" : "null").",";
1108 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1109 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1110 $sql .= " fk_statut=".(isset($this->status) ? (int) $this->status : "0").",";
1111 $sql .= " resolution=".(isset($this->resolution) ? (int) $this->resolution : "null").",";
1112 $sql .= " progress=".(isset($this->progress) ? "'".$this->db->escape((string) $this->progress)."'" : "null").",";
1113 $sql .= " timing=".(isset($this->timing) ? "'".$this->db->escape($this->timing)."'" : "null").",";
1114 $sql .= " type_code=".(isset($this->type_code) ? "'".$this->db->escape($this->type_code)."'" : "null").",";
1115 $sql .= " category_code=".(isset($this->category_code) ? "'".$this->db->escape($this->category_code)."'" : "null").",";
1116 $sql .= " severity_code=".(isset($this->severity_code) ? "'".$this->db->escape($this->severity_code)."'" : "null").",";
1117 $sql .= " datec=".(isDolTms($this->datec) ? "'".$this->db->idate($this->datec)."'" : 'null').",";
1118 $sql .= " date_read=".(dol_strlen((string) $this->date_read) != 0 ? "'".$this->db->idate($this->date_read)."'" : 'null').",";
1119 $sql .= " date_last_msg_sent=".(dol_strlen((string) $this->date_last_msg_sent) != 0 ? "'".$this->db->idate($this->date_last_msg_sent)."'" : 'null').",";
1120 $sql .= " model_pdf=".(isset($this->model_pdf) ? "'".$this->db->escape($this->model_pdf)."'" : "null").",";
1121 $sql .= " date_close=".(dol_strlen((string) $this->date_close) != 0 ? "'".$this->db->idate($this->date_close)."'" : 'null');
1122 $sql .= " WHERE rowid=".((int) $this->id);
1123
1124 $this->db->begin();
1125
1126 $resql = $this->db->query($sql);
1127 if (!$resql) {
1128 $error++;
1129 $this->errors[] = "Error ".$this->db->lasterror();
1130 }
1131
1132 if (!$error) {
1133 // Update extrafields
1134 $result = $this->insertExtraFields();
1135 if ($result < 0) {
1136 $error++;
1137 }
1138 }
1139
1140 if (!$error && !$notrigger) {
1141 // Call trigger
1142 $result = $this->call_trigger('TICKET_MODIFY', $user);
1143 if ($result < 0) {
1144 $error++;
1145 }
1146 // End call triggers
1147 }
1148
1149 // Commit or rollback
1150 if ($error) {
1151 foreach ($this->errors as $errmsg) {
1152 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1153 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1154 }
1155 $this->db->rollback();
1156 return -1 * $error;
1157 } else {
1158 $this->db->commit();
1159 return 1;
1160 }
1161 }
1162
1170 public function delete($user, $notrigger = 0)
1171 {
1172 $error = 0;
1173
1174 $this->db->begin();
1175
1176 if (!$notrigger) {
1177 // Call trigger
1178 $result = $this->call_trigger('TICKET_DELETE', $user);
1179 if ($result < 0) {
1180 $error++;
1181 }
1182 // End call triggers
1183 }
1184
1185 if (!$error) {
1186 // Delete linked contacts
1187 $res = $this->delete_linked_contact();
1188 if ($res < 0) {
1189 dol_syslog(get_class($this)."::delete error", LOG_ERR);
1190 $error++;
1191 }
1192 }
1193
1194 if (!$error) {
1195 // Delete linked object
1196 $res = $this->deleteObjectLinked();
1197 if ($res < 0) {
1198 $error++;
1199 }
1200 }
1201
1202 // Removed extrafields
1203 if (!$error) {
1204 $result = $this->deleteExtraFields();
1205 if ($result < 0) {
1206 $error++;
1207 dol_syslog(get_class($this)."::delete error -3 ".$this->error, LOG_ERR);
1208 }
1209 }
1210
1211 // Delete all child tables
1212
1213 if (!$error) {
1214 $sql = "DELETE FROM ".MAIN_DB_PREFIX."categorie_ticket";
1215 $sql .= " WHERE fk_ticket = ".(int) $this->id;
1216
1217 $result = $this->db->query($sql);
1218 if (!$result) {
1219 $error++;
1220 $this->errors[] = $this->db->lasterror();
1221 }
1222 }
1223
1224 if (!$error) {
1225 $sql = "DELETE FROM ".MAIN_DB_PREFIX."ticket";
1226 $sql .= " WHERE rowid=".((int) $this->id);
1227
1228 dol_syslog(get_class($this)."::delete sql=".$sql);
1229 $resql = $this->db->query($sql);
1230 if (!$resql) {
1231 $error++;
1232 $this->errors[] = "Error ".$this->db->lasterror();
1233 } else {
1234 // we delete file with dol_delete_dir_recursive
1235 $this->deleteEcmFiles(1);
1236
1237 $dir = DOL_DATA_ROOT.'/'.$this->element.'/'.$this->ref;
1238 // For remove dir
1239 if (dol_is_dir($dir)) {
1240 if (!dol_delete_dir_recursive($dir)) {
1241 $this->errors[] = $this->error;
1242 }
1243 }
1244 }
1245 }
1246
1247 // Commit or rollback
1248 if ($error) {
1249 foreach ($this->errors as $errmsg) {
1250 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
1251 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1252 }
1253 $this->db->rollback();
1254 return -1 * $error;
1255 } else {
1256 $this->db->commit();
1257 return 1;
1258 }
1259 }
1260
1268 public function createFromClone(User $user, $fromid)
1269 {
1270 $error = 0;
1271
1272 $object = new Ticket($this->db);
1273
1274 $this->db->begin();
1275
1276 // Load source object
1277 $object->fetch($fromid);
1278
1279 // Clear fields
1280 $object->id = 0;
1281 $object->statut = 0;
1282 $object->status = 0;
1283 $object->ref = $object->getDefaultRef();
1284 $object->track_id = generate_random_id(16);
1285 $object->progress = 0;
1286 // Reset lifecycle timestamps so the clone starts fresh: datec is filled by
1287 // create() with dol_now() when empty, date_read and date_close stay null
1288 // because the new ticket has not been read or closed yet (see issue #38559).
1289 // Reset lifecycle timestamps and resolution so the clone starts fresh.
1290 // datec stays at 0 so Ticket::create()'s `if (empty($this->datec))` guard
1291 // fills it with dol_now(). date_read / date_close / resolution are unset
1292 // so the SQL writers' `!isset` checks emit NULL. We use these forms
1293 // rather than `= null` so phpstan stays happy with the int-typed
1294 // property declarations.
1295 $object->datec = 0;
1296 unset($object->date_read);
1297 unset($object->date_close);
1298 unset($object->resolution);
1299
1300 // Create clone
1301 $object->context['createfromclone'] = 'createfromclone';
1302 $result = $object->create($user);
1303
1304 // Other options
1305 if ($result < 0) {
1306 $this->error = $object->error;
1307 $error++;
1308 }
1309
1310 unset($object->context['createfromclone']);
1311
1312 // End
1313 if (!$error) {
1314 $this->db->commit();
1315 return $object->id;
1316 } else {
1317 $this->db->rollback();
1318 return -1;
1319 }
1320 }
1321
1328 public function initAsSpecimen()
1329 {
1330 $this->id = 0;
1331 $this->entity = 1;
1332 $this->ref = 'TI0501-001';
1333 $this->track_id = 'XXXXaaaa';
1334 $this->origin_email = 'email@email.com';
1335 $this->fk_project = 1;
1336 $this->fk_user_create = 1;
1337 $this->fk_user_assign = 1;
1338 $this->subject = 'Subject of ticket';
1339 $this->message = 'Message of ticket';
1340 $this->status = 0;
1341 $this->resolution = 1;
1342 $this->progress = 10;
1343 // $this->timing = '30';
1344 $this->type_code = 'TYPECODE';
1345 $this->category_code = 'CATEGORYCODE';
1346 $this->severity_code = 'SEVERITYCODE';
1347 $this->datec = dol_now();
1348 $this->date_read = dol_now();
1349 $this->date_last_msg_sent = dol_now();
1350 $this->date_close = dol_now();
1351 $this->tms = dol_now();
1352
1353 return 1;
1354 }
1355
1362 public function printSelectStatus($selected = "")
1363 {
1364 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 = '');
1365 }
1366
1367
1373 public function loadCacheTypesTickets()
1374 {
1375 global $langs;
1376
1377 if (!empty($this->cache_types_tickets) && count($this->cache_types_tickets)) {
1378 return 0;
1379 }
1380 // Cache deja charge
1381
1382 $sql = "SELECT rowid, code, label, use_default, pos, description";
1383 $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_type";
1384 $sql .= " WHERE entity IN (".getEntity('c_ticket_type').")";
1385 $sql .= " AND active > 0";
1386 $sql .= " ORDER BY pos";
1387 dol_syslog(get_class($this)."::load_cache_type_tickets", LOG_DEBUG);
1388 $resql = $this->db->query($sql);
1389 if ($resql) {
1390 $num = $this->db->num_rows($resql);
1391 $i = 0;
1392 while ($i < $num) {
1393 $obj = $this->db->fetch_object($resql);
1394 $label = ($langs->trans("TicketTypeShort".$obj->code) != "TicketTypeShort".$obj->code ? $langs->trans("TicketTypeShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1395 $this->cache_types_tickets[$obj->rowid]['code'] = $obj->code;
1396 $this->cache_types_tickets[$obj->rowid]['label'] = $label;
1397 $this->cache_types_tickets[$obj->rowid]['use_default'] = $obj->use_default;
1398 $this->cache_types_tickets[$obj->rowid]['pos'] = $obj->pos;
1399 $i++;
1400 }
1401 return $num;
1402 } else {
1403 dol_print_error($this->db);
1404 return -1;
1405 }
1406 }
1407
1414 public function loadCacheCategoriesTickets($publicgroup = -1)
1415 {
1416 global $conf, $langs;
1417
1418 if ($publicgroup == -1 && !empty($conf->cache['category_tickets']) && count($conf->cache['category_tickets'])) {
1419 // Cache already loaded
1420 return 0;
1421 }
1422
1423 $sql = "SELECT rowid, code, label, use_default, pos, description, public, active, force_severity, fk_parent";
1424 $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_category";
1425 $sql .= " WHERE entity IN (".getEntity('c_ticket_category').")";
1426 $sql .= " AND active > 0";
1427 if ($publicgroup > -1) {
1428 $sql .= " AND public = ".((int) $publicgroup);
1429 }
1430 $sql .= " ORDER BY pos";
1431
1432 dol_syslog(get_class($this)."::load_cache_categories_tickets", LOG_DEBUG);
1433
1434 $resql = $this->db->query($sql);
1435 if ($resql) {
1436 $num = $this->db->num_rows($resql);
1437 $i = 0;
1438 $categorytickets = array();
1439 '@phan-var-force array<int,array{code:string,label:string,use_default:int,pos:int,public:int,active:int,force_severity:?string,fk_parent:int}> $categorytickets';
1440 while ($i < $num) {
1441 $obj = $this->db->fetch_object($resql);
1442 $categorytickets[$obj->rowid]['code'] = $obj->code;
1443 $categorytickets[$obj->rowid]['use_default'] = $obj->use_default;
1444 $categorytickets[$obj->rowid]['pos'] = $obj->pos;
1445 $categorytickets[$obj->rowid]['public'] = $obj->public;
1446 $categorytickets[$obj->rowid]['active'] = $obj->active;
1447 $categorytickets[$obj->rowid]['force_severity'] = $obj->force_severity;
1448 $categorytickets[$obj->rowid]['fk_parent'] = $obj->fk_parent;
1449
1450 // If translation exists, we use it to store already translated string.
1451 // Warning: You should not use this and recompute the translated string into caller code to get the value into expected language
1452 $label = ($langs->trans("TicketCategoryShort".$obj->code) != "TicketCategoryShort".$obj->code ? $langs->trans("TicketCategoryShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1453 $categorytickets[$obj->rowid]['label'] = $label;
1454
1455 $i++;
1456 }
1457 $conf->cache['category_tickets'] = $categorytickets;
1458 return $num;
1459 } else {
1460 dol_print_error($this->db);
1461 return -1;
1462 }
1463 }
1464
1471 {
1472 global $conf, $langs;
1473
1474 if (!empty($conf->cache['severity_tickets']) && count($conf->cache['severity_tickets'])) {
1475 // Cache already loaded
1476 return 0;
1477 }
1478
1479 $sql = "SELECT rowid, code, label, use_default, pos, description";
1480 $sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_severity";
1481 $sql .= " WHERE entity IN (".getEntity('c_ticket_severity').")";
1482 $sql .= " AND active > 0";
1483 $sql .= " ORDER BY pos";
1484 dol_syslog(get_class($this)."::loadCacheSeveritiesTickets", LOG_DEBUG);
1485 $resql = $this->db->query($sql);
1486 if ($resql) {
1487 $num = $this->db->num_rows($resql);
1488 $i = 0;
1489 $severitytickets = array();
1490 '@phan-var-force array<int,array{code:string,label:string,use_default:int,pos:int}> $severitytickets';
1491 while ($i < $num) {
1492 $obj = $this->db->fetch_object($resql);
1493
1494 $severitytickets[$obj->rowid]['code'] = $obj->code;
1495 $label = ($langs->trans("TicketSeverityShort".$obj->code) != "TicketSeverityShort".$obj->code ? $langs->trans("TicketSeverityShort".$obj->code) : ($obj->label != '-' ? $obj->label : ''));
1496 $severitytickets[$obj->rowid]['label'] = $label;
1497 $severitytickets[$obj->rowid]['use_default'] = $obj->use_default;
1498 $severitytickets[$obj->rowid]['pos'] = $obj->pos;
1499 $i++;
1500 }
1501 $conf->cache['severity_tickets'] = $severitytickets;
1502 return $num;
1503 } else {
1504 dol_print_error($this->db);
1505 return -1;
1506 }
1507 }
1508
1509
1516 public function getLibStatut($mode = 0)
1517 {
1518 return $this->LibStatut($this->status, $mode, 0, $this->progress);
1519 }
1520
1521
1522 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1532 public function LibStatut($status, $mode = 0, $notooltip = 0, $progress = 0)
1533 {
1534 // phpcs:enable
1535 global $langs, $hookmanager;
1536
1537 $labelStatus = (isset($status) && !empty($this->labelStatus[$status])) ? $this->labelStatus[$status] : '';
1538 $labelStatusShort = (isset($status) && !empty($this->labelStatusShort[$status])) ? $this->labelStatusShort[$status] : '';
1539 $statusType = '';
1540
1541 $isUnknown = false;
1542 switch ($status) {
1543 case self::STATUS_NOT_READ: // Not read
1544 $statusType = 'status0';
1545 break;
1546 case self::STATUS_READ: // Read
1547 $statusType = 'status1';
1548 break;
1549 case self::STATUS_ASSIGNED: // Assigned
1550 $statusType = 'status2';
1551 break;
1552 case self::STATUS_IN_PROGRESS: // In progress
1553 $statusType = 'status4';
1554 break;
1555 case self::STATUS_WAITING: // Waiting/pending/suspended
1556 $statusType = 'status7';
1557 break;
1558 case self::STATUS_NEED_MORE_INFO: // Waiting more information from the requester
1559 $statusType = 'status3';
1560 break;
1561 case self::STATUS_CANCELED: // Canceled
1562 $statusType = 'status9';
1563 break;
1564 case self::STATUS_CLOSED: // Closed
1565 $statusType = 'status6';
1566 break;
1567 default:
1568 $isUnknown = true;
1569 break;
1570 }
1571
1572 $parameters = array(
1573 'status' => $status,
1574 'mode' => $mode,
1575 );
1576
1577 // Note that $action and $object may have been modified by hook
1578 $reshook = $hookmanager->executeHooks('LibStatut', $parameters, $this);
1579
1580 if ($reshook > 0) {
1581 return $hookmanager->resPrint;
1582 }
1583
1584 if ($isUnknown) {
1585 $labelStatus = 'Unknown';
1586 $labelStatusShort = 'Unknown';
1587 $statusType = 'status0';
1588 $mode = 0;
1589 }
1590
1591 $params = array();
1592 if ($notooltip) {
1593 $params = array('tooltip' => 'no');
1594 }
1595
1596 $labelStatus = $langs->transnoentitiesnoconv($labelStatus);
1597 $labelStatusShort = $langs->transnoentitiesnoconv($labelStatusShort);
1598
1599 if ($status == self::STATUS_IN_PROGRESS && $progress > 0) {
1600 $labelStatus .= ' ('.round($progress).'%)';
1601 $labelStatusShort .= ' ('.round($progress).'%)';
1602 }
1603
1604 return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', $params);
1605 }
1606
1613 public function getTooltipContentArray($params)
1614 {
1615 global $langs;
1616
1617 $langs->load('ticket');
1618 $nofetch = !empty($params['nofetch']);
1619
1620 $datas = array();
1621 $datas['picto'] = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Ticket").'</u>';
1622 $datas['picto'] .= ' '.$this->getLibStatut(4);
1623 $datas['ref'] = '<br><b>'.$langs->trans('Ref').':</b> '.$this->ref;
1624 $datas['track_id'] = '<br><b>'.$langs->trans('TicketTrackId').':</b> '.$this->track_id;
1625 $datas['subject'] = '<br><b>'.$langs->trans('Subject').':</b> '.$this->subject;
1626 if ($this->date_creation) {
1627 $datas['date_creation'] = '<br><b>'.$langs->trans('DateCreation').':</b> '.dol_print_date($this->date_creation, 'dayhour');
1628 }
1629 if ($this->date_modification) {
1630 $datas['date_modification'] = '<br><b>'.$langs->trans('DateModification').':</b> '.dol_print_date($this->date_modification, 'dayhour');
1631 }
1632 // show categories for this record only in ajax to not overload lists
1633 if (isModEnabled('category') && !$nofetch) {
1634 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
1635 $form = new Form($this->db);
1636 $datas['categories'] = '<br>' . $form->showCategories($this->id, Categorie::TYPE_TICKET, 1);
1637 }
1638
1639 return $datas;
1640 }
1641
1652 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1)
1653 {
1654 global $action, $conf, $hookmanager, $langs;
1655
1656 if (!empty($conf->dol_no_mouse_hover)) {
1657 $notooltip = 1; // Force disable tooltips
1658 }
1659
1660 $result = '';
1661
1662 $params = [
1663 'id' => $this->id,
1664 'objecttype' => $this->element,
1665 'option' => $option,
1666 'nofetch' => 1,
1667 ];
1668 $classfortooltip = 'classfortooltip';
1669 $dataparams = '';
1670 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
1671 $classfortooltip = 'classforajaxtooltip';
1672 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
1673 $label = '';
1674 } else {
1675 $label = implode($this->getTooltipContentArray($params));
1676 }
1677
1678 $url = DOL_URL_ROOT.'/ticket/card.php?id='.$this->id;
1679
1680 if ($option != 'nolink') {
1681 // Add param to save lastsearch_values or not
1682 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1683 if ($save_lastsearch_value == -1 && isset($_SERVER["PHP_SELF"]) && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1684 $add_save_lastsearch_values = 1;
1685 }
1686 if ($add_save_lastsearch_values) {
1687 $url .= '&save_lastsearch_values=1';
1688 }
1689 }
1690
1691 $linkclose = '';
1692 if (empty($notooltip)) {
1693 if (getDolGlobalString('MAIN_OPTIMIZEFORTEXTBROWSER')) {
1694 $label = $langs->trans("ShowTicket");
1695 $linkclose .= ' alt="'.dolPrintHTMLForAttribute($label).'"';
1696 }
1697 $linkclose .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
1698 $linkclose .= $dataparams.' class="'.$classfortooltip.($morecss ? ' '.$morecss : '').'"';
1699 } else {
1700 $linkclose = ($morecss ? ' class="'.$morecss.'"' : '');
1701 }
1702
1703 $linkstart = '<a href="'.$url.'"';
1704 $linkstart .= $linkclose.'>';
1705 $linkend = '</a>';
1706
1707 $result .= $linkstart;
1708 if ($withpicto) {
1709 $result .= img_object(($notooltip ? '' : $label), ($this->picto ?: 'generic'), (($withpicto != 2) ? 'class="paddingright"' : ''), 0, 0, $notooltip ? 0 : 1);
1710 }
1711 if ($withpicto != 2) {
1712 $result .= $this->ref;
1713 }
1714 $result .= $linkend;
1715 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : '');
1716
1717 $hookmanager->initHooks(array('ticketdao'));
1718 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
1719 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1720 if ($reshook > 0) {
1721 $result = $hookmanager->resPrint;
1722 } else {
1723 $result .= $hookmanager->resPrint;
1724 }
1725
1726 return $result;
1727 }
1728
1729
1737 public function markAsRead($user, $notrigger = 0)
1738 {
1739 global $langs;
1740
1741 $error = 0;
1742
1743 if ($this->status != self::STATUS_CANCELED) { // no closed
1744 $this->oldcopy = dol_clone($this, 2);
1745
1746 $this->db->begin();
1747
1748 $this->status = Ticket::STATUS_READ;
1749
1750 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1751 $sql .= " SET fk_statut = ".((int) $this->status) .", date_read = '".$this->db->idate(dol_now())."'";
1752 $sql .= " WHERE rowid = ".((int) $this->id);
1753
1754 dol_syslog(get_class($this)."::markAsRead");
1755 $resql = $this->db->query($sql);
1756 if ($resql) {
1757 $this->context['actionmsg'] = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1758 $this->context['actionmsg2'] = $langs->trans('TicketLogMesgReadBy', $this->ref, $user->getFullName($langs));
1759
1760 if (!$notrigger) {
1761 // Call trigger
1762 $result = $this->call_trigger('TICKET_MODIFY', $user);
1763 if ($result < 0) {
1764 $error++;
1765 }
1766 // End call triggers
1767 }
1768
1769 if (!$error) {
1770 $this->db->commit();
1771 return 1;
1772 } else {
1773 $this->status = $this->oldcopy->status;
1774
1775 $this->db->rollback();
1776
1777 $this->error = implode(',', $this->errors);
1778
1779 dol_syslog(get_class($this)."::markAsRead ".$this->error, LOG_ERR);
1780 return -1;
1781 }
1782 } else {
1783 $this->status = $this->oldcopy->status;
1784
1785 $this->db->rollback();
1786 $this->error = $this->db->lasterror();
1787 dol_syslog(get_class($this)."::markAsRead ".$this->error, LOG_ERR);
1788 return -1;
1789 }
1790 }
1791
1792 return 0;
1793 }
1794
1802 public function setReadDate($user, $notrigger = 0)
1803 {
1804 global $langs;
1805
1806 if (!empty($this->date_read)) {
1807 return 0;
1808 }
1809
1810 $this->oldcopy = dol_clone($this, 2);
1811
1812 $this->db->begin();
1813
1814 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1815 $sql .= " SET date_read = '".$this->db->idate(dol_now())."'";
1816 $sql .= ", fk_user_modif = ".((int) $user->id);
1817 $sql .= " WHERE rowid = ".((int) $this->id);
1818
1819 dol_syslog(get_class($this)."::setReadDate");
1820
1821 $resql = $this->db->query($sql);
1822
1823 if (!$resql) {
1824 $this->db->rollback();
1825 $this->date_read = $this->oldcopy->date_read;
1826 $this->error = $this->db->lasterror();
1827
1828 dol_syslog(get_class($this)."::setReadDate ".$this->error, LOG_ERR);
1829 return -1;
1830 }
1831
1832 if (!$notrigger) {
1833 $result = $this->call_trigger('TICKET_MODIFY', $user);
1834 if ($result < 0) {
1835 $this->db->rollback();
1836 $this->date_read = $this->oldcopy->date_read;
1837 $this->error = implode(',', $this->errors);
1838
1839 dol_syslog(get_class($this)."::setReadDate ".$this->error, LOG_ERR);
1840 return -1;
1841 }
1842 }
1843
1844 $this->db->commit();
1845
1846 $this->date_read = dol_now();
1847 $this->fk_user_modif = $user->id;
1848 $this->user_modification_id = $user->id;
1849
1850 return 1;
1851 }
1852
1861 public function assignUser($user, $id_assign_user, $notrigger = 0)
1862 {
1863 $error = 0;
1864
1865 $this->oldcopy = dol_clone($this, 2);
1866
1867 $this->db->begin();
1868
1869 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
1870 if ($id_assign_user > 0) {
1871 $newstatus = Ticket::STATUS_ASSIGNED;
1872 if (getDolGlobalString('TICKET_AUTO_READ_WHEN_ASSIGN')) {
1873 $newstatus = Ticket::STATUS_READ;
1874 }
1875 $sql .= " SET fk_user_assign=".((int) $id_assign_user).", fk_statut = ".((int) $newstatus);
1876 } else {
1877 $sql .= " SET fk_user_assign=null, fk_statut = ".Ticket::STATUS_READ;
1878 }
1879 $sql .= " WHERE rowid = ".((int) $this->id);
1880
1881 dol_syslog(get_class($this)."::assignUser sql=".$sql);
1882 $resql = $this->db->query($sql);
1883 if ($resql) {
1884 $this->fk_user_assign = $id_assign_user; // May be used by trigger
1885
1886 if (!$notrigger) {
1887 // Call trigger
1888 $result = $this->call_trigger('TICKET_ASSIGNED', $user);
1889 if ($result < 0) {
1890 $error++;
1891 }
1892 // End call triggers
1893 }
1894
1895 if (!$error) {
1896 $this->db->commit();
1897 return 1;
1898 } else {
1899 $this->db->rollback();
1900 $this->error = implode(',', $this->errors);
1901 dol_syslog(get_class($this)."::assignUser ".$this->error, LOG_ERR);
1902 return -1;
1903 }
1904 } else {
1905 $this->db->rollback();
1906 $this->error = $this->db->lasterror();
1907 dol_syslog(get_class($this)."::assignUser ".$this->error, LOG_ERR);
1908 return -1;
1909 }
1910 }
1911
1926 public function createTicketMessage($user, $notrigger = 0, $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array(), $send_email = false, $public_area = 0, $summary = '', $external_contacts = [])
1927 {
1928 global $conf, $langs, $hookmanager;
1929 $error = 0;
1930
1931 $now = dol_now();
1932
1933 // Clean parameters
1934 if (isset($this->track_id)) {
1935 $this->track_id = trim($this->track_id);
1936 }
1937
1938 if (isset($this->message)) {
1939 $this->message = trim($this->message);
1940 }
1941
1942 $this->db->begin();
1943
1944 // Insert entry into agenda with code 'TICKET_MSG'
1945 include_once DOL_DOCUMENT_ROOT.'/comm/action/class/actioncomm.class.php';
1946 $actioncomm = new ActionComm($this->db);
1947 $actioncomm->type_code = 'AC_OTH_AUTO'; // This is not an entry that must appears into manual calendar but only into CRM calendar
1948 $actioncomm->code = 'TICKET_MSG';
1949 if ($this->private) {
1950 $actioncomm->code = 'TICKET_MSG_PRIVATE';
1951 }
1952 if ($send_email) {
1953 $actioncomm->code .= '_SENTBYMAIL';
1954 }
1955 if ((empty($user->id) || $user->id == 0) && isset($_SESSION['email_customer'])) {
1956 $actioncomm->email_from = $_SESSION['email_customer'];
1957 }
1958 $actioncomm->socid = $this->socid;
1959 $actioncomm->label = $this->subject;
1960 if ($summary) {
1961 $actioncomm->label = preg_replace('/(\[[^\]]*\]).*$/', '\1', $actioncomm->label);
1962 if ($actioncomm->label) {
1963 $actioncomm->label .= ' ';
1964 }
1965 $actioncomm->label .= (string) $summary;
1966 }
1967
1968 $actioncomm->note_private = $this->message;
1969 $actioncomm->userassigned = array($user->id => array('id' => $user->id,'transparency' => 0));
1970
1971 $parameters = array();
1972 $reshook = $hookmanager->executeHooks('createTicketMessageExternalContacts', $parameters, $actioncomm);
1973 if ($reshook < 0) {
1974 $actioncomm->socpeopleassigned = $external_contacts;
1975 } elseif ($reshook == 0) {
1976 $actioncomm->socpeopleassigned += $external_contacts;
1977 }
1978
1979 $actioncomm->userownerid = $user->id;
1980 $actioncomm->datep = $now;
1981 $actioncomm->percentage = -1; // percentage is not relevant for punctual events
1982 $actioncomm->elementtype = 'ticket';
1983 $actioncomm->fk_element = $this->id;
1984 $actioncomm->elementid = $this->id;
1985 $actioncomm->elementtype = 'ticket';
1986 $actioncomm->fk_project = $this->fk_project;
1987
1988 // Add first contact id found in database from submitter email entered into public interface
1989 // Feature disabled by default: This has a security trouble. The public interface is a no login interface, so being able to show the contact info from an
1990 // email decided by the submiter allows anybody to get information on any contact (customer or supplier) in Dolibarr database.
1991 // He can even check if contact exists by trying any email if this feature is enabled.
1992 if ($public_area && !empty($this->origin_email) && getDolGlobalString('TICKET_ASSIGN_CONTACT_TO_MESSAGE')) {
1993 $contacts = $this->searchContactByEmail($this->origin_email);
1994 if (!empty($contacts)) {
1995 // Ensure that contact is active and select first active contact
1996 foreach ($contacts as $contact) {
1997 if ((int) $contact->status == 1) {
1998 $actioncomm->contact_id = $contact->id;
1999 break;
2000 }
2001 }
2002 }
2003 }
2004
2005 $attachedfiles = array();
2006 $attachedfiles['paths'] = $filename_list;
2007 $attachedfiles['names'] = $mimefilename_list;
2008 $attachedfiles['mimes'] = $mimetype_list;
2009 if (is_array($attachedfiles) && count($attachedfiles) > 0) {
2010 $actioncomm->attachedfiles = $attachedfiles;
2011 }
2012
2013 //if (!empty($mimefilename_list) && is_array($mimefilename_list)) {
2014 // $actioncomm->note_private = dol_concatdesc($actioncomm->note_private, "\n".$langs->transnoentities("AttachedFiles").': '.implode(';', $mimefilename_list));
2015 //}
2016 $actionid = $actioncomm->create($user);
2017 if ($actionid <= 0) {
2018 $error++;
2019 $this->error = $actioncomm->error;
2020 $this->errors = $actioncomm->errors;
2021 }
2022
2023 if ($actionid > 0) {
2024 if (is_array($attachedfiles) && array_key_exists('paths', $attachedfiles) && count($attachedfiles['paths']) > 0) {
2025 // If there is some files, we must now link them to the event, so we can show them per event.
2026 foreach ($attachedfiles['paths'] as $key => $filespath) {
2027 // Disabled the move into another directory, Files for a ticket should be stored into ticket directory. It generates too much troubles.
2028 $destdir = $conf->ticket->dir_output.'/'.$this->ref;
2029 //$destfile = $destdir.'/'.$attachedfiles['names'][$key];
2030 //if (dol_mkdir($destdir) >= 0) {
2031 //require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2032 //dol_move($filespath, $destfile); // Disabled, a file for a ticket should be stored into ticket directory. It generates big trouble.
2033 if (in_array($actioncomm->code, array('TICKET_MSG', 'TICKET_MSG_SENTBYMAIL', 'TICKET_MSG_PRIVATE'))) {
2034 $ecmfile = new EcmFiles($this->db);
2035 $destdir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $destdir);
2036 $destdir = preg_replace('/[\\/]$/', '', $destdir);
2037 $destdir = preg_replace('/^[\\/]/', '', $destdir);
2038
2039 $result = $ecmfile->fetch(0, '', $destdir.'/'.$attachedfiles['names'][$key]);
2040
2041 // We also store the ID of event.
2042 $ecmfile->agenda_id = $actionid;
2043
2044 if ($result > 0) {
2045 $result = $ecmfile->update($user);
2046 if ($result < 0) {
2047 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
2048 }
2049 }
2050 }
2051 //}
2052 }
2053 }
2054 }
2055
2056 // Commit or rollback
2057 if ($error) {
2058 $this->db->rollback();
2059 return -1 * $error;
2060 } else {
2061 $this->db->commit();
2062 return $actionid;
2063 }
2064 }
2065
2071 public function loadCacheMsgsTicket()
2072 {
2073 if (!empty($this->cache_msgs_ticket) && is_array($this->cache_msgs_ticket) && count($this->cache_msgs_ticket)) {
2074 return 0;
2075 }
2076
2077 // Cache already loaded
2078
2079 $sql = "SELECT id as rowid, fk_user_author, fk_user_action, email_from, datec, datep, label, note as message, code";
2080 $sql .= " FROM ".MAIN_DB_PREFIX."actioncomm";
2081 $sql .= " WHERE fk_element = ".(int) $this->id;
2082 $sql .= " AND elementtype = 'ticket'";
2083 $sql .= " ORDER BY datep DESC";
2084
2085 dol_syslog(get_class($this)."::load_cache_actions_ticket", LOG_DEBUG);
2086 $resql = $this->db->query($sql);
2087 if ($resql) {
2088 $num = $this->db->num_rows($resql);
2089 $i = 0;
2090 while ($i < $num) {
2091 $obj = $this->db->fetch_object($resql);
2092 $this->cache_msgs_ticket[$i]['id'] = $obj->rowid;
2093 $this->cache_msgs_ticket[$i]['fk_user_author'] = $obj->fk_user_author;
2094 $this->cache_msgs_ticket[$i]['fk_user_action'] = $obj->fk_user_action; // owner of the action
2095 if (in_array($obj->code, array('TICKET_MSG', 'AC_TICKET_CREATE')) && empty($obj->fk_user_author)) {
2096 $this->cache_msgs_ticket[$i]['fk_contact_author'] = $obj->email_from;
2097 }
2098 $this->cache_msgs_ticket[$i]['datec'] = $this->db->jdate($obj->datec);
2099 $this->cache_msgs_ticket[$i]['datep'] = $this->db->jdate($obj->datep);
2100 $this->cache_msgs_ticket[$i]['subject'] = $obj->label;
2101 $this->cache_msgs_ticket[$i]['message'] = $obj->message;
2102 $this->cache_msgs_ticket[$i]['private'] = (preg_match('/^TICKET_MSG_PRIVATE/', $obj->code) ? 1 : 0);
2103 $i++;
2104 }
2105 return $num;
2106 } else {
2107 $this->error = "Error ".$this->db->lasterror();
2108 dol_syslog(get_class($this)."::load_cache_actions_ticket ".$this->error, LOG_ERR);
2109 return -1;
2110 }
2111 }
2112
2120 public function close(User $user, $mode = 0)
2121 {
2122
2123 if ($this->status != Ticket::STATUS_CLOSED && $this->status != Ticket::STATUS_CANCELED) { // not closed
2124 $this->db->begin();
2125
2126 $this->oldcopy = dol_clone($this);
2127 $this->status = ($mode ? Ticket::STATUS_CANCELED : Ticket::STATUS_CLOSED);
2128
2129 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2130 $sql .= " SET fk_statut = ".((int) $this->status).", progress=100, date_close='".$this->db->idate(dol_now())."'";
2131 $sql .= " WHERE rowid = ".((int) $this->id);
2132
2133 dol_syslog(get_class($this)."::close mode=".$mode);
2134 $resql = $this->db->query($sql);
2135 if ($resql) {
2136 $error = 0;
2137
2138 // Valid and close fichinter linked
2139 if (isModEnabled('intervention') && getDolGlobalString('WORKFLOW_TICKET_CLOSE_INTERVENTION')) {
2140 dol_syslog("We have closed the ticket, so we close all linked interventions");
2141 $this->fetchObjectLinked($this->id, $this->element, null, 'fichinter');
2142 if ($this->linkedObjectsIds) {
2143 foreach ($this->linkedObjectsIds['fichinter'] as $fichinter_id) {
2144 $fichinter = new Fichinter($this->db);
2145 $fichinter->fetch($fichinter_id);
2146 if ($fichinter->statut == 0) {
2147 $result = $fichinter->setValid($user);
2148 if (!$result) {
2149 $this->errors[] = $fichinter->error;
2150 $error++;
2151 }
2152 }
2153 if ($fichinter->statut < 3) {
2154 $result = $fichinter->setStatut(3);
2155 if (!$result) {
2156 $this->errors[] = $fichinter->error;
2157 $error++;
2158 }
2159 }
2160 }
2161 }
2162 }
2163
2164 // Call trigger
2165 $result = $this->call_trigger('TICKET_CLOSE', $user);
2166 if ($result < 0) {
2167 $error++;
2168 }
2169 // End call triggers
2170
2171 if (!$error) {
2172 $this->db->commit();
2173 return 1;
2174 } else {
2175 $this->db->rollback();
2176 $this->error = implode(',', $this->errors);
2177 dol_syslog(get_class($this)."::close ".$this->error, LOG_ERR);
2178 return -1;
2179 }
2180 } else {
2181 $this->db->rollback();
2182 $this->error = $this->db->lasterror();
2183 dol_syslog(get_class($this)."::close ".$this->error, LOG_ERR);
2184 return -1;
2185 }
2186 }
2187
2188 return 0;
2189 }
2190
2200 public function searchSocidByEmail($email, $type = 0, $filters = array(), $clause = 'AND')
2201 {
2202 $thirdparties = array();
2203 $exact = 0;
2204
2205 // Generation requete recherche
2206 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."societe";
2207 $sql .= " WHERE entity IN (".getEntity('ticket', 1).")";
2208 if (!empty($type)) {
2209 if ($type == 1 || $type == 2) {
2210 $sql .= " AND client = ".((int) $type);
2211 } elseif ($type == 3) {
2212 $sql .= " AND fournisseur = 1";
2213 }
2214 }
2215 if (!empty($email)) {
2216 if (empty($exact)) {
2217 $regs = array();
2218 if (preg_match('/^([\*])?[^*]+([\*])?$/', $email, $regs) && count($regs) > 1) {
2219 $email = str_replace('*', '%', $email);
2220 } else {
2221 $email = '%'.$email.'%';
2222 }
2223 }
2224 $sql .= " AND ";
2225 if (is_array($filters) && !empty($filters)) {
2226 $sql .= "(";
2227 }
2228
2229 $sql .= "email LIKE '".$this->db->escape($email)."'";
2230 }
2231 if (is_array($filters) && !empty($filters)) {
2232 foreach ($filters as $field => $value) {
2233 $sql .= " ".$clause." ".$this->db->sanitize($field)." LIKE '".$this->db->escape($value)."'";
2234 }
2235 if (!empty($email)) {
2236 $sql .= ")";
2237 }
2238 }
2239
2240 $res = $this->db->query($sql);
2241 if ($res) {
2242 while ($rec = $this->db->fetch_array($res)) {
2243 $soc = new Societe($this->db);
2244 $soc->fetch($rec['rowid']);
2245 $thirdparties[] = $soc;
2246 }
2247
2248 return $thirdparties;
2249 } else {
2250 $this->error = $this->db->error().' sql='.$sql;
2251 dol_syslog(get_class($this)."::searchSocidByEmail ".$this->error, LOG_ERR);
2252 return -1;
2253 }
2254 }
2255
2264 public function searchContactByEmail($email, $socid = 0, $case = '')
2265 {
2266 $contacts = array();
2267
2268 // Forge the search SQL
2269 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX."socpeople";
2270 $sql .= " WHERE entity IN (".getEntity('contact').")";
2271 if (!empty($socid)) {
2272 $sql .= " AND fk_soc = ".((int) $socid);
2273 }
2274 if (!empty($email)) {
2275 $sql .= " AND ";
2276 if (!$case) {
2277 $sql .= "email = '".$this->db->escape($email)."'";
2278 } else {
2279 $sql .= "email LIKE BINARY '".$this->db->escape($this->db->escapeforlike($email))."'";
2280 }
2281 }
2282
2283 $res = $this->db->query($sql);
2284 if ($res) {
2285 while ($rec = $this->db->fetch_object($res)) {
2286 include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
2287 $contactstatic = new Contact($this->db);
2288 $contactstatic->fetch($rec->rowid);
2289 $contacts[] = $contactstatic;
2290 }
2291
2292 return $contacts;
2293 } else {
2294 $this->error = $this->db->error().' sql='.$sql;
2295 dol_syslog(get_class($this)."::searchContactByEmail ".$this->error, LOG_ERR);
2296 return -1;
2297 }
2298 }
2299
2306 public function setCustomer($id)
2307 {
2308 if ($this->id) {
2309 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2310 $sql .= " SET fk_soc = ".($id > 0 ? (int) $id : "null");
2311 $sql .= " WHERE rowid = ".((int) $this->id);
2312 dol_syslog(get_class($this).'::setCustomer sql='.$sql);
2313 $resql = $this->db->query($sql);
2314 if ($resql) {
2315 return 1;
2316 } else {
2317 return -1;
2318 }
2319 } else {
2320 return -1;
2321 }
2322 }
2323
2330 public function setProgression($percent)
2331 {
2332 if ($this->id) {
2333 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2334 $sql .= " SET progress = ".($percent > 0 ? (float) $percent : "null");
2335 $sql .= " WHERE rowid = ".((int) $this->id);
2336 dol_syslog(get_class($this).'::set_progression sql='.$sql);
2337 $resql = $this->db->query($sql);
2338 if ($resql) {
2339 return 1;
2340 } else {
2341 return -1;
2342 }
2343 } else {
2344 return -1;
2345 }
2346 }
2347
2354 public function setContract($contractid)
2355 {
2356 if ($this->id) {
2357 $sql = "UPDATE ".MAIN_DB_PREFIX."ticket";
2358 $sql .= " SET fk_contract = ".($contractid > 0 ? (int) $contractid : "null");
2359 $sql .= " WHERE rowid = ".((int) $this->id);
2360 dol_syslog(get_class($this).'::setContract sql='.$sql);
2361 $resql = $this->db->query($sql);
2362 if ($resql) {
2363 return 1;
2364 } else {
2365 return -1;
2366 }
2367 } else {
2368 return -1;
2369 }
2370 }
2371
2372 /* gestion des contacts d'un ticket */
2373
2380 {
2381 return $this->getIdContact('internal', 'SUPPORTTEC');
2382 }
2383
2390 public function getInfosTicketInternalContact($status = -1)
2391 {
2392 return $this->listeContact(-1, 'internal', 0, '', $status);
2393 }
2394
2401 {
2402 return $this->getIdContact('external', 'SUPPORTCLI');
2403 }
2404
2411 public function getInfosTicketExternalContact($status = -1)
2412 {
2413 return $this->listeContact(-1, 'external', 0, '', $status);
2414 }
2415
2422 {
2423 return $this->getIdContact('internal', 'CONTRIBUTOR');
2424 }
2425
2432 {
2433 return $this->getIdContact('external', 'CONTRIBUTOR');
2434 }
2435
2441 public function getTicketAllContacts()
2442 {
2443 $array_contact = $this->getIdTicketInternalContact();
2444
2445 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());
2446
2447 $array_contact = array_merge($array_contact, $this->getIdTicketInternalInvolvedContact());
2448
2449 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());
2450
2451 return $array_contact;
2452 }
2453
2460 {
2461 $array_contact = array();
2462
2463 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerContact());
2464
2465 $array_contact = array_merge($array_contact, $this->getIdTicketCustomerInvolvedContact());
2466
2467 return $array_contact;
2468 }
2469
2470
2482 public function listeContact($statusoflink = -1, $source = 'external', $list = 0, $code = '', $status = -1)
2483 {
2484 global $langs;
2485
2486 $tab = array();
2487
2488 $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
2489 if ($source == 'internal') {
2490 $sql .= ", '-1' as socid, t.statut as statuscontact";
2491 }
2492
2493 if ($source == 'external' || $source == 'thirdparty') {
2494 $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
2495 }
2496
2497 $sql .= ", t.civility, t.lastname as lastname, t.firstname, t.email";
2498 if ($source == 'internal') {
2499 $sql .= ", t.office_phone as phone, t.user_mobile as phone_mobile";
2500 }
2501
2502 if ($source == 'external') {
2503 $sql .= ", t.phone as phone, t.phone_mobile as phone_mobile, t.phone_perso as phone_perso";
2504 }
2505
2506 $sql .= ", tc.source, tc.element, tc.code, tc.libelle as type_contact_label";
2507 $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
2508 $sql .= ", ".MAIN_DB_PREFIX."element_contact ec";
2509 if ($source == 'internal') {
2510 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
2511 }
2512
2513 if ($source == 'external' || $source == 'thirdparty') {
2514 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
2515 }
2516
2517 $sql .= " WHERE ec.element_id = ".((int) $this->id);
2518 $sql .= " AND ec.fk_c_type_contact=tc.rowid";
2519 $sql .= " AND tc.element='".$this->db->escape($this->element)."'";
2520 if ($source == 'internal') {
2521 $sql .= " AND tc.source = 'internal'";
2522 if ($status >= 0) {
2523 $sql .= " AND t.statut = ".((int) $status);
2524 }
2525 }
2526
2527 if ($source == 'external' || $source == 'thirdparty') {
2528 $sql .= " AND tc.source = 'external'";
2529 if ($status >= 0) {
2530 $sql .= " AND t.statut = ".((int) $status);
2531 }
2532 }
2533
2534 if (!empty($code)) {
2535 $sql .= " AND tc.code = '".$this->db->escape($code)."'";
2536 }
2537
2538 $sql .= " AND tc.active=1";
2539 if ($statusoflink >= 0) {
2540 $sql .= " AND ec.statut = ".((int) $statusoflink);
2541 }
2542
2543 $sql .= " ORDER BY t.lastname ASC";
2544
2545 $resql = $this->db->query($sql);
2546 if ($resql) {
2547 $num = $this->db->num_rows($resql);
2548 $i = 0;
2549 while ($i < $num) {
2550 $obj = $this->db->fetch_object($resql);
2551
2552 if (!$list) {
2553 $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
2554 $labelType = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_contact_label);
2555 $tab[$i] = array(
2556 'source' => $obj->source,
2557 'socid' => (int) $obj->socid,
2558 'id' => (int) $obj->id,
2559 'nom' => $obj->lastname, // For backward compatibility
2560 'civility' => $obj->civility,
2561 'lastname' => $obj->lastname,
2562 'firstname' => $obj->firstname,
2563 'email' => $obj->email,
2564 'rowid' => (int) $obj->rowid,
2565 'code' => $obj->code,
2566 'libelle' => $labelType, // deprecated, replaced with labeltype
2567 'labeltype' => $labelType,
2568 'status' => $obj->statuslink,
2569 'statuscontact' => (int) $obj->statuscontact,
2570 'fk_c_type_contact' => (int) $obj->fk_c_type_contact,
2571 'phone' => $obj->phone,
2572 'phone_mobile' => $obj->phone_mobile);
2573 if ($source == 'external') {
2574 $tab[$i]['phone_perso'] = $obj->phone_perso;
2575 }
2576 } else {
2577 $tab[$i] = $obj->id;
2578 }
2579
2580 $i++;
2581 }
2582
2583 return $tab;
2584 } else {
2585 $this->error = $this->db->error();
2586 dol_print_error($this->db);
2587 return -1;
2588 }
2589 }
2590
2597 public function getDefaultRef($thirdparty = null)
2598 {
2599 global $conf;
2600
2601 $defaultref = '';
2602 $modele = getDolGlobalString('TICKET_ADDON', 'mod_ticket_simple');
2603
2604 // Search template files
2605 $file = '';
2606 $classname = '';
2607 $reldir = '';
2608 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
2609 foreach ($dirmodels as $reldir) {
2610 $file = dol_buildpath($reldir."core/modules/ticket/".$modele.'.php', 0);
2611 if (file_exists($file)) {
2612 $classname = $modele;
2613 break;
2614 }
2615 }
2616
2617 if ($classname !== '') {
2618 $result = dol_include_once($reldir."core/modules/ticket/".$modele.'.php');
2619 $modTicket = new $classname();
2620 '@phan-var-force ModeleNumRefTicket $modTicket';
2621
2622 $defaultref = $modTicket->getNextValue($thirdparty, $this);
2623 }
2624
2625 if (is_numeric($defaultref) && $defaultref <= 0) {
2626 $defaultref = '';
2627 }
2628
2629 return $defaultref;
2630 }
2631
2632
2633 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2640 public function is_photo_available($sdir)
2641 {
2642 // phpcs:enable
2643 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2644
2645 $dir = $sdir.'/';
2646
2647 $dir_osencoded = dol_osencode($dir);
2648 if (file_exists($dir_osencoded)) {
2649 $handle = opendir($dir_osencoded);
2650 if (is_resource($handle)) {
2651 while (($file = readdir($handle)) !== false) {
2652 if (!utf8_check($file)) {
2653 $file = mb_convert_encoding($file, 'UTF-8', 'ISO-8859-1'); // To be sure data is stored in UTF8 in memory
2654 }
2655 if (dol_is_file($dir.$file)) {
2656 return true;
2657 }
2658 }
2659 }
2660 }
2661 return false;
2662 }
2663
2664
2673 public function copyFilesForTicket($forcetrackid = null)
2674 {
2675 global $conf;
2676
2677 // Create form object
2678 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
2679 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
2680 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
2681
2682 $maxwidthsmall = 270;
2683 $maxheightsmall = 150;
2684 $maxwidthmini = 128;
2685 $maxheightmini = 72;
2686
2687 $formmail = new FormMail($this->db);
2688 $formmail->trackid = (is_null($forcetrackid) ? 'tic'.$this->id : '');
2689 $attachedfiles = $formmail->get_attached_files();
2690
2691 $filepath = $attachedfiles['paths']; // path is for example user->dir_temp.'/'.$user->id.'/'...
2692 $filename = $attachedfiles['names'];
2693 $mimetype = $attachedfiles['mimes'];
2694
2695 // Copy files into ticket directory
2696 $destdir = $conf->ticket->dir_output.'/'.$this->ref;
2697
2698 if (!dol_is_dir($destdir)) {
2699 dol_mkdir($destdir);
2700 }
2701
2702 $listofpaths = array();
2703 $listofnames = array();
2704 foreach ($filename as $i => $val) {
2705 $destfile = $destdir.'/'.$filename[$i];
2706 // If destination file already exists, we add a suffix to avoid to overwrite
2707 if (is_file($destfile)) {
2708 $pathinfo = pathinfo($filename[$i]);
2709 $now = dol_now();
2710 $destfile = $destdir.'/'.$pathinfo['filename'].' - '.dol_print_date($now, 'dayhourlog').'.'.$pathinfo['extension'];
2711 }
2712
2713 $moreinfo = array(
2714 'description' => 'File saved by copyFilesForTicket',
2715 'src_object_type' => $this->element,
2716 'src_object_id' => $this->id,
2717 'gen_or_uploaded' => 'uploaded'
2718 );
2719
2720 $res = dol_move($filepath[$i], $destfile, '0', 1, 0, 1, $moreinfo);
2721
2722 if (!$res) {
2723 // Move has failed
2724 $this->error = "Failed to move file ".dirbasename($filepath[$i])." into ".dirbasename($destfile);
2725 return -1;
2726 } else {
2727 // If file is an image, we create thumbs
2728 if (image_format_supported($destfile) == 1) {
2729 // Create small thumbs for image (Ratio is near 16/9)
2730 // Used on logon for example
2731 $imgThumbSmall = vignette($destfile, $maxwidthsmall, $maxheightsmall, '_small', 50, "thumbs");
2732 // Create mini thumbs for image (Ratio is near 16/9)
2733 // Used on menu or for setup page for example
2734 $imgThumbMini = vignette($destfile, $maxwidthmini, $maxheightmini, '_mini', 50, "thumbs");
2735 }
2736 }
2737
2738 // Clear variables into session
2739 $formmail->remove_attached_files($i);
2740
2741 // Fill array with new names
2742 $listofpaths[$i] = $destfile;
2743 $listofnames[$i] = basename($destfile);
2744 }
2745
2746 return array('listofpaths' => $listofpaths, 'listofnames' => $listofnames, 'listofmimes' => $mimetype);
2747 }
2748
2759 public function setCategories($categories)
2760 {
2761 // Handle single category
2762 if (!is_array($categories)) {
2763 $categories = array($categories);
2764 }
2765
2766 // Get current categories
2767 include_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
2768 $c = new Categorie($this->db);
2769 $existing = $c->containing($this->id, Categorie::TYPE_TICKET, 'id');
2770
2771 // Diff
2772 if (is_array($existing)) {
2773 $to_del = array_diff($existing, $categories);
2774 $to_add = array_diff($categories, $existing);
2775 } else {
2776 $to_del = array(); // Nothing to delete
2777 $to_add = $categories;
2778 }
2779
2780 // Process
2781 foreach ($to_del as $del) {
2782 if ($c->fetch($del) > 0) {
2783 $c->del_type($this, Categorie::TYPE_TICKET);
2784 }
2785 }
2786 foreach ($to_add as $add) {
2787 if ($c->fetch($add) > 0) {
2788 $c->add_type($this, Categorie::TYPE_TICKET);
2789 }
2790 }
2791
2792 return 1;
2793 }
2794
2807 public function newMessage($user, &$action, $private = 1, $public_area = 0)
2808 {
2809 global $mysoc, $langs, $hookmanager;
2810
2811 $error = 0;
2812
2813 $object = new Ticket($this->db);
2814
2815 $ret = $object->fetch(0, '', GETPOST('track_id', 'alpha'));
2816
2817 $object->socid = $object->fk_soc;
2818 $object->fetch_thirdparty();
2819 $object->fetchProject();
2820
2821 if ($ret < 0) {
2822 $error++;
2823 array_push($this->errors, $langs->trans("ErrorTicketIsNotValid"));
2824 $action = '';
2825 }
2826
2827 if (!GETPOST("message")) {
2828 $error++;
2829 array_push($this->errors, $langs->trans("ErrorFieldRequired", $langs->transnoentities("Message")));
2830 $action = 'add_message';
2831 }
2832
2833 if (!$error) {
2834 $summary = mb_strcut(GETPOST('summary', 'alphanohtml'), 0, 42, 'UTF-8');
2835 $object->subject = GETPOST('subject', 'alphanohtml');
2836 $object->message = GETPOST("message", "restricthtml");
2837 $object->private = GETPOST("private_message", "alpha");
2838
2839 $send_email = (bool) GETPOSTINT('send_email');
2840
2841 // Copy attached files (saved into $_SESSION) as linked files to ticket. Return array with final name used.
2842 $resarray = $object->copyFilesForTicket();
2843 if (is_numeric($resarray) && $resarray == -1) {
2844 setEventMessages($object->error, $object->errors, 'errors');
2845 return -1;
2846 }
2847
2848 $listofpaths = $resarray['listofpaths'];
2849 $listofnames = $resarray['listofnames'];
2850 $listofmimes = $resarray['listofmimes'];
2851
2852 // Retrieve email of all contacts (external)
2853 $external_contacts = $object->getInfosTicketExternalContact(1);
2854 $external_resources = [];
2855 if (!empty($external_contacts)) {
2856 foreach ($external_contacts as $eContact) {
2857 $external_resources[$eContact['id']] = $eContact;
2858 }
2859 }
2860
2861 // Add the ticket message in database (even if email is requested, we store a simple record
2862 // like a simple private message, with no information about emails) because
2863 // information about emails sent will be fill later after email sending.
2864 $id = $object->createTicketMessage($user, 0, $listofpaths, $listofmimes, $listofnames, $send_email, $public_area, $summary, $external_resources);
2865
2866 if ($id <= 0) {
2867 $error++;
2868 $this->error = $object->error;
2869 $this->errors = $object->errors;
2870 $action = 'add_message';
2871 }
2872
2873 if (!$error && $id > 0) {
2874 setEventMessages($langs->trans('TicketMessageSuccessfullyAdded'), null, 'mesgs');
2875
2876 // Now send emails
2877 if (!empty($public_area)) {
2878 /*
2879 * Message created from the Public interface
2880 *
2881 * Send emails to assigned users (public area notification)
2882 */
2883 if (getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_ENABLED')) {
2884 // Retrieve internal contact datas
2885 $internal_contacts = $object->getInfosTicketInternalContact(1);
2886
2887 $assigned_user_dont_have_email = '';
2888
2889 $sendto = array();
2890
2891 if ($this->fk_user_assign > 0) {
2892 $assigned_user = new User($this->db);
2893 $assigned_user->fetch($this->fk_user_assign);
2894 if (!empty($assigned_user->email)) {
2895 $sendto[$assigned_user->email] = $assigned_user->getFullName($langs)." <".$assigned_user->email.">";
2896 } else {
2897 $assigned_user_dont_have_email = $assigned_user->getFullName($langs);
2898 }
2899 } else {
2900 $assigned_user = null;
2901 }
2902
2903 // Build array to display recipient list
2904 foreach ($internal_contacts as $key => $info_sendto) {
2905 // Avoid duplicate notifications
2906 if ($info_sendto['id'] == $user->id) {
2907 continue;
2908 }
2909
2910 // We check if the email address is not the assignee's address to prevent notification from being sent twice
2911 if (!empty($info_sendto['email']) && ($assigned_user === null || $assigned_user->email != $info_sendto['email'])) {
2912 $sendto[] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";
2913 }
2914 }
2915
2916 if (empty($sendto)) {
2917 if (getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL')) {
2918 $sendto[getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL')] = getDolGlobalString('TICKET_PUBLIC_NOTIFICATION_NEW_MESSAGE_DEFAULT_EMAIL');
2919 } elseif (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2920 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2921 }
2922 }
2923
2924 // Add global email address recipient
2925 if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {
2926 if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
2927 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
2928 }
2929 }
2930
2931 $parameters = array('sendto' => $sendto);
2932 $reshook = $hookmanager->executeHooks('updateSendtoTicketMessage', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
2933 if (empty($reshook)) {
2934 $sendto = array_merge($sendto, $hookmanager->resArray);
2935 } elseif ($reshook > 0) {
2936 $sendto = $hookmanager->resArray;
2937 }
2938
2939 if (!empty($sendto)) {
2940 $appli = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2941
2942 $subject = '['.$appli.'- ticket #'.$object->track_id.'] '.$this->subject;
2943
2944 // Message send
2945 $message = getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO', $langs->trans('TicketMessageMailIntroText'));
2946 $message .= '<br><br>';
2947 $messagePost = GETPOST('message', 'restricthtml');
2948 if (!dol_textishtml($messagePost)) {
2949 $messagePost = dol_nl2br($messagePost);
2950 }
2951 $message .= $messagePost;
2952
2953 // Customer company infos
2954 $message .= '<br><br>';
2955 $message .= "==============================================";
2956 $message .= !empty($object->thirdparty->name) ? '<br>'.$langs->trans('ThirdParty')." : ".$object->thirdparty->name : '';
2957 $message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';
2958 $message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';
2959
2960 // Email send to
2961 $message .= '<br><br>';
2962 if (!empty($assigned_user_dont_have_email)) {
2963 $message .= '<br>'.$langs->trans('NoEMail').' : '.$assigned_user_dont_have_email;
2964 }
2965 foreach ($sendto as $val) {
2966 $message .= '<br>'.$langs->trans('TicketNotificationRecipient').' : '.$val;
2967 }
2968
2969 // URL ticket
2970 $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;
2971 $message .= '<br><br>';
2972 $message .= $langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a>';
2973
2974 $email_from = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
2975
2976 $replyto = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_REPLYTO');
2977
2978 // don't try to send email if no recipient
2979 $this->sendTicketMessageByEmail($subject, $message, 0, $sendto, $listofpaths, $listofmimes, $listofnames, array(), $email_from, $replyto);
2980 }
2981 }
2982 } else {
2983 /*
2984 * Message created from the Backoffice / Private area
2985 *
2986 * 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)
2987 */
2988 if ((int) $send_email > 0) {
2989 // Retrieve internal contact datas
2990 $internal_contacts = $object->getInfosTicketInternalContact(1);
2991
2992 $sendto = array();
2993 if (is_array($internal_contacts) && count($internal_contacts) > 0) {
2994 // Set default subject
2995 $appli = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
2996
2997 $subject = GETPOST('subject', 'alphanohtml') ? GETPOST('subject', 'alphanohtml') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
2998
2999 $message_intro = $langs->trans('TicketNotificationEmailBody', "#".$object->id);
3000 $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');
3001
3002 $message = getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO', $langs->trans('TicketMessageMailIntroText'));
3003 $message .= '<br><br>';
3004 $messagePost = GETPOST('message', 'restricthtml');
3005 if (!dol_textishtml($messagePost)) {
3006 $messagePost = dol_nl2br($messagePost);
3007 }
3008 $message .= $messagePost;
3009
3010 // Data about customer
3011 $message .= '<br><br>';
3012 $message .= "==============================================<br>";
3013 $message .= !empty($object->thirdparty->name) ? $langs->trans('ThirdParty')." : ".$object->thirdparty->name : '';
3014 $message .= !empty($object->thirdparty->town) ? '<br>'.$langs->trans('Town')." : ".$object->thirdparty->town : '';
3015 $message .= !empty($object->thirdparty->phone) ? '<br>'.$langs->trans('Phone')." : ".$object->thirdparty->phone : '';
3016
3017 // Build array to display recipient list
3018 foreach ($internal_contacts as $key => $info_sendto) {
3019 // Check if recipient email is current user, if yes, we avoid to send email to him.
3020 if ($info_sendto['id'] == $user->id) {
3021 dol_syslog("We cancel sending email to internal user ".$info_sendto['email']." because it is current user", LOG_DEBUG);
3022 continue;
3023 }
3024
3025 if ($info_sendto['email'] != '') {
3026 $email = $info_sendto['email'];
3027 if ($email != null) {
3028 $sendto[$email] = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'])." <".$info_sendto['email'].">";
3029 }
3030
3031 // Contact type
3032 $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], -1).' ('.strtolower((string) $info_sendto['libelle']).')';
3033 $message .= (!empty($recipient) ? '<br>'.$langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');
3034 }
3035 }
3036 $message .= '<br>';
3037 // URL ticket
3038 $url_internal_ticket = dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id;
3039
3040 // Add html link on url
3041 $message .= '<br>'.$langs->trans('TicketNotificationEmailBodyInfosTrackUrlinternal').' : <a href="'.$url_internal_ticket.'">'.$object->track_id.'</a><br>';
3042
3043 // Add global email address recipient
3044 if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {
3045 if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
3046 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
3047 }
3048 }
3049
3050 $parameters = array('sendto' => $sendto);
3051 $reshook = $hookmanager->executeHooks('updateSendtoTicketMessage', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3052 if (empty($reshook)) {
3053 $sendto = array_merge($sendto, $hookmanager->resArray);
3054 } elseif ($reshook > 0) {
3055 $sendto = $hookmanager->resArray;
3056 }
3057
3058 $sendtocc = array();
3059 if (getDolGlobalString("TICKET_SEND_INTERNAL_CC")) {
3060 $sendtocc = explode(',', getDolGlobalString("TICKET_SEND_INTERNAL_CC"));
3061 }
3062
3063 $email_from = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
3064
3065 $replyto = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_REPLYTO');
3066
3067 // don't try to send email if no recipient
3068 if (!empty($sendto)) {
3069 $this->sendTicketMessageByEmail($subject, $message, 0, $sendto, $listofpaths, $listofmimes, $listofnames, $sendtocc, $email_from, $replyto);
3070 }
3071 }
3072
3073 /*
3074 * Send emails for externals users if not private (linked contacts)
3075 */
3076 if (empty($object->private)) {
3077 // If no contact, get email from thirdparty
3078 if (is_array($external_contacts) && count($external_contacts) === 0) {
3079 if (!empty($object->fk_soc)) {
3080 $object->fetch_thirdparty($object->fk_soc);
3081 $array_company = array(array('id' => -1, 'firstname' => '', 'lastname' => $object->thirdparty->name, 'email' => $object->thirdparty->email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => $object->thirdparty->id));
3082 $external_contacts = array_merge($external_contacts, $array_company);
3083 } elseif (empty($object->fk_soc) && !empty($object->origin_replyto)) {
3084 $array_external = array(array('id' => -1, 'firstname' => '', 'lastname' => $object->origin_replyto, 'email' => $object->origin_replyto, 'libelle' => $langs->transnoentities('Customer'), 'socid' => 0));
3085 $external_contacts = array_merge($external_contacts, $array_external);
3086 } elseif (empty($object->fk_soc) && !empty($object->origin_email)) {
3087 $array_external = array(array('id' => -1, 'firstname' => '', 'lastname' => $object->origin_email, 'email' => $object->origin_email, 'libelle' => $langs->transnoentities('Customer'), 'socid' => 0)); // no fk_soc here, so $object->thirdparty was never fetched (mirrors the origin_replyto branch above)
3088 $external_contacts = array_merge($external_contacts, $array_external);
3089 }
3090 }
3091
3092 $sendto = array();
3093 if (is_array($external_contacts) && count($external_contacts) > 0) {
3094 // Get default subject for email to external contacts
3095 $appli = getDolGlobalString('MAIN_APPLICATION_TITLE', $mysoc->name);
3096
3097 $subject = GETPOST('subject') ? GETPOST('subject') : '['.$appli.' - '.$langs->trans("Ticket").' #'.$object->track_id.'] '.$langs->trans('TicketNewMessage');
3098
3099 $message_intro = GETPOST('mail_intro') ? GETPOST('mail_intro', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_INTRO');
3100 $message_signature = GETPOST('mail_signature') ? GETPOST('mail_signature', 'restricthtml') : getDolGlobalString('TICKET_MESSAGE_MAIL_SIGNATURE');
3101 if (!dol_textishtml($message_intro)) {
3102 $message_intro = dol_nl2br($message_intro);
3103 }
3104 if (!dol_textishtml($message_signature)) {
3105 $message_signature = dol_nl2br($message_signature);
3106 }
3107
3108 // We put intro after
3109 $messagePost = GETPOST('message', 'restricthtml');
3110 if (!dol_textishtml($messagePost)) {
3111 $messagePost = dol_nl2br($messagePost);
3112 }
3113 $message = $messagePost;
3114 $message .= '<br><br>';
3115
3116 foreach ($external_contacts as $key => $info_sendto) {
3117 // Check if recipient email is current user, if yes, we avoid to send email to him.
3118 if ($info_sendto['id'] == $user->contact_id) {
3119 continue;
3120 }
3121
3122 if ($info_sendto['email'] != '' && $info_sendto['email'] != $object->origin_email) {
3123 $email = $info_sendto['email'];
3124 if ($email != null) {
3125 $sendto[$email] = trim($info_sendto['firstname']." ".$info_sendto['lastname'])." <".$info_sendto['email'].">";
3126 }
3127
3128 $recipient = dolGetFirstLastname($info_sendto['firstname'], $info_sendto['lastname'], -1).' ('.strtolower((string) $info_sendto['libelle']).')';
3129 $message .= (!empty($recipient) ? '<br>'.$langs->trans('TicketNotificationRecipient').' : '.$recipient.'<br>' : '');
3130 }
3131 }
3132
3133 // If public interface is not enable, use link to internal page into mail
3134 $url_public_ticket = (getDolGlobalInt('TICKET_ENABLE_PUBLIC_INTERFACE') ?
3135 (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='.urlencode($object->track_id);
3136
3137 if (getDolGlobalInt('TICKET_INCLUDE_LINK_TO_PUBLIC_INTERFACE_IN_MESSAGE')) {
3138 $message .= '<br>' . $langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer') . ' : <a href="' . $url_public_ticket . '">' . $object->track_id . '</a><br>';
3139 }
3140
3141 // Build final message
3142 $message = $message_intro.'<br><br>'.$message;
3143
3144 // Add signature
3145 $message .= '<br>'.$message_signature;
3146
3147 if (!empty($object->origin_replyto)) {
3148 $sendto[$object->origin_replyto] = $object->origin_replyto;
3149 } elseif (!empty($object->origin_email)) {
3150 $sendto[$object->origin_email] = $object->origin_email;
3151 }
3152
3153 if ($object->fk_soc > 0 && !array_key_exists($object->origin_replyto, $sendto) && !array_key_exists($object->origin_email, $sendto)) {
3154 $object->socid = $object->fk_soc;
3155 $object->fetch_thirdparty();
3156 if (!empty($object->thirdparty->email)) {
3157 $sendto[$object->thirdparty->email] = $object->thirdparty->email;
3158 }
3159 }
3160
3161 // Add global email address recipient
3162 if (getDolGlobalString('TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS') && !array_key_exists(getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO'), $sendto)) {
3163 if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')) {
3164 $sendto[getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO')] = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO');
3165 }
3166 }
3167
3168 $parameters = array('sendto' => $sendto);
3169 $reshook = $hookmanager->executeHooks('updateSendtoTicketMessage', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
3170 if (empty($reshook)) {
3171 $sendto = array_merge($sendto, $hookmanager->resArray);
3172 } elseif ($reshook > 0) {
3173 $sendto = $hookmanager->resArray;
3174 }
3175
3176 $sendtocc = array();
3177 if (getDolGlobalString("TICKET_SEND_INTERNAL_CC")) {
3178 $sendtocc = explode(',', getDolGlobalString("TICKET_SEND_INTERNAL_CC"));
3179 }
3180
3181 // Don't try to send email when no recipient
3182 if (!empty($sendto)) {
3183 $email_from = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
3184
3185 $replyto = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_REPLYTO');
3186
3187 $result = $this->sendTicketMessageByEmail($subject, $message, 0, $sendto, $listofpaths, $listofmimes, $listofnames, $sendtocc, $email_from, $replyto);
3188 if ($result) {
3189 // update last_msg_sent date of ticket (for last message sent to external users)
3190 $this->date_last_msg_sent = dol_now();
3191 $this->update($user, 1); // disable trigger when updating date_last_msg_sent. sendTicketMessageByEmail already create an event in actioncomm table.
3192
3193 // update event actioncomm $id
3194 //print 'update actioncomm id='.$id.' with sendto='.json_encode($sendto)." sendtocc=".json_encode($sendtocc)." email_msgid=".json_encode($this->email_msgid);
3195 $sql = "UPDATE ".MAIN_DB_PREFIX."actioncomm";
3196 $sql .= " SET email_msgid = '".$this->db->escape($this->email_msgid)."',";
3197 $sql .= " email_subject = '".$this->db->escape($subject)."',";
3198 $sql .= " email_from = '".$this->db->escape($email_from)."',";
3199 $sql .= " email_to = '".$this->db->escape(implode(',', $sendto))."',";
3200 $sql .= " email_tocc = '".$this->db->escape(implode(',', $sendtocc))."',";
3201 $sql .= " reply_to = '".$this->db->escape($replyto)."'";
3202 $sql .= " WHERE id = ".((int) $id);
3203
3204 $resql = $this->db->query($sql);
3205 }
3206 }
3207 }
3208 }
3209 }
3210 }
3211
3212 // Set status back to "In progress" if not set yet, but only if internal user and not a private message
3213 // Or set status to "In progress" if the client has answered and if the ticket has started
3214 // So we are sure to leave the STATUS_DRAFT, STATUS_NEED_INFO.
3215 // Except if TICKET_SET_STATUS_ON_ANSWER has been defined
3216 if ((getDolGlobalInt('TICKET_SET_STATUS_ON_ANSWER', -1) < 0
3217 && ($object->status < self::STATUS_IN_PROGRESS && !$user->socid && !$private))
3218 || ($object->status > self::STATUS_IN_PROGRESS && $public_area)) {
3219 // Set status
3220 $object->setStatut($object::STATUS_IN_PROGRESS, null, '', 'TICKET_MODIFY');
3221 } elseif (getDolGlobalInt('TICKET_SET_STATUS_ON_ANSWER', -1) >= 0 && empty($user->socid) && empty($private)) {
3222 // Set status
3223 $object->setStatut(getDolGlobalInt('TICKET_SET_STATUS_ON_ANSWER'), null, '', 'TICKET_MODIFY');
3224 }
3225
3226 return 1;
3227 } else {
3228 setEventMessages($object->error, $object->errors, 'errors');
3229 return -1;
3230 }
3231 } else {
3232 setEventMessages($this->error, $this->errors, 'errors');
3233 return -1;
3234 }
3235 }
3236
3237
3253 public function sendTicketMessageByEmail($subject, $message, $send_internal_cc = 0, $array_receiver = array(), $filename_list = array(), $mimetype_list = array(), $mimefilename_list = array(), $array_receiver_cc = array(), $email_from = '', $replyto = '')
3254 {
3255 global $conf, $langs, $user, $hookmanager;
3256
3257 if (getDolGlobalString('TICKET_DISABLE_ALL_MAILS')) {
3258 dol_syslog(get_class($this).'::sendTicketMessageByEmail: Emails are disable into ticket setup by option TICKET_DISABLE_ALL_MAILS', LOG_WARNING);
3259 return false;
3260 }
3261
3262 $langs->load("mails");
3263
3264 include_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
3265 //$contactstatic = new Contact($this->db);
3266
3267 // If no receiver defined, load all ticket linked contacts
3268 if (!is_array($array_receiver) || !count($array_receiver) > 0) {
3269 $array_receiver = $this->getInfosTicketInternalContact(1);
3270 $array_receiver = array_merge($array_receiver, $this->getInfosTicketExternalContact(1));
3271 }
3272
3273 dol_syslog("sendTicketMessageByEmail array_receiver=".json_encode($array_receiver), LOG_DEBUG);
3274 dol_syslog("sendTicketMessageByEmail array_receiver_cc=".json_encode($array_receiver_cc), LOG_DEBUG);
3275
3276 $sendtocc = '';
3277 if ($send_internal_cc) {
3278 $sendtocc = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
3279 }
3280 if (!empty($array_receiver_cc) && is_array($array_receiver_cc)) {
3281 $sendtocc .= ($sendtocc ? ',' : '').implode(',', $array_receiver_cc);
3282 }
3283
3284 if (empty($email_from)) {
3285 $email_from = getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM');
3286 }
3287
3288 $parameters = array('from' => $email_from);
3289 $action = '';
3290 $reshook = $hookmanager->executeHooks('getTicketMessageEmailFrom', $parameters, $this, $action);
3291 if ($reshook && !empty($hookmanager->resArray['from'])) {
3292 $email_from = $hookmanager->resArray['from'];
3293 }
3294
3295 $is_sent = false;
3296 $this->email_msgids = array();
3297
3298 if (is_array($array_receiver) && count($array_receiver) > 0) {
3299 //foreach ($array_receiver as $key => $receiver) {
3300 $deliveryreceipt = 0;
3301 $filepath = $filename_list;
3302 $filename = $mimefilename_list;
3303 $mimetype = $mimetype_list;
3304
3305 // Send email
3306
3307 $old_MAIN_MAIL_AUTOCOPY_TO = getDolGlobalString('MAIN_MAIL_AUTOCOPY_TO');
3308 if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
3309 $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
3310 }
3311
3312 $upload_dir_tmp = $conf->user->dir_output."/".$user->id.'/temp';
3313
3314 include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
3315 $trackid = "tic".$this->id;
3316
3317 $moreinheader = 'X-Dolibarr-Info: sendTicketMessageByEmail'."\r\n";
3318 if (!empty($this->email_msgid)) {
3319 // We must also add 1 entry In-Reply-To: <$this->email_msgid> with Message-ID we respond from (See RFC5322).
3320 $moreinheader .= 'In-Reply-To: <'.$this->email_msgid.'>'."\r\n";
3321 // TODO We should now be able to give the in_reply_to as a dedicated parameter of new CMailFile() instead of into $moreinheader.
3322 }
3323
3324 // We should add here also a header 'References:'
3325 // According to RFC5322, we should add here all the References fields of the initial message concatenated with
3326 // the Message-ID of the message we respond from (but each ID must be once).
3327 $references = '';
3328 if (!empty($this->origin_references)) { // $this->origin_references should be '<'.$this->origin_references.'>'
3329 $references .= (empty($references) ? '' : ' ').$this->origin_references;
3330 }
3331 if (!empty($this->email_msgid) && !preg_match('/'.preg_quote($this->email_msgid, '/').'/', $references)) {
3332 $references .= (empty($references) ? '' : ' ').'<'.$this->email_msgid.'>';
3333 }
3334 if ($references) {
3335 $moreinheader .= 'References: '.$references."\r\n";
3336 // TODO We should now be able to give the references as a dedicated parameter of new CMailFile() instead of into $moreinheader.
3337 }
3338
3339 $receiverstring = '';
3340 foreach ($array_receiver as $key => $receiver) {
3341 $receiverstring .= ($receiverstring ? ',' : '').$receiver;
3342 }
3343
3344 $sendcontext = 'ticket';
3345
3346 // Send email
3347 $mailfile = new CMailFile($subject, $receiverstring, $email_from, $message, $filepath, $mimetype, $filename, $sendtocc, '', $deliveryreceipt, -1, '', '', $trackid, $moreinheader, $sendcontext, $replyto, $upload_dir_tmp);
3348
3349
3350 if ($mailfile->error) {
3351 setEventMessages($mailfile->error, null, 'errors');
3352 } else {
3353 $result = $mailfile->sendfile();
3354
3355 if ($result) {
3356 setEventMessages($langs->trans('MailSuccessfulySent', $mailfile->getValidAddress($email_from, 2), $mailfile->getValidAddress($receiverstring, 2)), null, 'mesgs');
3357 $is_sent = true;
3358
3359 $this->email_msgid = $mailfile->msgid;
3360 } else {
3361 $langs->load("other");
3362 if ($mailfile->error) {
3363 setEventMessages($langs->trans('ErrorFailedToSendMail', $email_from, $receiverstring), null, 'errors');
3364 dol_syslog('ErrorFailedToSendMail from='.$email_from.' to='.$receiverstring.' : '.$mailfile->error);
3365 } elseif (getDolGlobalString('MAIN_DISABLE_ALL_MAILS')) {
3366 setEventMessages('No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS', null, 'errors');
3367 } else {
3368 setEventMessages($langs->trans('ErrorFailedToSendMail', $email_from, $receiverstring), null, 'errors');
3369 dol_syslog('ErrorFailedToSendMail from='.$email_from.' to='.$receiverstring.' : (no error details)', LOG_WARNING);
3370 }
3371 }
3372 }
3373
3374 if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
3375 $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
3376 }
3377 //}
3378 } else {
3379 $langs->load("other");
3380 setEventMessages($langs->trans('ErrorMailRecipientIsEmptyForSendTicketMessage'), null, 'warnings');
3381 }
3382
3383 return $is_sent;
3384 }
3385
3386 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3394 public function load_board($user, $mode)
3395 {
3396 // phpcs:enable
3397 global $user, $langs;
3398
3399 $now = dol_now();
3400 $delay_warning = 0;
3401
3402 $clause = " WHERE";
3403
3404 $sql = "SELECT p.rowid, p.ref, p.datec as datec";
3405 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
3406 if (empty($user->socid) && isModEnabled('societe') && !$user->hasRight('societe', 'client', 'voir') && !$user->socid) {
3407 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON p.fk_soc = sc.fk_soc";
3408 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3409 $clause = " AND";
3410 }
3411 $sql .= $clause." p.entity IN (".getEntity('ticket').")";
3412 if ($mode == 'opened') {
3413 $sql .= " AND p.fk_statut NOT IN (".Ticket::STATUS_CLOSED.", ".Ticket::STATUS_CANCELED.")";
3414 }
3415 if ($user->socid) {
3416 $sql .= " AND p.fk_soc = ".((int) $user->socid);
3417 }
3418
3419 $resql = $this->db->query($sql);
3420 if ($resql) {
3421 $label = $labelShort = '';
3422 $status = '';
3423 if ($mode == 'opened') {
3424 $status = 'openall';
3425 //$delay_warning = $conf->ticket->warning_delay;
3426 $delay_warning = 0;
3427 $label = $langs->trans("MenuListNonClosed");
3428 $labelShort = $langs->trans("MenuListNonClosed");
3429 }
3430
3431 $response = new WorkboardResponse();
3432 //$response->warning_delay = $delay_warning / 60 / 60 / 24;
3433 $response->label = $label;
3434 $response->labelShort = $labelShort;
3435 $response->url = DOL_URL_ROOT.'/ticket/list.php?search_fk_statut[]='.$status;
3436 $response->img = img_object('', "ticket");
3437
3438 // This assignment in condition is not a bug. It allows walking the results.
3439 while ($obj = $this->db->fetch_object($resql)) {
3440 $response->nbtodo++;
3441 if ($mode == 'opened') {
3442 $datelimit = (int) $this->db->jdate($obj->datec) + (int) $delay_warning;
3443 if ($datelimit < $now) {
3444 //$response->nbtodolate++;
3445 }
3446 }
3447 }
3448 return $response;
3449 } else {
3450 $this->error = $this->db->lasterror();
3451 return -1;
3452 }
3453 }
3454
3460 public function loadStateBoard()
3461 {
3462 global $user;
3463
3464 $this->nb = array();
3465 $clause = "WHERE";
3466
3467 $sql = "SELECT count(p.rowid) as nb";
3468 $sql .= " FROM ".MAIN_DB_PREFIX."ticket as p";
3469 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON p.fk_soc = s.rowid";
3470 if (empty($user->socid) && !$user->hasRight('societe', 'client', 'voir')) {
3471 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
3472 $sql .= " WHERE sc.fk_user = ".((int) $user->id);
3473 $clause = "AND";
3474 }
3475 $sql .= " ".$clause." p.entity IN (".getEntity('ticket').")";
3476
3477 $resql = $this->db->query($sql);
3478 if ($resql) {
3479 // This assignment in condition is not a bug. It allows walking the results.
3480 while ($obj = $this->db->fetch_object($resql)) {
3481 $this->nb["ticket"] = $obj->nb;
3482 }
3483 $this->db->free($resql);
3484 return 1;
3485 } else {
3486 dol_print_error($this->db);
3487 $this->error = $this->db->lasterror();
3488 return -1;
3489 }
3490 }
3491
3500 public static function replaceThirdparty($db, $origin_id, $dest_id)
3501 {
3502 $tables = array('ticket');
3503
3504 return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
3505 }
3506
3514 public function getKanbanView($option = '', $arraydata = null)
3515 {
3516 global $langs;
3517
3518 $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']);
3519
3520 $return = '<div class="box-flex-item box-flex-grow-zero">';
3521 $return .= '<div class="info-box info-box-sm">';
3522 $return .= '<span class="info-box-icon bg-infobox-action">';
3523 $return .= img_picto('', $this->picto);
3524 $return .= '</span>';
3525 $return .= '<div class="info-box-content">';
3526 $return .= '<span class="info-box-ref inline-block tdoverflowmax150 valignmiddle">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl(1) : $this->ref).'</span>';
3527 if ($selected >= 0) {
3528 $return .= '<input id="cb'.$this->id.'" class="flat checkforselect fright" type="checkbox" name="toselect[]" value="'.$this->id.'"'.($selected ? ' checked="checked"' : '').'>';
3529 }
3530 if (!empty($arraydata['user_assignment'])) {
3531 $return .= '<br><span class="info-box-label" title="'.dol_escape_htmltag($langs->trans("AssignedTo")).'">'.$arraydata['user_assignment'].'</span>';
3532 }
3533 if (property_exists($this, 'type_code') && !empty($this->type_code)) {
3534 $return .= '<br>';
3535 $return .= '<div class="tdoverflowmax125 inline-block">'.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$this->type_code, 'c_ticket_type', 'code', 'label', $this->type_code).'</div>';
3536 }
3537 if (method_exists($this, 'getLibStatut')) {
3538 $return .= '<br><div class="info-box-status">'.$this->getLibStatut(3).'</div>';
3539 }
3540 $return .= '</div>';
3541 $return .= '</div>';
3542 $return .= '</div>';
3543
3544 return $return;
3545 }
3546
3558 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
3559 {
3560 global $langs;
3561
3562 $langs->load("ticket");
3563 $outputlangs->load("ticket");
3564
3565 if (!dol_strlen($modele)) {
3566 $modele = 'generic_ticket_odt';
3567
3568 if (!empty($this->model_pdf)) {
3569 $modele = $this->model_pdf;
3570 } elseif (getDolGlobalString('TICKET_ADDON_PDF')) {
3571 $modele = getDolGlobalString('TICKET_ADDON_PDF');
3572 }
3573 }
3574
3575 $modelpath = "core/modules/ticket/doc/";
3576
3577 return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
3578 }
3579}
$id
Support class for third parties, contacts, members, users or resources.
Definition account.php:47
if(! $sortfield) if(! $sortorder) $object
Definition account.php:100
$c
Definition line.php:334
$object ref
Definition info.php:90
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.
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 Dolibarr database access.
Class to manage ECM files.
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 to manage a HTML form to send a unitary email Usage: $formail = new FormMail($db) $formmail->pr...
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.
static replaceThirdparty($db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
createTicketMessage($user, $notrigger=0, $filename_list=array(), $mimetype_list=array(), $mimefilename_list=array(), $send_email=false, $public_area=0, $summary='', $external_contacts=[])
Add message into database.
listeContact($statusoflink=-1, $source='external', $list=0, $code='', $status=-1)
Get array of all contacts for a ticket Override method of file commonobject.class....
loadCacheMsgsTicket()
Load the list of event on ticket into ->cache_msgs_ticket.
setProgression($percent)
Define progression of current ticket.
searchSocidByEmail($email, $type=0, $filters=array(), $clause='AND')
Search and fetch thirparties by email.
loadCacheSeveritiesTickets()
Load into cache the list of ticket severities (configurable in the dictionary)
$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.
setContract($contractid)
Link element with a contract.
setReadDate($user, $notrigger=0)
Set read date if not already set.
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 clickable 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.
const STATUS_NOT_READ
Status.
sendTicketMessageByEmail($subject, $message, $send_internal_cc=0, $array_receiver=array(), $filename_list=array(), $mimetype_list=array(), $mimefilename_list=array(), $array_receiver_cc=array(), $email_from='', $replyto='')
Send ticket by email to linked contacts.
getTicketAllCustomerContacts()
Return id of all contacts for ticket.
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).
getIdTicketCustomerContact()
Return id des contacts clients pour le suivi ticket.
checkExistingRef(string $action, string $getRef)
Check if ref exists or not.
__construct(DoliDB $db)
Constructor.
loadCacheTypesTickets()
Load into a cache the types of tickets (setup done into dictionaries)
getLibStatut($mode=0)
Return status label of object.
close(User $user, $mode=0)
Close a ticket.
getIdTicketInternalInvolvedContact()
Return id des contacts clients des intervenants.
copyFilesForTicket($forcetrackid=null)
Copy files defined into $_SESSION array into the ticket directory of attached files.
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.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:169
global $mysoc
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $db
API class for accounts.
dirbasename($pathfile)
Return the relative dirname (relative to DOL_DATA_ROOT) of a full path string.
dol_move($srcfile, $destfile, $newmask='0', $overwriteifexists=1, $testvirus=0, $indexdatabase=1, $moreinfo=array(), $entity=null)
Move a file into another name.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0, $level=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_is_file($pathoffile)
Return if path is a file.
dol_is_dir($folder)
Test if filename is a directory.
dol_now($mode='gmt')
Return date for now.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
setEntity($currentobject)
Set entity id to use when to create an object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
isDolTms($timestamp)
isDolTms check if a timestamp is valid.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
dol_strlen($string, $stringencoding='UTF-8')
Make a strlen call.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0, $forbiddenfields=array())
forgeSQLFromUniversalSearchCriteria
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
dol_textishtml($msg, $option=0)
Return if a text is a html content.
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_clone($srcobject, $native=2)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false, $decorate=0)
Output date in a string format according to outputlangs (or langs if not defined).
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 a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
utf8_check($str)
Check if a string is in UTF8.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_mkdir($dir, $dataroot='', $newmask='')
Creation of a directory (this can create recursive subdir)
vignette($file, $maxWidth=160, $maxHeight=120, $extName='_small', $quality=50, $outdir='thumbs', $targetformat=0)
Create a thumbnail from an image file (Supported extensions are gif, jpg, png and bmp).
image_format_supported($file, $acceptsvg=0)
Return if a filename is file name of a supported image format.
print $langs trans("Show") . '< td style="' . $timeColor . '" align="center"> s</td > badge status0 badge status4 badge status3 Error badge status8< td align="center">< span class="badge ' . $badge . '"></span ></td >< td align="center">< a href="#" class="button button-small" onclick="openLogModal(this)" data-req="' . dol_escape_htmltag($reqSafe) . '" data-res="' . dol_escape_htmltag($resSafe) . '" data-err="' . dol_escape_htmltag($errSafe) . '">< span class="fa fa-search-plus"></span ></a ></td ></tr >< tr >< td colspan="' . $colspan . '" class="opacitymedium"></td ></tr ></table ></div ></form > logModal none logModal none s a JSON string
buildzip.php
Class to generate the form for creating a new ticket.
generate_random_id($car=16)
Generate a random id.