__construct(); } function __construct() { global $wpdb, $appointments; $this->local_time = current_time('timestamp'); $this->options = get_option( 'appointments_options' ); // DB version $this->db_version = get_option( 'app_db_version' ); $this->app_table = $wpdb->prefix . "app_appointments"; $this->plugin_dir = $appointments->plugin_dir; $this->plugin_url = $appointments->plugin_url; require_once $this->plugin_dir . '/includes/gcal/Google_Client.php'; require_once $this->plugin_dir . '/includes/gcal/contrib/Google_CalendarService.php'; // Try to start a session. If cannot, log it. if ( !session_id() && !@session_start() ) { $appointments->log( __('Session could not be started. This may indicate a theme issue.', 'appointments' ) ); } // Let A+ main settings saved first add_action( 'admin_init', array( &$this, 'save_settings' ), 12 ); add_action( 'init', array( &$this, 'init' ), 12 ); // Add a custom column on users page add_filter( 'manage_users_custom_column', array( &$this, 'users_custom_column' ), 10, 3 ); add_filter( 'manage_users_columns', array( &$this, 'users_columns' ) ); // Prevent exceptions to kill the page if ( ( isset( $_GET['gcal_api_test'] ) && 1 == $_GET['gcal_api_test'] ) || ( isset( $_GET['gcal_import_now'] ) && $_GET['gcal_import_now'] ) ) set_exception_handler( array( &$this, 'exception_error_handler' ) ); // Set log file location $uploads = wp_upload_dir(); if ( isset( $uploads["basedir"] ) ) $this->uploads_dir = $uploads["basedir"] . "/"; else $this->uploads_dir = WP_CONTENT_DIR . "/uploads/"; } /** * Add a custom column for GCal mode * @since V1.2.7.1 */ function users_columns( $columns ) { // Nothing to do if providers are not allowed to set GCal API if ( 'yes' != @$this->options['gcal_api_allow_worker'] ) return $columns; $columns['gcal_mode'] = __( 'GCal Mode','appointments' ); return $columns; } /** * Add text inside a custom user column * @since V1.2.7.1 */ function users_custom_column( $text, $column_name, $user_id ) { // Nothing to do if providers are not allowed to set GCal API if ( 'yes' != @$this->options['gcal_api_allow_worker'] || 'gcal_mode' != $column_name ) return $text; global $appointments; if ( !$appointments->is_worker( $user_id ) ) return ' - '; $mode = $this->get_api_mode( $user_id ); switch ( $mode ) { case 'none': return __( 'None', 'appointments'); break; case 'gcal2app': return __( 'A+<-GCal', 'appointments'); break; case 'app2gcal': return __( 'A+->GCal', 'appointments'); break; case 'sync': return __( 'A+<->GCal', 'appointments'); break; default: return ' - '; break; } } /** * Refresh the page with the exception as GET parameter, so that page is not killed */ function exception_error_handler( $exception ) { // If we don't remove these GETs there will be an infinite loop if ( !headers_sent() ) wp_redirect( add_query_arg( array( 'gcal_api_test_result' => urlencode($exception), 'gcal_import_now' => false, 'gcal_api_test' => false, 'gcal_api_pre_test' => false ) ) ); else { // We cannot display it, so we save it global $appointments; $appointments->log( $exception ); } } /** * Outputs Google Calendar tab on admin settings page */ function render_tab( $worker_id=0 ) { // Set correct worker_id for test connection and import&update now if ( !$worker_id && isset( $_GET['gcal_api_worker_id'] ) ) $worker_id = $_GET['gcal_api_worker_id']; $this->display_nag( $worker_id ); ?>
is_not_suitable( $worker_id ) ) { $message .= $result; if ( __('Your server installation meets requirements.','appointments') != $result ) $error = true; } else { // Insert a test event $result = $this->insert_event( 0, true, $worker_id ); if ( $result ) $message .= __('Test is successful. Please REFRESH your Google Calendar and check that test appointment has been saved.','appointments'); else { $message .= Google_ServiceException::getErrors(); $error = true; } } } if ( isset( $_GET['gcal_import_now'] ) && 1 == $_GET['gcal_import_now'] ) { $this->updated = $this->inserted = $this->deleted = 0; $result = $this->import_and_update_events( $worker_id ); if ( $result ) $message .= $result; else $message .= __('No future new events are found in your Google calendar. Thus no events are imported and no updates and deletions made.','appointments'); } if ( $message ) $this->show_message( $message, $error ); if ( isset( $_GET['gcal_api_test_result'] ) && '' != $_GET['gcal_api_test_result'] ) { $m = stripslashes(urldecode($_GET['gcal_api_test_result'] )); // Get rid of unnecessary information if ( strpos( $m, 'Stack trace' ) !== false ) { $temp = explode( 'Stack trace', $m ); $m = $temp[0]; } if ( strpos( $this->get_selected_calendar( $worker_id ), 'group.calendar.google.com' ) === false ) $add = '
' . sprintf( __('[Appointments] %s', 'appointments'), $message ) . '
'; } /** * Save admin settings */ function save_settings() { global $appointments; if ( isset($_POST["action_api"]) && !wp_verify_nonce($_POST['api_nonce'],'update_api_settings') ) { add_action( 'admin_notices', array( $appointments, 'warning' ) ); return; } if ( !isset($_POST["action_api"]) || 'save_general' != $_POST["action_api"] ) { return; } if (!current_user_can('manage_options')) { wp_die( __('You do not have sufficient permissions to access this page.','appointments') ); } $kff = $this->key_file_folder( ); $kfn = $this->get_key_file(). '.p12'; // Copy key file to uploads folder if ( is_dir( $kff ) && !file_exists( $kff . $kfn ) && file_exists( $this->plugin_dir . '/includes/gcal/key/' . $kfn ) ) copy( $this->plugin_dir . '/includes/gcal/key/' . $kfn, $kff . $kfn ); $options["gcal"] = $_POST["gcal"]; $options["gcal_same_window"] = isset( $_POST["gcal_same_window"] ); $options["gcal_location"] = stripslashes( @$_POST["gcal_location"] ); $options['gcal_api_allow_worker'] = $_POST['gcal_api_allow_worker']; $options['gcal_api_scope'] = $_POST['gcal_api_scope']; $options['gcal_api_mode'] = $_POST['gcal_api_mode']; $options['gcal_service_account'] = trim( $_POST['gcal_service_account'] ); $options['gcal_key_file'] = trim( str_replace( '.p12', '', $_POST['gcal_key_file'] ) ); $options['gcal_selected_calendar'] = trim( $_POST['gcal_selected_calendar'] ); $options['gcal_summary'] = stripslashes( $_POST['gcal_summary'] ); $options['gcal_description'] = stripslashes( $_POST['gcal_description'] ); $this->options = array_merge( $this->options, $options ); if ( update_option('appointments_options', $this->options ) ) { add_action( 'admin_notices', array( $appointments, 'saved' ) ); $appointments->options = $this->options; } } /** * Set some default settings related to GCal * @since 1.2.1 */ function init( ) { if ( 'none' != $this->get_api_mode() ) { // Try to create key file folder if it doesn't exist $this->create_key_file_folder( ); $kff = $this->key_file_folder( ); // Copy index.php to this folder and to uploads folder if ( is_dir( $kff ) && !file_exists( $kff . 'index.php') ) @copy( $this->plugin_dir . '/includes/gcal/key/index.php', $kff . 'index.php' ); if ( is_dir( $this->uploads_dir ) && !file_exists( $this->uploads_dir . 'index.php') ) @copy( $this->plugin_dir . '/includes/gcal/key/index.php', $this->uploads_dir . 'index.php' ); // Copy key file to uploads folder $kfn = $this->get_key_file(). '.p12'; if ( $kfn && is_dir( $kff ) && !file_exists( $kff . $kfn ) && file_exists( $this->plugin_dir . '/includes/gcal/key/' . $kfn ) ) @copy( $this->plugin_dir . '/includes/gcal/key/' . $kfn, $kff . $kfn ); } $gcal_description = __("Client Name: CLIENT\nService Name: SERVICE\nService Provider Name: SERVICE_PROVIDER\n", "appointments"); $this->options = get_option( 'appointments_options' ); $changed = false; if ( !isset( $this->options['gcal_description'] ) ) { $this->options['gcal_description'] = $gcal_description; $changed = true; } if ( !isset( $this->options['gcal_summary'] ) ) { $this->options['gcal_summary'] = __('SERVICE Appointment','appointments'); $changed = true; } if ( $changed ) update_option( 'appointments_options', $this->options ); } /** * Return GCal API mode (none, app2gcal or sync ) * @param worker_id: Optional worker ID whose data will be restored * @return string */ function get_api_mode( $worker_id=0 ) { if ( !$worker_id ) { if ( isset( $this->options['gcal_api_mode'] ) ) return $this->options['gcal_api_mode']; else return 'none'; } else { $meta = get_user_meta( $worker_id, 'app_api_mode', true ); if ( $meta ) return $meta; else return 'none'; } } /** * Return GCal service account * @param worker_id: Optional worker ID whose data will be restored * @return string */ function get_service_account( $worker_id=0 ) { if ( !$worker_id ) { if ( isset( $this->options['gcal_service_account'] ) ) return $this->options['gcal_service_account']; else return ''; } else return get_user_meta( $worker_id, 'app_service_account', true ); } /** * Return GCal key file name without the extension * @param worker_id: Optional worker ID whose data will be restored * @return string */ function get_key_file( $worker_id=0 ) { if ( !$worker_id ) { if ( isset( $this->options['gcal_key_file'] ) ) return $this->options['gcal_key_file']; else return ''; } else return get_user_meta( $worker_id, 'app_key_file', true ); } /** * Return GCal selected calendar ID * @param worker_id: Optional worker ID whose data will be restored * @return string */ function get_selected_calendar( $worker_id=0 ) { if ( !$worker_id ) { if ( isset( $this->options['gcal_selected_calendar'] ) ) return $this->options['gcal_selected_calendar']; else return ''; } else return get_user_meta( $worker_id, 'app_selected_calendar', true ); } /** * Return GCal Summary (name of Event) * @param worker_id: Optional worker ID whose data will be restored * @since 1.2.1 * @return string */ function get_summary( $worker_id=0 ) { $text = ''; if ($worker_id) { $text = get_user_meta($worker_id, 'app_gcal_summary', true); } if (empty($text)) $text = !empty($this->options['gcal_summary']) ? $this->options['gcal_summary'] : '' ; return $text; /* if ( !$worker_id ) { if ( isset( $this->options['gcal_summary'] ) ) return $this->options['gcal_summary']; else return ''; } else return get_user_meta( $worker_id, 'app_gcal_summary', true ); */ } /** * Return GCal description * @param worker_id: Optional worker ID whose data will be restored * @since 1.2.1 * @return string */ function get_description( $worker_id=0 ) { $text = ''; if ($worker_id && !empty($this->options['gcal_api_allow_worker']) && 'yes' == $this->options['gcal_api_allow_worker']) { $text = get_user_meta($worker_id, 'app_gcal_description', true); } if (empty($text)) $text = !empty($this->options['gcal_description']) ? $this->options['gcal_description'] : '' ; return $text; /* if ( !$worker_id ) { if ( isset( $this->options['gcal_description'] ) ) return $this->options['gcal_description']; else return ''; } else return get_user_meta( $worker_id, 'app_gcal_description', true ); */ } /** * Checks if php version and extentions are correct * @param worker_id: Optional worker ID whose account is to be checked * @return string (Empty string means suitable) */ function is_not_suitable( $worker_id=0 ) { if ( version_compare( $this->db_version, '1.2.0', '<' ) ) return __('You have to update the Appointments tables. To do so, deactivate and reactivate the plugin.','appointments'); if ( version_compare(PHP_VERSION, '5.3.0', '<') ) return __('Google PHP API Client requires at least PHP 5.3','appointments'); // Disabled for now if ( false && memory_get_usage() < 31000000 ) return sprintf( __('Google PHP API Client requires at least 32 MByte Server RAM. Please check this link how to increase it: %s','appointments'), ''.__('Increasing_memory_allocated_to_PHP','appointments').''); if (!function_exists('curl_init')) return __('Google PHP API Client requires the CURL PHP extension','appointments' ); if (!function_exists('json_decode')) return __('Google PHP API Client requires the JSON PHP extension','appointments'); if ( !function_exists('http_build_query')) return __('Google PHP API Client requires http_build_query()','appointments'); // Dont continue further if this is pre check if ( isset( $_GET['gcal_api_pre_test'] ) && 1== $_GET['gcal_api_pre_test'] ) return __('Your server installation meets requirements.','appointments'); if ( !$this->_file_exists( $worker_id ) ) return __('Key file does not exist','appointments'); return ''; } /** * Checks if key file exists * @param worker_id: Optional worker ID whose account is to be checked * @return bool * @since 1.2.2 */ function _file_exists( $worker_id=0 ) { if ( file_exists( $this->key_file_folder( ). $this->get_key_file( $worker_id ) . '.p12' ) ) return true; else if ( file_exists( $this->plugin_dir . '/includes/gcal/key/'. $this->get_key_file( $worker_id ) . '.p12' ) ) return true; return false; } /** * Get contents of the key file * @param worker_id: Optional worker ID whose account is to be checked * @return string * @since 1.2.2 */ function _file_get_contents( $worker_id=0 ) { if ( file_exists( $this->key_file_folder( ). $this->get_key_file( $worker_id ) . '.p12' ) ) return @file_get_contents( $this->key_file_folder( ). $this->get_key_file( $worker_id ) . '.p12' ); else if ( file_exists( $this->plugin_dir . '/includes/gcal/key/'. $this->get_key_file( $worker_id ) . '.p12' ) ) return @file_get_contents( $this->plugin_dir . '/includes/gcal/key/'. $this->get_key_file( $worker_id ) . '.p12' ); return ''; } /** * Try to create an encrypted key file folder * @return string * @since 1.2.2 */ function create_key_file_folder( ) { if ( defined( 'AUTH_KEY' ) ) { $kff = $this->uploads_dir . md5( 'AUTH_KEY' ) . '/' ; if ( is_dir( $kff ) ) return; else @mkdir( $kff ); if ( is_dir( $kff ) ) return; } if ( !is_dir( $this->uploads_dir . '__app/' ) ) @mkdir( $this->uploads_dir . '__app/' ); } /** * Return key file folder name * @return string * @since 1.2.2 */ function key_file_folder( ) { if ( defined( 'AUTH_KEY' ) ) { $kff = $this->uploads_dir . md5( 'AUTH_KEY' ) . '/' ; if ( is_dir( $kff ) ) return $kff; } return $this->uploads_dir . '__app/'; } /** * Checks for settings and prerequisites * @param worker_id: Optional worker ID whose account is to be checked * @return bool */ function is_active( $worker_id=0 ) { // If integration is disabled, nothing to do if ( 'none' == $this->get_api_mode( $worker_id ) || !$this->get_api_mode( $worker_id ) ) return false; if ( $this->is_not_suitable( $worker_id ) ) return false; if ( $this->get_key_file( $worker_id ) && $this->get_service_account( $worker_id ) && $this->get_selected_calendar( $worker_id ) ) return true; // None of the other cases are allowed return false; } /** * Connects to GCal API */ function connect( $worker_id=0 ) { // Disallow faulty plugins to ruin what we are trying to do here @ob_start(); if ( !$this->is_active( $worker_id ) ) return false; // Just in case require_once $this->plugin_dir . '/includes/gcal/Google_Client.php'; $extra_config = apply_filters('app-gcal-client_parameters', array()); $this->client = new Google_Client($extra_config); $this->client->setApplicationName("Appointments"); $this->client->setUseObjects(true); $key = $this->_file_get_contents( $worker_id ); $this->client->setAssertionCredentials(new Google_AssertionCredentials( $this->get_service_account( $worker_id), array('https://www.googleapis.com/auth/calendar'), $key) ); $this->service = new Google_CalendarService($this->client); return true; } /** * Creates a Google Event object and set its parameters * @param app: Appointment object to be set as event */ function set_event_parameters( $app, $worker_id=0 ) { global $appointments; $a = $appointments; $summary = sprintf( __('%s Appointment', 'appointments'), $a->get_service_name( $app->service ) ); if ( isset( $this->options["gcal_location"] ) && '' != trim( $this->options["gcal_location"] ) ) $location = str_replace( array('ADDRESS', 'CITY'), array($app->address, $app->city), $this->options["gcal_location"] ); else $location = get_bloginfo( 'description' ); // Find time difference from Greenwich as GCal asks UTC if ( !current_time('timestamp') ) $tdif = 0; else $tdif = current_time('timestamp') - time(); $start = new Google_EventDateTime(); $start->setDateTime( date( "Y-m-d\TH:i:s\Z", strtotime($app->start) - $tdif ) ); $end = new Google_EventDateTime(); $end->setDateTime( date( "Y-m-d\TH:i:s\Z", strtotime($app->end) - $tdif ) ); // An email is always required if ( !$app->email ) $email = $a->get_worker_email( $app->worker ); else $email = $app->email; if ( !$email ) $email = $a->get_admin_email( ); $attendee1 = new Google_EventAttendee(); $attendee1->setEmail( $email ); $attendees = array($attendee1); $this->event = new Google_Event( ); $this->event->setSummary( $summary ); $this->event->setLocation( $location ); $this->event->setStart( $start ); $this->event->setEnd( $end ); $this->event->setSummary(apply_filters( 'app-gcal-set_summary', $a->_replace( $this->get_summary( $worker_id ), $app->name, $a->get_service_name($app->service), $a->get_worker_name($app->worker), $app->start, $app->price, $a->get_deposit($app->price), $app->phone, $app->note, $app->address, $app->email, $app->city ), $app )); $this->event->setDescription(apply_filters( 'app-gcal-set_description', $a->_replace( $this->get_description( $worker_id ), $app->name, $a->get_service_name($app->service), $a->get_worker_name($app->worker), $app->start, $app->price, $a->get_deposit($app->price), $app->phone, $app->note, $app->address, $app->email, $app->city ), $app )); $this->event->attendees = $attendees; // Alright, now deal with event sequencing if (!empty($app->gcal_ID)) { $tmp = $this->service->events->get($this->get_selected_calendar( $worker_id ), $app->gcal_ID); $sequence = is_object($tmp) && !empty($tmp->sequence) ? $tmp->sequence : (is_array($tmp) && !empty($tmp['sequence']) ? $tmp['sequence'] : false) ; if (!empty($sequence)) $this->event->sequence = $sequence; // Add sequence if we have it } } private function _is_writable_mode ($worker_id=false) { $mode = $this->get_api_mode($worker_id); if ($worker_id && empty($mode)) $mode = $this->get_api_mode(); // Fallback to site default if no option selected. $mode = empty($mode) ? 'none' : $mode ; // If we still don't have a mode, then we don't want to sync. return !in_array($mode, array( // Non-writable modes 'gcal2app', 'none' )); } /** * Handle insertion of events depending on settings * @param app_id: Appointment ID to be inserted */ function insert( $app_id ) { global $appointments; $app = $appointments->get_app( $app_id ); $worker_id = $app->worker; // No preference case if ( !$worker_id ) { /* if ( 'none' != $this->get_api_mode( ) ) $this->insert_event( $app_id ); */ if ($this->_is_writable_mode()) $this->insert_event($app_id); } else { //if ( isset( $this->options['gcal_api_allow_worker'] ) && 'yes' == $this->options['gcal_api_allow_worker'] && 'none' != $this->get_api_mode( $worker_id ) ) if (isset($this->options['gcal_api_allow_worker']) && 'yes' == $this->options['gcal_api_allow_worker'] && $this->_is_writable_mode($worker_id)) $this->insert_event($app_id, false, $worker_id); // Add this to general calendar if selected so //if ( isset( $this->options['gcal_api_scope'] ) && 'all' == $this->options['gcal_api_scope'] && 'none' != $this->get_api_mode() ) if (isset($this->options['gcal_api_scope']) && 'all' == $this->options['gcal_api_scope'] && $this->_is_writable_mode()) $this->insert_event($app_id); } } /** * Inserts an appointment to the selected calendar as event * @param app_id: Appointment ID to be inserted * @test: Insert a test appointment */ function insert_event( $app_id, $test=false, $worker_id=0 ) { if ( !$this->connect( $worker_id ) ) return false; global $appointments, $wpdb; if ( $test ) { $app = new stdClass(); $app->name = __('Test client name', 'appointments'); $app->phone = __('Test phone', 'appointments'); $app->address = __('Test address', 'appointments'); $app->city = __('Test city', 'appointments'); $app->worker = 0; $app->price = 123; $app->start = date( 'Y-m-d H:i:s', $this->local_time + 600 ); $app->end = date( 'Y-m-d H:i:s', $this->local_time + 2400 ); $app->service = $appointments->get_first_service_id(); $app->email = $appointments->get_admin_email( ); $app->note = __('This is a test appointment inserted by Appointments','appointments'); } else $app = $appointments->get_app( $app_id ); // Create Event object and set parameters $this->set_event_parameters( $app, $app->worker ); // Insert event try { $createdEvent = $this->service->events->insert( $this->get_selected_calendar( $worker_id ), $this->event ); if ($createdEvent && !is_object($createdEvent) && class_exists('Google_CalendarListEntry')) $createdEvent = new Google_CalendarListEntry($createdEvent); // Write Event ID to database $gcal_ID = $createdEvent->getId(); if ( $gcal_ID && !$test ) $wpdb->update( $this->app_table, array( 'gcal_ID' => $gcal_ID, 'gcal_updated' => date ("Y-m-d H:i:s", $this->local_time ) ), array( 'ID'=>$app_id ) ); // Test result successful if ( $gcal_ID ) return true; } catch (Exception $e) { $appointments->log("Insert went wrong: " . $e->getMessage()); return false; } } /** * Handle update of events, if not exists insert * @param app_id: Appointment ID to be updated */ function update( $app_id ) { global $appointments; $app = $appointments->get_app( $app_id ); $worker_id = $app->worker; if ( $app->gcal_ID ) { // Update this event from general calendar if ($this->_is_writable_mode()) $this->update_event( $app_id ); // Also update service provider event if we have a provider if ($worker_id && $this->_is_writable_mode($worker_id)) $this->update_event( $app_id, $worker_id ); } else if ($this->_is_writable_mode($worker_id)) $this->insert_event( $app_id, false, $worker_id ); } /** * Update event as appointment is modified * @param app_id: Appointment ID to be updated */ function update_event( $app_id, $worker_id=0 ) { if ( !$this->connect( $worker_id ) ) return false; global $appointments, $wpdb; $app = $appointments->get_app( $app_id ); if ( $app->gcal_ID ) { $this->set_event_parameters( $app, $worker_id ); // Update event try { $updatedEvent = $this->service->events->update( $this->get_selected_calendar( $worker_id ), $app->gcal_ID, $this->event ); if ($updatedEvent && !is_object($updatedEvent) && class_exists('Google_CalendarListEntry')) $updatedEvent = new Google_CalendarListEntry($updatedEvent); // Update Time of database $gcal_ID = $updatedEvent->getId(); if ( $gcal_ID && $gcal_ID == $app->gcal_ID ) $wpdb->update( $this->app_table, array( 'gcal_updated' => date ("Y-m-d H:i:s", $this->local_time ) ), array( 'ID'=>$app_id ) ); } catch (Exception $e) { $appointments->log("Update went wrong: " . $e->getMessage()); } } } /** * Handle deletion of events * @param app_id: Appointment ID that has been deleted */ function delete( $app_id ) { global $appointments; $app = $appointments->get_app( $app_id ); //$appointments->log(sprintf("Attempting to delete the appointment %s from GCal", $app_id)); $worker_id = $app->worker; // In any case delete this event from general calendar if ($this->_is_writable_mode()) $this->delete_event( $app_id ); // Also delete service provider event if we have a provider if ($worker_id && $this->_is_writable_mode($worker_id)) $this->delete_event( $app_id, $worker_id ); } /** * Delete event as appointment is either removed, completed or set as pending * @param app_id: Appointment ID that has been deleted */ function delete_event( $app_id, $worker_id=0 ) { if ( !$this->connect( $worker_id ) ) return false; global $appointments; $app = $appointments->get_app( $app_id ); //$appointments->log(sprintf("Deleting the appointment %s from GCal (worker id: %s)", $app_id, $worker_id)); if ($app->gcal_ID) { try { $result = $this->service->events->delete( $this->get_selected_calendar( $worker_id ), $app->gcal_ID ); } catch (Exception $e) { $appointments->log("Deleting went wrong: " . $e->getMessage()); } //$appointments->log(sprintf("The appointment %s (worker id: %s, GCal id: %s) deleted.", $app_id, $worker_id, $app->gcal_ID)); } else { //$appointments->log(sprintf("Deleting the appointment %s (worker id: %s) FAILED, unknown GCal id.", $app_id, $worker_id)); } } /** * Handle import and update of all Google Calendars */ function import_and_update( ) { global $appointments, $wpdb; // Check for general $this->updated = $this->inserted = $this->deleted = 0; if ( 'sync' == $this->get_api_mode() ) $this->import_and_update_events(); // Check for providers if ( isset( $this->options['gcal_api_allow_worker'] ) && 'yes' == $this->options['gcal_api_allow_worker'] ) { $results = $wpdb->get_results( "SELECT user_id FROM " . $wpdb->usermeta . " WHERE meta_key='app_api_mode' AND meta_value='sync' " ); if ( $results ) { foreach( $results as $result ) { $this->import_and_update_events( $result->user_id ); } } } } /** * Import the list of events for the selected calendar for the worker and update them * @param worker_id: ID of the worker whose list will be gotten */ function import_and_update_events( $worker_id=0 ) { global $appointments, $wpdb; if ( !$this->connect( $worker_id ) ) return false; // Find time difference from Greenwich as GCal time will be converted to UTC, but we want local time if ( !current_time('timestamp') ) $tdif = 0; else $tdif = current_time('timestamp') - time(); // Get only future events and limit them with appointment limit setting and 500 events $events = $this->service->events->listEvents( $this->get_selected_calendar( $worker_id ), apply_filters( 'app_gcal_args', array( 'timeMin' => apply_filters( 'app_gcal_time_min', date( "c", $this->local_time ) ), 'timeMax' => apply_filters( 'app_gcal_time_max', date( "c", $this->local_time + 3600 * 24 * $appointments->get_app_limit() ) ), 'singleEvents' => apply_filters( 'app_gcal_single_events', true ), 'maxResults' => apply_filters( 'app_gcal_max_results', APP_GCAL_MAX_RESULTS_LIMIT ), 'orderBy' => apply_filters( 'app_gcal_orderby', 'startTime' ) ) ) ); if ($events && !$this->service->events->useObjects() && class_exists('Google_Events')) { $events = new Google_Events($events); } $message = $event_ids = ''; $values = array(); $present_events = $wpdb->get_col("SELECT DISTINCT gcal_ID FROM {$this->app_table} WHERE gcal_ID IS NOT NULL"); $to_update = array(); // Drop all previously reserved GCALs, because we'll reimport them now if (version_compare($this->db_version, '1.2.3', '<') && version_compare($this->db_version, '1.2.2', '=')) { $wpdb->query("DELETE FROM {$this->app_table} WHERE status='reserved'"); $this->db_version = '1.2.3'; update_option('app_db_version', $this->db_version); } if ( $events && is_array( $events->getItems()) ) { // Service ID is not important as we will use this record for blocking our time slots only $service_id = $appointments->get_first_service_id(); // Create a list of event_id's foreach ($events->getItems() as $event) { $event_id = $event->getID(); // Add the ID to the list $event_ids .= "'". $event_id . "',"; $event_start = $event->getStart(); $event_end = $event->getEnd(); $event_updated = $event->getUpdated(); $event_start_timestamp = strtotime($event_start->dateTime) + $tdif; $event_end_timestamp = strtotime($event_end->dateTime) + $tdif; $event_updated_timestamp = strtotime($event_updated) + $tdif; // This is not datetime object // Check start and end times as in case of all day events this field is empty if ( $event_id && $event_start_timestamp > $this->local_time && $event_end_timestamp > $this->local_time ) { if (!in_array($event_id, $present_events)) { $present_events[] = $event_id; $values[] = "('". date( "Y-m-d H:i:s", $this->local_time ) ."',". $service_id .",". $worker_id .",'reserved','". date( "Y-m-d H:i:s", strtotime($event_start->dateTime) + $tdif ) ."','". date( "Y-m-d H:i:s", $event_end_timestamp ) ."','". $wpdb->escape($event->getSummary()) ."','". $event_id ."','". date( "Y-m-d H:i:s", $event_updated_timestamp ) ."')"; } else { $to_update[] = "start='" . date("Y-m-d H:i:s", $event_start_timestamp) . "', end='" . date("Y-m-d H:i:s", $event_end_timestamp) . "', gcal_updated='" . date("Y-m-d H:i:s", $event_updated_timestamp) . "' WHERE gcal_ID='" . $event_id . "' LIMIT 1"; } } } if ( !empty( $values ) ) { // Try to adjust auto increment value $this->adjust_auto_increment(); // Insert and update all events with a single query $result = $wpdb->query( "INSERT INTO " . $this->app_table . " (created,service,worker,status,start,end,note,gcal_ID,gcal_updated) VALUES ". implode(',',$values). " ON DUPLICATE KEY UPDATE start=VALUES(start), end=VALUES(end), gcal_updated=VALUES(gcal_updated)" ); // Key is autoincrement, it'll never update! if ( $result ) { $message = sprintf( __('%s appointment record(s) affected.','appointments'), $result ). ' '; } } if (!empty($to_update)) { $result = (int)$result; foreach ($to_update as $upd) { $res2 = $wpdb->query("UPDATE {$this->app_table} SET {$upd}"); if ($res2) $result++; } $message = sprintf( __('%s appointment record(s) affected.','appointments'), $result ). ' '; } } if ( $event_ids ) { // Delete unlisted events for the selected worker $event_ids = "(". rtrim( $event_ids, ',' ) . ")"; $r3 = $wpdb->query( "DELETE FROM " . $this->app_table . " WHERE status='reserved' AND worker=".$worker_id." AND gcal_ID NOT IN " . $event_ids . " " ); if ( $r3 ) { $this->deleted++; $this->adjust_auto_increment(); } } if ( $this->deleted ) $message .= sprintf( __('%s appointment(s) deleted.','appointments'), $this->deleted ). ' '; return $message; } /** * Prevent auto increment go to too high values because of INSERT INTO DUPLICATE KEY clause or multiple deletes * @since 1.2.4 */ function adjust_auto_increment() { global $wpdb; $max = $wpdb->get_var( "SELECT MAX(ID) FROM " . $this->app_table . " " ); if ( $max ) $wpdb->query( "ALTER TABLE " . $this->app_table ." AUTO_INCREMENT=". ($max+1). " " ); } } }