diff --git a/.github/SECURITY.md b/.github/SECURITY.md
new file mode 100644
index 00000000..1870e6f1
--- /dev/null
+++ b/.github/SECURITY.md
@@ -0,0 +1,4 @@
+# Security Vulnerabilities
+
+If you discover a security vulnerability within Easy!Appointments, please send an email to info@easyappointments.org.
+All security vulnerabilities will be promptly addressed.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index aa6e019e..1ec4719a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,36 @@
This file contains the code changes that were introduced into each release (starting from v1.1.0) so that is easy for
developers to maintain and readjust their custom modifications on the main project codebase.
+## [1.4.2] - 2021-07-27
+
+### Added
+
+- #1004: Add support for line breaks when displaying the service description in the frontend.
+- #1040: Support all-day events while syncing with Google Calendar.
+
+### Fixed
+
+- #961: Timezone/UX issue: Wrong day is selected when timezone differs by -1 day.
+- #966: Secretaries are getting notification emails for providers that are not assigned to them.
+- #980: Missing Pacific (and potentially other) timezones.
+- #982: The Any-Provider option might lead to double bookings, if all the providers have the same number of appointments for the selected date.
+- #986: Managed to replicate appointment hash collisions.
+- #989: Fix Critical mistake resulting in wrong date
+- #990: The API availabilities controller throws an error when generating availability for services with multiple attendants.
+- #991: Available hours generated with the "Any Provider" option in the booking page, may use the information of a provider that is not assigned to the selected service.
+- #993: Add support for PHP8 (vendor packages need to be updated).
+- #1000: Small fix for the display of the delete button in table view.
+- #1011: Working plan exception - details pane shows incorrect details.
+- #1023: Backend calendar table events missing or duplicated.
+- #1026: The timepicker sliders do not work when using an iOS device.
+- #1029: Enhance SMTP functions of PHPMailer.
+- #1043: Unavailable events do not block time from services with multiple attendants.
+- #1046: Make sure that saving the modifications of a single break does not cancel any pending break edits.
+- #1068: Set minimum service duration field value to honor the value of EVENT_MINIMUM_DURATION.
+- #1073: Update PHPMailer dependencies.
+- #1074: In case of deletion of one appointment, system sends email to admins anyway even if they have email notifications disabled.
+- #1092: Javascript RangeError on appointment change causing disabled calendar dates.
+
## [1.4.1] - 2020-12-14
### Added
diff --git a/application/config/config.php b/application/config/config.php
index c4d1aae5..c217dcfd 100644
--- a/application/config/config.php
+++ b/application/config/config.php
@@ -8,7 +8,7 @@
| Declare some of the global config values of Easy!Appointments.
|
*/
-$config['version'] = '1.4.1'; // This must be changed manually.
+$config['version'] = '1.4.2'; // This must be changed manually.
$config['release_label'] = ''; // Leave empty for no title or add Alpha, Beta etc ...
$config['debug'] = Config::DEBUG_MODE;
@@ -113,7 +113,7 @@ $languages = [
'tr' => 'turkish',
];
-$language_code = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
+$language_code = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2) : 'en';
$config['language'] = isset($_SERVER['HTTP_ACCEPT_LANGUAGE'], $languages[$language_code])
? $languages[$language_code]
@@ -314,7 +314,7 @@ $config['cache_path'] = __DIR__ . '/../../storage/cache/';
| new release.
|
*/
-$config['cache_busting_token'] = '924WX';
+$config['cache_busting_token'] = 'ZV947';
/*
|--------------------------------------------------------------------------
diff --git a/application/config/email.php b/application/config/email.php
index 905cf010..0f70a35d 100644
--- a/application/config/email.php
+++ b/application/config/email.php
@@ -7,11 +7,8 @@
$config['useragent'] = 'Easy!Appointments';
$config['protocol'] = 'mail'; // or 'smtp'
$config['mailtype'] = 'html'; // or 'text'
-
// $config['smtp_debug'] = '0'; // or '1'
-// $config['smtp_auth'] = TRUE; //or FALSE for anonymous relay NOTE: DONT USE QUOTES ' !
-
-// $config['smtp_host'] = '';
+// $config['smtp_auth'] = TRUE; //or FALSE for anonymous relay.
// $config['smtp_host'] = '';
// $config['smtp_user'] = '';
// $config['smtp_pass'] = '';
diff --git a/application/controllers/Appointments.php b/application/controllers/Appointments.php
index 7f173cc7..b3be8488 100755
--- a/application/controllers/Appointments.php
+++ b/application/controllers/Appointments.php
@@ -344,25 +344,34 @@ class Appointments extends EA_Controller {
// If the user has selected the "any-provider" option then we will need to search for an available provider
// that will provide the requested service.
+ $service = $this->services_model->get_row($service_id);
+
if ($provider_id === ANY_PROVIDER)
{
- $provider_id = $this->search_any_provider($service_id, $selected_date);
-
- if ($provider_id === NULL)
+ $providers = $this->providers_model->get_batch();
+
+ $available_hours = [];
+
+ foreach($providers as $provider)
{
- $this->output
- ->set_content_type('application/json')
- ->set_output(json_encode([]));
-
- return;
+ if (!in_array($service_id, $provider['services']))
+ {
+ continue;
+ }
+
+ $provider_available_hours = $this->availability->get_available_hours($selected_date, $service, $provider, $exclude_appointment_id);
+
+ $available_hours = array_merge($available_hours, $provider_available_hours);
}
+
+ $response = array_unique(array_values($available_hours));
}
+ else
+ {
+ $provider = $this->providers_model->get_row($provider_id);
- $service = $this->services_model->get_row($service_id);
-
- $provider = $this->providers_model->get_row($provider_id);
-
- $response = $this->availability->get_available_hours($selected_date, $service, $provider, $exclude_appointment_id);
+ $response = $this->availability->get_available_hours($selected_date, $service, $provider, $exclude_appointment_id);
+ }
}
catch (Exception $exception)
{
diff --git a/application/controllers/Backend_api.php b/application/controllers/Backend_api.php
index 0688964b..4a188199 100755
--- a/application/controllers/Backend_api.php
+++ b/application/controllers/Backend_api.php
@@ -362,98 +362,9 @@ class Backend_api extends EA_Controller {
// Delete appointment record from the database.
$this->appointments_model->delete($this->input->post('appointment_id'));
- // Sync removal with Google Calendar.
- if ($appointment['id_google_calendar'] != NULL)
- {
- try
- {
- $google_sync = $this->providers_model->get_setting('google_sync', $provider['id']);
+ $this->notifications->notify_appointment_deleted($appointment, $service, $provider, $customer, $settings);
- if ($google_sync == TRUE)
- {
- $google_token = json_decode($this->providers_model
- ->get_setting('google_token', $provider['id']));
- $this->google_sync->refresh_token($google_token->refresh_token);
- $this->google_sync->delete_appointment($provider, $appointment['id_google_calendar']);
- }
- }
- catch (Exception $exception)
- {
- $warnings[] = [
- 'message' => $exception->getMessage(),
- 'trace' => config('debug') ? $exception->getTrace() : []
- ];
- }
- }
-
- // Send notification emails to provider and customer.
- try
- {
- $this->config->load('email');
-
- $email = new EmailClient($this, $this->config->config);
-
- $send_provider = $this->providers_model
- ->get_setting('notifications', $provider['id']);
-
- if ((bool)$send_provider === TRUE)
- {
- $email->send_delete_appointment($appointment, $provider,
- $service, $customer, $settings, new Email($provider['email']),
- new Text($this->input->post('delete_reason')));
- }
-
- $send_customer = $this->settings_model->get_setting('customer_notifications');
-
- if ((bool)$send_customer === TRUE)
- {
- $email->send_delete_appointment($appointment, $provider,
- $service, $customer, $settings, new Email($customer['email']),
- new Text($this->input->post('delete_reason')));
- }
-
- // Notify admins
- $admins = $this->admins_model->get_batch();
-
- foreach ($admins as $admin)
- {
- if ( ! $admin['settings']['notifications'] === '0')
- {
- continue;
- }
-
- $email->send_delete_appointment($appointment, $provider,
- $service, $customer, $settings, new Email($admin['email']),
- new Text($this->input->post('cancel_reason')));
- }
-
- // Notify secretaries
- $secretaries = $this->secretaries_model->get_batch();
-
- foreach ($secretaries as $secretary)
- {
- if ( ! $secretary['settings']['notifications'] === '0')
- {
- continue;
- }
-
- if (in_array($provider['id'], $secretary['providers']))
- {
- continue;
- }
-
- $email->send_delete_appointment($appointment, $provider,
- $service, $customer, $settings, new Email($secretary['email']),
- new Text($this->input->post('cancel_reason')));
- }
- }
- catch (Exception $exception)
- {
- $warnings[] = [
- 'message' => $exception->getMessage(),
- 'trace' => config('debug') ? $exception->getTrace() : []
- ];
- }
+ $this->synchronization->sync_appointment_deleted($appointment, $provider);
if (empty($warnings))
{
diff --git a/application/controllers/api/v1/Providers.php b/application/controllers/api/v1/Providers.php
index a3739968..b6417d5b 100644
--- a/application/controllers/api/v1/Providers.php
+++ b/application/controllers/api/v1/Providers.php
@@ -102,7 +102,7 @@ class Providers extends API_V1_Controller {
throw new Exception('No settings property provided.');
}
- if ( ! array_key_exists('working_plan', $provider['settings']['working_plan']))
+ if ( ! array_key_exists('working_plan', $provider['settings']))
{
$provider['settings']['working_plan'] = $this->settings_model->get_setting('company_working_plan');
}
diff --git a/application/core/EA_Model.php b/application/core/EA_Model.php
index 3e66a3e7..58c1ffc8 100644
--- a/application/core/EA_Model.php
+++ b/application/core/EA_Model.php
@@ -58,5 +58,11 @@
* @property Timezones $timezones
*/
class EA_Model extends CI_Model {
- //
+ /**
+ * EA_Model constructor.
+ */
+ public function __construct()
+ {
+ //
+ }
}
diff --git a/application/language/czech/translations_lang.php b/application/language/czech/translations_lang.php
index 0d9dfc48..e217e734 100644
--- a/application/language/czech/translations_lang.php
+++ b/application/language/czech/translations_lang.php
@@ -1,7 +1,7 @@
diff($end_hour);
- while (($diff->h * 60 + $diff->i) >= (int)$service['duration'])
+ while (($diff->h * 60 + $diff->i) >= (int)$service['duration'] && $diff->invert === 0)
{
$available_hours[] = $current_hour->format('H:i');
$current_hour->add(new DateInterval('PT' . $interval . 'M'));
@@ -341,7 +341,8 @@ class Availability {
{
$unavailability_events = $this->CI->appointments_model->get_batch([
'is_unavailable' => TRUE,
- 'DATE(start_datetime)' => $date,
+ 'DATE(start_datetime) <=' => $date,
+ 'DATE(end_datetime) >=' => $date,
'id_users_provider' => $provider['id']
]);
diff --git a/application/libraries/Notifications.php b/application/libraries/Notifications.php
index 86ac282e..a691fca1 100644
--- a/application/libraries/Notifications.php
+++ b/application/libraries/Notifications.php
@@ -163,6 +163,13 @@ class Notifications {
{
$email = new EmailClient($this->CI, $this->CI->config->config);
+ $delete_reason = (string)$this->CI->input->post('delete_reason');
+
+ if ( ! $delete_reason)
+ {
+ $delete_reason = (string)$this->CI->input->post('cancel_reason');
+ }
+
$send_provider = filter_var($this->CI->providers_model->get_setting('notifications', $provider['id']),
FILTER_VALIDATE_BOOLEAN);
@@ -170,7 +177,7 @@ class Notifications {
{
$email->send_delete_appointment($appointment, $provider,
$service, $customer, $settings, new Email($provider['email']),
- new Text($this->CI->input->post('cancel_reason')));
+ new Text($delete_reason));
}
$send_customer = filter_var(
@@ -181,7 +188,7 @@ class Notifications {
{
$email->send_delete_appointment($appointment, $provider,
$service, $customer, $settings, new Email($customer['email']),
- new Text($this->CI->input->post('cancel_reason')));
+ new Text($delete_reason));
}
// Notify admins
diff --git a/application/libraries/Timezones.php b/application/libraries/Timezones.php
index 357bca0b..9df4a4c9 100644
--- a/application/libraries/Timezones.php
+++ b/application/libraries/Timezones.php
@@ -458,6 +458,44 @@ class Timezones {
'Australia/LHI' => 'LHI (+10:30)',
'Australia/Lord_Howe' => 'Lord_Howe (+10:30)',
],
+ 'Pacific' => [
+ 'Pacific/Apia' => 'Apia (+13:00)',
+ 'Pacific/Auckland' => 'Auckland (+12:00)',
+ 'Pacific/Bougainville' => 'Bougainville (+11:00)',
+ 'Pacific/Chatham' => 'Chatham (+12:45)',
+ 'Pacific/Chuuk' => 'Chuuk (+10:00)',
+ 'Pacific/Easter' => 'Easter (−06:00)',
+ 'Pacific/Efate' => 'Efate (+11:00)',
+ 'Pacific/Enderbury' => 'Enderbury (+13:00)',
+ 'Pacific/Fakaofo' => 'Fakaofo (+13:00)',
+ 'Pacific/Fiji' => 'Fiji (+12:00)',
+ 'Pacific/Funafuti' => 'Funafuti (+12:00)',
+ 'Pacific/Galapagos' => 'Galapagos (−06:00)',
+ 'Pacific/Gambier' => 'Gambier (−09:00)',
+ 'Pacific/Guadalcanal' => 'Guadalcanal (+11:00)',
+ 'Pacific/Guam' => 'Guam (+10:00)',
+ 'Pacific/Honolulu' => 'Honolulu (−10:00)',
+ 'Pacific/Kiritimati' => 'Kiritimati (+14:00)',
+ 'Pacific/Kosrae' => 'Kosrae (+11:00)',
+ 'Pacific/Kwajalein' => 'Kwajalein (+12:00)',
+ 'Pacific/Majuro' => 'Majuro (+12:00)',
+ 'Pacific/Marquesas' => 'Marquesas (−09:30)',
+ 'Pacific/Nauru' => 'Nauru (+12:00)',
+ 'Pacific/Niue' => 'Niue (−11:00)',
+ 'Pacific/Norfolk' => 'Norfolk (+11:00)',
+ 'Pacific/Noumea' => 'Noumea (+11:00)',
+ 'Pacific/Pago_Pago' => 'Pago_Pago (−11:00)',
+ 'Pacific/Palau' => 'Palau (+09:00)',
+ 'Pacific/Pitcairn' => 'Pitcairn (−08:00)',
+ 'Pacific/Pohnpei' => 'Pohnpei (+11:00)',
+ 'Pacific/Port_Moresby' => 'Port_Moresby (+10:00)',
+ 'Pacific/Rarotonga' => 'Rarotonga (−10:00)',
+ 'Pacific/Tahiti' => 'Tahiti (−10:00)',
+ 'Pacific/Tarawa' => 'Tarawa (+12:00)',
+ 'Pacific/Tongatapu' => 'Tongatapu (+13:00)',
+ 'Pacific/Wake' => 'Wake (+12:00)',
+ 'Pacific/Wallis' => 'Wallis (+12:00)',
+ ],
];
/**
diff --git a/application/models/Secretaries_model.php b/application/models/Secretaries_model.php
index 0a8bd153..6b81805a 100644
--- a/application/models/Secretaries_model.php
+++ b/application/models/Secretaries_model.php
@@ -162,7 +162,7 @@ class Secretaries_model extends EA_Model {
/**
* Validate Records Username
*
- * @param string $username The provider records username.
+ * @param string $username The secretary records username.
* @param int $user_id The user record id.
*
* @return bool Returns the validation result.
@@ -348,13 +348,13 @@ class Secretaries_model extends EA_Model {
}
/**
- * Set a provider's setting value in the database.
+ * Set a secretary's setting value in the database.
*
- * The provider and settings record must already exist.
+ * The secretary and settings record must already exist.
*
* @param string $setting_name The setting's name.
* @param string $value The setting's value.
- * @param int $secretary_id The selected provider id.
+ * @param int $secretary_id The selected secretary id.
*
* @return bool
*/
@@ -556,7 +556,7 @@ class Secretaries_model extends EA_Model {
}
/**
- * Get a providers setting from the database.
+ * Get a secretaries setting from the database.
*
* @param string $setting_name The setting name that is going to be returned.
* @param int $secretary_id The selected provider id.
@@ -565,8 +565,8 @@ class Secretaries_model extends EA_Model {
*/
public function get_setting($setting_name, $secretary_id)
{
- $provider_settings = $this->db->get_where('user_settings',
+ $secretary_settings = $this->db->get_where('user_settings',
['id_users' => $secretary_id])->row_array();
- return $provider_settings[$setting_name];
+ return $secretary_settings[$setting_name];
}
}
diff --git a/application/views/backend/header.php b/application/views/backend/header.php
index 789e8c10..1743c283 100755
--- a/application/views/backend/header.php
+++ b/application/views/backend/header.php
@@ -27,6 +27,7 @@
+
diff --git a/application/views/backend/services.php b/application/views/backend/services.php
index 80dd48ed..444d2c6d 100755
--- a/application/views/backend/services.php
+++ b/application/views/backend/services.php
@@ -116,7 +116,7 @@
= lang('duration_minutes') ?>
*
-
+
@@ -272,7 +281,7 @@
= lang('any_provider') ?>
-
+
= lang('display_any_provider_hint') ?>
@@ -473,77 +482,112 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+