From 3062dbc001e9163ae5827d1e5d23da28afbf1176 Mon Sep 17 00:00:00 2001 From: "alextselegidis@gmail.com" Date: Wed, 26 Jun 2013 09:31:57 +0000 Subject: [PATCH] =?UTF-8?q?=CE=9F=CE=BB=CE=BF=CE=BA=CE=BB=CE=AE=CF=81?= =?UTF-8?q?=CF=89=CF=83=CE=B7=20=CE=BB=CE=B5=CE=B9=CF=84=CE=BF=CF=85=CF=81?= =?UTF-8?q?=CE=B3=CE=B9=CF=8E=CE=BD=20=CE=B3=CE=B9=CE=B1=20=CF=84=CE=B7?= =?UTF-8?q?=CE=BD=20=CE=AD=CE=BA=CE=B4=CE=BF=CF=83=CE=B7=200.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- db/easy_appointments.sql | 18 +- src/application/controllers/appointments.php | 277 +++++++++++------- src/application/controllers/backend.php | 8 +- .../helpers/custom_exceptions_helper.php | 5 + .../libraries/Unit_tests/Unit_tests.php | 1 - src/application/libraries/google_sync.php | 36 +-- src/application/libraries/notifications.php | 130 +++----- .../views/appointments/book_success.php | 26 +- .../views/appointments/message.php | 62 ++++ ...ppointment.php => appointment_details.php} | 22 +- src/application/views/emails/book_success.php | 56 ---- ...appointment.php => delete_appointment.php} | 0 12 files changed, 333 insertions(+), 308 deletions(-) create mode 100644 src/application/views/appointments/message.php rename src/application/views/emails/{new_appointment.php => appointment_details.php} (81%) delete mode 100644 src/application/views/emails/book_success.php rename src/application/views/emails/{remove_appointment.php => delete_appointment.php} (100%) diff --git a/db/easy_appointments.sql b/db/easy_appointments.sql index 5dd8ad07..2383909d 100644 --- a/db/easy_appointments.sql +++ b/db/easy_appointments.sql @@ -3,7 +3,7 @@ -- http://www.phpmyadmin.net -- -- Φιλοξενητής: localhost --- Χρόνος δημιουργίας: 19 Ιουν 2013 στις 22:27:32 +-- Χρόνος δημιουργίας: 26 Ιουν 2013 στις 12:29:46 -- Έκδοση διακομιστή: 5.5.24-log -- Έκδοση PHP: 5.4.3 @@ -41,7 +41,7 @@ CREATE TABLE IF NOT EXISTS `ea_appointments` ( KEY `id_users_customer` (`id_users_customer`), KEY `id_services` (`id_services`), KEY `id_users_provider` (`id_users_provider`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=88 ; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=25 ; -- -------------------------------------------------------- @@ -184,17 +184,17 @@ CREATE TABLE IF NOT EXISTS `ea_users` ( `id_roles` bigint(20) unsigned NOT NULL, PRIMARY KEY (`id`), KEY `id_roles` (`id_roles`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=154 ; +) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ; -- -- Άδειασμα δεδομένων του πίνακα `ea_users` -- INSERT INTO `ea_users` (`id`, `first_name`, `last_name`, `email`, `mobile_number`, `phone_number`, `address`, `city`, `state`, `zip_code`, `notes`, `id_roles`) VALUES -(1, '', '1', 'alextselegidis@gmail.com', '123456789', '1', '', '', NULL, '', 'This is me making Easy!Appointments', 1), -(2, 'Γεώργιος', 'Παπαδόπουλος', 'alextselegidis@gmail.com', '1212121212', '1', '', '', NULL, '', 'This is a test provider', 2), -(3, 'Νίκος', 'Αναστασίου', 'prov2@test.gr', '1313133113131', '32132165146', 'Some Street 3', NULL, NULL, NULL, NULL, 2), -(4, 'Ηρώ', 'Καριοφύλη', 'prov3@test.gr', '239203490', '029340923', 'John Doe 3 ', NULL, NULL, NULL, NULL, 2); +(1, 'Alex', 'Tselegidis', 'alextselegidis@gmail.com', '123456789', '123456789', 'Some Str', 'Some City', 'Some State', '12345', 'This is me making Easy!Appointments :P', 1), +(2, 'Γεώργιος', 'Παπαδόπουλος', 'alextselegidis@gmail.com', '1111111111111', '1111111111111', '', '', NULL, '', 'This is a test provider (with my email for google syncing).', 2), +(3, 'Νίκος', 'Αναστασίου', 'prov2@test.gr', '2222222222222', '2222222222222', 'Some Street 3', NULL, NULL, NULL, NULL, 2), +(4, 'Ηρώ', 'Καριοφύλη', 'prov3@test.gr', '3333333333333', '3333333333333', 'John Doe 3 ', NULL, NULL, NULL, NULL, 2); -- -------------------------------------------------------- @@ -218,9 +218,9 @@ CREATE TABLE IF NOT EXISTS `ea_user_settings` ( -- INSERT INTO `ea_user_settings` (`id_users`, `username`, `password`, `working_plan`, `notifications`, `google_sync`, `google_token`) VALUES -(2, 'provider_1', 'provider_1', '{"monday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"tuesday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"wednesday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"thursday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"friday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"saturday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"sunday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]}}', NULL, 1, '{"access_token":"ya29.AHES6ZRsDBInIFSW1vdMEUt9N_teDoKPk6IVLS-mM41J7P0","token_type":"Bearer","expires_in":3600,"refresh_token":"1\\/9KusWyDci21Fv-PpgeZr3Yik56WnNQ7LDTcmeUhNTN8","created":1371639646}'), +(2, 'provider_1', 'provider_1', '{"monday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"tuesday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"wednesday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"thursday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"friday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"saturday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"sunday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]}}', NULL, 1, NULL), (3, 'provider_2', 'provider_2', '{"monday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"tuesday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"wednesday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"thursday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"friday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"saturday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"sunday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]}}', NULL, 0, NULL), -(4, 'provider_3', 'provider_3', '{"monday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"tuesday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"wednesday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"thursday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"friday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"saturday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"sunday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]}}', NULL, 1, '{"access_token":"ya29.AHES6ZQLXwNinpRgyZ30VP4aNy2MctNkj3fc6oJid8-Gc-TEifJ6WA","token_type":"Bearer","expires_in":3600,"refresh_token":"1\\/bBPokd195S2UX2so9-jclC3E3gpzxgyDjGhJkJxmkHU","created":1371639504}'); +(4, 'provider_3', 'provider_3', '{"monday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"tuesday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"wednesday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"thursday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"friday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"saturday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]},"sunday":{"start":"09:00","end":"18:00","breaks":[{"start":"11:20","end":"11:30"},{"start":"14:30","end":"15:00"}]}}', NULL, 1, NULL); -- -- Περιορισμοί για άχρηστους πίνακες diff --git a/src/application/controllers/appointments.php b/src/application/controllers/appointments.php index 25a1df9d..f177ae2a 100644 --- a/src/application/controllers/appointments.php +++ b/src/application/controllers/appointments.php @@ -12,28 +12,42 @@ class Appointments extends CI_Controller { * record. */ public function index($appointment_hash = '') { + $this->load->model('Appointments_Model'); + $this->load->model('Providers_Model'); + $this->load->model('Services_Model'); + $this->load->model('Customers_Model'); + $this->load->model('Settings_Model'); + if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST') { - $this->load->model('Settings_Model'); - $this->load->model('Services_Model'); - $this->load->model('Providers_Model'); - - $company_name = $this->Settings_Model->get_setting('company_name'); $available_services = $this->Services_Model->get_available_services(); $available_providers = $this->Providers_Model->get_available_providers(); + $company_name = $this->Settings_Model->get_setting('company_name'); // If an appointment hash is provided then it means that the customer // is trying to edit a registered record. if ($appointment_hash !== ''){ // Load the appointments data and set the manage mode of the page. - $this->load->model('Appointments_Model'); - $this->load->model('Customers_Model'); - $manage_mode = TRUE; $results = $this->Appointments_Model ->get_batch(array('hash' => $appointment_hash)); - $appointment_data = $results[0]; // Php 5.3 does not support treating a function as an array. + if (count($results) === 0) { + // The requested appointment doesn't exist in the database. Display + // a message to the customer. + $view_data = array( + 'message_title' => 'Appointment Not Found!', + 'message_text' => 'The appointment you requested does not exist in the ' + . 'database anymore.', + 'message_icon' => $this->config->item('base_url') . 'assets/images/error.png' + ); + + $this->load->view('appointments/message', $view_data); + return; + } + + // Php 5.3 does not support treating a function result as an array. + $appointment_data = $results[0]; $provider_data = $this->Providers_Model ->get_row($appointment_data['id_users_provider']); @@ -58,87 +72,111 @@ class Appointments extends CI_Controller { 'provider_data' => $provider_data, 'customer_data' => $customer_data ); + $this->load->view('appointments/book', $view_data); + } else { - // The page is a post-back. Register the appointment and send - // notification emails to the provider and the customer that are - // related to the appointment. - $post_data = json_decode($_POST['post_data'], true); - $appointment_data = $post_data['appointment']; - $customer_data = $post_data['customer']; - - $this->load->model('Customers_Model'); - $this->load->model('Appointments_Model'); - $this->load->model('Services_Model'); - $this->load->model('Providers_Model'); - $this->load->model('Settings_Model'); - - $customer_id = $this->Customers_Model->add($customer_data); - $appointment_data['id_users_customer'] = $customer_id; - $appointment_data['id'] = $this->Appointments_Model->add($appointment_data); - $appointment_data['hash'] = $this->Appointments_Model - ->get_value('hash', $appointment_data['id']); - - // Send an email to the customer with the appointment info. - $this->load->library('Notifications'); + // The page is a post-back. Register the appointment and send notification emails + // to the provider and the customer that are related to the appointment. If google + // sync is enabled then add the appointment to the provider's account. try { + $post_data = json_decode($_POST['post_data'], true); + $appointment_data = $post_data['appointment']; + $customer_data = $post_data['customer']; + + $customer_id = $this->Customers_Model->add($customer_data); + $appointment_data['id_users_customer'] = $customer_id; + + $appointment_data['id'] = $this->Appointments_Model->add($appointment_data); + $appointment_data['hash'] = $this->Appointments_Model + ->get_value('hash', $appointment_data['id']); + + // :: SEND NOTIFICATION EMAILS TO BOTH CUSTOMER AND PROVIDER + $this->load->library('Notifications'); + + $provider_data = $this->Providers_Model + ->get_row($appointment_data['id_users_provider']); + $service_data = $this->Services_Model->get_row($appointment_data['id_services']); + $company_settings = array( + 'company_name' => $this->Settings_Model->get_setting('company_name'), + 'company_link' => $this->Settings_Model->get_setting('company_link'), + 'company_email' => $this->Settings_Model->get_setting('company_email') + ); + if (!$post_data['manage_mode']) { $customer_title = 'Your appointment has been successfully booked!'; + $customer_message = 'Thank you for arranging an appointment with us. ' . + 'Below you can see the appointment details. Make changes ' . + 'by clicking the appointment link.'; + $customer_link = $this->config->item('base_url') . 'appointments/index/' + . $appointment_data['hash']; + $provider_title = 'A new appointment has been added to your plan.'; + $provider_message = 'You can make changes by clicking the appointment ' . + 'link below'; + $provider_link = $this->config->item('base_url') . 'backend/' + . $appointment_data['hash']; } else { - $customer_title = 'Appointment Changes Saved Successfully!'; - $provider_title = 'Appointment Details Have Changed'; + $customer_title = 'Appointment changes have been successfully saved!'; + $customer_message = ''; + $customer_link = $this->config->item('base_url') . 'appointments/index/' + . $appointment_data['hash']; + + $provider_title = 'Appointment details have changed.'; + $provider_message = ''; + $provider_link = $this->config->item('base_url') . 'backend/' + . $appointment_data['hash']; } - - $this->notifications->send_book_success( - $customer_data, $appointment_data, $customer_title); - $this->notifications->send_new_appointment( - $customer_data, $appointment_data, $provider_title); - - } catch (NotificationException $not_exc) { - $view_data['notification_error'] = '

