dolibarr  21.0.0-alpha
interface_50_modTicket_TicketEmail.class.php
Go to the documentation of this file.
1 <?php
2 /*
3  * Copyright (C) 2014-2016 Jean-François Ferry <hello@librethic.io>
4  * 2016 Christophe Battarel <christophe@altairis.fr>
5  * Copyright (C) 2023 Benjamin Falière <benjamin.faliere@altairis.fr>
6  * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
27 require_once DOL_DOCUMENT_ROOT.'/core/triggers/dolibarrtriggers.class.php';
28 
29 
34 {
40  public function __construct($db)
41  {
42  $this->db = $db;
43 
44  $this->name = preg_replace('/^Interface/i', '', get_class($this));
45  $this->family = "ticket";
46  $this->description = "Triggers of the module ticket to send notifications to internal users and to third-parties";
47  $this->version = self::VERSIONS['prod'];
48  $this->picto = 'ticket';
49  }
50 
62  public function runTrigger($action, $object, User $user, Translate $langs, Conf $conf)
63  {
64  global $mysoc;
65 
66  $ok = 0;
67 
68  if (empty($conf->ticket) || !isModEnabled('ticket')) {
69  return 0; // Module not active, we do nothing
70  }
71 
72  switch ($action) {
73  case 'TICKET_ASSIGNED':
74  dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
75 
76  if ($object->fk_user_assign > 0) {
77  if ($object->fk_user_assign != $user->id) {
78  $userstat = new User($this->db);
79  $res = $userstat->fetch($object->fk_user_assign);
80  if ($res > 0) {
81  // Send email to notification email
82  if (!getDolGlobalString('TICKET_DISABLE_ALL_MAILS')) {
83  // Init to avoid errors
84  $filepath = array();
85  $filename = array();
86  $mimetype = array();
87 
88  $appli = $mysoc->name;
89 
90  // Send email to assigned user
91  $subject = '['.$appli.'] '.$langs->transnoentities('TicketAssignedToYou');
92  $message = '<p>'.$langs->transnoentities('TicketAssignedEmailBody', $object->track_id, dolGetFirstLastname($user->firstname, $user->lastname))."</p>";
93  $message .= '<ul><li>'.$langs->trans('Title').' : '.$object->subject.'</li>';
94  $message .= '<li>'.$langs->trans('Type').' : '.$object->type_label.'</li>';
95  $message .= '<li>'.$langs->trans('Category').' : '.$object->category_label.'</li>';
96  $message .= '<li>'.$langs->trans('Severity').' : '.$object->severity_label.'</li>';
97  // Extrafields
98  if (is_array($object->array_options) && count($object->array_options) > 0) {
99  foreach ($object->array_options as $key => $value) {
100  $message .= '<li>'.$langs->trans($key).' : '.$value.'</li>';
101  }
102  }
103 
104  $message .= '</ul>';
105  $message .= '<p>'.$langs->trans('Message').' : <br>'.$object->message.'</p>';
106  $message .= '<p><a href="'.dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id.'">'.$langs->trans('SeeThisTicketIntomanagementInterface').'</a></p>';
107 
108  $sendto = $userstat->email;
109  $from = dolGetFirstLastname($user->firstname, $user->lastname).'<'.$user->email.'>';
110 
111  $message = dol_nl2br($message);
112 
113  if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
114  $old_MAIN_MAIL_AUTOCOPY_TO = getDolGlobalString('MAIN_MAIL_AUTOCOPY_TO');
115  $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
116  }
117  include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
118  $mailfile = new CMailFile($subject, $sendto, $from, $message, $filepath, $mimetype, $filename, '', '', 0, -1);
119  if ($mailfile->error) {
120  setEventMessages($mailfile->error, $mailfile->errors, 'errors');
121  } else {
122  $result = $mailfile->sendfile();
123  }
124  if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
125  $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
126  }
127  }
128  } else {
129  $this->error = $userstat->error;
130  $this->errors = $userstat->errors;
131  }
132  }
133 
134  // Send an email to the Customer to inform him that his ticket has been taken in charge.
135  if (getDolGlobalString('TICKET_NOTIFY_CUSTOMER_TICKET_ASSIGNED') && empty($object->oldcopy->fk_user_assign)) {
136  $langs->load('ticket');
137 
138  $subject_customer = 'TicketAssignedCustomerEmail';
139  $body_customer = 'TicketAssignedCustomerBody';
140  $see_ticket_customer = 'TicketNewEmailBodyInfosTrackUrlCustomer';
141 
142  // Get all external contacts linked to the ticket
143  $linked_contacts = $object->listeContact(-1, 'thirdparty');
144 
145  // Initialize and fill recipient addresses at least with origin_email
146  $sendto = '';
147  $temp_emails = [];
148  if ($object->origin_email) {
149  $temp_emails[] = $object->origin_email;
150  }
151 
152  if (!empty($linked_contacts)) {
153  foreach ($linked_contacts as $contact) {
154  // Avoid the email from being sent twice in case of duplicated contact
155  if (!in_array($contact['email'], $temp_emails)) {
156  $temp_emails[] = $contact['email'];
157  }
158  }
159  }
160 
161  $sendto = implode(", ", $temp_emails);
162  unset($temp_emails);
163  unset($linked_contacts);
164 
165  // If recipients, we send the email
166  if ($sendto) {
167  $this->composeAndSendCustomerMessage($sendto, $subject_customer, $body_customer, $see_ticket_customer, $object, $langs);
168  }
169  }
170  $ok = 1;
171  }
172  break;
173 
174  case 'TICKET_CREATE':
175  dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
176 
177  $langs->load('ticket');
178 
179  $subject_admin = 'TicketNewEmailSubjectAdmin';
180  $body_admin = 'TicketNewEmailBodyAdmin';
181  $subject_customer = 'TicketNewEmailSubjectCustomer';
182  $body_customer = 'TicketNewEmailBodyCustomer';
183  $see_ticket_customer = 'TicketNewEmailBodyInfosTrackUrlCustomer';
184 
185  // Send email to notification email
186  if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO') && empty($object->context['disableticketemail'])) {
187  $sendto = !getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO') ? '' : $conf->global->TICKET_NOTIFICATION_EMAIL_TO;
188  if ($sendto) {
189  $this->composeAndSendAdminMessage($sendto, $subject_admin, $body_admin, $object, $langs);
190  }
191  }
192 
193  // Send email to customer
194  if (!getDolGlobalString('TICKET_DISABLE_CUSTOMER_MAILS') && empty($object->context['disableticketemail']) && $object->notify_tiers_at_create) {
195  $sendto = '';
196 
197  // if contact selected send to email's contact else send to email's thirdparty
198 
199  $contactid = empty($object->context['contactid']) ? 0 : $object->context['contactid'];
200  $res = 0;
201 
202  if (!empty($contactid)) {
203  $contact = new Contact($this->db);
204  $res = $contact->fetch($contactid);
205  }
206 
207  if ($res > 0 && !empty($contact->email) && !empty($contact->statut)) {
208  $sendto = $contact->email;
209  } elseif (!empty($object->fk_soc)) {
210  $object->fetch_thirdparty();
211  $sendto = $object->thirdparty->email;
212  }
213 
214  if ($sendto) {
215  $this->composeAndSendCustomerMessage($sendto, $subject_customer, $body_customer, $see_ticket_customer, $object, $langs);
216  }
217  }
218 
219  $ok = 1;
220  break;
221 
222  case 'TICKET_DELETE':
223  dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
224  break;
225 
226  case 'TICKET_MODIFY':
227  dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
228  break;
229 
230  case 'TICKET_CLOSE':
231  dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
232  $langs->load('ticket');
233 
234  $subject_admin = 'TicketCloseEmailSubjectAdmin';
235  $body_admin = 'TicketCloseEmailBodyAdmin';
236  $subject_customer = 'TicketCloseEmailSubjectCustomer';
237  $body_customer = 'TicketCloseEmailBodyCustomer';
238  $see_ticket_customer = 'TicketCloseEmailBodyInfosTrackUrlCustomer';
239 
240  // Send email to notification email
241  if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO') && empty($object->context['disableticketemail'])) {
242  $sendto = !getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO') ? '' : $conf->global->TICKET_NOTIFICATION_EMAIL_TO;
243  if ($sendto) {
244  $this->composeAndSendAdminMessage($sendto, $subject_admin, $body_admin, $object, $langs);
245  }
246  }
247 
248  // Send email to customer.
249  if (!getDolGlobalString('TICKET_DISABLE_CUSTOMER_MAILS') && empty($object->context['disableticketemail'])) {
250  $linked_contacts = $object->listeContact(-1, 'thirdparty');
251  $linked_contacts = array_merge($linked_contacts, $object->listeContact(-1, 'internal'));
252  if (empty($linked_contacts) && getDolGlobalString('TICKET_NOTIFY_AT_CLOSING') && !empty($object->fk_soc)) {
253  $object->fetch_thirdparty();
254  $linked_contacts[]['email'] = $object->thirdparty->email;
255  }
256 
257  $contactid = empty($object->context['contactid']) ? 0 : $object->context['contactid'];
258  $res = 0;
259 
260  if ($contactid > 0) {
261  // TODO This security test has no sens. We must check that $contactid is inside $linked_contacts[]['id'] when $linked_contacts[]['source'] = 'external' or 'thirdparty'
262  // Refuse email if not
263  $contact = new Contact($this->db);
264  $res = $contact->fetch($contactid);
265  if (! in_array($contact, $linked_contacts)) {
266  $error_msg = $langs->trans('Error'). ': ';
267  $error_msg .= $langs->transnoentities('TicketWrongContact');
268  setEventMessages($error_msg, [], 'errors');
269  $ok = 0;
270  break;
271  }
272  }
273 
274  $sendto = '';
275  if ($res > 0 && !empty($contact->email) && !empty($contact->statut)) {
276  $sendto = $contact->email;
277  } elseif (!empty($linked_contacts) && ($contactid == -2 || (GETPOST('massaction', 'alpha') == 'close' && GETPOST('confirm', 'alpha') == 'yes'))) {
278  // if sending to all contacts or sending to contacts while mass closing
279  $temp_emails = [];
280  foreach ($linked_contacts as $contact) {
281  $temp_emails[] = $contact['email'];
282  }
283  $sendto = implode(", ", $temp_emails);
284  unset($temp_emails);
285  unset($linked_contacts);
286  }
287  if ($sendto) {
288  $this->composeAndSendCustomerMessage($sendto, $subject_customer, $body_customer, $see_ticket_customer, $object, $langs);
289  }
290  }
291  $ok = 1;
292  break;
293  }
294 
295  return $ok;
296  }
297 
308  private function composeAndSendAdminMessage($sendto, $base_subject, $body, Ticket $object, Translate $langs)
309  {
310  global $conf, $mysoc;
311 
312  // Init to avoid errors
313  $filepath = array();
314  $filename = array();
315  $mimetype = array();
316 
317  $appli = $mysoc->name;
318 
319  /* Send email to admin */
320  $subject = '['.$appli.'] '.$langs->transnoentities($base_subject, $object->ref, $object->track_id);
321  $message_admin = $langs->transnoentities($body, $object->track_id).'<br>';
322  $message_admin .= '<ul><li>'.$langs->trans('Title').' : '.$object->subject.'</li>';
323  $message_admin .= '<li>'.$langs->trans('Type').' : '.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$object->type_code, 'c_ticket_type', 'code', 'label', $object->type_code).'</li>';
324  $message_admin .= '<li>'.$langs->trans('TicketCategory').' : '.$langs->getLabelFromKey($this->db, 'TicketCategoryShort'.$object->category_code, 'c_ticket_category', 'code', 'label', $object->category_code).'</li>';
325  $message_admin .= '<li>'.$langs->trans('Severity').' : '.$langs->getLabelFromKey($this->db, 'TicketSeverityShort'.$object->severity_code, 'c_ticket_severity', 'code', 'label', $object->severity_code).'</li>';
326  $message_admin .= '<li>'.$langs->trans('From').' : '.($object->email_from ? $object->email_from : ($object->fk_user_create > 0 ? $langs->trans('Internal') : '')).'</li>';
327  // Extrafields
328  $extraFields = new ExtraFields($this->db);
329  $extraFields->fetch_name_optionals_label($object->table_element);
330  if (is_array($object->array_options) && count($object->array_options) > 0) {
331  foreach ($object->array_options as $key => $value) {
332  $key = substr($key, 8); // remove "options_"
333  $message_admin .= '<li>'.$langs->trans($extraFields->attributes[$object->element]['label'][$key]).' : '.$extraFields->showOutputField($key, $value, '', $object->table_element).'</li>';
334  }
335  }
336  if ($object->fk_soc > 0) {
337  $object->fetch_thirdparty();
338  $message_admin .= '<li>'.$langs->trans('Company').' : '.$object->thirdparty->name.'</li>';
339  }
340  $message_admin .= '</ul>';
341 
342  $message = $object->message;
343  if (!dol_textishtml($message)) {
344  $message = dol_nl2br($message);
345  }
346  $message_admin .= '<p>'.$langs->trans('Message').' : <br><br>'.$message.'</p><br>';
347  $message_admin .= '<p><a href="'.dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id.'">'.$langs->trans('SeeThisTicketIntomanagementInterface').'</a></p>';
348 
349  $from = getDolGlobalString('MAIN_INFO_SOCIETE_NOM') . '<' . getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM').'>';
350 
351  $trackid = 'tic'.$object->id;
352 
353  if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
354  $old_MAIN_MAIL_AUTOCOPY_TO = getDolGlobalString('MAIN_MAIL_AUTOCOPY_TO');
355  $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
356  }
357  include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
358  $mailfile = new CMailFile($subject, $sendto, $from, $message_admin, $filepath, $mimetype, $filename, '', '', 0, -1, '', '', $trackid, '', 'ticket');
359  if ($mailfile->error) {
360  dol_syslog($mailfile->error, LOG_DEBUG);
361  } else {
362  $result = $mailfile->sendfile();
363  }
364  if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
365  $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
366  }
367  }
368 
380  private function composeAndSendCustomerMessage($sendto, $base_subject, $body, $see_ticket, Ticket $object, Translate $langs)
381  {
382  global $conf, $extrafields, $mysoc, $user;
383 
384  // Init to avoid errors
385  $filepath = array();
386  $filename = array();
387  $mimetype = array();
388 
389  $appli = $mysoc->name;
390 
391  $subject = '['.$appli.'] '.$langs->transnoentities($base_subject);
392  $message_customer = $langs->transnoentities($body, $object->track_id).'<br>';
393  $message_customer .= '<ul><li>'.$langs->trans('Title').' : '.$object->subject.'</li>';
394  $message_customer .= '<li>'.$langs->trans('Type').' : '.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$object->type_code, 'c_ticket_type', 'code', 'label', $object->type_code).'</li>';
395  $message_customer .= '<li>'.$langs->trans('TicketCategory').' : '.$langs->getLabelFromKey($this->db, 'TicketCategoryShort'.$object->category_code, 'c_ticket_category', 'code', 'label', $object->category_code).'</li>';
396  $message_customer .= '<li>'.$langs->trans('Severity').' : '.$langs->getLabelFromKey($this->db, 'TicketSeverityShort'.$object->severity_code, 'c_ticket_severity', 'code', 'label', $object->severity_code).'</li>';
397 
398  // Extrafields
399  if (is_array($extrafields->attributes[$object->table_element]['label'])) {
400  foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $value) {
401  $enabled = 1;
402  if ($enabled && isset($extrafields->attributes[$object->table_element]['list'][$key])) {
403  $enabled = (int) dol_eval($extrafields->attributes[$object->table_element]['list'][$key], 1);
404  }
405  $perms = 1;
406  if ($perms && isset($extrafields->attributes[$object->table_element]['perms'][$key])) {
407  $perms = (int) dol_eval($extrafields->attributes[$object->table_element]['perms'][$key], 1);
408  }
409 
410  $qualified = true;
411  if (empty($enabled)) {
412  $qualified = false;
413  }
414  if (empty($perms)) {
415  $qualified = false;
416  }
417 
418  if ($qualified) {
419  $message_customer .= '<li>' . $langs->trans($key) . ' : ' . $value . '</li>';
420  }
421  }
422  }
423 
424  $message_customer .= '</ul>';
425 
426  $message = $object->message;
427  if (!dol_textishtml($message)) {
428  $message = dol_nl2br($message);
429  }
430  $message_customer .= '<p>'.$langs->trans('Message').' : <br><br>'.$message.'</p><br>';
431 
432  $url_public_ticket = getDolGlobalString('TICKET_URL_PUBLIC_INTERFACE', dol_buildpath('/public/ticket/', 2)).'view.php?track_id='.$object->track_id;
433 
434  $message_customer .= '<p>'.$langs->trans($see_ticket).' : <a href="'.$url_public_ticket.'">'.$url_public_ticket.'</a></p>';
435  $message_customer .= '<p>'.$langs->trans('TicketEmailPleaseDoNotReplyToThisEmail').'</p>';
436 
437  $from = (!getDolGlobalString('MAIN_INFO_SOCIETE_NOM') ? '' : getDolGlobalString('MAIN_INFO_SOCIETE_NOM') . ' ').'<' . getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM').'>';
438 
439  $trackid = 'tic'.$object->id;
440 
441  $old_MAIN_MAIL_AUTOCOPY_TO = getDolGlobalString('MAIN_MAIL_AUTOCOPY_TO');
442 
443  if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
444  $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
445  }
446 
447  include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
448  $mailfile = new CMailFile($subject, $sendto, $from, $message_customer, $filepath, $mimetype, $filename, '', '', 0, -1, '', '', $trackid, '', 'ticket');
449  if ($mailfile->error) {
450  dol_syslog($mailfile->error, LOG_DEBUG);
451  } else {
452  $result = $mailfile->sendfile();
453  if ($result) {
454  // update last_msg_sent date
455  $object->fetch($object->id);
456  $object->date_last_msg_sent = dol_now();
457  $object->update($user);
458  }
459  }
460  if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
461  $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
462  }
463  }
464 }
if($user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition: card.php:58
Class to send emails (with attachments or not) Usage: $mailfile = new CMailFile($subject,...
Class to stock current configuration.
Definition: conf.class.php:34
Class to manage contact/addresses.
Class that all triggers must inherit.
Class to manage standard extra fields.
Class of triggers for ticket module.
composeAndSendCustomerMessage($sendto, $base_subject, $body, $see_ticket, Ticket $object, Translate $langs)
Composes and sends a message concerning a ticket, to be sent to customer addresses.
runTrigger($action, $object, User $user, Translate $langs, Conf $conf)
Function called when a Dolibarr business event is done.
composeAndSendAdminMessage($sendto, $base_subject, $body, Ticket $object, Translate $langs)
Composes and sends a message concerning a ticket, to be sent to admin address.
Class to manage translations.
Class to manage Dolibarr users.
Definition: user.class.php:50
print *****$script_file(".$version.") pid cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
dol_now($mode='auto')
Return date for now.
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
dol_textishtml($msg, $option=0)
Return if a text is a html content.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
Class to generate the form for creating a new ticket.
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition: repair.php:142