Ολοκλήρωση της διαδικασίας OAuth της Google. Συχρονισμός των ραντεβού που προστίθονται από τους πελάτες στο ημερολόγιο του αντίστοιχου πάροχου.

This commit is contained in:
alextselegidis@gmail.com 2013-06-19 19:29:00 +00:00
parent fc53817e81
commit dc586ecefb
12 changed files with 442 additions and 110 deletions

View file

@ -6,10 +6,11 @@ Main
- Javascript Google API usage from the customer's perspective.
- Backend google calendar authentication Process.
- Sync every appointment change made from E!A to Google Calendar.
- Display user friendly error messages.
Minor
- Display user friendly error messages.
- Added sync exception to Google Sync library.

View file

@ -3,7 +3,7 @@
-- http://www.phpmyadmin.net
--
-- Φιλοξενητής: localhost
-- Χρόνος δημιουργίας: 08 Ιουν 2013 στις 12:49:21
-- Χρόνος δημιουργίας: 19 Ιουν 2013 στις 22:27:32
-- Έκδοση διακομιστή: 5.5.24-log
-- Έκδοση PHP: 5.4.3
@ -36,18 +36,12 @@ CREATE TABLE IF NOT EXISTS `ea_appointments` (
`id_users_provider` bigint(20) unsigned NOT NULL,
`id_users_customer` bigint(20) unsigned NOT NULL,
`id_services` bigint(20) unsigned NOT NULL,
`id_google_calendar` text,
PRIMARY KEY (`id`),
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=43 ;
--
-- Άδειασμα δεδομένων του πίνακα `ea_appointments`
--
INSERT INTO `ea_appointments` (`id`, `book_datetime`, `start_datetime`, `end_datetime`, `notes`, `hash`, `id_users_provider`, `id_users_customer`, `id_services`) VALUES
(10, NULL, '2013-06-07 15:30:00', '2013-06-07 15:50:00', '', 'c4baf9ea27dcd0fdc5449eb91b0ee2c5', 2, 20, 1);
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=88 ;
-- --------------------------------------------------------
@ -93,7 +87,7 @@ CREATE TABLE IF NOT EXISTS `ea_services` (
`id_service_categories` bigint(20) unsigned DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `id_service_categories` (`id_service_categories`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=22 ;
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=10 ;
--
-- Άδειασμα δεδομένων του πίνακα `ea_services`
@ -156,7 +150,7 @@ CREATE TABLE IF NOT EXISTS `ea_settings` (
`name` varchar(512) DEFAULT NULL,
`value` longtext,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=22 ;
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=10 ;
--
-- Άδειασμα δεδομένων του πίνακα `ea_settings`
@ -190,7 +184,7 @@ 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=116 ;
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=154 ;
--
-- Άδειασμα δεδομένων του πίνακα `ea_users`
@ -200,9 +194,7 @@ INSERT INTO `ea_users` (`id`, `first_name`, `last_name`, `email`, `mobile_number
(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),
(20, 'Alex', 'Tselegidis', 'alextselegidis@yahoo.gr', NULL, '123456789', 'Some Str', 'Some City', NULL, '12345', NULL, 3),
(76, '', 'a', 'alextselegidis@yahoo.gr', NULL, 'a', '', '', NULL, '', NULL, 3);
(4, 'Ηρώ', 'Καριοφύλη', 'prov3@test.gr', '239203490', '029340923', 'John Doe 3 ', NULL, NULL, NULL, NULL, 2);
-- --------------------------------------------------------
@ -216,7 +208,8 @@ CREATE TABLE IF NOT EXISTS `ea_user_settings` (
`password` varchar(512) DEFAULT NULL,
`working_plan` text,
`notifications` text,
`google_sync` tinyint(4) DEFAULT NULL,
`google_sync` tinyint(4) DEFAULT '0',
`google_token` text,
PRIMARY KEY (`id_users`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
@ -224,10 +217,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`) 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),
(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),
(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);
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}'),
(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}');
--
-- Περιορισμοί για άχρηστους πίνακες

View file

@ -15,7 +15,7 @@
|
*/
require_once dirname(dirname(dirname(__FILE__))) . '/configuration.php';
$config['base_url'] = SystemConfiguration::$base_url; //'http://localhost/dev/external/Easy!Appointments/trunk/src/WRONG';
$config['base_url'] = SystemConfiguration::$base_url;
/*
|--------------------------------------------------------------------------

View file

@ -30,8 +30,11 @@ class Appointments extends CI_Controller {
$manage_mode = TRUE;
$appointment_data = $this->Appointments_Model
->get_batch(array('hash' => $appointment_hash))[0];
$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.
$provider_data = $this->Providers_Model
->get_row($appointment_data['id_users_provider']);
$customer_data = $this->Customers_Model
@ -106,22 +109,22 @@ class Appointments extends CI_Controller {
$appointment_data['id_users_provider']);
if ($google_sync == TRUE) {
$google_token = $this->Providers_Model->get_setting('google_token',
$appointment_data['id_users_provider']);
$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 ($this->google_sync->authenticate($google_token) === TRUE) {
if ($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']);
}
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.

View file

@ -95,6 +95,10 @@ 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 {
@ -102,6 +106,18 @@ class Backend extends CI_Controller {
$appointment_data = json_decode(stripcslashes($_POST['appointment_data']), true);
$this->load->model('Appointments_Model');
$this->Appointments_Model->add($appointment_data);
if ($appointment_data['id_google_calendar'] != NULL) {
$this->load->model('Providers_Model');
$provider_settings = json_decode($this->Providers_Model
->get_setting('google_token', $appointment_data['id_users_provider']));
if ($provider_settings->google_sync == TRUE) {
$this->load->library('Google_Sync');
$this->google_sync->refresh_token($provider_settings->refresh_token);
$this->google_sync->update_appointment($appointment_data['id']);
}
}
}
if (isset($_POST['customer_data'])) {
@ -113,11 +129,100 @@ class Backend extends CI_Controller {
echo json_encode('SUCCESS');
} catch(Exception $exc) {
$js_error = array(
echo json_encode(array(
'error' => $exc->getMessage()
);
));
}
}
/**
* [AJAX] 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.
*
* @param int $_POST['appointment_id'] The appointment id to be deleted.
*
* @task Sync action with GCal.
* @task Send email notifications to provider and customer.
*/
public function ajax_delete_appointment() {
try {
if (!isset($_POST['appointment_id'])) {
throw new Exception('No appointment id provided.');
}
echo json_encode($js_error);
// :: STORE APPOINTMENT DATA FOR LATER USE IN THIS METHOD
$this->load->model('Appointments_Model');
$this->load->model('Providers_Model');
$this->load->model('Customers_Model');
$this->load->model('Services_Model');
$appointment_data = $this->Appointments_Model->get_row($_POST['appointment_id']);
$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']);
// :: DELETE APPOINTMENT RECORD FROM DATABASE.
$this->Appointments_Model->delete($_POST['appointment_id']);
// :: SYNC CHANGE TO GOOGLE CALENDAR
$google_sync = $this->Providers_Model->get_setting('google_sync',
$provider_data['id']);
if ($google_sync == TRUE) {
$google_token = json_decode($this->Providers_Model->get_setting('google_token',
$provider_data['id']));
$this->load->library('Google_Sync');
$this->google_sync->refresh_token($google_token->refresh_token);
$this->google_sync->delete_appointment($appointment_data['id_google_calendar']);
}
// :: SEND NOTIFICATION EMAILS TO PROVIDER AND CUSTOMER.
$this->load->library('Notifications');
$this->notification->send_delete_appointment($appointment_data, $provider_data,
$service_data, $customer_data);
echo json_encode('SUCCESS');
} catch(Exception $exc) {
echo json_encode(array(
'error' => $exc->getMessage()
));
}
}
/**
* [AJAX] Disable a providers sync setting.
*
* This method deletes the "google_sync" and "google_token" settings from the
* database. After that the provider's appointments will be no longer synced
* with google calendar.
*
* @param string $_POST['provider_id'] The selected provider record id.
*/
public function ajax_disable_provider_sync() {
try {
if (!isset($_POST['provider_id'])) {
throw new Exception('Provider id not specified.');
}
$this->load->model('Providers_Model');
$this->Providers_Model->set_setting('google_sync', FALSE,
$_POST['provider_id']);
$this->Providers_Model->set_setting('google_token', NULL,
$_POST['provider_id']);
echo json_encode('SUCCESS');
} catch(Exception $exc) {
echo json_encode(array(
'error' => $exc->getMessage()
));
}
}
}

View file

@ -12,9 +12,15 @@ class Google extends CI_Controller {
* made.
*/
public function oauth($provider_id) {
// Store the provider id for use on the callback function.
if (!isset($_SESSION)) {
@session_start();
}
$_SESSION['oauth_provider_id'] = $provider_id;
// Redirect browser to google user content page.
$this->load->library('Google_Sync');
// @task Create auth link and redirect browser window.
header('Location: ' . $this->google_sync->get_auth_url());
}
/**
@ -29,10 +35,33 @@ class Google extends CI_Controller {
* using the web server flow (see official documentation of OAuth), every
* Easy!Appointments installation should use its own calendar api key. So in every
* api console account, the "http://path-to-e!a/google/oauth_callback" should be
* included in the allowed redirect urls.
* included in an allowed redirect url.
*/
public function oauth_callback() {
// @task Store refresh token.
if (isset($_GET['code'])) {
$this->load->library('Google_Sync');
$token = $this->google_sync->authenticate($_GET['code']);
// Store the token into the database for future reference.
if (!isset($_SESSION)) {
@session_start();
}
if (isset($_SESSION['oauth_provider_id'])) {
$this->load->model('Providers_Model');
$this->Providers_Model->set_setting('google_sync', TRUE,
$_SESSION['oauth_provider_id']);
$this->Providers_Model->set_setting('google_token', $token,
$_SESSION['oauth_provider_id']);
} else {
echo '<h1>Sync provider id not specified!</h1>';
}
} else {
echo '<h1>Authorization Failed!</h1>';
}
}
}

View file

@ -12,6 +12,7 @@ require_once dirname(dirname(dirname(__FILE__))) . '/configuration.php';
* and the Easy!Appointments system.
*/
class Google_Sync {
private $CI;
private $client;
private $service;
@ -21,10 +22,12 @@ class Google_Sync {
* This method initializes the Google client class and the Calendar service
* class so that they can be used by the other methods.
*/
public function __construct() {
session_start();
public function __construct() {
$this->CI =& get_instance();
$CI =& get_instance();
if (!isset($_SESSION)) {
@session_start();
}
// Initialize google client and calendar service.
$this->client = new Google_Client();
@ -32,13 +35,40 @@ class Google_Sync {
$this->client->setClientId(SystemConfiguration::$google_client_id);
$this->client->setClientSecret(SystemConfiguration::$google_client_secret);
$this->client->setDeveloperKey(SystemConfiguration::$google_api_key);
$this->client->setRedirectUri('http://localhost/oauth_callback');
$this->client->setRedirectUri($this->CI->config->item('base_url') . 'google/oauth_callback');
$this->service = new Google_CalendarService($this->client);
}
/**
* Get Google OAuth authorization url.
*
* This url must be used to redirect the user to the Google user consent page,
* where the user grants access to his data for the Easy!Appointments app.
*/
public function get_auth_url() {
// "max_auth_age" is needed because the user needs to always log in
// and not use an existing session.
return $this->client->createAuthUrl() . '&max_auth_age=0';
}
/**
* Authenticate the Google API usage.
*
* When the user grants consent for his data usage, google is going to redirect
* the browser back to the given redirect url. There a authentication code is
* provided. Using this code, we can authenticate the API usage and store the
* token information to the database.
*
* @see Google Controller
*/
public function authenticate($auth_code) {
$this->client->authenticate($auth_code);
return $this->client->getAccessToken();
}
/**
* Refresh the Google Client access token.
*
* This method must be executed every time we need to make actions on a
* provider's Google Calendar account. A new token is necessary and the
* only way to get it is to use the stored refresh token that was provided
@ -48,10 +78,9 @@ class Google_Sync {
* @param string $refresh_token The provider's refresh token. This value is
* stored in the database and used every time we need to make actions to his
* Google Caledar account.
* @return bool Returns the authenticate operation result.
*/
public function authenticate($refresh_token) {
public function refresh_token($refresh_token) {
$this->client->refreshToken($refresh_token);
}
/**
@ -70,23 +99,24 @@ class Google_Sync {
* @param int $appointment_id The record id of the appointment that is going to
* be added to the database.
* @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) {
$CI =& get_instance();
$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');
$CI->load->model('Appointments_Model');
$CI->load->model('Service_Model');
$CI->load->model('Provider_Model');
$CI->load->model('Customers_Model');
$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');
$appointment_data = $CI->Appointments_Model->get_row($appointment_id);
$provider_data = $CI->Providers_Model->get_row($appointment_data['id_users_provider']);
$customer_data = $CI->Customers_Model->get_row($appointment_data['id_users_customer']);
$service_data = $CI->Services_Model->get_row($appointment_data['id_services']);
$company_name = $CI->Settings_Model->get_setting('company_name');
$CI->load->helper('general');
$this->CI->load->helper('general');
$event = new Google_Event();
$event->setSummary($service_data['name']);
@ -118,25 +148,44 @@ class Google_Sync {
// Add the new event to the "primary" calendar.
$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'];
$this->CI->Appointments_Model->add($appointment_data);
return $created_event;
}
/**
* Update an existing appointment that is already synced with Google Calendar.
*
* @param int $appointment_id
* @param int $appointment_id The appointment record id.
*/
public function update_appointment($appointment_id) {
public function update_appointment($appointment_data, $provider_data,
$service_data, $customer_data) {
$this->CI->load->model('Appointments_Model');
$this->CI->load->model('Services_Model');
$this->CI->load->model('Providers_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');
$this->CI->load->helper('general');
}
/**
* Delete an existing appointment from Google Calendar.
*
* @param type $appointment_id
* @param string $google_calendar_id The Google Calendar event id to
* be deleted.
*/
public function delete_appointment($appointment_id) {
public function delete_appointment($google_calendar_id) {
$this->service->events->delete('primary', $google_calendar_id);
}
}

View file

@ -201,6 +201,19 @@ class Notifications {
return TRUE;
}
/**
* Send delete appointment notification.
*
* This method should be called after the appointment has been deleted.
*
* <strong>IMPORTANT!</strong> This method's arguments should be taken
* from database before the appointment record is deleted.
*/
public function send_delete_appointment($appointment_data, $provider_data,
$service_data, $customer_data) {
// @task Implement sending delete appointment notification.
}
}

View file

@ -1,4 +1,4 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed.');
class Providers_Model extends CI_Model {
/**
@ -96,6 +96,7 @@ class Providers_Model extends CI_Model {
$providers = $this->db->get()->result_array();
// :: GET PROVIDER SERVICES
// Return also an array with the services that each provider can provide
// to the customers.
foreach($providers as &$provider) {
@ -115,6 +116,16 @@ class Providers_Model extends CI_Model {
}
}
// :: GET PROVIDER SETTINGS
foreach($providers as &$provider) {
$this->db
->select('*')
->from('ea_user_settings')
->where('id_users', $provider['id']);
$provider['settings'] = $this->db->get()->row_array();
unset($provider['settings']['id_users']); // Do not need it.
}
return $providers;
}

View file

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<title>Book Appointment | <?php echo $company_name; ?></title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<?php

View file

@ -29,30 +29,34 @@
<select id="select-filter-item"></select>
</div>
<div id="calendar-actions" class="btn-group">
<button id="google-sync" class="btn btn-primary"
title="Trigger the Google Calendar synchronization process.">
<i class="icon-refresh icon-white"></i>
Synchronize
</button>
<div id="calendar-actions">
<div class="btn-group">
<button id="google-sync" class="btn btn-primary"
title="Trigger the Google Calendar synchronization process.">
<i class="icon-refresh icon-white"></i>
<span>Synchronize</span>
</button>
<button id="enable-sync" class="btn" data-toggle="button"
title="Enable appointment synchronization with provider's Google Calendar account.">
<i class="icon-calendar"></i>
<span>Enable Sync</span>
</button>
</div>
<button id="enable-sync" class="btn" data-toggle="button"
title="Enable appointment synchronization with provider's Google Calendar account.">
<i class="icon-calendar"></i>
Enable Sync
</button>
<button id="insert-new-appointment" class="btn"
title="Create a new appointment and store it into the database.">
<i class="icon-plus"></i>
New Appointment
</button>
<button id="insert-unavailable-period" class="btn"
title="During unavailalbe period the provider won't accept new appointments.">
<i class="icon-ban-circle"></i>
Unavailable
</button>
<div class="btn-group">
<button id="insert-appointment" class="btn"
title="Create a new appointment and store it into the database.">
<i class="icon-plus"></i>
<span>Appointment</span>
</button>
<button id="insert-unavailable" class="btn"
title="During unavailalbe period the provider won't accept new appointments.">
<i class="icon-ban-circle"></i>
<span>Unavailable</span>
</button>
</div>
</div>
</div>

View file

@ -60,8 +60,12 @@ var BackendCalendar = {
// :: FILL THE SELECT ELEMENTS OF THE PAGE
var optgroupHtml = '<optgroup label="Providers">';
$.each(GlobalVariables.availableProviders, function(index, provider) {
var hasGoogleSync = (provider['settings']['google_sync'] === '1')
? 'true' : 'false';
optgroupHtml += '<option value="' + provider['id'] + '" ' +
'type="' + BackendCalendar.FILTER_TYPE_PROVIDER + '">' +
'type="' + BackendCalendar.FILTER_TYPE_PROVIDER + '" ' +
'google-sync="' + hasGoogleSync + '">' +
provider['first_name'] + ' ' + provider['last_name'] + '</option>';
});
optgroupHtml += '</optgroup>';
@ -112,6 +116,20 @@ var BackendCalendar = {
} else {
$('#google-sync, #enable-sync, #insert-unavailable-period')
.prop('disabled', false);
// If the user has already the sync enabled then apply the proper
// style changes.
if ($('#select-filter-item option:selected').attr('google-sync')
=== 'true') {
$('#enable-sync').addClass('btn-success enabled');
$('#enable-sync i').addClass('icon-white');
$('#enable-sync span').text('Disable Sync');
$('#google-sync').prop('disabled', false);
} else {
$('#enable-sync').removeClass('btn-success enabled');
$('#enable-sync i').removeClass('icon-white');
$('#enable-sync span').text('Enable Sync');
$('#google-sync').prop('disabled', true);
}
}
});
@ -201,6 +219,52 @@ var BackendCalendar = {
$('#manage-appointment').modal('show');
});
/**
* Event: Delete Popover Button "Click"
*
* Displays a prompt on whether the user wants the appoinmtent to be
* deleted. If he confirms the deletion then an ajax call is made to
* the server and deletes the appointment from the database.
*/
$(document).on('click', '.delete-popover', function() {
$(this).parents().eq(2).remove(); // Hide the popover
var messageButtons = {
'Delete' : function() {
var postUrl = GlobalVariables.baseUrl + 'backend/ajax_delete_appointment';
var postData = {
'appointment_id' : BackendCalendar.lastFocusedEventData.data['id']
};
$.post(postUrl, postData, function(response) {
/////////////////////////////////////////////////////////
console.log('Delete Appointment Response :', response);
/////////////////////////////////////////////////////////
if (response.error) {
GeneralFunctions.displayMessageBox('Delete Appointment Error',
'An unexpected error occured during the deletion of the '
+ 'appointment. Please try again.');
return;
}
// Close dialog and refresh calendar events.
$('#message_box').dialog('close');
$('#select-filter-item').trigger('change');
}, 'json');
},
'Cancel' : function() {
$('#message_box').dialog('close');
}
};
GeneralFunctions.displayMessageBox('Delete Appointment', 'Are you sure '
+ 'that you want to delete this appointment? This action cannot '
+ 'be undone.', messageButtons);
});
/**
* Event: Manage Appointments Dialog Cancel Button "Click"
*
@ -289,30 +353,60 @@ var BackendCalendar = {
});
/**
* Event: Enable Synchronization Button "Click"
* Event: Enable - Disable Synchronization Button "Click"
*
* When the user clicks on the "Enable Sync" button, a popup should appear
* that is going to follow the web server authorization flow of OAuth.
*
* @task Check whether the selected provider has already enabled the sync
* or not.
*/
$('#enable-sync').click(function() {
var authUrl = GlobalVariables.baseUrl + 'google/oauth/'
+ $('#select-filter-item').val();
var redirectUrl = GlobalVariables.baseUrl + 'google/oauth_callback';
var windowHandle = window.open(authUrl, 'Authorize Easy!Appointments',
'width=800, height=600');
var authInterval = window.setInterval(function() {
if (windowHandle.document.URL.indexOf(redirectUrl) !== -1) {
// The user has granted access to his data.
windowHandle.close();
window.clearInterval(authInterval);
$('#enable-sync').addClass('btn-success');
}
}, 100);
if ($('#enable-sync').hasClass('enabled') === false) {
// :: ENABLE SYNCHRONIZATION FOR SELECTED PROVIDER
var authUrl = GlobalVariables.baseUrl + 'google/oauth/'
+ $('#select-filter-item').val();
var redirectUrl = GlobalVariables.baseUrl + 'google/oauth_callback';
var windowHandle = window.open(authUrl, 'Authorize Easy!Appointments',
'width=800, height=600');
var authInterval = window.setInterval(function() {
// When the browser redirects to the google user consent page the
// "window.document" variable becomes "undefined" and when it comes
// back to the redirect url it changes back. So check whether the
// variable is undefined to avoid javascript errors.
if (windowHandle.document !== undefined) {
if (windowHandle.document.URL.indexOf(redirectUrl) !== -1) {
// The user has granted access to his data.
windowHandle.close();
window.clearInterval(authInterval);
$('#enable-sync').addClass('btn-success enabled');
$('#enable-sync i').addClass('icon-white');
$('#enable-sync span').text('Disable Sync');
$('#google-sync').prop('disabled', false);
}
}
}, 100);
} else {
// :: DISABLE SYNCHRONIZATION FOR SELECTED PROVIDER
// Update page elements and make an ajax call to remove the google
// sync setting of the selected provider.
$.each(GlobalVariables.availableProviders, function(index, provider) {
if (provider['id'] == $('#select-filter-item').val()) {
provider['settings']['google_sync'] = '0';
provider['settings']['google_token'] = null;
BackendCalendar.disableProviderSync(provider['id']);
$('#enable-sync').removeClass('btn-success enabled');
$('#enable-sync i').removeClass('icon-white');
$('#enable-sync span').text('Enable Sync');
$('#google-sync').prop('disabled', true);
return;
}
});
}
});
},
@ -556,6 +650,7 @@ var BackendCalendar = {
+ '<hr>' +
'<center>' +
'<button class="edit-popover btn btn-primary">Edit</button>' +
'<button class="delete-popover btn btn-danger">Delete</button>' +
'<button class="close-popover btn" data-po=' + jsEvent.target + '>Close</button>' +
'</center>';
@ -674,5 +769,33 @@ var BackendCalendar = {
$('.fv-events').each(function(index, eventHandle) {
$(eventHandle).popover();
});
},
/**
* This method disables the google synchronization for a specific provider.
*
* @param {int} providerId The selected provider record id.
*/
disableProviderSync: function(providerId) {
// Make an ajax call to the server in order to disable the setting
// from the database.
var postUrl = GlobalVariables.baseUrl + 'backend/ajax_disable_provider_sync';
var postData = {
'provider_id' : providerId
};
$.post(postUrl, postData, function(response) {
////////////////////////////////////////////////////////////
//console.log('Disable Provider Sync Response :', response);
////////////////////////////////////////////////////////////
if (response.error) {
GeneralFunctions.displayMessageBox('Disable Sync Error', 'An unexpected ' +
'error occured during the disable provider sync operation : ' +
response.error);
}
}, 'json');
}
};