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