' - . '
An unexpected error occured while sending you an ' 
-                        . 'email. Please backup the appointment details so that ' 
-                        . 'you can restore them later. 

Error:
' - . $not_exc->getMessage() . '
'; + + $this->notifications->send_appointment_details( + $appointment_data, $provider_data, $service_data, $customer_data, + $company_settings, $customer_title, $customer_message, $customer_link, + $customer_data['email']); + + $this->notifications->send_appointment_details( + $appointment_data, $provider_data, $service_data, $customer_data, + $company_settings, $provider_title, $provider_message, $provider_link, + $provider_data['email']); + + // :: SYNCHRONIZE APPOINTMENT WITH PROVIDER'S GOOGLE CALENDAR + // The provider must have previously granted access to his google calendar account + // in order to sync the appointment. + $google_sync = $this->Providers_Model->get_setting('google_sync', + $appointment_data['id_users_provider']); + + if ($google_sync == TRUE) { + $google_token = json_decode($this->Providers_Model + ->get_setting('google_token', $appointment_data['id_users_provider'])); + + $this->load->library('google_sync'); + $this->google_sync->refresh_token($google_token->refresh_token); + + if ($post_data['manage_mode'] === FALSE) { + // Add appointment to Google Calendar. + $this->google_sync->add_appointment($appointment_data['id']); + } else { + // Update appointment to Google Calendar. + $appointment_data['id_google_calendar'] = $this->Appointments_Model + ->get_value('id_google_calendar', $appointment_data['id']); + $this->google_sync->update_appointment($appointment_data, $provider_data, + $service_data, $customer_data, $company_settings); + } + } + + // :: LOAD THE BOOK SUCCESS VIEW + $view_data = array( + 'appointment_data' => $appointment_data, + 'provider_data' => $provider_data, + 'service_data' => $service_data, + 'company_name' => $company_settings['company_name'] + ); + + } catch(Exception $exc) { + $view_data['error'] = array( + 'message' => $exc->getMessage(), + 'technical' => $exc->getTraceAsString() + ); } - - // Synchronize the appointment with the providers plan, if the - // google sync option is enabled. - $google_sync = $this->Providers_Model->get_setting('google_sync', - $appointment_data['id_users_provider']); - - if ($google_sync == TRUE) { - $google_token = json_decode($this->Providers_Model->get_setting('google_token', - $appointment_data['id_users_provider'])); - - // Authenticate the token. If it isn't valid, the sync operation cannot - // be completed. - $this->load->library('google_sync'); - $this->google_sync->refresh_token($google_token->refresh_token); - - if ($post_data['manage_mode'] === FALSE) { - // Add appointment to Google Calendar. - $this->google_sync->add_appointment($appointment_data['id']); - } else { - // Update appointment to Google Calendar. - $this->google_sync->update_appointment($appointment_data['id']); - } - - } - - // Load the book success view. - $service_data = $this->Services_Model->get_row($appointment_data['id_services']); - $provider_data = $this->Providers_Model->get_row($appointment_data['id_users_provider']); - $company_name = $this->Settings_Model->get_setting('company_name'); - - $view_data = array( - 'appointment_data' => $appointment_data, - 'service_data' => $service_data, - 'provider_data' => $provider_data, - 'company_name' => $company_name - ); - $this->load->view('appointments/book_success', $view_data); } } @@ -169,13 +207,13 @@ class Appointments extends CI_Controller { } $appointment_data = $records[0]; - $provider_data = $this->Providers_Model->get_row($appointment_data['id_users_provider']); - $customer_data = $this->Customers_Model->get_row($appointment_data['id_users_customer']); - $service_data = $this->Services_Model->get_row($appointment_data['id_services']); - $company_settings = array( - 'company_name' => $this->Settings_Model->get_setting('company_name'), - 'company_email' => $this->Settings_Model->get_setting('company_email'), - 'company_link' => $this->Settings_Model->get_setting('company_link') + $provider_data = $this->Providers_Model->get_row($appointment_data['id_users_provider']); + $customer_data = $this->Customers_Model->get_row($appointment_data['id_users_customer']); + $service_data = $this->Services_Model->get_row($appointment_data['id_services']); + $company_settings = array( + 'company_name' => $this->Settings_Model->get_setting('company_name'), + 'company_email' => $this->Settings_Model->get_setting('company_email'), + 'company_link' => $this->Settings_Model->get_setting('company_link') ); // :: DELETE APPOINTMENT RECORD FROM THE DATABASE. @@ -200,9 +238,9 @@ class Appointments extends CI_Controller { // :: SEND NOTIFICATION EMAILS TO CUSTOMER AND PROVIDER $this->load->library('Notifications'); - $this->notifications->send_remove_appointment($appointment_data, $provider_data, + $this->notifications->send_delete_appointment($appointment_data, $provider_data, $service_data, $customer_data, $company_settings, $provider_data['email']); - $this->notifications->send_remove_appointment($appointment_data, $provider_data, + $this->notifications->send_delete_appointment($appointment_data, $provider_data, $service_data, $customer_data, $company_settings, $customer_data['email']); } catch(Exception $exc) { @@ -216,12 +254,11 @@ class Appointments extends CI_Controller { /** * [AJAX] Get the available appointment hours for the given date. * - * This method answers to an AJAX request. It calculates the - * available hours for the given service, provider and date. + * This method answers to an AJAX request. It calculates the available hours for the + * given service, provider and date. * - * @param array $_POST['post_data'] An associative array that - * contains the user selected 'service_id', 'provider_id', - * 'selected_date' and 'service_duration' in minutes. + * @param array $_POST['post_data'] An associative array that contains the user selected + * 'service_id', 'provider_id', 'selected_date' and 'service_duration' in minutes. * @return Returns a json object with the available hours. */ public function ajax_get_available_hours() { @@ -236,8 +273,7 @@ class Appointments extends CI_Controller { $where_clause = array( 'DATE(start_datetime)' => date('Y-m-d', strtotime($_POST['selected_date'])), 'id_users_provider' => $_POST['provider_id'] - ); - + ); $reserved_appointments = $this->Appointments_Model->get_batch($where_clause); if ($_POST['manage_mode'] === 'true') { @@ -283,7 +319,7 @@ class Appointments extends CI_Controller { 'end' => $sel_date_working_plan['end'] ); } - // PROBLEM + // Break the empty spaces with the reserved appointments. $empty_spaces_with_appointments = array(); if (count($reserved_appointments) > 0) { @@ -295,22 +331,49 @@ class Appointments extends CI_Controller { $space_end = date('H:i', strtotime($space['end'])); if ($space_start < $appointment_start && $space_end > $appointment_end) { + // We need to check whether another appointment fits in the current + // space. If this happens, then we need to consider the whole appointment + // time as one, because the provider will not be available. + foreach ($reserved_appointments as $appt) { + $appt_start = date('H:i', strtotime($appt['start_datetime'])); + $appt_end = date('H:i', strtotime($appt['end_datetime'])); + + if ($space_start < $appt_start && $space_end > $appt_end) { + if ($appointment_start > $appt_start) { + $appointment_start = $appt_start; + } + + if ($appointment_end < $appt_end) { + $appointment_end = $appt_end; + } + } + } + // Current appointment is within the current empty space. So // we need to break the empty space into two other spaces that // don't include the appointment. - $empty_spaces_with_appointments[] = array( - 'start' => $space_start, - 'end' => $appointment_start - ); - $empty_spaces_with_appointments[] = array( - 'start' => $appointment_end, - 'end' => $space_end - ); + $new_space = array( + 'start' => $space_start, + 'end' => $appointment_start + ); + + if (!in_array($new_space, $empty_spaces_with_appointments)) { + $empty_spaces_with_appointments[] = $new_space; + } + + $new_space = array( + 'start' => $appointment_end, + 'end' => $space_end + ); + if (!in_array($new_space, $empty_spaces_with_appointments)) { + $empty_spaces_with_appointments[] = $new_space; + } + } else { // Check if there are any other appointments between this // time space. If not, it is going to be added as it is. $found = FALSE; - foreach($reserved_appointments as $appt) { + foreach ($reserved_appointments as $appt) { $appt_start = date('H:i', strtotime($appt['start_datetime'])); $appt_end = date('H:i', strtotime($appt['end_datetime'])); if ($space_start < $appt_start && $space_end > $appt_end) { diff --git a/src/application/controllers/backend.php b/src/application/controllers/backend.php index 63c0b940..0d1fac70 100644 --- a/src/application/controllers/backend.php +++ b/src/application/controllers/backend.php @@ -95,10 +95,6 @@ class Backend extends CI_Controller { * appointment data. * @param array $_POST['customer_data'] (OPTIONAL) Array with the customer * data. - * - * @task Send email notifications to both provider and customer that changes - * have been made to the appointment. - * @task Sync changes with google calendar. */ public function ajax_save_appointment_changes() { try { @@ -215,9 +211,9 @@ class Backend extends CI_Controller { // :: SEND NOTIFICATION EMAILS TO PROVIDER AND CUSTOMER. $this->load->library('Notifications'); - $this->notifications->send_remove_appointment($appointment_data, $provider_data, + $this->notifications->send_delete_appointment($appointment_data, $provider_data, $service_data, $customer_data, $company_settings, $provider_data['email']); - $this->notifications->send_remove_appointment($appointment_data, $provider_data, + $this->notifications->send_delete_appointment($appointment_data, $provider_data, $service_data, $customer_data, $company_settings, $customer_data['email']); echo json_encode('SUCCESS'); diff --git a/src/application/helpers/custom_exceptions_helper.php b/src/application/helpers/custom_exceptions_helper.php index d0de816d..c1a01c25 100644 --- a/src/application/helpers/custom_exceptions_helper.php +++ b/src/application/helpers/custom_exceptions_helper.php @@ -15,5 +15,10 @@ class ValidationException extends Exception {} */ class NotificationException extends Exception {} +/** + * Sync Exception Class + */ +class SyncException extends Exception {} + /* End of file exception_types_helper.php */ /* Location: ./application/helpers/exception_types_helper.php */ \ No newline at end of file diff --git a/src/application/libraries/Unit_tests/Unit_tests.php b/src/application/libraries/Unit_tests/Unit_tests.php index 67342736..7f5870d9 100644 --- a/src/application/libraries/Unit_tests/Unit_tests.php +++ b/src/application/libraries/Unit_tests/Unit_tests.php @@ -69,7 +69,6 @@ class Unit_tests extends CI_Driver_Library { */ public function run_library_tests($output_report = true) { // @task Implement unit tests for the libraries. - if ($output_report) { $this->CI->output->append_output($this->CI->unit->report()); } diff --git a/src/application/libraries/google_sync.php b/src/application/libraries/google_sync.php index 68d75140..d1413337 100644 --- a/src/application/libraries/google_sync.php +++ b/src/application/libraries/google_sync.php @@ -94,36 +94,23 @@ class Google_Sync { * If yes, the selected appointment record is going to be added to the Google * Calendar account. * - * IMPORTANT! If the access token is not valid anymore the - * appointment cannot be added to the Google Calendar. A notification warning - * must be sent to the provider in order to authorize the E!A again, and store - * the new access token to the database. - * - * @param int $appointment_id The record id of the appointment that is going to - * be added to the database. + * @param array $appointment_data Contains the appointment record data. + * @param array $provider_data Contains the provider record data. + * @param array $service_data Contains the service record data. + * @param array $customer_data Contains the customer recod data. + * @parma array $company_settings Contains some company settings that are used + * by this method. By the time the following values must be in the array: + * 'company_name'. * @return Google_Event Returns the Google_Event class object. - * - * @task This library should not use the models. The data must be provided from - * the controllers (same for notification library). */ - public function add_appointment($appointment_id) { - $this->CI->load->model('Appointments_Model'); - $this->CI->load->model('Providers_Model'); - $this->CI->load->model('Services_Model'); - $this->CI->load->model('Customers_Model'); - $this->CI->load->model('Settings_Model'); - - $appointment_data = $this->CI->Appointments_Model->get_row($appointment_id); - $provider_data = $this->CI->Providers_Model->get_row($appointment_data['id_users_provider']); - $customer_data = $this->CI->Customers_Model->get_row($appointment_data['id_users_customer']); - $service_data = $this->CI->Services_Model->get_row($appointment_data['id_services']); - $company_name = $this->CI->Settings_Model->get_setting('company_name'); + public function add_appointment($appointment_data, $provider_data, $service_data, + $customer_data, $company_settings) { $this->CI->load->helper('general'); $event = new Google_Event(); $event->setSummary($service_data['name']); - $event->setLocation($company_name); + $event->setLocation($company_settings['company_name']); $start = new Google_EventDateTime(); $start->setDateTime(date3339(strtotime($appointment_data['start_datetime']))); @@ -152,7 +139,7 @@ class Google_Sync { $created_event = $this->service->events->insert('primary', $event); // Set the Google Calendar event id to the E!A database record. - $appointment_data['id_google_calendar'] = $created_event['id']; + $appointment_data['id_google_calendar'] = $created_event->id; $this->CI->Appointments_Model->add($appointment_data); return $created_event; @@ -171,7 +158,6 @@ class Google_Sync { * @parma array $company_settings Contains some company settings that are used * by this method. By the time the following values must be in the array: * 'company_name'. - * * @return Google_Event Returns the Google_Event class object. */ public function update_appointment($appointment_data, $provider_data, diff --git a/src/application/libraries/notifications.php b/src/application/libraries/notifications.php index 2b91f460..72160cca 100644 --- a/src/application/libraries/notifications.php +++ b/src/application/libraries/notifications.php @@ -41,94 +41,43 @@ class Notifications { } /** - * Send a success email to the customer that booked - * a new appointment. + * Send an email with the appointment details. * - * @expectedException NotificationException Raises when an unexpected - * error has occured when the email is send. + * This email template also needs an email title and an email text in order to complete + * the appointment details. * - * @param array $customer_data Associative array with the customer's - * data. Each key has the same name as the corresponding field in db. - * @param array $appointment_data Associative array with the appointment's - * data. Each key has the same name as the corresponding field in db. - * @param string $email_title The email title is going to inform the customer - * for the action that was taken. + * @expectedException NotificationException Raises when an unexpected error occures. + * + * @param array $appointment_data Contains the appointment data. + * @param array $provider_data Contains the provider data. + * @param array $service_data Contains the service data. + * @param array $company_settings Contains settings of the company. By the time the + * "company_name", "company_link" and "company_email" values are required in the array. + * @param string $title The email title may vary depending the receiver. + * @param string $message The email message may vary depending the receiver. + * @param string $appointment_link This link is going to enable the receiver to make changes + * to the appointment record. + * @param string $receiver_address The receiver email address. * @return bool Returns the operation result. */ - public function send_book_success($customer_data, $appointment_data, $email_title) { - $this->CI->load->model('Providers_Model'); - $this->CI->load->model('Services_Model'); - $this->CI->load->model('Settings_Model'); - - $provider_data = $this->CI->Providers_Model - ->get_row($appointment_data['id_users_provider']); - $service_data = $this->CI->Services_Model - ->get_row($appointment_data['id_services']); + public function send_appointment_details($appointment_data, $provider_data, $service_data, + $customer_data, $company_settings, $title, $message, $appointment_link, + $receiver_address) { + // :: PREPARE THE EMAIL TEMPLATE REPLACE ARRAY $replace_array = array( - '$email_title' => $email_title, + '$email_title' => $title, + '$email_message' => $message, + '$appointment_service' => $service_data['name'], '$appointment_provider' => $provider_data['first_name'] . ' ' . $provider_data['last_name'], - '$appointment_date' => date('d/m/Y H:i', strtotime($appointment_data['start_datetime'])), - '$appointment_duration' => $service_data['duration'] . ' minutes', - '$appointment_link' => $this->CI->config->item('base_url') . 'appointments/index/' . $appointment_data['hash'], - '$company_link' => $this->CI->Settings_Model->get_setting('company_link'), - '$company_name' => $this->CI->Settings_Model->get_setting('company_name'), - '$customer_name' => $customer_data['first_name'] . ' ' . $customer_data['last_name'] - ); - - $email_html = file_get_contents(dirname(dirname(__FILE__)) . '/views/emails/book_success.php'); - $email_html = $this->replace_template_variables($replace_array, $email_html); - - $mail = new PHPMailer(); - $mail->From = $this->CI->Settings_Model->get_setting('company_email'); - $mail->FromName = $this->CI->Settings_Model->get_setting('company_name'); - $mail->AddAddress($customer_data['email']); // Do not use the name argument, phpmailer crushes. - $mail->IsHTML(true); - $mail->CharSet = 'UTF-8'; - $mail->Subject = $email_title; - $mail->Body = $email_html; - - if(!$mail->Send()) { - throw new NotificationException('Email could not been sent. ' - . 'Mailer Error (' . __LINE__ . '): ' . $mail->ErrorInfo); - } - - return TRUE; - } - - /** - * Send an email notification to a provider that - * a new appointment has been added to his plan. - * - * @expectedException NotificationException Raises when an unexpected - * error has occured when the email is send. - * - * @param array $customer_data Associative array with the customer's - * data. Each key has the same name as the corresponding field in db. - * @param array $appointment_data Associative array with the appointment's - * data. Each key has the same name as the corresponding field in db. - * @param string $email_title The email title is going to inform the provider - * for the action that was taken. - * @return bool Returns the operation result. - */ - public function send_new_appointment($customer_data, $appointment_data, $email_title) { - $this->CI->load->model('Providers_Model'); - $this->CI->load->model('Services_Model'); - $this->CI->load->model('Settings_Model'); - - $provider_data = $this->CI->Providers_Model->get_row($appointment_data['id_users_provider']); - $service_data = $this->CI->Services_Model->get_row($appointment_data['id_services']); - - $replace_array = array( - '$email_title' => $email_title, - '$appointment_service' => $service_data['name'], - '$appointment_provider' => $provider_data['first_name'] . ' ' . $provider_data['last_name'], - '$appointment_date' => date('d/m/Y H:i', strtotime($appointment_data['start_datetime'])), - '$appointment_duration' => $service_data['duration'] . ' minutes', - '$appointment_link' => $this->CI->config->item('base_url') . 'appointments/admin/' . $appointment_data['hash'], - '$company_link' => $this->CI->Settings_Model->get_setting('company_link'), - '$company_name' => $this->CI->Settings_Model->get_setting('company_name'), + '$appointment_start_date' => date('d/m/Y H:i', strtotime($appointment_data['start_datetime'])), + '$appointment_end_date' => date('d/m/Y H:i', strtotime($appointment_data['end_datetime'])), + '$appointment_link' => $appointment_link, + + '$company_link' => $company_settings['company_link'], + '$company_name' => $company_settings['company_name'], + '$customer_name' => $customer_data['first_name'] . ' ' . $customer_data['last_name'], '$customer_email' => $customer_data['email'], '$customer_phone' => $customer_data['phone_number'], @@ -136,16 +85,17 @@ class Notifications { ); $email_html = file_get_contents(dirname(dirname(__FILE__)) - . '/views/emails/new_appointment.php'); + . '/views/emails/appointment_details.php'); $email_html = $this->replace_template_variables($replace_array, $email_html); + // :: INSTANTIATE EMAIL OBJECT AND SEND EMAIL $mail = new PHPMailer(); - $mail->From = $this->CI->Settings_Model->get_setting('company_email'); - $mail->FromName = $this->CI->Settings_Model->get_setting('company_name'); - $mail->AddAddress($provider_data['email']); // "Name" argument crushes the phpmailer class. + $mail->From = $company_settings['company_email']; + $mail->FromName = $company_settings['company_name']; + $mail->AddAddress($receiver_address); // "Name" argument crushes the phpmailer class. $mail->IsHTML(true); $mail->CharSet = 'UTF-8'; - $mail->Subject = $email_title; + $mail->Subject = $title; $mail->Body = $email_html; if (!$mail->Send()) { @@ -174,7 +124,7 @@ class Notifications { * "company_name", "company_email". * @param string $to_address The email address of the email receiver. */ - public function send_remove_appointment($appointment_data, $provider_data, + public function send_delete_appointment($appointment_data, $provider_data, $service_data, $customer_data, $company_settings, $to_address) { // :: PREPARE EMAIL REPLACE ARRAY $replace_array = array( @@ -184,11 +134,15 @@ class Notifications { '$appointment_date' => date('d/m/Y H:i', strtotime($appointment_data['start_datetime'])), '$appointment_duration' => $service_data['duration'] . ' minutes', '$company_link' => $company_settings['company_link'], - '$company_name' => $company_settings['company_name'] + '$company_name' => $company_settings['company_name'], + '$customer_name' => $customer_data['first_name'] . ' ' . $customer_data['last_name'], + '$customer_email' => $customer_data['email'], + '$customer_phone' => $customer_data['phone_number'], + '$customer_address' => $customer_data['address'] ); $email_html = file_get_contents(dirname(dirname(__FILE__)) - . '/views/emails/remove_appointment.php'); + . '/views/emails/delete_appointment.php'); $email_html = $this->replace_template_variables($replace_array, $email_html); // :: SETUP EMAIL OBJECT AND SEND NOTIFICATION diff --git a/src/application/views/appointments/book_success.php b/src/application/views/appointments/book_success.php index 4c7c6fee..b5d92bca 100644 --- a/src/application/views/appointments/book_success.php +++ b/src/application/views/appointments/book_success.php @@ -190,12 +190,28 @@ Add to Google Calendar - + +

