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
26require_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.
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.
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after 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.
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