From e1e643efba9edf44469aefb066101128b409acd5 Mon Sep 17 00:00:00 2001 From: Thomas Negre Date: Tue, 8 Feb 2022 16:32:02 +0100 Subject: [PATCH 1/7] Ticket : add option TICKET_NOTIFY_AT_CLOSING to ticket config panel --- htdocs/admin/ticket.php | 21 +++++++++++++++++++++ htdocs/core/modules/modTicket.class.php | 3 ++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/htdocs/admin/ticket.php b/htdocs/admin/ticket.php index c36f6ae1d1b..68f256e2009 100644 --- a/htdocs/admin/ticket.php +++ b/htdocs/admin/ticket.php @@ -203,6 +203,12 @@ if ($action == 'setvarother') { if (!($res > 0)) { $error++; } + + $param_auto_notify_close = GETPOST('TICKET_NOTIFY_AT_CLOSING', 'alpha'); + $res = dolibarr_set_const($db, 'TICKET_NOTIFY_AT_CLOSING', $param_auto_notify_close, 'chaine', 0, '', $conf->entity); + if (!($res > 0)) { + $error++; + } } @@ -530,6 +536,21 @@ print $form->textwithpicto('', $langs->trans("TicketsAutoAssignTicketHelp"), 1, print ''; print ''; +// Auto notify contacts when closing the ticket +print ''.$langs->trans("TicketsAutoNotifyClose").''; +print ''; +if ($conf->use_javascript_ajax) { + print ajax_constantonoff('TICKET_NOTIFY_AT_CLOSING'); +} else { + $arrval = array('0' => $langs->trans("No"), '1' => $langs->trans("Yes")); + print $form->selectarray("TICKET_NOTIFY_AT_CLOSING", $arrval, $conf->global->TICKET_NOTIFY_AT_CLOSING); +} +print ''; +print ''; +print $form->textwithpicto('', $langs->trans("TicketsAutoNotifyCloseHelp"), 1, 'help'); +print ''; +print ''; + if (!$conf->use_javascript_ajax) { print ''; } diff --git a/htdocs/core/modules/modTicket.class.php b/htdocs/core/modules/modTicket.class.php index 0ae6b00b1c3..86841ce5932 100644 --- a/htdocs/core/modules/modTicket.class.php +++ b/htdocs/core/modules/modTicket.class.php @@ -109,7 +109,8 @@ class modTicket extends DolibarrModules 3 => array('TICKET_ADDON_PDF_ODT_PATH', 'chaine', 'DOL_DATA_ROOT/doctemplates/tickets', 'Ticket templates ODT/ODS directory for templates', 0), 4 => array('TICKET_AUTO_READ_WHEN_CREATED_FROM_BACKEND', 'chaine', 0, 'Automatically mark ticket as read when created from backend', 0), 5 => array('TICKET_DELAY_BEFORE_FIRST_RESPONSE', 'chaine', '0', 'Maximum wanted elapsed time before a first answer to a ticket (in hours). Display a warning in tickets list if not respected.', 0), - 6 => array('TICKET_DELAY_SINCE_LAST_RESPONSE', 'chaine', '0', 'Maximum wanted elapsed time between two answers on the same ticket (in hours). Display a warning in tickets list if not respected.', 0) + 6 => array('TICKET_DELAY_SINCE_LAST_RESPONSE', 'chaine', '0', 'Maximum wanted elapsed time between two answers on the same ticket (in hours). Display a warning in tickets list if not respected.', 0), + 7 => array('TICKET_NOTIFY_AT_CLOSING', 'chaine', '0', 'Default notify contacts when closing a module', 0), ); From 5b1b862687cd43513af6a5cd727b436807c80c11 Mon Sep 17 00:00:00 2001 From: Thomas Negre Date: Wed, 9 Feb 2022 11:25:31 +0100 Subject: [PATCH 2/7] reformat trigger/interface_50_modTicket_TicketEmail.class.php to create methods for email composition and sending --- ...terface_50_modTicket_TicketEmail.class.php | 269 +++++++++--------- 1 file changed, 141 insertions(+), 128 deletions(-) diff --git a/htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php b/htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php index dfd83022393..4b155082453 100644 --- a/htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php +++ b/htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php @@ -133,71 +133,21 @@ class InterfaceTicketEmail extends DolibarrTriggers $langs->load('ticket'); + $subject_admin = 'TicketNewEmailSubjectAdmin'; + $body_admin = 'TicketNewEmailBodyAdmin'; + $subject_customer = 'TicketNewEmailSubjectCustomer'; + $body_customer = 'TicketNewEmailBodyCustomer'; + $see_ticket_customer = 'TicketNewEmailBodyInfosTrackUrlCustomer'; + // Send email to notification email if (!empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO) && empty($object->context['disableticketemail'])) { $sendto = empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO) ? '' : $conf->global->TICKET_NOTIFICATION_EMAIL_TO; - if ($sendto) { - // Init to avoid errors - $filepath = array(); - $filename = array(); - $mimetype = array(); - - /* Send email to admin */ - $subject = '['.$conf->global->MAIN_INFO_SOCIETE_NOM.'] '.$langs->transnoentities('TicketNewEmailSubjectAdmin'); - $message_admin = $langs->transnoentities('TicketNewEmailBodyAdmin', $object->track_id).'

'; - $message_admin .= ''; - - if ($object->fk_soc > 0) { - $object->fetch_thirdparty(); - $message_admin .= '

'.$langs->trans('Company').' : '.$object->thirdparty->name.'

'; - } - - $message = $object->message; - if (!dol_textishtml($message)) { - $message = dol_nl2br($message); - } - $message_admin .= '

'.$langs->trans('Message').' :
'.$message.'

'; - $message_admin .= '

'.$langs->trans('SeeThisTicketIntomanagementInterface').'

'; - - $from = $conf->global->MAIN_INFO_SOCIETE_NOM.'<'.$conf->global->TICKET_NOTIFICATION_EMAIL_FROM.'>'; - $replyto = $from; - - $trackid = 'tic'.$object->id; - - if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) { - $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO; - $conf->global->MAIN_MAIL_AUTOCOPY_TO = ''; - } - include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php'; - $mailfile = new CMailFile($subject, $sendto, $from, $message_admin, $filepath, $mimetype, $filename, '', '', 0, -1, '', '', $trackid, '', 'ticket'); - if ($mailfile->error) { - dol_syslog($mailfile->error, LOG_DEBUG); - } else { - $result = $mailfile->sendfile(); - } - if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) { - $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO; - } + $this->ComposeAndSendAdminMessage($sendto, $subject_admin, $body_admin, $object, $langs, $conf); } } // Send email to customer - if (empty($conf->global->TICKET_DISABLE_CUSTOMER_MAILS) && empty($object->context['disableticketemail']) && $object->notify_tiers_at_create) { $sendto = ''; @@ -218,77 +168,7 @@ class InterfaceTicketEmail extends DolibarrTriggers } if ($sendto) { - // Init to avoid errors - $filepath = array(); - $filename = array(); - $mimetype = array(); - - $subject = '['.$conf->global->MAIN_INFO_SOCIETE_NOM.'] '.$langs->transnoentities('TicketNewEmailSubjectCustomer'); - $message_customer = $langs->transnoentities('TicketNewEmailBodyCustomer', $object->track_id).'