An Unexpected Error Occured

+
+
+ +
+
+
' . $error['technical'] . '
+
+
+
+
'; } ?> diff --git a/src/application/views/appointments/message.php b/src/application/views/appointments/message.php new file mode 100644 index 00000000..0164a444 --- /dev/null +++ b/src/application/views/appointments/message.php @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + +
+ +

+

+
+ + \ No newline at end of file diff --git a/src/application/views/emails/new_appointment.php b/src/application/views/emails/appointment_details.php similarity index 81% rename from src/application/views/emails/new_appointment.php rename to src/application/views/emails/appointment_details.php index 5df07bf5..dc6e3f06 100644 --- a/src/application/views/emails/new_appointment.php +++ b/src/application/views/emails/appointment_details.php @@ -1,18 +1,19 @@ - New Appointment + Appointment Details -

$email_title

+

$email_message

+

Appointment Details

@@ -24,17 +25,17 @@ - - + + - - + +
$appointment_provider
Date$appointment_dateStart$appointment_start_date
Duration$appointment_durationEnd$appointment_end_date
- +

Customer Details

- +
@@ -54,12 +55,11 @@
Name $customer_name

Appointment Link

-

You can make more actions by pressing the following link.

$appointment_link
diff --git a/src/application/views/emails/book_success.php b/src/application/views/emails/book_success.php deleted file mode 100644 index 6c1287f1..00000000 --- a/src/application/views/emails/book_success.php +++ /dev/null @@ -1,56 +0,0 @@ - - - Appointment Book Success - - -
- - -
-

$email_title

-

- Thank you $customer_name for arranging an appointment with us. - Below you can see the appointment details. Click on the edit - link to make changes to your appointment. -

- -

Appointment Details

- - - - - - - - - - - - - - - - - -
Service$appointment_service
Provider$appointment_provider
Date$appointment_date
Duration$appointment_duration
- -

Edit Link

-

- Press the following link to make changes to your appointment reservation. - You are able to change the appointment details three hours before - the appointment. -

- $appointment_link -
- - -
- - \ No newline at end of file diff --git a/src/application/views/emails/remove_appointment.php b/src/application/views/emails/delete_appointment.php similarity index 100% rename from src/application/views/emails/remove_appointment.php rename to src/application/views/emails/delete_appointment.php