interface_50_modTicket_TicketEmail.class.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  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. /**
  21. * \file htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php
  22. * \ingroup core
  23. * \brief File of trigger for ticket module
  24. */
  25. require_once DOL_DOCUMENT_ROOT.'/core/triggers/dolibarrtriggers.class.php';
  26. /**
  27. * Class of triggers for ticket module
  28. */
  29. class InterfaceTicketEmail extends DolibarrTriggers
  30. {
  31. /**
  32. * Constructor
  33. *
  34. * @param DoliDB $db Database handler
  35. */
  36. public function __construct($db)
  37. {
  38. $this->db = $db;
  39. $this->name = preg_replace('/^Interface/i', '', get_class($this));
  40. $this->family = "ticket";
  41. $this->description = "Triggers of the module ticket to send notifications to internal users and to third-parties";
  42. $this->version = self::VERSION_DOLIBARR; // 'development', 'experimental', 'dolibarr' or version
  43. $this->picto = 'ticket';
  44. }
  45. /**
  46. * Function called when a Dolibarrr business event is done.
  47. * All functions "runTrigger" are triggered if file is inside directory htdocs/core/triggers
  48. *
  49. * @param string $action Event action code
  50. * @param Ticket $object Object
  51. * @param User $user Object user
  52. * @param Translate $langs Object langs
  53. * @param conf $conf Object conf
  54. * @return int Return integer <0 if KO, 0 if no triggered ran, >0 if OK
  55. */
  56. public function runTrigger($action, $object, User $user, Translate $langs, Conf $conf)
  57. {
  58. global $mysoc;
  59. $ok = 0;
  60. if (empty($conf->ticket) || !isModEnabled('ticket')) {
  61. return 0; // Module not active, we do nothing
  62. }
  63. switch ($action) {
  64. case 'TICKET_ASSIGNED':
  65. dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
  66. if ($object->fk_user_assign > 0) {
  67. if ($object->fk_user_assign != $user->id) {
  68. $userstat = new User($this->db);
  69. $res = $userstat->fetch($object->fk_user_assign);
  70. if ($res > 0) {
  71. // Send email to notification email
  72. if (!getDolGlobalString('TICKET_DISABLE_ALL_MAILS')) {
  73. // Init to avoid errors
  74. $filepath = array();
  75. $filename = array();
  76. $mimetype = array();
  77. $appli = $mysoc->name;
  78. // Send email to assigned user
  79. $subject = '['.$appli.'] '.$langs->transnoentities('TicketAssignedToYou');
  80. $message = '<p>'.$langs->transnoentities('TicketAssignedEmailBody', $object->track_id, dolGetFirstLastname($user->firstname, $user->lastname))."</p>";
  81. $message .= '<ul><li>'.$langs->trans('Title').' : '.$object->subject.'</li>';
  82. $message .= '<li>'.$langs->trans('Type').' : '.$object->type_label.'</li>';
  83. $message .= '<li>'.$langs->trans('Category').' : '.$object->category_label.'</li>';
  84. $message .= '<li>'.$langs->trans('Severity').' : '.$object->severity_label.'</li>';
  85. // Extrafields
  86. if (is_array($object->array_options) && count($object->array_options) > 0) {
  87. foreach ($object->array_options as $key => $value) {
  88. $message .= '<li>'.$langs->trans($key).' : '.$value.'</li>';
  89. }
  90. }
  91. $message .= '</ul>';
  92. $message .= '<p>'.$langs->trans('Message').' : <br>'.$object->message.'</p>';
  93. $message .= '<p><a href="'.dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id.'">'.$langs->trans('SeeThisTicketIntomanagementInterface').'</a></p>';
  94. $sendto = $userstat->email;
  95. $from = dolGetFirstLastname($user->firstname, $user->lastname).'<'.$user->email.'>';
  96. $message = dol_nl2br($message);
  97. if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
  98. $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO;
  99. $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
  100. }
  101. include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
  102. $mailfile = new CMailFile($subject, $sendto, $from, $message, $filepath, $mimetype, $filename, '', '', 0, -1);
  103. if ($mailfile->error) {
  104. setEventMessages($mailfile->error, $mailfile->errors, 'errors');
  105. } else {
  106. $result = $mailfile->sendfile();
  107. }
  108. if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
  109. $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
  110. }
  111. }
  112. } else {
  113. $this->error = $userstat->error;
  114. $this->errors = $userstat->errors;
  115. }
  116. }
  117. // Send an email to the Customer to inform him that his ticket has been taken in charge.
  118. if (getDolGlobalString('TICKET_NOTIFY_CUSTOMER_TICKET_ASSIGNED') && empty($object->oldcopy->fk_user_assign)) {
  119. $langs->load('ticket');
  120. $subject_customer = 'TicketAssignedCustomerEmail';
  121. $body_customer = 'TicketAssignedCustomerBody';
  122. $see_ticket_customer = 'TicketNewEmailBodyInfosTrackUrlCustomer';
  123. // Get all external contacts linked to the ticket
  124. $linked_contacts = $object->listeContact(-1, 'thirdparty');
  125. // Initialize and fill recipient addresses at least with origin_email
  126. $sendto = '';
  127. $temp_emails = [];
  128. if ($object->origin_email) {
  129. $temp_emails[] = $object->origin_email;
  130. }
  131. if (!empty($linked_contacts)) {
  132. foreach ($linked_contacts as $contact) {
  133. // Avoid the email from being sent twice in case of duplicated contact
  134. if (!in_array($contact['email'], $temp_emails)) {
  135. $temp_emails[] = $contact['email'];
  136. }
  137. }
  138. }
  139. $sendto = implode(", ", $temp_emails);
  140. unset($temp_emails);
  141. unset($linked_contacts);
  142. // If recipients, we send the email
  143. if ($sendto) {
  144. $this->composeAndSendCustomerMessage($sendto, $subject_customer, $body_customer, $see_ticket_customer, $object, $langs);
  145. }
  146. }
  147. $ok = 1;
  148. }
  149. break;
  150. case 'TICKET_CREATE':
  151. dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
  152. $langs->load('ticket');
  153. $subject_admin = 'TicketNewEmailSubjectAdmin';
  154. $body_admin = 'TicketNewEmailBodyAdmin';
  155. $subject_customer = 'TicketNewEmailSubjectCustomer';
  156. $body_customer = 'TicketNewEmailBodyCustomer';
  157. $see_ticket_customer = 'TicketNewEmailBodyInfosTrackUrlCustomer';
  158. // Send email to notification email
  159. if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO') && empty($object->context['disableticketemail'])) {
  160. $sendto = !getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO') ? '' : $conf->global->TICKET_NOTIFICATION_EMAIL_TO;
  161. if ($sendto) {
  162. $this->composeAndSendAdminMessage($sendto, $subject_admin, $body_admin, $object, $langs);
  163. }
  164. }
  165. // Send email to customer
  166. if (!getDolGlobalString('TICKET_DISABLE_CUSTOMER_MAILS') && empty($object->context['disableticketemail']) && $object->notify_tiers_at_create) {
  167. $sendto = '';
  168. //if contact selected send to email's contact else send to email's thirdparty
  169. $contactid = GETPOST('contactid', 'alpha');
  170. $res = 0;
  171. if (!empty($contactid)) {
  172. $contact = new Contact($this->db);
  173. $res = $contact->fetch($contactid);
  174. }
  175. if ($res > 0 && !empty($contact->email) && !empty($contact->statut)) {
  176. $sendto = $contact->email;
  177. } elseif (!empty($object->fk_soc)) {
  178. $object->fetch_thirdparty();
  179. $sendto = $object->thirdparty->email;
  180. }
  181. if ($sendto) {
  182. $this->composeAndSendCustomerMessage($sendto, $subject_customer, $body_customer, $see_ticket_customer, $object, $langs);
  183. }
  184. }
  185. $ok = 1;
  186. break;
  187. case 'TICKET_DELETE':
  188. dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
  189. break;
  190. case 'TICKET_MODIFY':
  191. dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
  192. break;
  193. case 'TICKET_CLOSE':
  194. dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id);
  195. $langs->load('ticket');
  196. $subject_admin = 'TicketCloseEmailSubjectAdmin';
  197. $body_admin = 'TicketCloseEmailBodyAdmin';
  198. $subject_customer = 'TicketCloseEmailSubjectCustomer';
  199. $body_customer = 'TicketCloseEmailBodyCustomer';
  200. $see_ticket_customer = 'TicketCloseEmailBodyInfosTrackUrlCustomer';
  201. // Send email to notification email
  202. if (getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO') && empty($object->context['disableticketemail'])) {
  203. $sendto = !getDolGlobalString('TICKET_NOTIFICATION_EMAIL_TO') ? '' : $conf->global->TICKET_NOTIFICATION_EMAIL_TO;
  204. if ($sendto) {
  205. $this->composeAndSendAdminMessage($sendto, $subject_admin, $body_admin, $object, $langs);
  206. }
  207. }
  208. // Send email to customer.
  209. if (!getDolGlobalString('TICKET_DISABLE_CUSTOMER_MAILS') && empty($object->context['disableticketemail'])) {
  210. $linked_contacts = $object->listeContact(-1, 'thirdparty');
  211. $linked_contacts = array_merge($linked_contacts, $object->listeContact(-1, 'internal'));
  212. if (empty($linked_contacts) && getDolGlobalString('TICKET_NOTIFY_AT_CLOSING') && !empty($object->fk_soc)) {
  213. $object->fetch_thirdparty();
  214. $linked_contacts[]['email'] = $object->thirdparty->email;
  215. }
  216. $contactid = GETPOST('contactid', 'int');
  217. $res = 0;
  218. if ($contactid > 0) {
  219. // TODO This security test has no sens. We must check that $contactid is inside $linked_contacts[]['id'] when $linked_contacts[]['source'] = 'external' or 'thirdparty'
  220. // Refuse email if not
  221. $contact = new Contact($this->db);
  222. $res = $contact->fetch($contactid);
  223. if (! in_array($contact, $linked_contacts)) {
  224. $error_msg = $langs->trans('Error'). ': ';
  225. $error_msg .= $langs->transnoentities('TicketWrongContact');
  226. setEventMessages($error_msg, [], 'errors');
  227. $ok = 0;
  228. break;
  229. }
  230. }
  231. $sendto = '';
  232. if ($res > 0 && !empty($contact->email) && !empty($contact->statut)) {
  233. $sendto = $contact->email;
  234. } elseif (!empty($linked_contacts) && ($contactid == -2 || (GETPOST('massaction', 'alpha') == 'close' && GETPOST('confirm', 'alpha') == 'yes'))) {
  235. // if sending to all contacts or sending to contacts while mass closing
  236. $temp_emails = [];
  237. foreach ($linked_contacts as $contact) {
  238. $temp_emails[] = $contact['email'];
  239. }
  240. $sendto = implode(", ", $temp_emails);
  241. unset($temp_emails);
  242. unset($linked_contacts);
  243. }
  244. if ($sendto) {
  245. $this->composeAndSendCustomerMessage($sendto, $subject_customer, $body_customer, $see_ticket_customer, $object, $langs);
  246. }
  247. }
  248. $ok = 1;
  249. break;
  250. }
  251. return $ok;
  252. }
  253. /**
  254. * Composes and sends a message concerning a ticket, to be sent to admin address.
  255. *
  256. * @param string $sendto Addresses to send the mail, format "first@address.net, second@address.net," etc.
  257. * @param string $base_subject email subject. Non-translated string.
  258. * @param string $body email body (first line). Non-translated string.
  259. * @param Ticket $object the ticket thet the email refers to
  260. * @param Translate $langs the translation object
  261. * @return void
  262. */
  263. private function composeAndSendAdminMessage($sendto, $base_subject, $body, Ticket $object, Translate $langs)
  264. {
  265. global $conf, $mysoc;
  266. // Init to avoid errors
  267. $filepath = array();
  268. $filename = array();
  269. $mimetype = array();
  270. $appli = $mysoc->name;
  271. /* Send email to admin */
  272. $subject = '['.$appli.'] '.$langs->transnoentities($base_subject, $object->ref, $object->track_id);
  273. $message_admin = $langs->transnoentities($body, $object->track_id).'<br>';
  274. $message_admin .= '<ul><li>'.$langs->trans('Title').' : '.$object->subject.'</li>';
  275. $message_admin .= '<li>'.$langs->trans('Type').' : '.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$object->type_code, 'c_ticket_type', 'code', 'label', $object->type_code).'</li>';
  276. $message_admin .= '<li>'.$langs->trans('TicketCategory').' : '.$langs->getLabelFromKey($this->db, 'TicketCategoryShort'.$object->category_code, 'c_ticket_category', 'code', 'label', $object->category_code).'</li>';
  277. $message_admin .= '<li>'.$langs->trans('Severity').' : '.$langs->getLabelFromKey($this->db, 'TicketSeverityShort'.$object->severity_code, 'c_ticket_severity', 'code', 'label', $object->severity_code).'</li>';
  278. $message_admin .= '<li>'.$langs->trans('From').' : '.($object->email_from ? $object->email_from : ($object->fk_user_create > 0 ? $langs->trans('Internal') : '')).'</li>';
  279. // Extrafields
  280. $extraFields = new ExtraFields($this->db);
  281. $extraFields->fetch_name_optionals_label($object->table_element);
  282. if (is_array($object->array_options) && count($object->array_options) > 0) {
  283. foreach ($object->array_options as $key => $value) {
  284. $key = substr($key, 8); // remove "options_"
  285. $message_admin .= '<li>'.$langs->trans($extraFields->attributes[$object->element]['label'][$key]).' : '.$extraFields->showOutputField($key, $value, '', $object->table_element).'</li>';
  286. }
  287. }
  288. if ($object->fk_soc > 0) {
  289. $object->fetch_thirdparty();
  290. $message_admin .= '<li>'.$langs->trans('Company').' : '.$object->thirdparty->name.'</li>';
  291. }
  292. $message_admin .= '</ul>';
  293. $message = $object->message;
  294. if (!dol_textishtml($message)) {
  295. $message = dol_nl2br($message);
  296. }
  297. $message_admin .= '<p>'.$langs->trans('Message').' : <br><br>'.$message.'</p><br>';
  298. $message_admin .= '<p><a href="'.dol_buildpath('/ticket/card.php', 2).'?track_id='.$object->track_id.'">'.$langs->trans('SeeThisTicketIntomanagementInterface').'</a></p>';
  299. $from = getDolGlobalString('MAIN_INFO_SOCIETE_NOM') . '<' . getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM').'>';
  300. $trackid = 'tic'.$object->id;
  301. if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
  302. $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO;
  303. $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
  304. }
  305. include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
  306. $mailfile = new CMailFile($subject, $sendto, $from, $message_admin, $filepath, $mimetype, $filename, '', '', 0, -1, '', '', $trackid, '', 'ticket');
  307. if ($mailfile->error) {
  308. dol_syslog($mailfile->error, LOG_DEBUG);
  309. } else {
  310. $result = $mailfile->sendfile();
  311. }
  312. if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
  313. $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
  314. }
  315. }
  316. /**
  317. * Composes and sends a message concerning a ticket, to be sent to customer addresses.
  318. *
  319. * @param string $sendto Addresses to send the mail, format "first@address.net, second@address.net, " etc.
  320. * @param string $base_subject email subject. Non-translated string.
  321. * @param string $body email body (first line). Non-translated string.
  322. * @param string $see_ticket string indicating the ticket public address
  323. * @param Ticket $object the ticket thet the email refers to
  324. * @param Translate $langs the translation object
  325. * @return void
  326. */
  327. private function composeAndSendCustomerMessage($sendto, $base_subject, $body, $see_ticket, Ticket $object, Translate $langs)
  328. {
  329. global $conf, $extrafields, $mysoc, $user;
  330. // Init to avoid errors
  331. $filepath = array();
  332. $filename = array();
  333. $mimetype = array();
  334. $appli = $mysoc->name;
  335. $subject = '['.$appli.'] '.$langs->transnoentities($base_subject);
  336. $message_customer = $langs->transnoentities($body, $object->track_id).'<br>';
  337. $message_customer .= '<ul><li>'.$langs->trans('Title').' : '.$object->subject.'</li>';
  338. $message_customer .= '<li>'.$langs->trans('Type').' : '.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$object->type_code, 'c_ticket_type', 'code', 'label', $object->type_code).'</li>';
  339. $message_customer .= '<li>'.$langs->trans('TicketCategory').' : '.$langs->getLabelFromKey($this->db, 'TicketCategoryShort'.$object->category_code, 'c_ticket_category', 'code', 'label', $object->category_code).'</li>';
  340. $message_customer .= '<li>'.$langs->trans('Severity').' : '.$langs->getLabelFromKey($this->db, 'TicketSeverityShort'.$object->severity_code, 'c_ticket_severity', 'code', 'label', $object->severity_code).'</li>';
  341. // Extrafields
  342. if (is_array($extrafields->attributes[$object->table_element]['label'])) {
  343. foreach ($extrafields->attributes[$object->table_element]['label'] as $key => $value) {
  344. $enabled = 1;
  345. if ($enabled && isset($extrafields->attributes[$object->table_element]['list'][$key])) {
  346. $enabled = dol_eval($extrafields->attributes[$object->table_element]['list'][$key], 1);
  347. }
  348. $perms = 1;
  349. if ($perms && isset($extrafields->attributes[$object->table_element]['perms'][$key])) {
  350. $perms = dol_eval($extrafields->attributes[$object->table_element]['perms'][$key], 1);
  351. }
  352. $qualified = true;
  353. if (empty($enabled)) {
  354. $qualified = false;
  355. }
  356. if (empty($perms)) {
  357. $qualified = false;
  358. }
  359. if ($qualified) {
  360. $message_customer .= '<li>' . $langs->trans($key) . ' : ' . $value . '</li>';
  361. }
  362. }
  363. }
  364. $message_customer .= '</ul>';
  365. $message = $object->message;
  366. if (!dol_textishtml($message)) {
  367. $message = dol_nl2br($message);
  368. }
  369. $message_customer .= '<p>'.$langs->trans('Message').' : <br><br>'.$message.'</p><br>';
  370. $url_public_ticket = getDolGlobalString('TICKET_URL_PUBLIC_INTERFACE', dol_buildpath('/public/ticket/', 2)).'view.php?track_id='.$object->track_id;
  371. $message_customer .= '<p>'.$langs->trans($see_ticket).' : <a href="'.$url_public_ticket.'">'.$url_public_ticket.'</a></p>';
  372. $message_customer .= '<p>'.$langs->trans('TicketEmailPleaseDoNotReplyToThisEmail').'</p>';
  373. $from = (!getDolGlobalString('MAIN_INFO_SOCIETE_NOM') ? '' : getDolGlobalString('MAIN_INFO_SOCIETE_NOM') . ' ').'<' . getDolGlobalString('TICKET_NOTIFICATION_EMAIL_FROM').'>';
  374. $trackid = 'tic'.$object->id;
  375. $old_MAIN_MAIL_AUTOCOPY_TO = getDolGlobalString('MAIN_MAIL_AUTOCOPY_TO');
  376. if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
  377. $conf->global->MAIN_MAIL_AUTOCOPY_TO = '';
  378. }
  379. include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php';
  380. $mailfile = new CMailFile($subject, $sendto, $from, $message_customer, $filepath, $mimetype, $filename, '', '', 0, -1, '', '', $trackid, '', 'ticket');
  381. if ($mailfile->error) {
  382. dol_syslog($mailfile->error, LOG_DEBUG);
  383. } else {
  384. $result = $mailfile->sendfile();
  385. if ($result) {
  386. // update last_msg_sent date
  387. $object->fetch($object->id);
  388. $object->date_last_msg_sent = dol_now();
  389. $object->update($user);
  390. }
  391. }
  392. if (getDolGlobalString('TICKET_DISABLE_MAIL_AUTOCOPY_TO')) {
  393. $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO;
  394. }
  395. }
  396. }