- Αλλαγή του τρόπου προσθήκης ενός νέου ραντεβού στο Google Calendar του πελάτη (χρήση javascript και popup).

- Αλλαγές στο αρχείο google_sync.php
This commit is contained in:
alextselegidis@gmail.com 2013-06-10 15:51:23 +00:00
parent ecca0df535
commit fcf58a7cf2
10 changed files with 336 additions and 214 deletions

View file

@ -66,6 +66,9 @@ class Appointments extends CI_Controller {
$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;
@ -96,9 +99,41 @@ class Appointments extends CI_Controller {
. 'you can restore them later. <br><br>Error: <br>'
. $not_exc->getMessage() . '</pre>';
}
// Synchronize the appointment with the providers plan, if the
// google sync option is enabled.
$this->load->library('google_sync');
$google_sync = $this->Providers_Model->get_setting('google_sync',
$appointment_data['id_users_provider']);
if ($google_sync == TRUE) {
$google_token = $this->Providers_Model->get_setting('google_token',
$appointment_data['id_users_provider']);
// Validate the token. If it isn't valid, the sync operation cannot
// be completed.
if ($this->google_sync->validate_token($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']);
}
}
}
// Load the book appointment view.
$view_data['appointment_id'] = $appointment_data['id'];
$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);
}
}
@ -143,6 +178,12 @@ class Appointments extends CI_Controller {
$this->notifications->send_cancel_appointment($appointment_data, $provider_email);
$this->notifications->send_cancel_appointment($appointment_data, $customer_email);
// Delete the appointment from Google Calendar, if it is synced.
if ($appointment_data['id_google_calendar'] != NULL) {
$this->load->library('google_sync');
$this->google_sync->delete_appointment($appointment_data['id']);
}
} catch(Exception $exc) {
// Display the error message to the customer.
$view_data['error_message'] = $exc->getMessage();
@ -179,8 +220,8 @@ class Appointments extends CI_Controller {
$reserved_appointments = $this->Appointments_Model->get_batch($where_clause);
if ($_POST['manage_mode'] === 'true') {
// Current record id shouldn't be included as reserved time,
// whent the manage mode is true.
// Current record id shouldn't be included as reserved time, when the
// manage mode is true.
foreach($reserved_appointments as $index=>$appointment) {
if ($appointment['id'] == $_POST['appointment_id']) {
unset($reserved_appointments[$index]);
@ -339,33 +380,6 @@ class Appointments extends CI_Controller {
echo json_encode($available_hours);
}
/**
* Synchronize appointment with its' providers google calendar.
*
* This method syncs the registered appointment with the
* google calendar of the user.
*
* @task This method needs to be changed. Everytime a customer
* books a new appointment the synchronization process must be
* executed.
*/
public function google_sync($appointment_id) {
try {
$this->load->library('Google_Sync');
$this->google_sync->sync_appointment($appointment_id);
$view_data['message'] = 'Your appointment has been successfully added'
. 'to Google Calendar!';
$view_data['image'] = 'success.png';
} catch (Exception $exc) {
$view_data['message'] = 'An unexpected error occured during the sync '
. 'operation: <br/><pre>' . $exc->getMessage() . '<br/>'
. $exc->getTraceAsString() . '</pre>';
$view_data['image'] = 'error.png';
}
$this->load->view('appointments/google_sync', $view_data);
}
}
/* End of file appointments.php */

View file

@ -1,34 +0,0 @@
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Google extends CI_Controller {
/**
* Class Constructor
*/
public function __construct() {
parent::__construct();
}
/**
* Google API authorization redirect page.
*
* This is the page that the google api should redirect after the
* user allows the api to handle his data. A redirect page must be
* already set in the $_SESSION array in order to redirect to the
* correct page.
*
* @task Make redirect page with session variable.
*/
public function api_auth() {
session_start();
if (isset($_SESSION['sync_appointment_id'])) {
header('Location: ' . $this->config->base_url() . 'appointments/google_sync/'
. $_SESSION['sync_appointment_id'] . '?code=' . $_GET['code']);
} else {
echo 'An error occured during the Google Calendar API authorization process.';
}
}
}
/* End of file google.php */
/* Location: ./application/controllers/google.php */

View file

@ -5,6 +5,12 @@ require_once dirname(__FILE__) . '/external/google-api-php-client/Google_Client.
require_once dirname(__FILE__) . '/external/google-api-php-client/contrib/Google_CalendarService.php';
require_once dirname(dirname(dirname(__FILE__))) . '/configuration.php';
/**
* Google Synchronization Class
*
* This class implements all the core synchronization between the Google Calendar
* and the Easy!Appointments system.
*/
class Google_Sync {
private $client;
private $service;
@ -12,8 +18,8 @@ class Google_Sync {
/**
* Class Constructor
*
* This method initializes the google client and the calendar service
* so that they can be used by the other methods.
* 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();
@ -26,117 +32,113 @@ 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($CI->config->item('base_url') . 'google/api_auth');
$this->client->setRedirectUri('http://localhost/oauth_callback');
$this->service = new Google_CalendarService($this->client);
}
/**
* Authorize google calendar api.
* Validate the Google API access token of a provider.
*
* Before the system is able to use the google calendar api
* it must be authorized to access the users appointment. This
* method checks where an authorization token exists in the
* session cookie and handles the necessary actions.
* In order to manage a Google user's data, one need a valid access token.
* This token is provided when the user grants the permission to a system
* to access his Google account data. So before making any action we need
* to make sure that the available token is still valid.
*
* <strong>IMPORTANT</strong> This method must be called every
* time before the usage of the google api.
* <strong>IMPORTANT!</strong> Always use this method before anything else
* in order to make sure that the token is being set and still valid.
*
* @param string $access_token This token is normally stored in the database.
* @return bool Returns the validation result.
*/
public function authorize_api() {
// USE ONLY FOR RESETTING THE TOKEN - DEBUGGING
//unset($_SESSION['google_api_token']);
// If the user is logged out there shouldn't be a token
// entry to the session array.
if (isset($_GET['logout'])) {
unset($_SESSION['google_api_token']);
}
// If there is a 'code' entry available, authenticate the api.
if (isset($_GET['code'])) {
$this->client->authenticate($_GET['code']);
$_SESSION['google_api_token'] = $this->client->getAccessToken();
header('Location: http://' . $_SERVER['HTTP_HOST']
. $_SERVER['PHP_SELF']); // refreshes current url
}
// If there is an active token then assign it to the client object.
if (isset($_SESSION['google_api_token'])) {
$this->client->setAccessToken($_SESSION['google_api_token']);
}
if (!$this->client->getAccessToken()) {
$authUrl = $this->client->createAuthUrl();
header('Location: ' . $authUrl);
}
public function validate_token($access_token) {
$this->client->setAccessToken($access_token);
return $this->client->isAccessTokenExpired();
}
/**
* Synchronize a sigle appointment with Google Calendar.
* Add an appointment record to its providers Google Calendar account.
*
* This method syncs an existing appointment with its' providers
* google calendar account.
* This method checks whether the appointment's provider has enabled the Google
* Sync utility of Easy!Appointments and the stored access token is still valid.
* If yes, the selected appointment record is going to be added to the Google
* Calendar account.
*
* @expectedException Exception An error has occured during
* the sync operation.
* <strong>IMPORTANT!</strong> 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 appointments database id.
* @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.
*/
public function sync_appointment($appointment_id) {
// Store the appointment id in the session cookies in case that
// we need to redirect to the authorization page of google.
$_SESSION['sync_appointment_id'] = $appointment_id;
// Before procceeding to the sync operation we must authorize
// the user must authorize the application.
$this->authorize_api();
// Load the models and retrieve the necessary data from db.
public function add_appointment($appointment_id) {
$CI =& get_instance();
$CI->load->model('Appointments_Model');
$CI->load->model('Service_Model');
$CI->load->model('Provider_Model');
$CI->load->model('Customers_Model');
$CI->load->model('Providers_Model');
$CI->load->model('Services_Model');
$CI->load->model('Settings_Model');
$appointment = $CI->Appointments_Model->get_row($appointment_id);
$customer = $CI->Customers_Model->get_row($appointment['id_users_customer']);
$provider = $CI->Providers_Model->get_row($appointment['id_users_provider']);
$service = $CI->Services_Model->get_row($appointment['id_services']);
$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');
// Add a new event to the user's google calendar.
$CI->load->helper('general');
$event = new Google_Event();
$event->setSummary($service['name']);
$event->setLocation($CI->Settings_Model->get_setting('company_name'));
$event->setSummary($service_data['name']);
$event->setLocation($company_name);
$start = new Google_EventDateTime();
$start->setDateTime(date3339(strtotime($appointment['start_datetime'])));
$start->setDateTime(date3339(strtotime($appointment_data['start_datetime'])));
$event->setStart($start);
$end = new Google_EventDateTime();
$end->setDateTime(date3339(strtotime($appointment['end_datetime'])));
$end->setDateTime(date3339(strtotime($appointment_data['end_datetime'])));
$event->setEnd($end);
$eventProvider = new Google_EventAttendee();
$eventProvider->setDisplayName($provider['first_name'] . ' ' . $provider['last_name']);
//$eventProvider->setSelf(true);
$eventProvider->setEmail($provider['email']);
$eventProvider->setDisplayName($provider_data['first_name'] . ' '
. $provider_data['last_name']);
$eventProvider->setEmail($provider_data['email']);
$eventCustomer = new Google_EventAttendee();
$eventCustomer->setDisplayName($customer['first_name'] . ' ' . $customer['last_name']);
$eventCustomer->setEmail($customer['email']);
$eventCustomer->setDisplayName($customer_data['first_name'] . ' '
. $customer_data['last_name']);
$eventCustomer->setEmail($customer_data['email']);
$event->attendees = array ($eventProvider, $eventCustomer);
$event->attendees = array(
$eventProvider,
$eventCustomer
);
// Add the new event to the "primary" calendar.
$created_event = $this->service->events->insert('primary', $event);
unset($_SESSION['sync_appointment_id']);
return $created_event;
}
/**
* Update an existing appointment that is already synced with Google Calendar.
*
* @param int $appointment_id
*/
public function update_appointment($appointment_id) {
}
/**
* Delete an existing appointment from Google Calendar.
*
* @param type $appointment_id
*/
public function delete_appointment($appointment_id) {
}
}
/* End of file google_sync.php */

View file

@ -18,7 +18,8 @@ class Providers_Model extends CI_Model {
*/
public function get_row($provider_id) {
if (!is_numeric($provider_id)) {
throw new InvalidArgumentException('$provider_id argument is not an integer : ' . $provider_id);
throw new InvalidArgumentException('$provider_id argument is not an integer : '
. $provider_id);
}
return $this->db->get_where('ea_users', array('id' => $provider_id))->row_array();
}
@ -135,6 +136,18 @@ class Providers_Model extends CI_Model {
return $this->db->get_where('ea_user_settings', array('id_users' => $provider_id))
->row_array()[$setting_name];
}
/**
* Set a provider's setting value in the database.
*
* @param string $setting_name The setting's name.
* @param string $value The setting's value.
* @param numeric $provider_id The selected provider id.
*/
public function set_setting($setting_name, $value, $provider_id) {
$this->db->where(array('id_users' => $provider_id));
return $this->db->update('ea_user_settings', array($setting_name => $value));
}
}
/* End of file providers_model.php */

View file

@ -132,7 +132,8 @@
Press the "Cancel" button to remove the appointment
from the company schedule.
</p>
<form method="get" action="' . $this->config->item('base_url')
<form id="cancel-appointment-form" method="get"
action="' . $this->config->item('base_url')
. 'appointments/cancel/' . $appointment_data['hash'] . '">
<button id="cancel-appointment" class="btn btn-inverse">
Cancel</button>

View file

@ -6,9 +6,17 @@
<?php // INCLUDE JS FILES ?>
<script
type="text/javascript"
src="<?php echo $this->config->base_url(); ?>assets/js/libs/jquery/jquery.min.js">
</script>
src="<?php echo $this->config->base_url(); ?>assets/js/libs/jquery/jquery.min.js"></script>
<script
type="text/javascript"
src="<?php echo $this->config->base_url(); ?>assets/js/libs/bootstrap/bootstrap.min.js"></script>
<script
type="text/javascript"
src="<?php echo $this->config->base_url(); ?>assets/js/libs/date.js"></script>
<script
type="text/javascript"
src="<?php echo $this->config->base_url(); ?>assets/js/general_functions.js"></script>
<?php // INCLUDE CSS FILES ?>
<link
rel="stylesheet"
@ -31,35 +39,161 @@
}
#success-frame {
width: 660px;
width: 630px;
margin: 150px auto 0 auto;
background: #FFF;
border: 1px solid #DDDADA;
min-height: 197px;
padding: 108px 10px;
padding: 70px;
}
#success-icon {
float: left;
margin: 10px 25px 100px 50px;
float: right;
margin-top: 17px;
}
</style>
<script src="https://apis.google.com/js/client.js"></script>
<script type="text/javascript">
var GlobalVariables = {
'appointmentData' : <?php echo json_encode($appointment_data); ?>,
'providerData' : <?php echo json_encode($provider_data); ?>,
'serviceData' : <?php echo json_encode($service_data); ?>,
'companyName' : <?php echo '"' . $company_name . '"'; ?>,
'googleApiKey' : <?php echo '"' . SystemConfiguration::$google_api_key . '"'; ?>,
'googleClientId' : <?php echo '"' . SystemConfiguration::$google_client_id . '"'; ?>,
'googleApiScope' : 'https://www.googleapis.com/auth/calendar'
};
$(document).ready(function() {
/**
* Event: Add Appointment to Google Calendar "Click"
*
* This event handler adds the appointment to the users Google
* Calendar Account. The event is going to be added to the "primary"
* calendar. In order to use the API the javascript client library
* provided by Google is necessary.
*/
$('#add-to-google-calendar').click(function() {
gapi.client.setApiKey(GlobalVariables.googleApiKey);
gapi.auth.authorize({
'client_id' : GlobalVariables.googleClientId,
'scope' : GlobalVariables.googleApiScope,
'immediate' : false
}, handleAuthResult);
});
/**
* This method handles the authorization result. If the user granted access
* to his data, then the appointment is going to be added to his calendar.
*
* @param {bool} authResult The user's auth result.
*/
function handleAuthResult(authResult) {
try {
///////////////////////////////////////////////////////////
console.log('Google Authorization Result: ', authResult);
///////////////////////////////////////////////////////////
if (authResult.error) {
throw 'Could not authorize user.';
}
// The user has granted access, add the appointment to his calendar.
// Before making the event.insert request the the event resource data
// must be prepared.
var appointmentData = GlobalVariables.appointmentData;
appointmentData['start_datetime'] = GeneralFunctions.ISODateString(
Date.parseExact(appointmentData['start_datetime'],
'yyyy-MM-dd HH:mm:ss'));
appointmentData['end_datetime'] = GeneralFunctions.ISODateString(
Date.parseExact(appointmentData['end_datetime'],
'yyyy-MM-dd HH:mm:ss'));
// Create a valid Google Calendar API resource for the new event.
var resource = {
'summary' : GlobalVariables.serviceData['name'],
'location' : GlobalVariables.companyName,
'start' : {
'dateTime': appointmentData['start_datetime']
},
'end' : {
'dateTime': appointmentData['end_datetime']
},
'attendees' : [
{
'email' : GlobalVariables.providerData['email'],
'displayName' : GlobalVariables.providerData['first_name'] + ' '
+ GlobalVariables.providerData['last_name']
}
]
};
gapi.client.load('calendar', 'v3', function() {
var request = gapi.client.calendar.events.insert({
'calendarId' : 'primary',
'resource' : resource
});
request.execute(function(response) {
/////////////////////////////////////////////////
console.log('Google API Response:', response);
/////////////////////////////////////////////////
if (!response.error) {
$('#success-frame').append(
'<br><br>' +
'<div class="alert alert-success">' +
'<h4>Success!</h4>' +
'<p>' +
'Your appointment has successfully been added to ' +
'your Google Calendar account. <br>' +
'<a href="' + response.htmlLink + '">' +
'Appointment Link' +
'</a>' +
'</p>' +
'</div>'
);
} else {
throw 'Could not add the event to Google Calendar.';
}
});
});
} catch(exc) {
// The user denied access or something else happened, display
// corresponding message on the screen.
$('#success-frame').append(
'<br><br>' +
'<div class="alert alert-error">' +
'<h4>Oops! Something Went Wrong!</h4>' +
'<p>' +
'Your appointment could not be added to your ' +
'Google Calendar account.' +
'<pre>' + exc + '</pre>' +
'</p>' +
'</div>');
}
}
});
</script>
</head>
<body>
<div id="success-frame" class="frame-container">
<img id="success-icon" src="<?php echo $this->config->base_url(); ?>assets/images/success.png" />
<h2>Your appointment has been successfully registered.</h2>
<p>An email with the appointment details has been sented to you.</p>
<a id="google-sync"
href="<?php echo $this->config->base_url() . 'appointments/google_sync/' . $appointment_id; ?>">
Sync with Google Calendar
</a>
<h3>Your appointment has been successfully registered!</h3>
<p>An email with the appointment details has been sent to you.</p>
<button id="add-to-google-calendar" class="btn btn-primary">
<i class="icon-plus icon-white"></i>
Add to Google Calendar
</button>
<?php
// Display exception message (if any).
// @task Special display of exceptions within the app.
if (isset($notification_error)) {
echo $notification_error;
}

View file

@ -31,17 +31,16 @@
}
#success-frame {
width: 660px;
width: 650px;
margin: 150px auto 0 auto;
background: #FFF;
border: 1px solid #DDDADA;
min-height: 197px;
padding: 108px 10px;
padding: 70px;
}
#success-icon {
float: left;
margin: 10px 25px 100px 50px;
float: right;
margin-top: 10px;
}
</style>
@ -50,7 +49,7 @@
<div id="success-frame" class="frame-container">
<img id="success-icon" src="<?php echo $this->config->base_url(); ?>assets/images/success.png" />
<h2>Your appointment has been successfully cancelled</h2>
<h3>Your appointment has been successfully cancelled!</h3>
<?php
// Display exception message (if any).

View file

@ -1,52 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<?php // INCLUDE CSS FILES ?>
<link
rel="stylesheet"
type="text/css"
href="<?php echo $this->config->base_url(); ?>assets/css/libs/bootstrap/bootstrap.css">
<link
rel="stylesheet"
type="text/css"
href="<?php echo $this->config->base_url(); ?>assets/css/libs/bootstrap/bootstrap-responsive.css">
<?php // SET FAVICON FOR PAGE ?>
<link
rel="icon"
type="image/x-icon"
href="<?php echo $this->config->base_url(); ?>assets/images/favicon.ico">
<style>
body {
background-color: #CAEDF3;
}
#message-frame {
width: 660px;
margin: 150px auto 0 auto;
background: #FFF;
border: 1px solid #DDDADA;
min-height: 197px;
padding: 108px 10px;
}
#message-icon {
float: left;
margin: 10px 25px 25px 50px;
}
</style>
</head>
<body>
<div id="message-frame" class="frame-container">
<img
id="message-icon"
src="<?php echo $this->config->base_url(); ?>assets/images/<?php echo $image; ?>" />
<h2>Google Calendar Sync</h2>
<p><?php echo $message; ?></p>
</div>
</body>
</html>

