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