From c139df2135f7a45b5a105ff75a154c25fefbe2af Mon Sep 17 00:00:00 2001 From: "alextselegidis@gmail.com" Date: Mon, 15 Jul 2013 07:32:19 +0000 Subject: [PATCH] * Finished google synchronization algorithm. * Prepared trunk for new version. --- Release Notes.txt | 1 + db/easy_appointments.sql | 49 ++++------ ...τυχιακής.pdf => thesis-topic.pdf} | Bin src/application/controllers/appointments.php | 4 +- src/application/controllers/backend.php | 5 +- src/application/controllers/google.php | 85 ++++++++++-------- .../helpers/custom_exceptions_helper.php | 8 +- .../drivers/Unit_tests_appointments_model.php | 21 ++++- src/application/libraries/google_sync.php | 19 ++++ src/application/models/appointments_model.php | 38 ++++---- .../views/appointments/book_success.php | 4 +- src/application/views/backend/header.php | 2 +- src/assets/css/backend.css | 2 +- src/assets/js/backend.js | 4 + src/assets/js/backend_calendar.js | 78 ++++++++++++++-- src/assets/js/frontend_book.js | 1 + 16 files changed, 216 insertions(+), 105 deletions(-) rename doc/{Θέμα Πτυχιακής.pdf => thesis-topic.pdf} (100%) diff --git a/Release Notes.txt b/Release Notes.txt index e17f568c..903429d5 100644 --- a/Release Notes.txt +++ b/Release Notes.txt @@ -10,4 +10,5 @@ Main Minor - Display javascript ajax error messages to users. +- Provider can edit appointment from email link. \ No newline at end of file diff --git a/db/easy_appointments.sql b/db/easy_appointments.sql index d66c0435..a703614e 100644 --- a/db/easy_appointments.sql +++ b/db/easy_appointments.sql @@ -3,7 +3,7 @@ -- http://www.phpmyadmin.net -- -- Φιλοξενητής: localhost --- Χρόνος δημιουργίας: 09 Ιουλ 2013 στις 15:16:49 +-- Χρόνος δημιουργίας: 15 Ιουλ 2013 στις 10:07:35 -- Έκδοση διακομιστή: 5.5.24-log -- Έκδοση PHP: 5.4.3 @@ -42,23 +42,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=25 ; - --- --- Άδειασμα δεδομένων του πίνακα `ea_appointments` --- - -INSERT INTO `ea_appointments` (`id`, `book_datetime`, `start_datetime`, `end_datetime`, `notes`, `hash`, `is_unavailable`, `id_users_provider`, `id_users_customer`, `id_services`, `id_google_calendar`) VALUES -(2, '2013-06-28 13:41:37', '2013-06-28 10:00:00', '2013-06-28 15:50:00', '', 'df05e96357b1f7eb9717524d40400b92', 0, 2, 5, 1, NULL), -(3, '2013-06-28 17:03:04', '2013-06-29 09:00:00', '2013-06-29 12:50:00', '', '0493a6fef5215cd83b9eca4de63cb9e9', 0, 2, 5, 1, 'vhd5nlv9pt32caanvtu8c9c8r0'), -(7, '2013-06-29 00:48:15', '2013-06-29 12:15:00', '2013-06-29 12:35:00', '', '5774937851046f65b87617eb14c6ee97', 0, 2, 5, 1, NULL), -(15, '2013-07-03 13:34:54', '2013-07-03 15:15:00', '2013-07-03 15:35:00', '', '98c777b9ee21be6091e15c4af35f6752', 0, 2, 5, 1, 'ibcgjhj1fu484s1c4bquroqtm0'), -(16, '2013-07-03 13:45:12', '2013-07-03 15:45:00', '2013-07-03 16:05:00', '', 'b828b3bb5dbc4e50f05b05cb239bfcd4', 0, 2, 5, 1, 'c48fcvqak1pulu78is971cmutg'), -(17, '2013-07-03 13:45:46', '2013-07-03 16:15:00', '2013-07-03 16:35:00', '', '1edb1f698d8c3606d8ac3c3371604782', 0, 2, 5, 1, 'cgqaavskrvp8048hvba8i9qa28'), -(18, '2013-07-03 13:46:08', '2013-07-03 16:45:00', '2013-07-03 17:05:00', '', 'a4dacd561468e89a74267dc148690a74', 0, 2, 5, 1, 'rrtfqiok2c7i7vbuuh4hjtcp4c'), -(20, '2013-07-03 17:51:49', '2013-07-04 09:00:00', '2013-07-04 09:20:00', '', '9e9814cf15a156d8d795d1e363463c65', 0, 2, 5, 1, 'i3v1unm6miuu1ran9idbivl130'), -(23, '2013-07-03 18:35:25', '2013-07-04 13:00:00', '2013-07-04 13:20:00', '', '64d9143d6004a03d2887d4f9909fd59e', 0, 2, 5, 1, 'fquo0ibainofdpbs26j8ae3r0c'), -(24, '2013-07-05 14:00:38', '2013-07-05 10:00:00', '2013-07-05 10:20:00', '', '13db3ecb2fe7b3e8a9131a21183db62c', 0, 2, 5, 1, 'jno45dehlqh8qufilgvnvj1dl0'); +) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=102 ; -- -------------------------------------------------------- @@ -111,10 +95,9 @@ CREATE TABLE IF NOT EXISTS `ea_services` ( -- INSERT INTO `ea_services` (`id`, `name`, `duration`, `price`, `currency`, `description`, `id_service_categories`) VALUES -(1, 'Γενική Εξέταση', 20, '50.00', 'euro', 'Γενική εξέταση του ασθενή.', NULL), -(2, 'Εξέταση Καρδιάς', 30, '40.00', 'euro', 'Εξέταση του ασθενή για νοσήματα καρδιάς.', NULL), -(3, 'Νευρολογική Εξέταση', 20, '35.00', 'euro', 'Νευρολογική εξέταση του ασθενή.', NULL), -(9, 'General Examination', 30, '50.00', 'euro', 'This is some service description.', NULL); +(1, 'General Examination', 20, '50.00', 'euro', 'General examination of the patient.', NULL), +(2, 'Heart Examination', 30, '40.00', 'euro', 'Checkup for heart problems.', NULL), +(3, 'Neurological Examination', 20, '35.00', 'euro', 'Neurological tests for the patient.', NULL); -- -------------------------------------------------------- @@ -177,7 +160,7 @@ INSERT INTO `ea_settings` (`id`, `name`, `value`) VALUES (1, 'company_name', 'Javation & Co'), (2, 'company_working_plan', '{"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"}]}}'), (3, 'company_email', 'alextselegidis@gmail.com'), -(8, 'company_link', 'http://google.gr'), +(8, 'company_link', 'http://javation.com'), (9, 'book_advance_timeout', '30'); -- -------------------------------------------------------- @@ -208,11 +191,11 @@ CREATE TABLE IF NOT EXISTS `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, '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), -(5, '', 'a', 'alextselegidis@yahoo.gr', NULL, 'a', '', '', NULL, '', NULL, 3); +(1, 'Ringo', 'Starr', 'alextselegidis@gmail.com', '0000000000000', '0000000000000', 'Some Str', 'Some City', 'Some State 0', '00000', 'This is me making Easy!Appointments :P', 1), +(2, 'George', 'Harrison', 'alextselegidis@gmail.com', '1111111111111', '1111111111111', 'Some Str 1', 'Some City 1', 'Some State 1', '11111', 'This is a test provider (with my email for google syncing).', 2), +(3, 'John', 'Lennon', 'prov2@test.gr', '2222222222222', '2222222222222', 'Some Str 2', 'Some City 2', 'Some State 2', '22222', NULL, 2), +(4, 'Paul', 'McCartney', 'prov3@test.gr', '3333333333333', '3333333333333', 'Some Str 3', 'Some City 3', 'Some State 3', '33333', NULL, 2), +(5, 'John', 'Doe', 'alextselegidis@yahoo.gr', NULL, '0123456789', '', '', NULL, '', 'This is my testing customer.', 3); -- -------------------------------------------------------- @@ -228,6 +211,8 @@ CREATE TABLE IF NOT EXISTS `ea_user_settings` ( `notifications` text, `google_sync` tinyint(4) DEFAULT '0', `google_token` text, + `sync_past_days` int(11) DEFAULT '5', + `sync_future_days` int(11) DEFAULT '5', PRIMARY KEY (`id_users`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -235,10 +220,10 @@ CREATE TABLE IF NOT EXISTS `ea_user_settings` ( -- Άδειασμα δεδομένων του πίνακα `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.AHES6ZRq0T7vB1k0nueWwvYM8gQw7QapJm8_EyHaRwljzXo","token_type":"Bearer","expires_in":3600,"refresh_token":"1\\/o4GiIEXKTxm3HkWHAPGplqW2CcmGLQm7CV3iv19DrTw","created":1372844816}'), -(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, 0, NULL); +INSERT INTO `ea_user_settings` (`id_users`, `username`, `password`, `working_plan`, `notifications`, `google_sync`, `google_token`, `sync_past_days`, `sync_future_days`) 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, 0, NULL, 5, 5), +(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, 5, 5), +(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, 0, NULL, 5, 5); -- -- Περιορισμοί για άχρηστους πίνακες diff --git a/doc/Θέμα Πτυχιακής.pdf b/doc/thesis-topic.pdf similarity index 100% rename from doc/Θέμα Πτυχιακής.pdf rename to doc/thesis-topic.pdf diff --git a/src/application/controllers/appointments.php b/src/application/controllers/appointments.php index ed510221..7d27815d 100644 --- a/src/application/controllers/appointments.php +++ b/src/application/controllers/appointments.php @@ -149,7 +149,7 @@ class Appointments extends CI_Controller { $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/' + $provider_link = $this->config->item('base_url') . 'backend/index/' . $appointment['hash']; } else { $customer_title = 'Appointment changes have been successfully saved!'; @@ -159,7 +159,7 @@ class Appointments extends CI_Controller { $provider_title = 'Appointment details have changed.'; $provider_message = ''; - $provider_link = $this->config->item('base_url') . 'backend/' + $provider_link = $this->config->item('base_url') . 'backend/index/' . $appointment['hash']; } diff --git a/src/application/controllers/backend.php b/src/application/controllers/backend.php index 33be1ed4..02612537 100644 --- a/src/application/controllers/backend.php +++ b/src/application/controllers/backend.php @@ -18,6 +18,7 @@ class Backend extends CI_Controller { $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'); $view['base_url'] = $this->config->item('base_url'); @@ -28,7 +29,9 @@ class Backend extends CI_Controller { if ($appointment_hash != '') { $results = $this->appointments_model->get_batch(array('hash' => $appointment_hash)); - $view['edit_appointment'] = $results[0]; // This will display the appointment edit dialog on page load. + $appointment = $results[0]; + $appointment['customer'] = $this->customers_model->get_row($appointment['id_users_customer']); + $view['edit_appointment'] = $appointment; // This will display the appointment edit dialog on page load. } else { $view['edit_appointment'] = NULL; } diff --git a/src/application/controllers/google.php b/src/application/controllers/google.php index fcf25975..8f39fecf 100644 --- a/src/application/controllers/google.php +++ b/src/application/controllers/google.php @@ -73,7 +73,8 @@ class Google extends CI_Controller { * * @param numeric $provider_id Provider record to be synced. * - * @task This method must be executed only by the system and noone else outside. It is a big security issue. + * @task This method must be executed only by the system and noone else outside. + * It is a big security issue. */ public function sync($provider_id = NULL) { try { @@ -127,52 +128,62 @@ class Google extends CI_Controller { $service = $this->services_model->get_row($appointment['id_services']); $customer = $this->customers_model->get_row($appointment['id_users_customer']); - // :: APPOINTMENT WITH NO GCAL_ID -> ADD TO GCAL + // If current appointment not synced yet, add to gcal. if ($appointment['id_google_calendar'] == NULL) { $google_event = $this->google_sync->add_appointment($appointment, $provider, $service, $customer, $company_settings); $appointment['id_google_calendar'] = $google_event->id; - $this->appointments_model->add($appointment); // save gcal id - } - - // :: SYNCED APPOINTMENT NOT FOUND ON GCAL -> DELETE E!A RECORD - if ($appointment['id_google_calendar'] != NULL) { + $this->appointments_model->add($appointment); // Save gcal id + } else { + // Appointment is synced with google calendar. try { $google_event = $this->google_sync->get_event($appointment['id_google_calendar']); - } catch(Exception $exc) { - $this->appointments_model->delete($appointment['id']); - $appointment['id_google_calendar'] = NULL; // Do not proceed with the rest sync actions. - } - } - - // :: SYNCED APPOINTMENT DIFFERENT FROM GCAL EVENT -> UPDATE E!A RECORD - if ($appointment['id_google_calendar'] != NULL) { - $is_different = FALSE; - $appt_start = strtotime($appointment['start_datetime']); - $appt_end = strtotime($appointment['end_datetime']); - $event_start = strtotime($google_event->getStart()->getDateTime()); - $event_end = strtotime($google_event->getEnd()->getDateTime()); - - if ($appt_start != $event_start - || $appt_end != $event_end) { - $is_different = TRUE; - } - - if ($is_different) { - $appointment['start_datetime'] = date('Y-m-d H:i:s', $event_start); - $appointment['end_datetime'] = date('Y-m-d H:i:s', $event_end); - $this->appointments_model->add($appointment); - } - - } - - // @task :: GCAL EVENT NOT FOUND ON E!A -> ADD EVENT TO E!A - + // If gcal event is different from e!a appointment then update e!a record. + $is_different = FALSE; + $appt_start = strtotime($appointment['start_datetime']); + $appt_end = strtotime($appointment['end_datetime']); + $event_start = strtotime($google_event->getStart()->getDateTime()); + $event_end = strtotime($google_event->getEnd()->getDateTime()); + + if ($appt_start != $event_start || $appt_end != $event_end) { + $is_different = TRUE; + } + + if ($is_different) { + $appointment['start_datetime'] = date('Y-m-d H:i:s', $event_start); + $appointment['end_datetime'] = date('Y-m-d H:i:s', $event_end); + $this->appointments_model->add($appointment); + } + } catch(Exception $exc) { + // Appointment not found on gcal, delete from e!a. + $this->appointments_model->delete($appointment['id']); + $appointment['id_google_calendar'] = NULL; + } + } } - // @task Sync unavailable periods with Google Calendar + // :: ADD GCAL EVENTS THAT ARE NOT PRESENT ON E!A + $events = $this->google_sync->get_sync_events($start, $end); + foreach($events->getItems() as $event) { + $results = $this->appointments_model->get_batch(array('id_google_calendar' => $event->getId())); + if (count($results) == 0) { + // Record doesn't exist in E!A, so add the event now. + $appointment = array( + 'start_datetime' => date('Y-m-d H:i:s', strtotime($event->start->getDateTime())), + 'end_datetime' => date('Y-m-d H:i:s', strtotime($event->end->getDateTime())), + 'is_unavailable' => TRUE, + 'notes' => $event->getSummary() . ' ' . $event->getDescription(), + 'id_users_provider' => $provider_id, + 'id_google_calendar' => $event->getId(), + 'id_users_customer' => NULL, + 'id_services' => NULL, + ); + + $this->appointments_model->add($appointment); + } + } echo json_encode('SUCCESS'); diff --git a/src/application/helpers/custom_exceptions_helper.php b/src/application/helpers/custom_exceptions_helper.php index b0cd60b6..63e46690 100644 --- a/src/application/helpers/custom_exceptions_helper.php +++ b/src/application/helpers/custom_exceptions_helper.php @@ -29,22 +29,22 @@ class SyncException extends Exception {} * We display only one exceptions at a time because the user needs to be able * to display the details of each exception seperately. (In contrast with js). * - * @param Exception $exception The exception to be displayed. + * @param Exception $exc The exception to be displayed. * @return string Returns the html markup of the exception. */ -function exceptionToHtml($exception) { +function exceptionToHtml($exc) { return '
-
' . $exception->getTraceAsString() . '
+
' . $exc->getTraceAsString() . '
diff --git a/src/application/libraries/Unit_tests/drivers/Unit_tests_appointments_model.php b/src/application/libraries/Unit_tests/drivers/Unit_tests_appointments_model.php index 083acfc0..0b2cfa5a 100644 --- a/src/application/libraries/Unit_tests/drivers/Unit_tests_appointments_model.php +++ b/src/application/libraries/Unit_tests/drivers/Unit_tests_appointments_model.php @@ -65,6 +65,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013-05-01 12:30:00', 'end_datetime' => '2013-05-01 13:00:00', 'notes' => 'Some notes right here...', + 'is_unavailable' => FALSE, 'id_users_provider' => $this->provider_id, 'id_users_customer' => $this->customer_id, 'id_services' => $this->service_id @@ -82,7 +83,6 @@ class Unit_tests_appointments_model extends CI_Driver { unset($db_data['hash']); unset($db_data['book_datetime']); unset($db_data['id_google_calendar']); - unset($db_data['is_unavailable']); $this->CI->unit->run($appointment, $db_data, 'Test if add() appointment (insert ' . 'operation) has successfully inserted a record.'); @@ -100,6 +100,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013-05-01 12:30:00', 'end_datetime' => '2013-05-01 13:00:00', 'notes' => 'Some notes right here...', + 'is_unavailable' => FALSE, 'id_users_provider' => $this->provider_id, 'id_users_customer' => $this->customer_id, 'id_services' => $this->service_id @@ -134,6 +135,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013-0WRONG5-01 12:30WRONG:00', 'end_datetime' => '2013-0WRONG5-01WRONG 13:00WRONG:00', 'notes' => 'Some notes right here...', + 'is_unavailable' => FALSE, 'id_users_provider' => $this->provider_id, 'id_users_customer' => $this->customer_id, 'id_services' => $this->service_id @@ -161,6 +163,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013-05-01 12:30:00', 'end_datetime' => '2013-05-01 13:00:00', 'notes' => 'Some notes right here...', + 'is_unavailable' => FALSE, 'id_users_provider' => $this->provider_id, 'id_users_customer' => $this->customer_id, 'id_services' => $this->service_id @@ -182,6 +185,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013-05-01 08:33:45', 'end_datetime' => '2013-05-02 13:13:13', 'notes' => 'This is totally random!', + 'is_unavailable' => FALSE, 'id_users_provider' => '198678', 'id_users_customer' => '194702', 'id_services' => '8766293' @@ -197,6 +201,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2WRONG013-05-01 0WRONG8:33:45', 'end_datetime' => '2013-0WRONG5-02 13:13:WRONG13', 'notes' => 'This is totally random!', + 'is_unavailable' => FALSE, 'id_users_provider' => '1986WRONG78', 'id_users_customer' => '1WRONG94702', 'id_services' => '876WRONG6293' @@ -216,6 +221,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013-05-01 12:30:00', 'end_datetime' => '2013-05-01 13:00:00', 'notes' => 'Some notes right here...', + 'is_unavailable' => FALSE, 'id_users_provider' => $this->provider_id, 'id_users_customer' => $this->customer_id, 'id_services' => $this->service_id @@ -245,6 +251,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013-05-01 12:30:00', 'end_datetime' => '2013-05-01 13:00:00', 'notes' => 'Some notes right here...', + 'is_unavailable' => FALSE, 'id_users_provider' => $this->provider_id, 'id_users_customer' => $this->customer_id, 'id_services' => $this->service_id @@ -276,6 +283,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013WRONG-05-0WRONG1 12:WRONG30:00', 'end_datetime' => '2013-05-01 13:00:00WRONG', 'notes' => 'Some notes righWRONGt here...', + 'is_unavailable' => FALSE, 'id_users_provider' => 'WRONG', 'id_users_customer' => 'WRONG', 'id_services' => 'WRONG' @@ -304,6 +312,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013-05-01 12:30:00', 'end_datetime' => '2013-05-01 13:00:00', 'notes' => 'Some notes right here...', + 'is_unavailable' => FALSE, 'id_users_provider' => $this->provider_id, 'id_users_customer' => $this->customer_id, 'id_services' => $this->service_id @@ -375,6 +384,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013-05-01 12:30:00', 'end_datetime' => '2013-05-01 13:00:00', 'notes' => 'Some notes right here...', + 'is_unavailable' => FALSE, 'id_users_provider' => $this->provider_id, 'id_users_customer' => $this->customer_id, 'id_services' => $this->service_id @@ -427,6 +437,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013-05-01 12:30:00', 'end_datetime' => '2013-05-01 13:00:00', 'notes' => 'Some notes right here...', + 'is_unavailable' => FALSE, 'hash' => '91de2d31f5cbb6d26a5b1b3e710d38d1', 'id_users_provider' => $this->provider_id, 'id_users_customer' => $this->customer_id, @@ -439,7 +450,6 @@ class Unit_tests_appointments_model extends CI_Driver { $db_data = $this->CI->appointments_model->get_row($appointment['id']); unset($db_data['book_datetime']); unset($db_data['id_google_calendar']); - unset($db_data['is_unavailable']); // Check if this is the record we seek. $this->CI->unit->run($db_data, $appointment, 'Test get_row() method.'); @@ -487,6 +497,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013-05-01 12:30:00', 'end_datetime' => '2013-05-01 13:00:00', 'notes' => 'Some notes right here...', + 'is_unavailable' => FALSE, 'id_users_provider' => $this->provider_id, 'id_users_customer' => $this->customer_id, 'id_services' => $this->service_id @@ -537,6 +548,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013-05-01 12:30:00', 'end_datetime' => '2013-05-01 13:00:00', 'notes' => 'Some notes right here...', + 'is_unavailable' => FALSE, 'id_users_provider' => $this->provider_id, 'id_users_customer' => $this->customer_id, 'id_services' => $this->service_id @@ -566,6 +578,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013-05-01 12:30:00', 'end_datetime' => '2013-05-01 13:00:00', 'notes' => 'Some notes right here...', + 'is_unavailable' => FALSE, 'id_users_provider' => $this->provider_id, 'id_users_customer' => $this->customer_id, 'id_services' => $this->service_id @@ -579,6 +592,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '20WRONG13-05-01 12WRONG:30:00', 'end_datetime' => '2013-05WRONG-01 13:00WRONG:00', 'notes' => 'Some notes right here...', + 'is_unavailable' => FALSE, 'id_users_provider' => $this->provider_id, 'id_users_customer' => $this->customer_id, 'id_services' => $this->service_id @@ -593,6 +607,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013-05-01 12:30:00', 'end_datetime' => '2013-05-01 13:00:00', 'notes' => 'Some notes right here...', + 'is_unavailable' => FALSE, 'id_users_provider' => 'THIS IS WRONG', 'id_users_customer' => $this->customer_id, 'id_services' => $this->service_id @@ -607,6 +622,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013-05-01 12:30:00', 'end_datetime' => '2013-05-01 13:00:00', 'notes' => 'Some notes right here...', + 'is_unavailable' => FALSE, 'id_users_provider' => $this->provider_id, 'id_users_customer' => 'THIS IS WRONG', 'id_services' => $this->service_id @@ -621,6 +637,7 @@ class Unit_tests_appointments_model extends CI_Driver { 'start_datetime' => '2013-05-01 12:30:00', 'end_datetime' => '2013-05-01 13:00:00', 'notes' => 'Some notes right here...', + 'is_unavailable' => FALSE, 'id_users_provider' => $this->provider_id, 'id_users_customer' => $this->customer_id, 'id_services' => 'THIS IS WRONG' diff --git a/src/application/libraries/google_sync.php b/src/application/libraries/google_sync.php index b37b7c88..e4e95f2b 100644 --- a/src/application/libraries/google_sync.php +++ b/src/application/libraries/google_sync.php @@ -272,6 +272,25 @@ class Google_Sync { public function get_event($google_calendar_id) { return $this->service->events->get('primary', $google_calendar_id); } + + /** + * Get all the events between the sync period. + * + * @param date $start The start date of sync period. + * @param date $end The end date of sync period. + * @return object Returns an array with Google_Event objects that belong on the given + * sync period (start, end). + */ + public function get_sync_events($start, $end) { + $this->CI->load->helper('general'); + + $params = array( + 'timeMin' => date3339($start), + 'timeMax' => date3339($end) + ); + + return $this->service->events->listEvents('primary', $params); + } } /* End of file google_sync.php */ diff --git a/src/application/models/appointments_model.php b/src/application/models/appointments_model.php index c980011a..01518421 100644 --- a/src/application/models/appointments_model.php +++ b/src/application/models/appointments_model.php @@ -152,7 +152,7 @@ class Appointments_Model extends CI_Model { // If a appointment id is given, check wether the record exists // in the database. if (isset($appointment['id'])) { - $num_rows = $this->db->get_where('ea_appointments', + $num_rows = $this->db->get_where('ea_appointments', array('id' => $appointment['id']))->num_rows(); if ($num_rows == 0) { throw new Exception('Provided appointment id does not ' @@ -181,23 +181,25 @@ class Appointments_Model extends CI_Model { throw new Exception('Appointment provider id is invalid.'); } - // Check if the customer's id is valid. - $num_rows = $this->db - ->select('*') - ->from('ea_users') - ->join('ea_roles', 'ea_roles.id = ea_users.id_roles', 'inner') - ->where('ea_users.id', $appointment['id_users_customer']) - ->where('ea_roles.slug', DB_SLUG_CUSTOMER) - ->get()->num_rows(); - if ($num_rows == 0) { - throw new Exception('Appointment customer id is invalid.'); - } - - // Check if the service id is valid. - $num_rows = $this->db->get_where('ea_services', - array('id' => $appointment['id_services']))->num_rows(); - if ($num_rows == 0) { - throw new Exception('Appointment customer id is invalid.'); + if ($appointment['is_unavailable'] == FALSE) { + // Check if the customer's id is valid. + $num_rows = $this->db + ->select('*') + ->from('ea_users') + ->join('ea_roles', 'ea_roles.id = ea_users.id_roles', 'inner') + ->where('ea_users.id', $appointment['id_users_customer']) + ->where('ea_roles.slug', DB_SLUG_CUSTOMER) + ->get()->num_rows(); + if ($num_rows == 0) { + throw new Exception('Appointment customer id is invalid.'); + } + + // Check if the service id is valid. + $num_rows = $this->db->get_where('ea_services', + array('id' => $appointment['id_services']))->num_rows(); + if ($num_rows == 0) { + throw new Exception('Appointment customer id is invalid.'); + } } return TRUE; diff --git a/src/application/views/appointments/book_success.php b/src/application/views/appointments/book_success.php index b145182f..5320ea81 100644 --- a/src/application/views/appointments/book_success.php +++ b/src/application/views/appointments/book_success.php @@ -196,8 +196,8 @@ if (isset($exceptions)) { echo '
'; echo '

Unexpected Errors

'; - foreach($exceptions as $exception) { - echo exceptionToHtml($exception); + foreach($exceptions as $exc) { + echo exceptionToHtml($exc); } echo '
'; } diff --git a/src/application/views/backend/header.php b/src/application/views/backend/header.php index 05c3579a..0e3e4b05 100644 --- a/src/application/views/backend/header.php +++ b/src/application/views/backend/header.php @@ -1,7 +1,7 @@ - Easy!Appointments Backend | <?php echo $company_name; ?> + <?php echo $company_name; ?> | Easy!Appointments ' + message + ''; diff --git a/src/assets/js/backend_calendar.js b/src/assets/js/backend_calendar.js index 7031ea51..e8ae7e77 100644 --- a/src/assets/js/backend_calendar.js +++ b/src/assets/js/backend_calendar.js @@ -90,7 +90,36 @@ var BackendCalendar = { // :: DISPLAY EDIT DIALOG IF APPOINTMENT HASH IS PROVIDED if (GlobalVariables.editAppointment != null) { - // @task Display appointment edit dialog to user. + var $dialog = $('#manage-appointment'); + var appointment = GlobalVariables.editAppointment; + BackendCalendar.resetAppointmentDialog(); + + $dialog.find('.modal-header h3').text('Edit Appointment'); + $dialog.find('#appointment-id').val(appointment['id']); + $dialog.find('#select-service').val(appointment['id_services']); + $dialog.find('#select-provider').val(appointment['id_users_provider']); + + // Set the start and end datetime of the appointment. + var startDatetime = Date.parseExact(appointment['start_datetime'], + 'yyyy-MM-dd HH:mm:ss').toString('dd/MM/yyyy HH:mm'); + $dialog.find('#start-datetime').val(startDatetime); + + var endDatetime = Date.parseExact(appointment['end_datetime'], + 'yyyy-MM-dd HH:mm:ss').toString('dd/MM/yyyy HH:mm'); + $dialog.find('#end-datetime').val(endDatetime); + + var customer = appointment['customer']; + $dialog.find('#customer-id').val(appointment['id_users_customer']); + $dialog.find('#first-name').val(customer['first_name']); + $dialog.find('#last-name').val(customer['last_name']); + $dialog.find('#email').val(customer['email']); + $dialog.find('#phone-number').val(customer['phone_number']); + $dialog.find('#address').val(customer['address']); + $dialog.find('#city').val(customer['city']); + $dialog.find('#zip-code').val(customer['zip_code']); + $dialog.find('#notes').val(appointment['notes']); + + $dialog.modal('show'); } }, @@ -140,6 +169,45 @@ var BackendCalendar = { } }); + /** + * Event: Google Sync Button "Click" + * + * Trigger the synchronization algorithm. + */ + $('#google-sync').click(function() { + var getUrl = GlobalVariables.baseUrl + 'google/sync/' + $('#select-filter-item').val(); + $.ajax({ + 'type': 'GET', + 'url': getUrl, + 'dataType': 'json', + 'success': function(response) { + ///////////////////////////////////////////////// + console.log('Google Sync Response:', response); + ///////////////////////////////////////////////// + + if (response.exceptions) { + response.exceptions = GeneralFunctions.parseExceptions(response.exceptions); + GeneralFunctions.displayMessageBox(Backend.EXCEPTIONS_TITLE, Backend.EXCEPTIONS_MESSAGE); + $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.exceptions)); + return; + } + + if (response.warnings) { + response.warnings = GeneralFunctions.parseExceptions(response.warnings); + GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE); + $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings)); + } + + Backend.displayNotification('Google synchronization completed successfully!'); + $('#reload-appointments').trigger('click'); + }, + 'error': function(jqXHR, textStatus, errorThrown) { + Backend.displayNotification('Google synchronization failed: Could not establish ' + + 'server connection.'); + } + }); + }); + /** * Event: Reload Button "Click" * @@ -181,7 +249,7 @@ var BackendCalendar = { $dialog.find('#select-service').val(appointment['id_services']); $dialog.find('#select-provider').val(appointment['id_users_provider']); - // Set the start and end datetime of the appointment.\ + // Set the start and end datetime of the appointment. var startDatetime = Date.parseExact(appointment['start_datetime'], 'yyyy-MM-dd HH:mm:ss').toString('dd/MM/yyyy HH:mm'); $dialog.find('#start-datetime').val(startDatetime); @@ -218,7 +286,7 @@ var BackendCalendar = { $dialog.find('#unavailable-notes').val(unavailable.notes); } - // :: DIAPLY EDIT DIALOG + // :: DISPLAY EDIT DIALOG $dialog.modal('show'); }); @@ -751,7 +819,7 @@ var BackendCalendar = { 'start': Date.parse(unavailable.start_datetime), 'end': Date.parse(unavailable.end_datetime), 'allDay': false, - 'color': '#123456', + 'color': '#879DB4', 'editable': true, 'className': 'fc-unavailable fc-custom', 'data': unavailable @@ -829,7 +897,7 @@ var BackendCalendar = { 'start': Date.parse(unavailable.start_datetime), 'end': Date.parse(unavailable.end_datetime), 'allDay': false, - 'color': '#123456', + 'color': '#879DB4', 'editable': true, 'className': 'fc-unavailable fc-custom', 'data': unavailable diff --git a/src/assets/js/frontend_book.js b/src/assets/js/frontend_book.js index 056cf665..2b0af3a6 100644 --- a/src/assets/js/frontend_book.js +++ b/src/assets/js/frontend_book.js @@ -443,6 +443,7 @@ var FrontendBook = { + ' ' + $('.selected-hour').text() + ':00', 'end_datetime': FrontendBook.calcEndDatetime(), 'notes': $('#notes').val(), + 'is_unavailable': false, 'id_users_provider': $('#select-provider').val(), 'id_services': $('#select-service').val() };