View file

@ -182,6 +182,33 @@ var bookAppointment = {
$(this).addClass('selected-hour');
bookAppointment.updateConfirmFrame();
});
if (bookAppointment.manageMode) {
/**
* Event: Cancel Appointment Button "Click"
*
* When the user clicks the "Cancel" button this form is going to
* be submitted. We need the user to confirm this action because
* once the appointment is cancelled, it will be delete from the
* database.
*/
$('#cancel-appointment').click(function() {
event.preventDefault();
var dialogButtons = {
'Yes' : function() {
$('#cancel-appointment-form').submit();
},
'No' : function() {
$('#message_box').dialog('close');
}
};
GeneralFunctions.displayMessageBox('Cancel Appointment', 'Are you sure '
+ 'that you want to cancel this appointment? This action can\'t '
+ 'be undone.', dialogButtons);
});
}
},
/**

View file

@ -102,4 +102,22 @@ GeneralFunctions.getUrlParameter = function(url, parameterName) {
return "";
else
return results[1];
}
/**
* This function creates a RFC 3339 date string. This string is needed
* by the Google Calendar API in order to pass dates as parameters.
*
* @param {date} dt The given date that will be transformed
* @returns {String} Returns the transformed string.
*/
GeneralFunctions.ISODateString = function(dt){
function pad(n){return n<10 ? '0'+n : n}
return dt.getUTCFullYear()+'-'
+ pad(dt.getUTCMonth()+1)+'-'
+ pad(dt.getUTCDate())+'T'
+ pad(dt.getUTCHours())+':'
+ pad(dt.getUTCMinutes())+':'
+ pad(dt.getUTCSeconds())+'Z'
}