Notifications go out on the recipient's timezone (#914)

This commit is contained in:
Alex Tselegidis 2020-11-16 11:47:10 +02:00
parent af5ddfaac4
commit 084aed9e23
3 changed files with 106 additions and 71 deletions

View file

@ -156,7 +156,7 @@ class Ics_file {
$calendar $calendar
->setProdId('-//EasyAppointments//Open Source Web Scheduler//EN') ->setProdId('-//EasyAppointments//Open Source Web Scheduler//EN')
->setTimezone(new DateTimeZone(date_default_timezone_get())) ->setTimezone(new DateTimeZone($provider['timezone']))
->addEvent($event); ->addEvent($event);
// Setup exporter. // Setup exporter.

View file

@ -33,13 +33,18 @@ class Notifications {
public function __construct() public function __construct()
{ {
$this->CI =& get_instance(); $this->CI =& get_instance();
$this->CI->load->model('providers_model'); $this->CI->load->model('providers_model');
$this->CI->load->model('secretaries_model'); $this->CI->load->model('secretaries_model');
$this->CI->load->model('secretaries_model'); $this->CI->load->model('secretaries_model');
$this->CI->load->model('admins_model'); $this->CI->load->model('admins_model');
$this->CI->load->model('appointments_model'); $this->CI->load->model('appointments_model');
$this->CI->load->model('settings_model'); $this->CI->load->model('settings_model');
$this->CI->load->library('ics_file'); $this->CI->load->library('ics_file');
$this->CI->load->library('timezones');
$this->CI->config->load('email');
} }
/** /**
@ -54,44 +59,39 @@ class Notifications {
*/ */
public function notify_appointment_saved($appointment, $service, $provider, $customer, $settings, $manage_mode = FALSE) public function notify_appointment_saved($appointment, $service, $provider, $customer, $settings, $manage_mode = FALSE)
{ {
// Send email notifications to customer and provider.
try try
{ {
$this->CI->config->load('email');
$email = new EmailClient($this->CI, $this->CI->config->config); $email = new EmailClient($this->CI, $this->CI->config->config);
if ($manage_mode === FALSE) if ($manage_mode)
{
$customer_title = new Text(lang('appointment_booked'));
$customer_message = new Text(lang('thank_you_for_appointment'));
$provider_title = new Text(lang('appointment_added_to_your_plan'));
$provider_message = new Text(lang('appointment_link_description'));
}
else
{ {
$customer_title = new Text(lang('appointment_changes_saved')); $customer_title = new Text(lang('appointment_changes_saved'));
$customer_message = new Text(''); $customer_message = new Text('');
$provider_title = new Text(lang('appointment_details_changed')); $provider_title = new Text(lang('appointment_details_changed'));
$provider_message = new Text(''); $provider_message = new Text('');
} }
else
{
$customer_title = new Text(lang('appointment_booked'));
$customer_message = new Text(lang('thank_you_for_appointment'));
$provider_title = new Text(lang('appointment_added_to_your_plan'));
$provider_message = new Text(lang('appointment_link_description'));
}
$customer_link = new Url(site_url('appointments/index/' . $appointment['hash'])); $customer_link = new Url(site_url('appointments/index/' . $appointment['hash']));
$provider_link = new Url(site_url('backend/index/' . $appointment['hash'])); $provider_link = new Url(site_url('backend/index/' . $appointment['hash']));
$ics_stream = $this->CI->ics_file->get_stream($appointment, $service, $provider, $customer);
$send_customer = filter_var( $send_customer = filter_var(
$this->CI->settings_model->get_setting('customer_notifications'), $this->CI->settings_model->get_setting('customer_notifications'),
FILTER_VALIDATE_BOOLEAN); FILTER_VALIDATE_BOOLEAN);
$ics_stream = $this->CI->ics_file->get_stream($appointment, $service, $provider, $customer);
if ($send_customer === TRUE) if ($send_customer === TRUE)
{ {
$email->sendAppointmentDetails($appointment, $provider, $email->sendAppointmentDetails($appointment, $provider,
$service, $customer, $settings, $customer_title, $service, $customer, $settings, $customer_title,
$customer_message, $customer_link, new Email($customer['email']), new Text($ics_stream)); $customer_message, $customer_link, new Email($customer['email']), new Text($ics_stream), $customer['timezone']);
} }
$send_provider = filter_var( $send_provider = filter_var(
@ -102,7 +102,7 @@ class Notifications {
{ {
$email->sendAppointmentDetails($appointment, $provider, $email->sendAppointmentDetails($appointment, $provider,
$service, $customer, $settings, $provider_title, $service, $customer, $settings, $provider_title,
$provider_message, $provider_link, new Email($provider['email']), new Text($ics_stream)); $provider_message, $provider_link, new Email($provider['email']), new Text($ics_stream), $provider['timezone']);
} }
// Notify admins // Notify admins
@ -110,14 +110,14 @@ class Notifications {
foreach ($admins as $admin) foreach ($admins as $admin)
{ {
if ( ! $admin['settings']['notifications'] === '0') if ($admin['settings']['notifications'] === '0')
{ {
continue; continue;
} }
$email->sendAppointmentDetails($appointment, $provider, $email->sendAppointmentDetails($appointment, $provider,
$service, $customer, $settings, $provider_title, $service, $customer, $settings, $provider_title,
$provider_message, $provider_link, new Email($admin['email']), new Text($ics_stream)); $provider_message, $provider_link, new Email($admin['email']), new Text($ics_stream), $admin['timezone']);
} }
// Notify secretaries // Notify secretaries
@ -125,7 +125,7 @@ class Notifications {
foreach ($secretaries as $secretary) foreach ($secretaries as $secretary)
{ {
if ( ! $secretary['settings']['notifications'] === '0') if ($secretary['settings']['notifications'] === '0')
{ {
continue; continue;
} }
@ -137,7 +137,7 @@ class Notifications {
$email->sendAppointmentDetails($appointment, $provider, $email->sendAppointmentDetails($appointment, $provider,
$service, $customer, $settings, $provider_title, $service, $customer, $settings, $provider_title,
$provider_message, $provider_link, new Email($secretary['email']), new Text($ics_stream)); $provider_message, $provider_link, new Email($secretary['email']), new Text($ics_stream), $secretary['timezone']);
} }
} }
catch (Exception $exception) catch (Exception $exception)

View file

@ -13,11 +13,16 @@
namespace EA\Engine\Notifications; namespace EA\Engine\Notifications;
use \EA\Engine\Types\Text; use DateTime;
use \EA\Engine\Types\NonEmptyText; use DateTimeZone;
use \EA\Engine\Types\Url; use EA\Engine\Types\Email as EmailAddress;
use \EA\Engine\Types\Email as EmailAddress; use EA\Engine\Types\NonEmptyText;
use \PHPMailer\PHPMailer\PHPMailer; use EA\Engine\Types\Text;
use EA\Engine\Types\Url;
use EA_Controller;
use Exception;
use PHPMailer\PHPMailer\PHPMailer;
use RuntimeException;
/** /**
* Email Notifications Class * Email Notifications Class
@ -32,7 +37,7 @@ class Email {
/** /**
* Framework Instance * Framework Instance
* *
* @var CI_Controller * @var EA_Controller
*/ */
protected $framework; protected $framework;
@ -65,32 +70,37 @@ class Email {
* @param array $provider Contains the provider data. * @param array $provider Contains the provider data.
* @param array $service Contains the service data. * @param array $service Contains the service data.
* @param array $customer Contains the customer data. * @param array $customer Contains the customer data.
* @param array $company Contains settings of the company. By the time the * @param array $settings Contains settings of the company. At the time the "company_name", "company_link" and
* "company_name", "company_link" and "company_email" values are required in the array. * "company_email" values are required in the array.
* @param \EA\Engine\Types\Text $title The email title may vary depending the receiver. * @param \EA\Engine\Types\Text $title The email title may vary depending the receiver.
* @param \EA\Engine\Types\Text $message The email message may vary depending the receiver. * @param \EA\Engine\Types\Text $message The email message may vary depending the receiver.
* @param \EA\Engine\Types\Url $appointmentLink This link is going to enable the receiver to make changes * @param \EA\Engine\Types\Url $appointmentLink This link is going to enable the receiver to make changes to the
* to the appointment record. * appointment record.
* @param \EA\Engine\Types\Email $recipientEmail The recipient email address. * @param \EA\Engine\Types\Email $recipientEmail The recipient email address.
* @param \EA\Engine\Types\Text $icsStream Stream contents of the ICS file. * @param \EA\Engine\Types\Text $icsStream Stream contents of the ICS file.
* @param string|null $timezone Custom timezone for the notification.
*
* @throws \PHPMailer\PHPMailer\Exception
*/ */
public function sendAppointmentDetails( public function sendAppointmentDetails(
array $appointment, array $appointment,
array $provider, array $provider,
array $service, array $service,
array $customer, array $customer,
array $company, array $settings,
Text $title, Text $title,
Text $message, Text $message,
Url $appointmentLink, Url $appointmentLink,
EmailAddress $recipientEmail, EmailAddress $recipientEmail,
Text $icsStream Text $icsStream,
) { $timezone = NULL
)
{
$framework = get_instance(); $framework = get_instance();
$timezones = $framework->timezones->to_array(); $timezones = $framework->timezones->to_array();
switch ($company['date_format']) switch ($settings['date_format'])
{ {
case 'DMY': case 'DMY':
$date_format = 'd/m/Y'; $date_format = 'd/m/Y';
@ -102,19 +112,30 @@ class Email {
$date_format = 'Y/m/d'; $date_format = 'Y/m/d';
break; break;
default: default:
throw new \Exception('Invalid date_format value: ' . $company['date_format']); throw new Exception('Invalid date_format value: ' . $settings['date_format']);
} }
switch ($company['time_format']) switch ($settings['time_format'])
{ {
case 'military': case 'military':
$timeFormat = 'H:i'; $time_format = 'H:i';
break; break;
case 'regular': case 'regular':
$timeFormat = 'g:i A'; $time_format = 'g:i A';
break; break;
default: default:
throw new \Exception('Invalid time_format value: ' . $company['time_format']); throw new Exception('Invalid time_format value: ' . $settings['time_format']);
}
$appointment_timezone = new DateTimeZone($provider['timezone']);
$appointment_start = new DateTime($appointment['start_datetime'], $appointment_timezone);
$appointment_end = new DateTime($appointment['end_datetime'], $appointment_timezone);
if ($timezone && $timezone !== $provider['timezone'])
{
$appointment_timezone = new DateTimeZone($timezone);
$appointment_start->setTimezone($appointment_timezone);
$appointment_end->setTimezone($appointment_timezone);
} }
// Prepare template replace array. // Prepare template replace array.
@ -122,12 +143,12 @@ class Email {
$email_message = $message->get(); $email_message = $message->get();
$appointment_service = $service['name']; $appointment_service = $service['name'];
$appointment_provider = $provider['first_name'] . ' ' . $provider['last_name']; $appointment_provider = $provider['first_name'] . ' ' . $provider['last_name'];
$appointment_start_date = date($date_format . ' ' . $timeFormat, strtotime($appointment['start_datetime'])); $appointment_start_date = $appointment_start->format($date_format . ' ' . $time_format);
$appointment_end_date = date($date_format . ' ' . $timeFormat, strtotime($appointment['end_datetime'])); $appointment_end_date = $appointment_end->format($date_format . ' ' . $time_format);
$appointment_timezone = $timezones[$provider['timezone']]; $appointment_timezone = $timezones[empty($timezone) ? $provider['timezone'] : $timezone];
$appointment_link = $appointmentLink->get(); $appointment_link = $appointmentLink->get();
$company_link = $company['company_link']; $company_link = $settings['company_link'];
$company_name = $company['company_name']; $company_name = $settings['company_name'];
$customer_name = $customer['first_name'] . ' ' . $customer['last_name']; $customer_name = $customer['first_name'] . ' ' . $customer['last_name'];
$customer_email = $customer['email']; $customer_email = $customer['email'];
$customer_phone = $customer['phone_number']; $customer_phone = $customer['phone_number'];
@ -137,9 +158,9 @@ class Email {
require __DIR__ . '/../../application/views/emails/appointment_details.php'; require __DIR__ . '/../../application/views/emails/appointment_details.php';
$html = ob_get_clean(); $html = ob_get_clean();
$mailer = $this->_createMailer(); $mailer = $this->create_mailer();
$mailer->From = $company['company_email']; $mailer->From = $settings['company_email'];
$mailer->FromName = $company['company_name']; $mailer->FromName = $settings['company_name'];
$mailer->AddAddress($recipientEmail->get()); $mailer->AddAddress($recipientEmail->get());
$mailer->Subject = $title->get(); $mailer->Subject = $title->get();
$mailer->Body = $html; $mailer->Body = $html;
@ -147,7 +168,7 @@ class Email {
if ( ! $mailer->Send()) if ( ! $mailer->Send())
{ {
throw new \RuntimeException('Email could not been sent. Mailer Error (Line ' . __LINE__ . '): ' throw new RuntimeException('Email could not been sent. Mailer Error (Line ' . __LINE__ . '): '
. $mailer->ErrorInfo); . $mailer->ErrorInfo);
} }
} }
@ -165,26 +186,31 @@ class Email {
* @param array $provider The record data of the appointment provider. * @param array $provider The record data of the appointment provider.
* @param array $service The record data of the appointment service. * @param array $service The record data of the appointment service.
* @param array $customer The record data of the appointment customer. * @param array $customer The record data of the appointment customer.
* @param array $company Some settings that are required for this function. By now this array must contain * @param array $settings Some settings that are required for this function. As of now this array must contain
* the following values: "company_link", "company_name", "company_email". * the following values: "company_link", "company_name", "company_email".
* @param \EA\Engine\Types\Email $recipientEmail The email address of the email recipient. * @param \EA\Engine\Types\Email $recipientEmail The email address of the email recipient.
* @param \EA\Engine\Types\Text $reason The reason why the appointment is deleted. * @param \EA\Engine\Types\Text $reason The reason why the appointment is deleted.
* @param string|null $timezone Custom timezone.
*
* @throws \PHPMailer\PHPMailer\Exception
*/ */
public function sendDeleteAppointment( public function sendDeleteAppointment(
array $appointment, array $appointment,
array $provider, array $provider,
array $service, array $service,
array $customer, array $customer,
array $company, array $settings,
EmailAddress $recipientEmail, EmailAddress $recipientEmail,
Text $reason Text $reason,
) { $timezone = NULL
)
{
$framework = get_instance(); $framework = get_instance();
$timezones = $framework->timezones->to_array(); $timezones = $framework->timezones->to_array();
switch ($company['date_format']) switch ($settings['date_format'])
{ {
case 'DMY': case 'DMY':
$date_format = 'd/m/Y'; $date_format = 'd/m/Y';
@ -196,29 +222,38 @@ class Email {
$date_format = 'Y/m/d'; $date_format = 'Y/m/d';
break; break;
default: default:
throw new \Exception('Invalid date_format value: ' . $company['date_format']); throw new Exception('Invalid date_format value: ' . $settings['date_format']);
} }
switch ($company['time_format']) switch ($settings['time_format'])
{ {
case 'military': case 'military':
$timeFormat = 'H:i'; $time_format = 'H:i';
break; break;
case 'regular': case 'regular':
$timeFormat = 'g:i A'; $time_format = 'g:i A';
break; break;
default: default:
throw new \Exception('Invalid time_format value: ' . $company['time_format']); throw new Exception('Invalid time_format value: ' . $settings['time_format']);
}
$appointment_timezone = new DateTimeZone($provider['timezone']);
$appointment_start = new DateTime($appointment['start_datetime'], $appointment_timezone);
if ($timezone && $timezone !== $provider['timezone'])
{
$appointment_timezone = new DateTimeZone($timezone);
$appointment_start->setTimezone($appointment_timezone);
} }
// Prepare email template data. // Prepare email template data.
$appointment_service = $service['name']; $appointment_service = $service['name'];
$appointment_provider = $provider['first_name'] . ' ' . $provider['last_name']; $appointment_provider = $provider['first_name'] . ' ' . $provider['last_name'];
$appointment_date = date($date_format . ' ' . $timeFormat, strtotime($appointment['start_datetime'])); $appointment_date = $appointment_start->format($date_format . ' ' . $time_format);
$appointment_duration = $service['duration'] . ' ' . $this->framework->lang->line('minutes'); $appointment_duration = $service['duration'] . ' ' . $this->framework->lang->line('minutes');
$appointment_timezone = $timezones[$provider['timezone']]; $appointment_timezone = $timezones[empty($timezone) ? $provider['timezone'] : $timezone];
$company_link = $company['company_link']; $company_link = $settings['company_link'];
$company_name = $company['company_name']; $company_name = $settings['company_name'];
$customer_name = $customer['first_name'] . ' ' . $customer['last_name']; $customer_name = $customer['first_name'] . ' ' . $customer['last_name'];
$customer_email = $customer['email']; $customer_email = $customer['email'];
$customer_phone = $customer['phone_number']; $customer_phone = $customer['phone_number'];
@ -229,18 +264,18 @@ class Email {
require __DIR__ . '/../../application/views/emails/delete_appointment.php'; require __DIR__ . '/../../application/views/emails/delete_appointment.php';
$html = ob_get_clean(); $html = ob_get_clean();
$mailer = $this->_createMailer(); $mailer = $this->create_mailer();
// Send email to recipient. // Send email to recipient.
$mailer->From = $company['company_email']; $mailer->From = $settings['company_email'];
$mailer->FromName = $company['company_name']; $mailer->FromName = $settings['company_name'];
$mailer->AddAddress($recipientEmail->get()); // "Name" argument crushes the phpmailer class. $mailer->AddAddress($recipientEmail->get()); // "Name" argument crushes the phpmailer class.
$mailer->Subject = $this->framework->lang->line('appointment_cancelled_title'); $mailer->Subject = $this->framework->lang->line('appointment_cancelled_title');
$mailer->Body = $html; $mailer->Body = $html;
if ( ! $mailer->Send()) if ( ! $mailer->Send())
{ {
throw new \RuntimeException('Email could not been sent. Mailer Error (Line ' . __LINE__ . '): ' throw new RuntimeException('Email could not been sent. Mailer Error (Line ' . __LINE__ . '): '
. $mailer->ErrorInfo); . $mailer->ErrorInfo);
} }
} }
@ -265,7 +300,7 @@ class Email {
require __DIR__ . '/../../application/views/emails/new_password.php'; require __DIR__ . '/../../application/views/emails/new_password.php';
$html = ob_get_clean(); $html = ob_get_clean();
$mailer = $this->_createMailer(); $mailer = $this->create_mailer();
$mailer->From = $company['company_email']; $mailer->From = $company['company_email'];
$mailer->FromName = $company['company_name']; $mailer->FromName = $company['company_name'];
@ -275,7 +310,7 @@ class Email {
if ( ! $mailer->Send()) if ( ! $mailer->Send())
{ {
throw new \RuntimeException('Email could not been sent. Mailer Error (Line ' . __LINE__ . '): ' throw new RuntimeException('Email could not been sent. Mailer Error (Line ' . __LINE__ . '): '
. $mailer->ErrorInfo); . $mailer->ErrorInfo);
} }
} }
@ -285,7 +320,7 @@ class Email {
* *
* @return PHPMailer * @return PHPMailer
*/ */
protected function _createMailer() protected function create_mailer()
{ {
$mailer = new PHPMailer(); $mailer = new PHPMailer();