easyappointments/application/controllers/Calendar.php

612 lines
21 KiB
PHP

<?php defined('BASEPATH') or exit('No direct script access allowed');
/* ----------------------------------------------------------------------------
* Easy!Appointments - Open Source Web Scheduler
*
* @package EasyAppointments
* @author A.Tselegidis <alextselegidis@gmail.com>
* @copyright Copyright (c) 2013 - 2020, Alex Tselegidis
* @license https://opensource.org/licenses/GPL-3.0 - GPLv3
* @link https://easyappointments.org
* @since v1.5.0
* ---------------------------------------------------------------------------- */
/**
* Calendar controller.
*
* Handles calendar related operations.
*
* @package Controllers
*/
class Calendar extends EA_Controller {
/**
* @var array
*/
protected $permissions;
/**
* Calendar constructor.
*/
public function __construct()
{
parent::__construct();
$this->load->model('appointments_model');
$this->load->model('customers_model');
$this->load->model('services_model');
$this->load->model('providers_model');
$this->load->model('roles_model');
$this->load->library('accounts');
$this->load->library('google_sync');
$this->load->library('notifications');
$this->load->library('synchronization');
$this->load->library('timezones');
}
/**
* Display the main backend page.
*
* This method displays the main backend page. All login permission can view this page which displays a calendar
* with the events of the selected provider or service. If a user has more privileges he will see more menus at the
* top of the page.
*
* @param string $appointment_hash Appointment hash.
*/
public function index(string $appointment_hash = '')
{
session(['dest_url' => site_url('backend/index' . (! empty($appointment_hash) ? '/' . $appointment_hash : ''))]);
if (cannot('view', PRIV_APPOINTMENTS))
{
return;
}
$user_id = session('user_id');
$role_slug = session('role_slug');
$user = $this->users_model->find($user_id);
$secretary_providers = [];
if ($role_slug === DB_SLUG_SECRETARY)
{
$secretary = $this->secretaries_model->find(session('user_id'));
$secretary_providers = $secretary['providers'];
}
$edit_appointment = NULL;
if ( ! empty($appointment_hash))
{
$occurrences = $this->appointments_model->get(['hash' => $appointment_hash]);
if ($appointment_hash !== '' && ! empty($occurrences))
{
$edit_appointment = $occurrences[0];
$this->appointments_model->load($edit_appointment, ['customer']);
}
}
html_vars([
'page_title' => lang('calendar'),
'active_menu' => PRIV_APPOINTMENTS,
'user_display_name' => $this->accounts->get_user_display_name($user_id),
'timezones' => $this->timezones->to_array(),
'privileges' => $this->roles_model->get_permissions_by_slug($role_slug),
'calendar_view' => request('view', $user['settings']['calendar_view']),
'available_providers' => $this->providers_model->get_available_providers(),
'available_services' => $this->services_model->get_available_services(),
'secretary_providers' => $secretary_providers,
'edit_appointment' => $edit_appointment,
]);
$this->load->view('pages/calendar', html_vars());
}
/**
* Render the calendar page and display the selected appointment.
*
* This method will call the "index" callback to handle the page rendering.
*
* @param string $appointment_hash Appointment hash.
*/
public function reschedule(string $appointment_hash)
{
$this->index($appointment_hash);
}
/**
* Save appointment changes that are made from the backend calendar page.
*/
public function ajax_save_appointment()
{
try
{
// Save customer changes to the database.
$customer_data = request('customer_data');
if ($customer_data)
{
$customer = json_decode($customer_data, TRUE);
$required_permissions = ! empty($customer['id'])
? can('add', PRIV_CUSTOMERS)
: can('edit', PRIV_CUSTOMERS);
if ( ! $required_permissions)
{
throw new Exception('You do not have the required permissions for this task.');
}
$customer['id'] = $this->customers_model->save($customer);
}
// Save appointment changes to the database.
$appointment_data = request('appointment_data');
$manage_mode = ! empty($appointment_data['id']);
if ($appointment_data)
{
$appointment = json_decode($appointment_data, TRUE);
$required_permissions = ! empty($appointment['id'])
? can('add', PRIV_APPOINTMENTS)
: can('edit', PRIV_APPOINTMENTS);
if ($required_permissions == FALSE)
{
throw new Exception('You do not have the required permissions for this task.');
}
// If the appointment does not contain the customer record id, then it means that is going to be
// inserted.
if ( ! isset($appointment['id_users_customer']))
{
$appointment['id_users_customer'] = $customer['id'] ?? $customer_data['id'];
}
$appointment['id'] = $this->appointments_model->save($appointment);
}
if (empty($appointment['id']))
{
throw new RuntimeException('The appointment ID is not available.');
}
$appointment = $this->appointments_model->find($appointment['id']);
$provider = $this->providers_model->find($appointment['id_users_provider']);
$customer = $this->customers_model->find($appointment['id_users_customer']);
$service = $this->services_model->find($appointment['id_services']);
$settings = [
'company_name' => setting('company_name'),
'company_link' => setting('company_link'),
'company_email' => setting('company_email'),
'date_format' => setting('date_format'),
'time_format' => setting('time_format')
];
$this->synchronization->sync_appointment_saved($appointment, $service, $provider, $customer, $settings, $manage_mode);
$this->notifications->notify_appointment_saved($appointment, $service, $provider, $customer, $settings, $manage_mode);
json_response([
'success' => TRUE,
]);
}
catch (Throwable $e)
{
json_exception($e);
}
}
/**
* Delete appointment from the database.
*
* This method deletes an existing appointment from the database. Once this action is finished it cannot be undone.
* Notification emails are send to both provider and customer and the delete action is executed to the Google
* Calendar account of the provider, if the "google_sync" setting is enabled.
*/
public function ajax_delete_appointment()
{
try
{
if (cannot('delete', 'appointments'))
{
throw new Exception('You do not have the required permissions for this task.');
}
$appointment_id = request('appointment_id');
if (empty($appointment_id))
{
throw new Exception('No appointment id provided.');
}
// Store appointment data for later use in this method.
$appointment = $this->appointments_model->find($appointment_id);
$provider = $this->providers_model->find($appointment['id_users_provider']);
$customer = $this->customers_model->find($appointment['id_users_customer']);
$service = $this->services_model->find($appointment['id_services']);
$settings = [
'company_name' => setting('company_name'),
'company_email' => setting('company_email'),
'company_link' => setting('company_link'),
'date_format' => setting('date_format'),
'time_format' => setting('time_format')
];
// Delete appointment record from the database.
$this->appointments_model->delete($appointment_id);
$this->notifications->notify_appointment_deleted($appointment, $service, $provider, $customer, $settings);
$this->synchronization->sync_appointment_deleted($appointment, $provider);
json_response([
'success' => TRUE,
]);
}
catch (Throwable $e)
{
json_exception($e);
}
}
/**
* Insert of update unavailable time period to database.
*/
public function ajax_save_unavailable()
{
try
{
// Check privileges
$unavailable = json_decode(request('unavailable'), TRUE);
$required_permissions = ( ! isset($unavailable['id']))
? can('add', PRIV_APPOINTMENTS)
: can('edit', PRIV_APPOINTMENTS);
if ( ! $required_permissions)
{
throw new Exception('You do not have the required permissions for this task.');
}
$provider = $this->providers_model->find($unavailable['id_users_provider']);
// Add appointment
$unavailable['id'] = $this->unavailabilities_model->save($unavailable);
$unavailable = $this->unavailabilities_model->find($unavailable['id']); // fetch all inserted data
// Google Sync
try
{
$google_sync = $this->providers_model->get_setting($unavailable['id_users_provider'], 'google_sync');
if ($google_sync)
{
$google_token = json_decode($this->providers_model->get_setting($unavailable['id_users_provider'], 'google_token'));
$this->google_sync->refresh_token($google_token->refresh_token);
if ($unavailable['id_google_calendar'] == NULL)
{
$google_event = $this->google_sync->add_unavailable($provider, $unavailable);
$unavailable['id_google_calendar'] = $google_event->id;
$this->unavailabilities_model->save($unavailable);
}
else
{
$this->google_sync->update_unavailable($provider, $unavailable);
}
}
}
catch (Throwable $e)
{
$warnings[] = $e;
}
json_response([
'success' => TRUE,
'warnings' => $warnings ?? []
]);
}
catch (Throwable $e)
{
json_exception($e);
}
}
/**
* Delete an unavailable time period from database.
*/
public function ajax_delete_unavailable()
{
try
{
if (can('delete', PRIV_APPOINTMENTS))
{
throw new Exception('You do not have the required permissions for this task.');
}
$unavailable_id = request('unavailable_id');
$unavailable = $this->appointments_model->find($unavailable_id);
$provider = $this->providers_model->find($unavailable['id_users_provider']);
$this->appointments_model->delete($unavailable['id']);
// Google Sync
try
{
$google_sync = $this->providers_model->get_setting($provider['id'], 'google_sync');
if ($google_sync == TRUE)
{
$google_token = json_decode($this->providers_model->get_setting($provider['id'], 'google_token'));
$this->google_sync->refresh_token($google_token->refresh_token);
$this->google_sync->delete_unavailable($provider, $unavailable['id_google_calendar']);
}
}
catch (Throwable $e)
{
$warnings[] = $e;
}
json_response([
'success' => TRUE,
'warnings' => $warnings ?? []
]);
}
catch (Throwable $e)
{
json_exception($e);
}
}
/**
* Insert of update working plan exceptions to database.
*/
public function ajax_save_working_plan_exception()
{
try
{
$required_permissions = can('edit', PRIV_USERS);
if ( ! $required_permissions)
{
throw new Exception('You do not have the required permissions for this task.');
}
$date = request('date');
$working_plan_exception = request('working_plan_exception');
$provider_id = request('provider_id');
$this->providers_model->save_working_plan_exception($provider_id, $date, $working_plan_exception);
json_response([
'success' => TRUE,
]);
}
catch (Throwable $e)
{
json_exception($e);
}
}
/**
* Delete a working plan exceptions time period to database.
*/
public function ajax_delete_working_plan_exception()
{
try
{
$required_permissions = can('edit', PRIV_CUSTOMERS);
if ( ! $required_permissions)
{
throw new Exception('You do not have the required permissions for this task.');
}
$date = request('date');
$provider_id = request('provider_id');
$this->providers_model->delete_working_plan_exception($provider_id, $date);
json_response([
'success' => TRUE
]);
}
catch (Throwable $e)
{
json_exception($e);
}
}
/**
* Get Calendar Events
*
* This method will return all the calendar events within a specified period.
*/
public function ajax_get_calendar_events()
{
try
{
$start_date = request('startDate') . ' 00:00:00';
$end_date = request('endDate') . ' 23:59:59';
$response = [
'appointments' => $this->appointments_model->get([
'is_unavailable' => FALSE,
'start_datetime >=' => $start_date,
'end_datetime <=' => $end_date
]),
'unavailability_events' => $this->appointments_model->get([
'is_unavailable' => TRUE,
'start_datetime >=' => $start_date,
'end_datetime <=' => $end_date
])
];
foreach ($response['appointments'] as &$appointment)
{
$appointment['provider'] = $this->providers_model->find($appointment['id_users_provider']);
$appointment['service'] = $this->services_model->find($appointment['id_services']);
$appointment['customer'] = $this->customers_model->find($appointment['id_users_customer']);
}
unset($appointment);
$user_id = session('user_id');
$role_slug = session('role_slug');
// If the current user is a provider he must only see his own appointments.
if ($role_slug === DB_SLUG_PROVIDER)
{
foreach ($response['appointments'] as $index => $appointment)
{
if ((int)$appointment['id_users_provider'] !== (int)$user_id)
{
unset($response['appointments'][$index]);
}
}
foreach ($response['unavailability_events'] as $index => $unavailability_event)
{
if ((int)$unavailability_event['id_users_provider'] !== (int)$user_id)
{
unset($response['unavailability_events'][$index]);
}
}
}
// If the current user is a secretary he must only see the appointments of his providers.
if ($role_slug === DB_SLUG_SECRETARY)
{
$providers = $this->secretaries_model->find($user_id)['providers'];
foreach ($response['appointments'] as $index => $appointment)
{
if ( ! in_array((int)$appointment['id_users_provider'], $providers))
{
unset($response['appointments'][$index]);
}
}
foreach ($response['unavailability_events'] as $index => $unavailability_event)
{
if ( ! in_array((int)$unavailability_event['id_users_provider'], $providers))
{
unset($response['unavailability_events'][$index]);
}
}
}
json_response($response);
}
catch (Throwable $e)
{
json_exception($e);
}
}
/**
* Get the registered appointments for the given date period and record.
*
* This method returns the database appointments and unavailable periods for the user selected date period and
* record type (provider or service).
*/
public function ajax_get_calendar_appointments()
{
try
{
if (cannot('view', PRIV_APPOINTMENTS))
{
throw new Exception('You do not have the required permissions for this task.');
}
$filter_type = request('filter_type');
if ( ! $filter_type)
{
json_response([
'appointments' => [],
'unavailables' => []
]);
return;
}
if ($filter_type == FILTER_TYPE_PROVIDER)
{
$where_id = 'id_users_provider';
}
else
{
$where_id = 'id_services';
}
// Get appointments
$record_id = $this->db->escape(request('record_id'));
$start_date = $this->db->escape(request('start_date'));
$end_date = $this->db->escape(date('Y-m-d', strtotime(request('end_date') . ' +1 day')));
$where_clause = $where_id . ' = ' . $record_id . '
AND ((start_datetime > ' . $start_date . ' AND start_datetime < ' . $end_date . ')
or (end_datetime > ' . $start_date . ' AND end_datetime < ' . $end_date . ')
or (start_datetime <= ' . $start_date . ' AND end_datetime >= ' . $end_date . '))
AND is_unavailable = 0
';
$response['appointments'] = $this->appointments_model->get($where_clause);
foreach ($response['appointments'] as &$appointment)
{
$appointment['provider'] = $this->providers_model->find($appointment['id_users_provider']);
$appointment['service'] = $this->services_model->find($appointment['id_services']);
$appointment['customer'] = $this->customers_model->find($appointment['id_users_customer']);
}
// Get unavailable periods (only for provider).
$response['unavailables'] = [];
if ($filter_type == FILTER_TYPE_PROVIDER)
{
$where_clause = $where_id . ' = ' . $record_id . '
AND ((start_datetime > ' . $start_date . ' AND start_datetime < ' . $end_date . ')
or (end_datetime > ' . $start_date . ' AND end_datetime < ' . $end_date . ')
or (start_datetime <= ' . $start_date . ' AND end_datetime >= ' . $end_date . '))
AND is_unavailable = 1
';
$response['unavailables'] = $this->appointments_model->get($where_clause);
}
foreach ($response['unavailables'] as &$unavailable)
{
$unavailable['provider'] = $this->providers_model->find($unavailable['id_users_provider']);
}
json_response($response);
}
catch (Throwable $e)
{
json_exception($e);
}
}
}