* Fixed google sync problem with unavailable time periods.

* Fixed minor issues with backend. 
* Fixed get_provider_available_time_periods() method - now works correctly. 
* Added unavailable notes in the calendar (under the "Unavailable" title). 
* Updated the thesis code snippets.
* Added listings.pdf, a free ebook about latex listings.
This commit is contained in:
alextselegidis@gmail.com 2013-10-27 17:41:37 +00:00
parent 140cb62627
commit 2e7e668465
20 changed files with 276 additions and 302 deletions

View file

@ -47,7 +47,7 @@
emph =[1]{php},
emphstyle =[1]\color{black},
emph =[2]{if,and,or,else,public,function,try,catch,return},
emphstyle =[2]\color{dkyellow},
emphstyle =[2]\color{dkblue},
numbers = left,
tabsize = 2,
backgroundcolor = \color{ltgrey},

View file

@ -1,7 +1,7 @@
<?php
/**
* Complete synchronization of appointments between Google Calendar
* and Easy!Appointments.
* Complete synchronization of appointments between Google
* Calendar and Easy!Appointments.
*
* This method will completely sync the appointments of a provider
* with his Google Calendar account. The sync period needs to be
@ -34,8 +34,8 @@ public function sync($provider_id = NULL) {
$google_sync = $this->providers_model
->get_setting('google_sync', $provider['id']);
if (!$google_sync) {
throw new Exception('The selected provider has not the '
. 'google synchronization setting enabled.');
throw new Exception('The selected provider has not the '
. 'Google synchronization setting enabled.');
}
$google_token = json_decode($this->providers_model
@ -43,66 +43,75 @@ public function sync($provider_id = NULL) {
$this->load->library('google_sync');
$this->google_sync->refresh_token($google_token->refresh_token);
// Fetch provider's appointments that belong to the
// sync time period.
// Fetch provider's appointments that belong to the sync
// time period.
$sync_past_days = $this->providers_model
->get_setting('sync_past_days', $provider['id']);
$sync_future_days = $this->providers_model
->get_setting('sync_future_days', $provider['id']);
$start = strtotime('-' . $sync_past_days . ' days',
strtotime(date('Y-m-d')));
$end = strtotime('+' . $sync_future_days . ' days',
strtotime(date('Y-m-d')));
$start = strtotime('-' . $sync_past_days
. ' days', strtotime(date('Y-m-d')));
$end = strtotime('+' . $sync_future_days
. ' days', strtotime(date('Y-m-d')));
$where_clause = array(
'start_datetime >=' => date('Y-m-d H:i:s', $start),
'end_datetime <=' => date('Y-m-d H:i:s', $end),
'id_users_provider' => $provider['id'],
'is_unavailable' => FALSE
'id_users_provider' => $provider['id']
);
$appointments = $this->appointments_model
->get_batch($where_clause);
$appointments =
$this->appointments_model->get_batch($where_clause);
$company_settings = array(
'company_name' =>
$this->settings_model->get_setting('company_name'),
'company_link' =>
$this->settings_model->get_setting('company_link'),
'company_email' =>
$this->settings_model->get_setting('company_email')
'company_name' => $this->settings_model
->get_setting('company_name'),
'company_link' => $this->settings_model
->get_setting('company_link'),
'company_email' => $this->settings_model
->get_setting('company_email')
);
// Sync each appointment with Google Calendar by following
// Sync each appointment with Google Calendar by following
// the project's sync protocol (see documentation).
foreach($appointments as $appointment) {
$service = $this->services_model
->get_row($appointment['id_services']);
$customer = $this->customers_model
->get_row($appointment['id_users_customer']);
if ($appointment['is_unavailable'] == FALSE) {
$service = $this->services_model
->get_row($appointment['id_services']);
$customer = $this->customers_model
->get_row($appointment['id_users_customer']);
} else {
$service = NULL;
$customer = NULL;
}
// 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);
->add_appointment($appointment, $provider,
$service, $customer, $company_settings);
$appointment['id_google_calendar'] = $google_event->id;
$this->appointments_model->add($appointment); // Save gcal id
$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']);
// If gcal event is different from e!a appointment then
// update Easy!Appointments record.
if ($google_event->status == 'cancelled') {
throw new Exception('Event is cancelled, remove the '
. 'record from Easy!Appointments.');
}
// 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());
strtotime($google_event->getStart()->getDateTime());
$event_end =
strtotime($google_event->getEnd()->getDateTime());
strtotime($google_event->getEnd()->getDateTime());
if ($appt_start != $event_start
|| $appt_end != $event_end) {
@ -146,7 +155,6 @@ public function sync($provider_id = NULL) {
'id_users_customer' => NULL,
'id_services' => NULL,
);
$this->appointments_model->add($appointment);
}
}

View file

@ -6,16 +6,16 @@
* This method is very important because there are many cases where
* the system needs to know when a provider is avaible for an
* appointment. This method will return an array that belongs to the
* selected date and contains values that have the start and the end
* selected date and contains values that have the start and the end
* time of an available time period.
*
* @param numeric $provider_id The provider's record id.
* @param string $selected_date The date to be checked (MySQL
* @param string $selected_date The date to be checked (MySQL
* formatted string).
* @param array $exclude_appointments This array contains the ids of
* the appointments that will not be taken into consideration when
* the available time periods are calculated.
* @return array Returns an array with the available time periods of
* @param array $exclude_appointments This array contains the ids of
* the appointments that will not be taken into consideration when the
* available time periods are calculated.
* @return array Returns an array with the available time periods of
* the provider.
*/
private function get_provider_available_time_periods($provider_id,
@ -28,16 +28,15 @@ private function get_provider_available_time_periods($provider_id,
->get_setting('working_plan', $provider_id), true);
$where_clause = array(
'DATE(start_datetime)' =>
date('Y-m-d', strtotime($selected_date)),
'id_users_provider' => $provider_id
'DATE(start_datetime)'=>date('Y-m-d', strtotime($selected_date)),
'id_users_provider'=>$provider_id
);
$reserved_appointments =
$this->appointments_model->get_batch($where_clause);
$reserved_appointments = $this->appointments_model
->get_batch($where_clause);
// Sometimes it might be necessary to not take into account some
// appointment records in order to display what the providers'
// appointment records in order to display what the providers'
// available time periods would be without them.
foreach ($exclude_appointments as $excluded_id) {
foreach ($reserved_appointments as $index => $reserved) {
@ -47,12 +46,11 @@ private function get_provider_available_time_periods($provider_id,
}
}
// Find the empty spaces on the plan. The first split between
// the plan is due to a break (if exist). After that every
// reserved appointment is considered to be a taken space in the
// plan.
// Find the empty spaces on the plan. The first split between the
// plan is due to a break (if exist). After that every reserved
// appointment is considered to be a taken space in the plan.
$selected_date_working_plan =
$working_plan[strtolower(date('l', strtotime($selected_date)))];
$working_plan[strtolower(date('l', strtotime($selected_date)))];
$available_periods_with_breaks = array();
if (isset($selected_date_working_plan['breaks'])) {
@ -65,8 +63,8 @@ private function get_provider_available_time_periods($provider_id,
$start_hour = $selected_date_working_plan['start'];
$end_hour = $break['start'];
} else {
$start_hour = $selected_date_working_plan
['breaks'][$last_break_index]['end'];
$start_hour = $selected_date_working_plan['breaks']
[$last_break_index]['end'];
$end_hour = $break['start'];
}
@ -75,118 +73,67 @@ private function get_provider_available_time_periods($provider_id,
'end' => $end_hour
);
}
// Add the period from the last break to the end of the day.
$available_periods_with_breaks[] = array(
'start' =>$selected_date_working_plan['breaks'][$index]['end'],
'end' => $selected_date_working_plan['end']
'start'=>$selected_date_working_plan['breaks'][$index]['end'],
'end'=>$selected_date_working_plan['end']
);
}
// Break the empty periods with the reserved appointments.
$available_periods_with_appointments = array();
$available_periods_with_appointments =
$available_periods_with_breaks;
if (count($reserved_appointments) > 0) {
foreach($available_periods_with_breaks as $period) {
foreach($reserved_appointments as $index=>$reserved) {
$appointment_start =
date('H:i', strtotime($reserved['start_datetime']));
$appointment_end =
date('H:i', strtotime($reserved['end_datetime']));
$period_start =
date('H:i', strtotime($period['start']));
$period_end =
date('H:i', strtotime($period['end']));
if ($period_start <= $appointment_start
&& $period_end >= $appointment_end) {
// We need to check whether another appointment fits
// in the current time period. If this happens, then
// we need to consider the whole appointment time as
// one, because the provider will not be available.
foreach ($reserved_appointments as $tmp_appointment) {
$appt_start = date('H:i',
strtotime($tmp_appointment['start_datetime']));
$appt_end = date('H:i',
strtotime($tmp_appointment['end_datetime']));
if ($period_start < $appt_start
&& $period_end > $appt_end) {
if ($appointment_start > $appt_start) {
$appointment_start = $appt_start;
}
if ($appointment_end < $appt_end) {
$appointment_end = $appt_end;
}
}
}
foreach($reserved_appointments as $appointment) {
foreach($available_periods_with_appointments as $index=>&$period){
$a_start =
date('H:i',strtotime($appointment['start_datetime']));
$a_end =
date('H:i', strtotime($appointment['end_datetime']));
$p_start =
date('H:i', strtotime($period['start']));
$p_end =
date('H:i', strtotime($period['end']));
// Current appointment is within the current
// empty space. So we need to break the empty
// space into two other spaces that don't include
// the appointment.
$new_period = array(
'start' => $period_start,
'end' => $appointment_start
);
if (!in_array($new_period,
$available_periods_with_appointments)) {
$available_periods_with_appointments[] = $new_period;
}
$new_period = array(
'start' => $appointment_end,
'end' => $period_end
);
if (!in_array($new_period,
$available_periods_with_appointments)) {
$available_periods_with_appointments[] = $new_period;
}
} else {
// Check if there are any other appointments
// between this time space. If not, it is going
// to be added as it is.
$found = FALSE;
foreach ($reserved_appointments as $tmp_appointment) {
$appt_start = date('H:i',
strtotime($tmp_appointment['start_datetime']));
$appt_end = date('H:i',
strtotime($tmp_appointment['end_datetime']));
if ($period_start < $appt_start
&& $period_end > $appt_end) {
$found = TRUE;
}
}
// It is also necessary to check that this time
// period doesn't already exist in the
// "$empty_spaces_with_appointments" array.
$empty_period = array(
'start' => $period_start,
'end' => $period_end
);
$already_exist = in_array($empty_period,
$available_periods_with_appointments);
if ($found === FALSE && $already_exist === FALSE) {
$available_periods_with_appointments[] = $empty_period;
}
}
if ($a_start <= $p_start && $a_end <= $p_end
&& $a_end <= $p_start) {
// The appointment does not belong in this time period, so
// we will not change anything.
} else if ($a_start <= $p_start && $a_end <= $p_end
&& $a_end >= $p_start) {
// The appointment starts before the period and finishes
// somewhere inside.We will need to break this period and
// leave the available part.
$period['start'] = $a_end;
} else if ($a_start >= $p_start && $a_end <= $p_start) {
// The appointment is inside the time period, so we will
// split the period into two new others.
unset($available_periods_with_appointments[$index]);
$available_periods_with_appointments[] = array(
'start' => $p_start,
'end' => $a_start
);
$available_periods_with_appointments[] = array(
'start' => $a_end,
'end' => $p_end
);
} else if ($a_start >= $p_start && $a_end >= $p_start
&& $a_start <= $p_end) {
// The appointment starts in the period and finishes out
// of it. We will need to remove the time that is taken
// from the appointment.
$period['end'] = $a_start;
} else if ($a_start >= $p_start && $a_end >= $p_end
&& $a_start >= $p_end) {
// The appointment does not belong in the period so do not
// change anything.
} else if ($a_start <= $p_start && $a_end >= $p_end
&& $a_start <= $p_end) {
// The appointment is bigger than the period, so this period
// needs to be removed.
unset($available_periods_with_appointments[$index]);
}
}
} else {
$available_periods_with_appointments =
$available_periods_with_breaks;
}
return $available_periods_with_appointments;
return array_values($available_periods_with_appointments);
}

Binary file not shown.

Binary file not shown.

View file

@ -513,92 +513,53 @@ class Appointments extends CI_Controller {
}
// Break the empty periods with the reserved appointments.
$available_periods_with_appointments = array();
$available_periods_with_appointments = $available_periods_with_breaks;
if (count($reserved_appointments) > 0) {
foreach($reserved_appointments as $appointment) {
foreach($available_periods_with_appointments as $index => &$period) {
foreach($available_periods_with_breaks as $period) {
foreach($reserved_appointments as $index=>$reserved) {
$appointment_start = date('H:i', strtotime($reserved['start_datetime']));
$appointment_end = date('H:i', strtotime($reserved['end_datetime']));
$period_start = date('H:i', strtotime($period['start']));
$period_end = date('H:i', strtotime($period['end']));
if ($period_start <= $appointment_start && $period_end >= $appointment_end) {
// We need to check whether another appointment fits in the current
// time period. If this happens, then we need to consider the whole
// appointment time as one, because the provider will not be available.
foreach ($reserved_appointments as $tmp_appointment) {
$appt_start = date('H:i', strtotime($tmp_appointment['start_datetime']));
$appt_end = date('H:i', strtotime($tmp_appointment['end_datetime']));
if ($period_start < $appt_start && $period_end > $appt_end) {
if ($appointment_start > $appt_start) {
$appointment_start = $appt_start;
}
if ($appointment_end < $appt_end) {
$appointment_end = $appt_end;
}
}
}
$a_start = date('H:i', strtotime($appointment['start_datetime']));
$a_end = date('H:i', strtotime($appointment['end_datetime']));
$p_start = date('H:i', strtotime($period['start']));
$p_end = date('H:i', strtotime($period['end']));
// Current appointment is within the current empty space. So
// we need to break the empty space into two other spaces that
// don't include the appointment.
$new_period = array(
'start' => $period_start,
'end' => $appointment_start
);
if (!in_array($new_period, $available_periods_with_appointments)) {
$available_periods_with_appointments[] = $new_period;
}
$new_period = array(
'start' => $appointment_end,
'end' => $period_end
);
if (!in_array($new_period, $available_periods_with_appointments)) {
$available_periods_with_appointments[] = $new_period;
}
} else {
// Check if there are any other appointments between this
// time space. If not, it is going to be added as it is.
$found = FALSE;
foreach ($reserved_appointments as $tmp_appointment) {
$appt_start = date('H:i', strtotime($tmp_appointment['start_datetime']));
$appt_end = date('H:i', strtotime($tmp_appointment['end_datetime']));
if ($period_start < $appt_start && $period_end > $appt_end) {
$found = TRUE;
}
}
// It is also necessary to check that this time period doesn't
// already exist in the "$empty_spaces_with_appointments" array.
$empty_period = array(
'start' => $period_start,
'end' => $period_end
);
$already_exist = in_array($empty_period, $available_periods_with_appointments);
if ($found === FALSE && $already_exist === FALSE) {
$available_periods_with_appointments[] = $empty_period;
}
}
if ($a_start <= $p_start && $a_end <= $p_end && $a_end <= $p_start) {
// The appointment does not belong in this time period, so we
// will not change anything.
} else if ($a_start <= $p_start && $a_end <= $p_end && $a_end >= $p_start) {
// The appointment starts before the period and finishes somewhere inside.
// We will need to break this period and leave the available part.
$period['start'] = $a_end;
} else if ($a_start >= $p_start && $a_end <= $p_start) {
// The appointment is inside the time period, so we will split the period
// into two new others.
unset($available_periods_with_appointments[$index]);
$available_periods_with_appointments[] = array(
'start' => $p_start,
'end' => $a_start
);
$available_periods_with_appointments[] = array(
'start' => $a_end,
'end' => $p_end
);
} else if ($a_start >= $p_start && $a_end >= $p_start && $a_start <= $p_end) {
// The appointment starts in the period and finishes out of it. We will
// need to remove the time that is taken from the appointment.
$period['end'] = $a_start;
} else if ($a_start >= $p_start && $a_end >= $p_end && $a_start >= $p_end) {
// The appointment does not belong in the period so do not change anything.
} else if ($a_start <= $p_start && $a_end >= $p_end && $a_start <= $p_end) {
// The appointment is bigger than the period, so this period needs to be
// removed.
unset($available_periods_with_appointments[$index]);
}
}
} else {
$available_periods_with_appointments = $available_periods_with_breaks;
}
return $available_periods_with_appointments;
return array_values($available_periods_with_appointments);
}
/**

View file

@ -112,8 +112,7 @@ class Google extends CI_Controller {
$where_clause = array(
'start_datetime >=' => date('Y-m-d H:i:s', $start),
'end_datetime <=' => date('Y-m-d H:i:s', $end),
'id_users_provider' => $provider['id'],
'is_unavailable' => FALSE
'id_users_provider' => $provider['id']
);
$appointments = $this->appointments_model->get_batch($where_clause);
@ -127,8 +126,13 @@ class Google extends CI_Controller {
// Sync each appointment with Google Calendar by following the project's sync
// protocol (see documentation).
foreach($appointments as $appointment) {
$service = $this->services_model->get_row($appointment['id_services']);
$customer = $this->customers_model->get_row($appointment['id_users_customer']);
if ($appointment['is_unavailable'] == FALSE) {
$service = $this->services_model->get_row($appointment['id_services']);
$customer = $this->customers_model->get_row($appointment['id_users_customer']);
} else {
$service = NULL;
$customer = NULL;
}
// If current appointment not synced yet, add to gcal.
if ($appointment['id_google_calendar'] == NULL) {
@ -141,6 +145,10 @@ class Google extends CI_Controller {
try {
$google_event = $this->google_sync->get_event($appointment['id_google_calendar']);
if ($google_event->status == 'cancelled') {
throw new Exception('Event is cancelled, remove the record from Easy!Appointments.');
}
// If gcal event is different from e!a appointment then update e!a record.
$is_different = FALSE;
$appt_start = strtotime($appointment['start_datetime']);

View file

@ -108,7 +108,7 @@ class Google_Sync {
$this->CI->load->helper('general');
$event = new Google_Event();
$event->setSummary($service['name']);
$event->setSummary(($service != NULL) ? $service['name'] : 'Unavailable');
$event->setLocation($company_settings['company_name']);
$start = new Google_EventDateTime();
@ -119,20 +119,21 @@ class Google_Sync {
$end->setDateTime(date3339(strtotime($appointment['end_datetime'])));
$event->setEnd($end);
$eventProvider = new Google_EventAttendee();
$eventProvider->setDisplayName($provider['first_name'] . ' '
$event->attendees = array();
$event_provider = new Google_EventAttendee();
$event_provider->setDisplayName($provider['first_name'] . ' '
. $provider['last_name']);
$eventProvider->setEmail($provider['email']);
$event_provider->setEmail($provider['email']);
$event->attendees[] = $event_provider;
$eventCustomer = new Google_EventAttendee();
$eventCustomer->setDisplayName($customer['first_name'] . ' '
. $customer['last_name']);
$eventCustomer->setEmail($customer['email']);
$event->attendees = array(
$eventProvider,
$eventCustomer
);
if ($customer != NULL) {
$event_customer = new Google_EventAttendee();
$event_customer->setDisplayName($customer['first_name'] . ' '
. $customer['last_name']);
$event_customer->setEmail($customer['email']);
$event->attendees[] = $event_customer;
}
// Add the new event to the "primary" calendar.
$created_event = $this->service->events->insert('primary', $event);
@ -171,20 +172,21 @@ class Google_Sync {
$end->setDateTime(date3339(strtotime($appointment['end_datetime'])));
$event->setEnd($end);
$event->attendees = array();
$event_provider = new Google_EventAttendee();
$event_provider->setDisplayName($provider['first_name'] . ' '
. $provider['last_name']);
$event_provider->setEmail($provider['email']);
$event->attendees[] = $event_provider;
$event_customer = new Google_EventAttendee();
$event_customer->setDisplayName($customer['first_name'] . ' '
. $customer['last_name']);
$event_customer->setEmail($customer['email']);
$event->attendees = array(
$event_provider,
$event_customer
);
if ($customer != NULL) {
$event_customer = new Google_EventAttendee();
$event_customer->setDisplayName($customer['first_name'] . ' '
. $customer['last_name']);
$event_customer->setEmail($customer['email']);
$event->attendees[] = $event_customer;
}
$updated_event = $this->service->events->update('primary', $event->getId(), $event);

View file

@ -133,7 +133,7 @@
<form id="cancel-appointment-form" method="post"
action="' . $this->config->item('base_url')
. 'appointments/cancel/' . $appointment_data['hash'] . '">
<textarea name="cancel_reason" style="display:none;"></textarea>
<textarea name="cancel_reason" style="display:none"></textarea>
<button id="cancel-appointment" class="btn btn-inverse">
Cancel</button>
</form>

View file

@ -48,7 +48,7 @@
<div id="success-frame" class="frame-container">
<img id="success-icon" src="<?php echo $this->config->base_url(); ?>assets/images/success.png" />
<h3>Your appointment has been successfully canceled!</h3>
<h3>Your appointment has been successfully cancelled!</h3>
<?php
// Display exceptions (if any).

View file

@ -43,7 +43,7 @@
<div class="btn-group">
<?php //if ($privileges[PRIV_USERS]['edit'] == TRUE) { ?>
<?php if (($role_slug == DB_SLUG_ADMIN || $role_slug == DB_SLUG_PROVIDER)
&& $this->config->item('ea_google_sync') == TRUE) { ?>
&& $this->config->item('ea_google_sync_feature') == TRUE) { ?>
<button id="google-sync" class="btn btn-primary"
title="Trigger the Google Calendar synchronization process.">
<i class="icon-refresh icon-white"></i>

View file

@ -65,7 +65,7 @@
<div id="save-cancel-group" class="btn-group" style="display:none;">
<button id="save-customer" class="btn btn-primary">
<i class="icon-ok"></i>
<i class="icon-ok icon-white"></i>
Save</button>
<button id="cancel-customer" class="btn">
<i class="icon-ban-circle"></i>

View file

@ -67,7 +67,7 @@
<div class="save-cancel-group btn-group" style="display:none;">
<button id="save-service" class="btn btn-primary">
<i class="icon-ok"></i>
<i class="icon-ok icon-white"></i>
Save</button>
<button id="cancel-service" class="btn">
<i class="icon-ban-circle"></i>
@ -144,7 +144,7 @@
<div class="save-cancel-group btn-group" style="display:none;">
<button id="save-category" class="btn btn-primary">
<i class="icon-ok"></i>
<i class="icon-ok icon-white"></i>
Save</button>
<button id="cancel-category" class="btn">
<i class="icon-ban-circle"></i>

View file

@ -93,7 +93,7 @@
<div class="save-cancel-group btn-group" style="display:none;">
<button id="save-admin" class="btn btn-primary">
<i class="icon-ok"></i>
<i class="icon-ok icon-white"></i>
Save</button>
<button id="cancel-admin" class="btn">
<i class="icon-ban-circle"></i>
@ -201,7 +201,7 @@
<div class="save-cancel-group btn-group" style="display:none;">
<button id="save-provider" class="btn btn-primary">
<i class="icon-ok"></i>
<i class="icon-ok icon-white"></i>
Save</button>
<button id="cancel-provider" class="btn">
<i class="icon-ban-circle"></i>
@ -409,7 +409,7 @@
<div class="save-cancel-group btn-group" style="display:none;">
<button id="save-secretary" class="btn btn-primary">
<i class="icon-ok"></i>
<i class="icon-ok icon-white"></i>
Save</button>
<button id="cancel-secretary" class="btn">
<i class="icon-ban-circle"></i>

View file

@ -195,7 +195,7 @@
}
#loading {
position: absolute;
position: fixed;
top: 0px;
left: 0px;
width: 100%;

View file

@ -11,7 +11,7 @@ root {
#header {
height: 70px;
background-color: #35B66F;
border-bottom: 6px solid #247A4B;
border-bottom: 4px solid #247A4B;
}
#header #header-logo {
@ -96,7 +96,7 @@ root {
}
#loading {
position: absolute;
position: fixed;
top: 0px;
left: 0px;
width: 100%;
@ -178,14 +178,19 @@ body .jspTrack {
#calendar-page #calendar .fc-unavailable {
background-image: url('../images/unavailable.jpg');
font-size: 24px;
font-size: 17px;
border-radius: 0;
font-weight: bold;
color: #333;
text-shadow: 0px 1px 0px #FFF;
text-shadow: 0px 1px 0px #E6E6E6;
opacity: 0.7;
}
#calendar-page #calendar .fc-event-title small {
font-weight: normal;
font-size: 12px;
}
#calendar-page #calendar .fc-break {
background-image: url('../images/break.jpg');
}

View file

@ -52,7 +52,10 @@ var BackendCalendar = {
'dayClick': BackendCalendar.calendarDayClick,
'eventClick': BackendCalendar.calendarEventClick,
'eventResize': BackendCalendar.calendarEventResize,
'eventDrop': BackendCalendar.calendarEventDrop
'eventDrop': BackendCalendar.calendarEventDrop,
'eventAfterAllRender': function(view) {
BackendCalendar.convertTitlesToHtml();
}
});
// Temporary fix: make the first letter capital in all the lowercase strings
@ -264,7 +267,7 @@ var BackendCalendar = {
if (response.warnings) {
response.warnings = GeneralFunctions.parseExceptions(response.warnings);
GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE);
GeneralFunctions.displayMessageBox(GeneralFunctions.WARNINGS_TITLE, GeneralFunctions.WARNINGS_MESSAGE);
$('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
}
@ -396,7 +399,7 @@ var BackendCalendar = {
if (response.warnings) {
response.warnings = GeneralFunctions.parseExceptions(response.warnings);
GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE);
GeneralFunctions.displayMessageBox(GeneralFunctions.WARNINGS_TITLE, GeneralFunctions.WARNINGS_MESSAGE);
$('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
}
@ -437,7 +440,7 @@ var BackendCalendar = {
if (response.warnings) {
response.warnings = GeneralFunctions.parseExceptions(response.warnings);
GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE);
GeneralFunctions.displayMessageBox(GeneralFunctions.WARNINGS_TITLE, GeneralFunctions.WARNINGS_MESSAGE);
$('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
}
@ -510,7 +513,7 @@ var BackendCalendar = {
}
// :: DEFINE SUCCESS EVENT CALLBACK
var successCallback = function(response) {
var successCallback = function(response) {
if (response.exceptions) {
response.exceptions = GeneralFunctions.parseExceptions(response.exceptions);
GeneralFunctions.displayMessageBox(GeneralFunctions.EXCEPTIONS_TITLE, GeneralFunctions.EXCEPTIONS_MESSAGE);
@ -527,6 +530,7 @@ var BackendCalendar = {
$dialog.find('.modal-message').text('Appointment saved successfully!');
$dialog.find('.modal-message').addClass('alert-success').removeClass('alert-error');
$dialog.find('.modal-message').fadeIn();
$dialog.find('.modal-body').scrollTop(0);
// Close the modal dialog and refresh the calendar appointments
// after one second.
@ -542,6 +546,7 @@ var BackendCalendar = {
$dialog.find('.modal-message').text('A server communication error occured, please try again.');
$dialog.find('.modal-message').addClass('alert-error');
$dialog.find('.modal-message').fadeIn();
$dialog.find('.modal-body').scrollTop(0);
};
// :: CALL THE UPDATE APPOINTMENT METHOD
@ -600,7 +605,7 @@ var BackendCalendar = {
if (response.warnings) {
response.warnings = GeneralFunctions.parseExceptions(response.warnings);
GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE);
GeneralFunctions.displayMessageBox(GeneralFunctions.WARNINGS_TITLE, GeneralFunctions.WARNINGS_MESSAGE);
$('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
}
@ -913,7 +918,7 @@ var BackendCalendar = {
if (calendarDateStart < workDateStart) {
unavailablePeriod = {
'title': 'Unavailable',
'title': 'Not Working',
'start': calendarDateStart,
'end': workDateStart,
'allDay': false,
@ -932,7 +937,7 @@ var BackendCalendar = {
'dd/MM/yyyy HH:mm'); // Use calendarDateStart ***
if (calendarDateEnd > workDateEnd) {
var unavailablePeriod = {
'title': 'Unavailable',
'title': 'Not Working',
'start': workDateEnd,
'end': calendarDateEnd,
'allDay': false,
@ -965,7 +970,9 @@ var BackendCalendar = {
// Add custom unavailable periods.
$.each(response.unavailables, function(index, unavailable) {
var unavailablePeriod = {
'title': 'Unavailable',
'title': 'Unavailable <br><small>' + ((unavailable.notes.length > 30)
? unavailable.notes.substring(0, 30) + '...'
: unavailable.notes) + '</small>',
'start': Date.parse(unavailable.start_datetime),
'end': Date.parse(unavailable.end_datetime),
'allDay': false,
@ -991,7 +998,9 @@ var BackendCalendar = {
if (currDateStart.toString('dd/MM/yyyy')
=== Date.parse(unavailable.start_datetime).toString('dd/MM/yyyy')) {
var unavailablePeriod = {
'title': 'Unavailable',
'title': 'Unavailable <br><small>' + ((unavailable.notes.length > 30)
? unavailable.notes.substring(0, 30) + '...'
: unavailable.notes) + '</small>',
'start': Date.parse(unavailable.start_datetime),
'end': Date.parse(unavailable.end_datetime),
'allDay': false,
@ -1004,7 +1013,6 @@ var BackendCalendar = {
}
});
if (workingDay == null) {
// Add a full day unavailable event.
unavailablePeriod = {
@ -1029,7 +1037,7 @@ var BackendCalendar = {
+ ' ' + workingDay.start, 'dd/MM/yyyy HH:mm');
if (currDateStart < start) {
unavailablePeriod = {
'title': 'Unavailable',
'title': 'Not Working',
'start': GeneralFunctions.clone(currDateStart),
'end': GeneralFunctions.clone(start),
'allDay': false,
@ -1045,7 +1053,7 @@ var BackendCalendar = {
+ ' ' + workingDay.end, 'dd/MM/yyyy HH:mm');
if (currDateEnd > end) {
unavailablePeriod = {
'title': 'Unavailable',
'title': 'Not Working',
'start': GeneralFunctions.clone(end),
'end': GeneralFunctions.clone(currDateEnd),
'allDay': false,
@ -1082,6 +1090,8 @@ var BackendCalendar = {
}
}
});
// Convert the titles to html code.
//BackendCalendar.convertTitlesToHtml();
}
}, 'json');
},
@ -1209,7 +1219,7 @@ var BackendCalendar = {
if (response.warnings) {
// Display warning information to the user.
response.warnings = GeneralFunctions.parseExceptions(response.warnings);
GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE);
GeneralFunctions.displayMessageBox(GeneralFunctions.WARNINGS_TITLE, GeneralFunctions.WARNINGS_MESSAGE);
$('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
}
@ -1262,7 +1272,7 @@ var BackendCalendar = {
if (response.warnings) {
// Display warning information to the user.
response.warnings = GeneralFunctions.parseExceptions(response.warnings);
GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE);
GeneralFunctions.displayMessageBox(GeneralFunctions.WARNINGS_TITLE, GeneralFunctions.WARNINGS_MESSAGE);
$('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
}
@ -1307,6 +1317,7 @@ var BackendCalendar = {
calendarWindowResize: function(view) {
$('#calendar').fullCalendar('option', 'height',
BackendCalendar.getCalendarHeight());
//BackendCalendar.convertTitlesToHtml();
},
/**
@ -1476,7 +1487,7 @@ var BackendCalendar = {
if (response.warnings) {
// Display warning information to the user.
response.warnings = GeneralFunctions.parseExceptions(response.warnings);
GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE);
GeneralFunctions.displayMessageBox(GeneralFunctions.WARNINGS_TITLE, GeneralFunctions.WARNINGS_MESSAGE);
$('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
}
@ -1539,7 +1550,7 @@ var BackendCalendar = {
if (response.warnings) {
reponse.warnings = GeneralFunctions.parseExceptions(response.warnings);
GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE);
GeneralFunctions.displayMessageBox(GeneralFunctions.WARNINGS_TITLE, GeneralFunctions.WARNINGS_MESSAGE);
$('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
}
@ -1598,8 +1609,17 @@ var BackendCalendar = {
$('#select-filter-item option:selected').attr('type'),
$('#calendar').fullCalendar('getView').visStart,
$('#calendar').fullCalendar('getView').visEnd);
$(window).trigger('resize');
$(window).trigger('resize'); // Places the footer on the bottom.
// Change string from "all-day" to "All Day".
$('#calendar .fc-agenda-allday .fc-agenda-axis').text('All Day');
// Remove all open popovers.
$('.close-popover').each(function() {
$(this).parents().eq(2).remove();
});
// Add new pop overs.
$('.fv-events').each(function(index, eventHandle) {
$(eventHandle).popover();
});
@ -1759,5 +1779,20 @@ var BackendCalendar = {
// Clear the unavailable notes field.
$dialog.find('#unavailable-notes').val('');
},
/**
* On some calendar events the titles contain html markup that is not
* displayed properly due to the fullcalendar plugin. This plugin sets
* the .fc-event-title value by using the $.text() method and not the
* $.html() method. So in order for the title to display the html properly
* we convert all the .fc-event-titles where needed into html.
*/
convertTitlesToHtml: function() {
// Convert the titles to html code.
$('.fc-custom').each(function() {
var title = $(this).find('.fc-event-title').text();
$(this).find('.fc-event-title').html(title);
});
}
};

View file

@ -397,13 +397,17 @@ CustomersHelper.prototype.filter = function(key, selectId, display) {
* @return {string} Returns the record html code.
*/
CustomersHelper.prototype.getFilterHtml = function(customer) {
var name = customer.first_name + ' ' + customer.last_name;
var info = customer.email;
info = (customer.phone_number != '' && customer.phone_number != null)
? info + ', ' + customer.phone_number : info;
var html =
'<div class="customer-row" data-id="' + customer.id + '">' +
'<strong>' +
customer.first_name + ' ' + customer.last_name +
name +
'</strong><br>' +
'<span>' + customer.email + '</span> | ' +
'<span>' + customer.phone_number + '</span>' +
info +
'</div><hr>';
return html;

View file

@ -214,8 +214,8 @@ var FrontendBook = {
+ 'minute to write the reason you are cancelling the appointment:',
dialogButtons);
$('#message_box').append('<textarea id="cancel-reason"></textarea>');
$('#cancel-reason').css('width', '300px');
$('#message_box').append('<textarea id="cancel-reason" rows="3"></textarea>');
$('#cancel-reason').css('width', '353px');
});
}
@ -553,11 +553,15 @@ var FrontendBook = {
html = '<strong>' + service.name + '</strong>';
if (service.description != '' && service.description != null) {
html += '<br>' + service.description;
html += '<br>' + service.description + '<br>';
}
if (service.duration != '' && service.duration != null) {
html += '[Duration ' + service.duration + ' Minutes] ';
}
if (service.price != '' && service.price != null) {
html += '<br> [Price ' + service.price + ' ' + service.currency + ']';
html += '[Price ' + service.price + ' ' + service.currency + ']';
}
html += '<br>';

View file

@ -10,7 +10,7 @@ class SystemConfiguration {
public static $db_password = '';
// Google Calendar API Settings
public static $google_sync_feature = FALSE; // Enter TRUE or FALSE;
public static $google_sync_feature = TRUE; // Enter TRUE or FALSE;
public static $google_product_name = 'Easy!Appointments';
public static $google_client_id = '396094740598-l9ohhdgs0hr6qi89628p3chf9lm59mkc.apps.googleusercontent.com';
public static $google_client_secret = '3kKEgx3mgxfFInrWf3jTUn4D';