'; - $message_customer .= ''; - - $message = $object->message; - if (!dol_textishtml($message)) { - $message = dol_nl2br($message); - } - $message_customer .= '

'.$langs->trans('Message').' :
'.$message.'

'; - $url_public_ticket = ($conf->global->TICKET_URL_PUBLIC_INTERFACE ? $conf->global->TICKET_URL_PUBLIC_INTERFACE.'/' : dol_buildpath('/public/ticket/view.php', 2)).'?track_id='.$object->track_id; - $message_customer .= '

'.$langs->trans('TicketNewEmailBodyInfosTrackUrlCustomer').' : '.$url_public_ticket.'

'; - $message_customer .= '

'.$langs->trans('TicketEmailPleaseDoNotReplyToThisEmail').'

'; - - $from = (empty($conf->global->MAIN_INFO_SOCIETE_NOM) ? '' : $conf->global->MAIN_INFO_SOCIETE_NOM.' ').'<'.$conf->global->TICKET_NOTIFICATION_EMAIL_FROM.'>'; - $replyto = $from; - - $trackid = 'tic'.$object->id; - - if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) { - $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO; - $conf->global->MAIN_MAIL_AUTOCOPY_TO = ''; - } - include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php'; - $mailfile = new CMailFile($subject, $sendto, $from, $message_customer, $filepath, $mimetype, $filename, '', '', 0, -1, '', '', $trackid, '', 'ticket'); - if ($mailfile->error) { - dol_syslog($mailfile->error, LOG_DEBUG); - } else { - $result = $mailfile->sendfile(); - if ($result) { - // update last_msg_sent date - $object->date_last_msg_sent = dol_now(); - $object->update($user); - } - } - if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) { - $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO; - } + $this->ComposeAndSendCustomerMessage($sendto, $subject_customer, $body_customer, $see_ticket_customer, $object, $langs, $conf); } } @@ -311,4 +191,137 @@ class InterfaceTicketEmail extends DolibarrTriggers return $ok; } + + private function ComposeAndSendAdminMessage($sendto, $base_subject, $body, Ticket $object, Translate $langs, $conf) + { + // Init to avoid errors + $filepath = array(); + $filename = array(); + $mimetype = array(); + + /* Send email to admin */ + $subject = '['.$conf->global->MAIN_INFO_SOCIETE_NOM.'] '.$langs->transnoentities($base_subject); + $message_admin = $langs->transnoentities($body, $object->track_id).'

'; + $message_admin .= ''; + + if ($object->fk_soc > 0) { + $object->fetch_thirdparty(); + $message_admin .= '

'.$langs->trans('Company').' : '.$object->thirdparty->name.'

'; + } + + $message = $object->message; + if (!dol_textishtml($message)) { + $message = dol_nl2br($message); + } + $message_admin .= '

'.$langs->trans('Message').' :
'.$message.'

'; + $message_admin .= '

'.$langs->trans('SeeThisTicketIntomanagementInterface').'

'; + + $from = $conf->global->MAIN_INFO_SOCIETE_NOM.'<'.$conf->global->TICKET_NOTIFICATION_EMAIL_FROM.'>'; + + $trackid = 'tic'.$object->id; + + if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) { + $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO; + $conf->global->MAIN_MAIL_AUTOCOPY_TO = ''; + } + include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php'; + $mailfile = new CMailFile($subject, $sendto, $from, $message_admin, $filepath, $mimetype, $filename, '', '', 0, -1, '', '', $trackid, '', 'ticket'); + if ($mailfile->error) { + dol_syslog($mailfile->error, LOG_DEBUG); + } else { + $result = $mailfile->sendfile(); + } + if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) { + $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO; + } + } + + private function ComposeAndSendCustomerMessage ($sendto, $base_subject, $body, $see_ticket, Ticket $object, Translate $langs, $conf) + { + // Init to avoid errors + $filepath = array(); + $filename = array(); + $mimetype = array(); + + $subject = '['.$conf->global->MAIN_INFO_SOCIETE_NOM.'] '.$langs->transnoentities($base_subject); + $message_customer = $langs->transnoentities($body, $object->track_id).'

'; + $message_customer .= ''; + + $message = $object->message; + if (!dol_textishtml($message)) { + $message = dol_nl2br($message); + } + $message_customer .= '

'.$langs->trans('Message').' :
'.$message.'

'; + $url_public_ticket = ($conf->global->TICKET_URL_PUBLIC_INTERFACE ? $conf->global->TICKET_URL_PUBLIC_INTERFACE.'/' : dol_buildpath('/public/ticket/view.php', 2)).'?track_id='.$object->track_id; + $message_customer .= '

'.$langs->trans($see_ticket).' : '.$url_public_ticket.'

'; + $message_customer .= '

'.$langs->trans('TicketEmailPleaseDoNotReplyToThisEmail').'

'; + + $from = (empty($conf->global->MAIN_INFO_SOCIETE_NOM) ? '' : $conf->global->MAIN_INFO_SOCIETE_NOM.' ').'<'.$conf->global->TICKET_NOTIFICATION_EMAIL_FROM.'>'; + + $trackid = 'tic'.$object->id; + + if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) { + $old_MAIN_MAIL_AUTOCOPY_TO = $conf->global->MAIN_MAIL_AUTOCOPY_TO; + $conf->global->MAIN_MAIL_AUTOCOPY_TO = ''; + } + include_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php'; + $mailfile = new CMailFile($subject, $sendto, $from, $message_customer, $filepath, $mimetype, $filename, '', '', 0, -1, '', '', $trackid, '', 'ticket'); + if ($mailfile->error) { + dol_syslog($mailfile->error, LOG_DEBUG); + } else { + $result = $mailfile->sendfile(); + if ($result) { + // update last_msg_sent date + $object->date_last_msg_sent = dol_now(); + $object->update($user); + } + } + if (!empty($conf->global->TICKET_DISABLE_MAIL_AUTOCOPY_TO)) { + $conf->global->MAIN_MAIL_AUTOCOPY_TO = $old_MAIN_MAIL_AUTOCOPY_TO; + } + } + } From dcc41d0ab70aebe19dd61abaedcab443f5a8811d Mon Sep 17 00:00:00 2001 From: Thomas Negre Date: Mon, 28 Feb 2022 15:09:58 +0100 Subject: [PATCH 3/7] Ticket triggers: allow to automatically send messages on TICKET_CLOSE event --- ...terface_50_modTicket_TicketEmail.class.php | 111 +++++++++++++----- 1 file changed, 84 insertions(+), 27 deletions(-) diff --git a/htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php b/htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php index 4b155082453..0816657fa13 100644 --- a/htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php +++ b/htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php @@ -185,10 +185,65 @@ class InterfaceTicketEmail extends DolibarrTriggers case 'TICKET_CLOSE': dol_syslog("Trigger '".$this->name."' for action '$action' launched by ".__FILE__.". id=".$object->id); + $langs->load('ticket'); + + $subject_admin = 'TicketCloseEmailSubjectAdmin'; + $body_admin = 'TicketCloseEmailBodyAdmin'; + $subject_customer = 'TicketCloseEmailSubjectCustomer'; + $body_customer = 'TicketCloseEmailBodyCustomer'; + $see_ticket_customer = 'TicketCloseEmailBodyInfosTrackUrlCustomer'; + + // Send email to notification email + if (!empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO) && empty($object->context['disableticketemail'])) { + $sendto = empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO) ? '' : $conf->global->TICKET_NOTIFICATION_EMAIL_TO; + if ($sendto) { + $this->ComposeAndSendAdminMessage($sendto, $subject_admin, $body_admin, $object, $langs, $conf); + } + } + + // Send email to customer. + if (empty($conf->global->TICKET_DISABLE_CUSTOMER_MAILS) && empty($object->context['disableticketemail'])) { + $linked_contacts = $object->listeContact(-1, 'thirdparty'); + $linked_contacts = array_merge($linked_contacts, $object->listeContact(-1, 'internal')); + if (empty($linked_contacts) && !empty($conf->global->TICKET_NOTIFY_AT_CLOSING) && !empty($object->fk_soc)) { + $object->fetch_thirdparty(); + $linked_contacts[] = $object->thirdparty->email; + } + + $contactid = GETPOST('contactid', 'int'); + if ($contactid > 0) { + $contact = new Contact($this->db); + $res = $contact->fetch($contactid); + if (! in_array($contact, $linked_contacts)) { + $error_msg = $langs->trans('Error'). ': '; + $error_msg .= $langs->transnoentities('TicketWrongContact'); + setEventMessages($error_msg, [], 'errors'); + $ok = 0; + break; + } + } + + $sendto = ''; + if ($res > 0 && !empty($contact->email) && !empty($contact->statut)) { + $sendto = $contact->email; + // if sending to all contacts or sending to contacts while mass closing + } elseif ( !empty($linked_contacts) && ($contactid == -2 || (GETPOST('massaction', 'alpha') == 'close' && GETPOST('confirm', 'alpha') == 'yes'))) { + $temp_emails = []; + foreach($linked_contacts as $contact) { + $temp_emails[] = $contact['email']; + } + $sendto = implode(", ", $temp_emails); + unset($temp_emails); + unset($linked_contacts); + } + if ($sendto) { + $this->ComposeAndSendCustomerMessage($sendto, $subject_customer, $body_customer, $see_ticket_customer, $object, $langs, $conf); + } + } + $ok = 1; break; } - return $ok; } @@ -200,8 +255,8 @@ class InterfaceTicketEmail extends DolibarrTriggers $mimetype = array(); /* Send email to admin */ - $subject = '['.$conf->global->MAIN_INFO_SOCIETE_NOM.'] '.$langs->transnoentities($base_subject); - $message_admin = $langs->transnoentities($body, $object->track_id).'

'; + $subject = '['.$conf->global->MAIN_INFO_SOCIETE_NOM.'] '.$langs->transnoentities($base_subject, $object->ref, $object->track_id); + $message_admin = $langs->transnoentities($body, $object->track_id).'
'; $message_admin .= '
  • '.$langs->trans('Title').' : '.$object->subject.'
  • '; $message_admin .= '
  • '.$langs->trans('Type').' : '.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$object->type_code, 'c_ticket_type', 'code', 'label', $object->type_code).'
  • '; $message_admin .= '
  • '.$langs->trans('Category').' : '.$langs->getLabelFromKey($this->db, 'TicketCategoryShort'.$object->category_code, 'c_ticket_category', 'code', 'label', $object->category_code).'
  • '; @@ -216,18 +271,17 @@ class InterfaceTicketEmail extends DolibarrTriggers $message_admin .= '
  • '.$langs->trans($extraFields->attributes[$object->element]['label'][$key]).' : '.$extraFields->showOutputField($key, $value, '', $object->table_element).'
  • '; } } - $message_admin .= '
'; - if ($object->fk_soc > 0) { $object->fetch_thirdparty(); - $message_admin .= '

'.$langs->trans('Company').' : '.$object->thirdparty->name.'

'; + $message_admin .= '
  • '.$langs->trans('Company').' : '.$object->thirdparty->name.'
  • '; } + $message_admin .= ''; $message = $object->message; if (!dol_textishtml($message)) { $message = dol_nl2br($message); } - $message_admin .= '

    '.$langs->trans('Message').' :
    '.$message.'

    '; + $message_admin .= '

    '.$langs->trans('Message').' :

    '.$message.'


    '; $message_admin .= '

    '.$langs->trans('SeeThisTicketIntomanagementInterface').'

    '; $from = $conf->global->MAIN_INFO_SOCIETE_NOM.'<'.$conf->global->TICKET_NOTIFICATION_EMAIL_FROM.'>'; @@ -258,33 +312,35 @@ class InterfaceTicketEmail extends DolibarrTriggers $mimetype = array(); $subject = '['.$conf->global->MAIN_INFO_SOCIETE_NOM.'] '.$langs->transnoentities($base_subject); - $message_customer = $langs->transnoentities($body, $object->track_id).'

    '; + $message_customer = $langs->transnoentities($body, $object->track_id).'
    '; $message_customer .= '
    • '.$langs->trans('Title').' : '.$object->subject.'
    • '; $message_customer .= '
    • '.$langs->trans('Type').' : '.$langs->getLabelFromKey($this->db, 'TicketTypeShort'.$object->type_code, 'c_ticket_type', 'code', 'label', $object->type_code).'
    • '; $message_customer .= '
    • '.$langs->trans('Category').' : '.$langs->getLabelFromKey($this->db, 'TicketCategoryShort'.$object->category_code, 'c_ticket_category', 'code', 'label', $object->category_code).'
    • '; $message_customer .= '
    • '.$langs->trans('Severity').' : '.$langs->getLabelFromKey($this->db, 'TicketSeverityShort'.$object->severity_code, 'c_ticket_severity', 'code', 'label', $object->severity_code).'
    • '; // Extrafields - foreach ($this->attributes[$object->table_element]['label'] as $key => $value) { - $enabled = 1; - if ($enabled && isset($this->attributes[$object->table_element]['list'][$key])) { - $enabled = dol_eval($this->attributes[$object->table_element]['list'][$key], 1); - } - $perms = 1; - if ($perms && isset($this->attributes[$object->table_element]['perms'][$key])) { - $perms = dol_eval($this->attributes[$object->table_element]['perms'][$key], 1); - } + if (is_array($this->attributes[$object->table_element]['label'])) { + foreach ($this->attributes[$object->table_element]['label'] as $key => $value) { + $enabled = 1; + if ($enabled && isset($this->attributes[$object->table_element]['list'][$key])) { + $enabled = dol_eval($this->attributes[$object->table_element]['list'][$key], 1); + } + $perms = 1; + if ($perms && isset($this->attributes[$object->table_element]['perms'][$key])) { + $perms = dol_eval($this->attributes[$object->table_element]['perms'][$key], 1); + } - $qualified = true; - if (empty($enabled)) { - $qualified = false; - } - if (empty($perms)) { - $qualified = false; - } + $qualified = true; + if (empty($enabled)) { + $qualified = false; + } + if (empty($perms)) { + $qualified = false; + } - if ($qualified) { - $message_customer .= '
    • '.$langs->trans($key).' : '.$value.'
    • '; + if ($qualified) { + $message_customer .= '
    • ' . $langs->trans($key) . ' : ' . $value . '
    • '; + } } } @@ -294,7 +350,7 @@ class InterfaceTicketEmail extends DolibarrTriggers if (!dol_textishtml($message)) { $message = dol_nl2br($message); } - $message_customer .= '

      '.$langs->trans('Message').' :
      '.$message.'

      '; + $message_customer .= '

      '.$langs->trans('Message').' :

      '.$message.'


      '; $url_public_ticket = ($conf->global->TICKET_URL_PUBLIC_INTERFACE ? $conf->global->TICKET_URL_PUBLIC_INTERFACE.'/' : dol_buildpath('/public/ticket/view.php', 2)).'?track_id='.$object->track_id; $message_customer .= '

      '.$langs->trans($see_ticket).' : '.$url_public_ticket.'

      '; $message_customer .= '

      '.$langs->trans('TicketEmailPleaseDoNotReplyToThisEmail').'

      '; @@ -315,6 +371,7 @@ class InterfaceTicketEmail extends DolibarrTriggers $result = $mailfile->sendfile(); if ($result) { // update last_msg_sent date + $object->fetch($object->id); $object->date_last_msg_sent = dol_now(); $object->update($user); } From e4ed4b83074d559b747451e472cd17b18528da03 Mon Sep 17 00:00:00 2001 From: Thomas Negre Date: Mon, 28 Feb 2022 15:10:59 +0100 Subject: [PATCH 4/7] Ticket card: enhance closing modal to allow chosing contacts to notify --- htdocs/ticket/card.php | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/htdocs/ticket/card.php b/htdocs/ticket/card.php index f1d33f4eeb0..17a07e6af4c 100755 --- a/htdocs/ticket/card.php +++ b/htdocs/ticket/card.php @@ -820,10 +820,28 @@ if ($action == 'create' || $action == 'presend') { // Confirmation close if ($action == 'close') { - print $form->formconfirm($url_page_current."?track_id=".$object->track_id, $langs->trans("CloseATicket"), $langs->trans("ConfirmCloseAticket"), "confirm_close", '', '', 1); - if ($ret == 'html') { - print '
      '; + $thirdparty_contacts = $object->getInfosTicketExternalContact(); + $contacts_select = array( + '-2' => $langs->trans('TicketNotifyAllTiersAtClose'), + '-3' => $langs->trans('TicketNotNotifyTiersAtClose') + ); + foreach ($thirdparty_contacts as $thirdparty_contact) { + $contacts_select[$thirdparty_contact['id']] = $thirdparty_contact['civility'] . ' ' . $thirdparty_contact['lastname'] . ' ' . $thirdparty_contact['firstname']; } + + // Default select all or no contact + $default = (!empty($conf->global->TICKET_NOTIFY_AT_CLOSING)) ? -2 : -3; + $formquestion = array( + array( + 'name' => 'contactid', + 'type' => 'select', + 'label' => $langs->trans('NotifyThirdpartyOnTicketClosing'), + 'values' => $contacts_select, + 'default' => $default + ), + ); + + print $form->formconfirm($url_page_current."?track_id=".$object->track_id, $langs->trans("CloseATicket"), $langs->trans("ConfirmCloseAticket"), "confirm_close", $formquestion, '', 1 ); } // Confirmation abandon if ($action == 'abandon') { From 472666182e24bb2610ad370225bda29a3372a9be Mon Sep 17 00:00:00 2001 From: Thomas Negre Date: Mon, 28 Feb 2022 15:11:45 +0100 Subject: [PATCH 5/7] Ticket list: add a warning before mass ticket closing to chose if contacts should be notified --- htdocs/ticket/list.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/htdocs/ticket/list.php b/htdocs/ticket/list.php index d362dcbdabd..57c9968164d 100644 --- a/htdocs/ticket/list.php +++ b/htdocs/ticket/list.php @@ -185,7 +185,7 @@ if (GETPOST('cancel', 'alpha')) { $action = 'list'; $massaction = ''; } -if (!GETPOST('confirmmassaction', 'alpha') && $massaction != 'presend' && $massaction != 'confirm_presend') { +if (!GETPOST('confirmmassaction', 'alpha') && $massaction != 'presend' && $massaction != 'confirm_presend' && $massaction != 'presendonclose' && $massaction != 'close') { $massaction = ''; } @@ -690,7 +690,7 @@ $arrayofmassactions = array( //'builddoc'=>img_picto('', 'pdf', 'class="pictofixedwidth"').$langs->trans("PDFMerge"), ); if ($permissiontoadd) { - $arrayofmassactions['close'] = img_picto('', 'close_title', 'class="pictofixedwidth"').$langs->trans("Close"); + $arrayofmassactions['presendonclose'] = img_picto('', 'close_title', 'class="pictofixedwidth"').$langs->trans("Close"); $arrayofmassactions['reopen'] = img_picto('', 'folder-open', 'class="pictofixedwidth"').$langs->trans("ReOpen"); } if ($permissiontodelete) { @@ -743,6 +743,17 @@ $objecttmp = new Ticket($db); $trackid = 'tic'.$object->id; include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php'; +// confirm auto send on close +if ($massaction == 'presendonclose') { + $hidden_form = array([ + "type" => "hidden", + "name" => "massaction", + "value" => "close" + ]); + $selectedchoice = (!empty($conf->global->TICKET_NOTIFY_AT_CLOSING)) ? "yes" : "no"; + print $form->formconfirm($_SERVER["PHP_SELF"], $langs->trans("ConfirmMassTicketClosingSendEmail"), $langs->trans("ConfirmMassTicketClosingSendEmailQuestion"), 'confirm_send_close', $hidden_form, $selectedchoice, 0, 200, 500, 1); +} + if ($search_all) { foreach ($fieldstosearchall as $key => $val) { $fieldstosearchall[$key] = $langs->trans($val); From 35531b63289e480cc68322cd70886de4621c0180 Mon Sep 17 00:00:00 2001 From: Thomas Negre Date: Mon, 28 Feb 2022 15:12:30 +0100 Subject: [PATCH 6/7] add strings for email notification on ticket closing --- htdocs/langs/en_US/ticket.lang | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/htdocs/langs/en_US/ticket.lang b/htdocs/langs/en_US/ticket.lang index 9dfc4874cc5..310ac8dd7f6 100644 --- a/htdocs/langs/en_US/ticket.lang +++ b/htdocs/langs/en_US/ticket.lang @@ -142,6 +142,9 @@ TicketsDelayBeforeFirstAnswer=A new ticket should receive a first answer before TicketsDelayBeforeFirstAnswerHelp=If a new ticket has not received an answer after this time period (in hours), an important warning icon will be displayed in the list view. TicketsDelayBetweenAnswers=An unresolved ticket should not be unactive during (hours): TicketsDelayBetweenAnswersHelp=If an unresolved ticket that has already received an answer has not had further interaction after this time period (in hours), a warning icon will be displayed in the list view. +TicketsAutoNotifyClose=Automatically notify thirdparty when closing a ticket +TicketsAutoNotifyCloseHelp=When closing a ticket, you will be proposed to send a message to one of thirdparty's contacts. On mass closing, a message will be sent to one contact of the thirdparty linked to the ticket. +TicketWrongContact=Provided contact is not part of current ticket contacts. Email not sent. # # Index & list page @@ -158,6 +161,8 @@ OrderByDateAsc=Sort by ascending date OrderByDateDesc=Sort by descending date ShowAsConversation=Show as conversation list MessageListViewType=Show as table list +ConfirmMassTicketClosingSendEmail=Automatically send emails when closing tickets +ConfirmMassTicketClosingSendEmailQuestion=Do you want to notify thirdparties when closing these tickets ? # # Ticket card @@ -245,6 +250,9 @@ TicketChangeStatus=Change status TicketConfirmChangeStatus=Confirm the status change: %s ? TicketLogStatusChanged=Status changed: %s to %s TicketNotNotifyTiersAtCreate=Not notify company at create +NotifyThirdpartyOnTicketClosing=Contacts to notify while closing the ticket +TicketNotifyAllTiersAtClose=All related contacts +TicketNotNotifyTiersAtClose=No related contact Unread=Unread TicketNotCreatedFromPublicInterface=Not available. Ticket was not created from public interface. ErrorTicketRefRequired=Ticket reference name is required @@ -279,6 +287,7 @@ TicketNewEmailBodyInfosTicket=Information for monitoring the ticket TicketNewEmailBodyInfosTrackId=Ticket tracking number: %s TicketNewEmailBodyInfosTrackUrl=You can view the progress of the ticket by clicking the link above. TicketNewEmailBodyInfosTrackUrlCustomer=You can view the progress of the ticket in the specific interface by clicking the following link +TicketCloseEmailBodyInfosTrackUrlCustomer=You can consult the history of this ticket by clicking the following link TicketEmailPleaseDoNotReplyToThisEmail=Please do not reply directly to this email! Use the link to reply into the interface. TicketPublicInfoCreateTicket=This form allows you to record a support ticket in our management system. TicketPublicPleaseBeAccuratelyDescribe=Please accurately describe the problem. Provide the most information possible to allow us to correctly identify your request. @@ -300,6 +309,10 @@ NewUser=New user NumberOfTicketsByMonth=Number of tickets per month NbOfTickets=Number of tickets # notifications +TicketCloseEmailSubjectCustomer=Ticket closed +TicketCloseEmailBodyCustomer=This is an automatic message to notify you that ticket %s has just been closed. +TicketCloseEmailSubjectAdmin=Ticket closed - Réf %s (public ticket ID %s) +TicketCloseEmailBodyAdmin=A ticket with ID #%s has just been closed, see information: TicketNotificationEmailSubject=Ticket %s updated TicketNotificationEmailBody=This is an automatic message to notify you that ticket %s has just been updated TicketNotificationRecipient=Notification recipient From b9e0fc7b02913411b7d1c376fa093dae9bdf14de Mon Sep 17 00:00:00 2001 From: Thomas Negre Date: Mon, 28 Feb 2022 17:15:06 +0100 Subject: [PATCH 7/7] stickler corrections --- ...terface_50_modTicket_TicketEmail.class.php | 39 +++++++++++++++---- htdocs/ticket/card.php | 4 +- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php b/htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php index 0816657fa13..6a7ac6c9527 100644 --- a/htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php +++ b/htdocs/core/triggers/interface_50_modTicket_TicketEmail.class.php @@ -143,7 +143,7 @@ class InterfaceTicketEmail extends DolibarrTriggers if (!empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO) && empty($object->context['disableticketemail'])) { $sendto = empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO) ? '' : $conf->global->TICKET_NOTIFICATION_EMAIL_TO; if ($sendto) { - $this->ComposeAndSendAdminMessage($sendto, $subject_admin, $body_admin, $object, $langs, $conf); + $this->composeAndSendAdminMessage($sendto, $subject_admin, $body_admin, $object, $langs, $conf); } } @@ -168,7 +168,7 @@ class InterfaceTicketEmail extends DolibarrTriggers } if ($sendto) { - $this->ComposeAndSendCustomerMessage($sendto, $subject_customer, $body_customer, $see_ticket_customer, $object, $langs, $conf); + $this->composeAndSendCustomerMessage($sendto, $subject_customer, $body_customer, $see_ticket_customer, $object, $langs, $conf); } } @@ -197,7 +197,7 @@ class InterfaceTicketEmail extends DolibarrTriggers if (!empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO) && empty($object->context['disableticketemail'])) { $sendto = empty($conf->global->TICKET_NOTIFICATION_EMAIL_TO) ? '' : $conf->global->TICKET_NOTIFICATION_EMAIL_TO; if ($sendto) { - $this->ComposeAndSendAdminMessage($sendto, $subject_admin, $body_admin, $object, $langs, $conf); + $this->composeAndSendAdminMessage($sendto, $subject_admin, $body_admin, $object, $langs, $conf); } } @@ -226,10 +226,10 @@ class InterfaceTicketEmail extends DolibarrTriggers $sendto = ''; if ($res > 0 && !empty($contact->email) && !empty($contact->statut)) { $sendto = $contact->email; - // if sending to all contacts or sending to contacts while mass closing } elseif ( !empty($linked_contacts) && ($contactid == -2 || (GETPOST('massaction', 'alpha') == 'close' && GETPOST('confirm', 'alpha') == 'yes'))) { + // if sending to all contacts or sending to contacts while mass closing $temp_emails = []; - foreach($linked_contacts as $contact) { + foreach ($linked_contacts as $contact) { $temp_emails[] = $contact['email']; } $sendto = implode(", ", $temp_emails); @@ -237,7 +237,7 @@ class InterfaceTicketEmail extends DolibarrTriggers unset($linked_contacts); } if ($sendto) { - $this->ComposeAndSendCustomerMessage($sendto, $subject_customer, $body_customer, $see_ticket_customer, $object, $langs, $conf); + $this->composeAndSendCustomerMessage($sendto, $subject_customer, $body_customer, $see_ticket_customer, $object, $langs, $conf); } } $ok = 1; @@ -247,7 +247,18 @@ class InterfaceTicketEmail extends DolibarrTriggers return $ok; } - private function ComposeAndSendAdminMessage($sendto, $base_subject, $body, Ticket $object, Translate $langs, $conf) + /** + * Composes and sends a message concerning a ticket, to be sent to admin address. + * @param string $sendto Addresses to send the mail, format "first@address.net, second@address.net," etc. + * @param string $base_subject email subject. Non-translated string. + * @param string $body email body (first line). Non-translated string. + * @param Ticket $object the ticket thet the email refers to + * @param Translate $langs the translation object + * @param conf $conf Object conf + * + * @return none + */ + private function composeAndSendAdminMessage($sendto, $base_subject, $body, Ticket $object, Translate $langs, $conf) { // Init to avoid errors $filepath = array(); @@ -304,7 +315,19 @@ class InterfaceTicketEmail extends DolibarrTriggers } } - private function ComposeAndSendCustomerMessage ($sendto, $base_subject, $body, $see_ticket, Ticket $object, Translate $langs, $conf) + /** + * Composes and sends a message concerning a ticket, to be sent to customer addresses. + * @param string $sendto Addresses to send the mail, format "first@address.net, second@address.net, " etc. + * @param string $base_subject email subject. Non-translated string. + * @param string $body email body (first line). Non-translated string. + * @param string $see_ticket string indicating the ticket public address + * @param Ticket $object the ticket thet the email refers to + * @param Translate $langs the translation object + * @param conf $conf Object conf + * + * @return none + */ + private function composeAndSendCustomerMessage($sendto, $base_subject, $body, $see_ticket, Ticket $object, Translate $langs, $conf) { // Init to avoid errors $filepath = array(); diff --git a/htdocs/ticket/card.php b/htdocs/ticket/card.php index 17a07e6af4c..20714f8d826 100755 --- a/htdocs/ticket/card.php +++ b/htdocs/ticket/card.php @@ -826,7 +826,7 @@ if ($action == 'create' || $action == 'presend') { '-3' => $langs->trans('TicketNotNotifyTiersAtClose') ); foreach ($thirdparty_contacts as $thirdparty_contact) { - $contacts_select[$thirdparty_contact['id']] = $thirdparty_contact['civility'] . ' ' . $thirdparty_contact['lastname'] . ' ' . $thirdparty_contact['firstname']; + $contacts_select[$thirdparty_contact['id']] = $thirdparty_contact['civility'] . ' ' . $thirdparty_contact['lastname'] . ' ' . $thirdparty_contact['firstname']; } // Default select all or no contact @@ -841,7 +841,7 @@ if ($action == 'create' || $action == 'presend') { ), ); - print $form->formconfirm($url_page_current."?track_id=".$object->track_id, $langs->trans("CloseATicket"), $langs->trans("ConfirmCloseAticket"), "confirm_close", $formquestion, '', 1 ); + print $form->formconfirm($url_page_current."?track_id=".$object->track_id, $langs->trans("CloseATicket"), $langs->trans("ConfirmCloseAticket"), "confirm_close", $formquestion, '', 1); } // Confirmation abandon if ($action == 'abandon') {