Contributor - Ve Bailovity (Incsub)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License (Version 2 - GPLv2) as published by
the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
if ( !class_exists( 'Appointments' ) ) {
class Appointments {
public $version = "2.0.3";
public $db_version;
public $timetables = array();
public $local_time;
/** @var bool|Appointments_Google_Calendar */
public $gcal_api = false;
public $locale_error;
public $time_format;
public $datetime_format;
public $log_file;
public $salt;
public $worker;
public $location;
public $service;
public $openid;
public $plugin_url;
/** @var Appointments_Admin */
public $admin;
/** @var Appointments_Addons_Loader */
public $addons_loader;
/** @var Appointments_Notifications_Manager */
public $notifications;
public $pro = false;
public $shortcodes = array();
function __construct() {
include_once( 'includes/helpers.php' );
include_once( 'includes/helpers-settings.php' );
include_once( 'includes/helpers-timetables.php' );
include_once( 'includes/deprecated-hooks.php' );
include_once( 'includes/class-app-notifications-manager.php' );
include_once( 'includes/class-app-api-logins.php' );
include_once( 'includes/class-app-sessions.php' );
// Load premium features
if ( is_readable( appointments_plugin_dir() . 'includes/pro/class-app-pro.php' ) ) {
include_once( appointments_plugin_dir() . 'includes/pro/class-app-pro.php' );
$this->pro = new Appointments_Pro();
}
$this->timetables = get_transient( 'app_timetables' );
if ( ! $this->timetables || ! is_array( $this->timetables ) ) {
$this->timetables = array();
}
$this->plugin_url = plugins_url(basename(dirname(__FILE__)));
// Read all options at once
$this->options = get_option( 'appointments_options' );
// To follow WP Start of week, time, date settings
$this->local_time = current_time('timestamp');
$this->start_of_week = appointments_week_start() - 1;
$this->time_format = appointments_get_date_format( 'time' );
$this->date_format = appointments_get_date_format( 'date' );
$this->datetime_format = appointments_get_date_format( 'full' );
add_action( 'delete_user', 'appointments_delete_worker' ); // Modify database in case a user is deleted
add_action( 'wpmu_delete_user', 'appointments_delete_worker' ); // Same as above
add_action( 'remove_user_from_blog', array( &$this, 'remove_user_from_blog' ), 10, 2 ); // Remove his records only for that blog
add_action( 'plugins_loaded', array(&$this, 'localization') ); // Localize the plugin
add_action( 'init', array( &$this, 'init' ), 20 ); // Initial stuff
add_filter( 'the_posts', array(&$this, 'load_styles') ); // Determine if we use shortcodes on the page
add_action( 'admin_init', array( $this, 'maybe_upgrade' ) );
include_once( 'includes/class-app-service.php' );
include_once( 'includes/class-app-worker.php' );
include_once( 'includes/class-app-appointment.php' );
include_once( 'includes/class-app-transaction.php' );
if ( is_admin() ) {
$this->load_admin();
}
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
include_once( 'includes/class-app-ajax.php' );
new Appointments_AJAX();
}
// Check for cookies
if (!empty($this->options['login_required']) && 'yes' === $this->options['login_required']) {
// If we require a login and we had an user logged in,
// we don't need cookies after they log out
add_action('wp_logout', array($this, 'drop_cookies_on_logout'));
}
// Widgets
require_once( appointments_plugin_dir() . 'includes/widgets.php' );
add_action( 'widgets_init', array( &$this, 'widgets_init' ) );
// Integration with other plugins/Themes
include_once( appointments_plugin_dir() . 'includes/integration/integration.php' );
$this->pages_to_be_cached = array();
$this->had_filter = false; // There can be a wpautop filter. We will check this later on.
add_action('init', array($this, 'get_gcal_api'), 10);
// Database variables
global $wpdb;
$this->db = &$wpdb;
$this->services_table = $wpdb->prefix . "app_services";
$this->transaction_table = $wpdb->prefix . "app_transactions";
$this->cache_table = $wpdb->prefix . "app_cache";
// DB version
$this->db_version = get_option( 'app_db_version' );
// Set meta tables
$wpdb->app_appointmentmeta = appointments_get_table( 'appmeta' );
// 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/";
$this->log_file = $this->uploads_dir . "appointments-log.txt";
// Other default settings
$this->script = $this->uri = $this->error_url = '';
$this->location = $this->service = $this->worker = 0;
$this->gcal_image = '';
$this->locale_errlocale_error = false;
// Create a salt, if it doesn't exist from the previous installation
if ( !$salt = get_option( "appointments_salt" ) ) {
$salt = mt_rand();
add_option( "appointments_salt", $salt ); // Save it to be used until it is cleared manually
}
$this->salt = $salt;
// Deal with zero-priced appointments auto-confirm
if ( isset( $this->options['payment_required'] ) && 'yes' == $this->options['payment_required'] && !empty($this->options['allow_free_autoconfirm'])) {
if (!defined('APP_CONFIRMATION_ALLOW_FREE_AUTOCONFIRM')) define('APP_CONFIRMATION_ALLOW_FREE_AUTOCONFIRM', true);
}
$this->notifications = new Appointments_Notifications_Manager();
}
public function load_admin() {
include_once( 'admin/class-app-admin.php' );
$this->admin = new Appointments_Admin();
}
function maybe_upgrade() {
if ( isset( $_GET['app-clear'] ) && current_user_can( 'manage_options' ) ) {
$this->flush_cache();
}
$db_version = get_option( 'app_db_version' );
if ( $db_version == $this->version ) {
return;
}
if ( false === $db_version ) {
appointments_activate();
}
appointments_clear_cache();
include_once( 'includes/class-app-upgrader.php' );
$upgrader = new Appointments_Upgrader( $this->version );
$upgrader->upgrade( $db_version, $this->version );
}
function get_gcal_api() {
if ( false === $this->gcal_api && ! defined( 'APP_GCAL_DISABLE' ) ) {
require_once appointments_plugin_dir() . 'includes/class-app-gcal.php';
$this->gcal_api = new Appointments_Google_Calendar();
}
return $this->gcal_api;
}
/**
***************************************************************************************************************
* Methods for optimization
*
* $l: location ID - For future use
* $s: service ID
* $w: worker ID
* $stat: Status (open: working or closed: not working)
* IMPORTANT: This plugin is NOT intended for hundreds of services or service providers,
* but it is intended to make database queries as cheap as possible with smaller number of services/providers.
* If you have lots of services and/or providers, codes will not scale and appointments pages will be VERY slow.
* If you need such an application, override some of the methods below with a child class.
***************************************************************************************************************
*/
/**
* Get location, service, worker
*/
function get_lsw() {
$this->location = $this->get_location_id();
$this->service = $this->get_service_id();
$this->worker = $this->get_worker_id();
}
/**
* Get location ID for future use
*/
function get_location_id() {
if ( isset( $_REQUEST["app_location_id"] ) )
return (int)$_REQUEST["app_location_id"];
return 0;
}
/**
* Get smallest service ID
* We assume total number of services is not too high, which is the practical case.
* Otherwise this method might be expensive
*
* @deprecated since 1.6
*
* @return integer
*/
function get_first_service_id() {
_deprecated_function( __FUNCTION__, '1.6', 'appointments_get_services_min_id()' );
return appointments_get_services_min_id();
}
/**
* Get service ID from front end
* @return integer
*/
function get_service_id() {
if ( isset( $_REQUEST["app_service_id"] ) )
return (int)$_REQUEST["app_service_id"];
else if ( !$service_id = appointments_get_services_min_id() )
$service_id = 0;
return $service_id;
}
/**
* Get worker ID from front end
* worker = provider
* @return integer
*/
function get_worker_id() {
if ( isset( $_REQUEST["app_provider_id"] ) )
return (int)$_REQUEST["app_provider_id"];
if ( isset( $_REQUEST["app_worker_id"] ) )
return (int)$_REQUEST["app_worker_id"];
return 0;
}
/**
* Allow only certain order_by clauses
* @since 1.2.8
*/
function sanitize_order_by( $order_by="ID" ) {
$whitelist = apply_filters( 'app_order_by_whitelist', array( 'ID', 'name', 'start', 'end', 'duration', 'price',
'ID DESC', 'name DESC', 'start DESC', 'end DESC', 'duration DESC', 'price DESC', 'RAND()' ) );
if ( in_array( $order_by, $whitelist ) )
return $order_by;
else
return 'ID';
}
/**
* Get a single service with given ID
*
* @deprecated Deprecated since version 1.6
*
* @param ID: Id of the service to be retrieved
* @return object
*/
function get_service( $ID ) {
_deprecated_function( __FUNCTION__, '1.6', 'appointments_get_service()' );
return appointments_get_service( $ID );
}
/**
* Get all workers
*
* @deprecated Deprecated since version 1.6
*
* @param order_by: ORDER BY clause for mysql
* @return array of objects
*/
function get_workers( $order_by="ID" ) {
_deprecated_function( __FUNCTION__, '1.6', 'appointments_get_workers()' );
$args = array(
'orderby' => $order_by
);
return appointments_get_workers( $args );
}
/**
* Get all services
* @param order_by: ORDER BY clause for mysql
* @deprecated Deprecated since version 1.6
* @return array of objects
*/
function get_services( $order_by="ID" ) {
_deprecated_function( __FUNCTION__, '1.6', 'appointments_get_services()' );
$args = array( 'orderby'=> $order_by );
return appointments_get_services( $args );
}
/**
* Get workers giving a specific service (by its ID)
* We assume total number of workers is not too high, which is the practical case.
* Otherwise this method would be expensive
*
* @deprecated Deprecated since version 1.6
*
* @param ID: Id of the service to be retrieved
* @param string $order_by ORDER BY clause for mysql
* @return array|false array of objects
*/
function get_workers_by_service( $ID, $order_by = "ID" ) {
_deprecated_function( __FUNCTION__, '1.6', 'appointments_get_workers_by_service()' );
$workers = appointments_get_workers_by_service( $ID, $order_by );
if ( empty( $workers ) )
return false;
return $workers;
}
/**
* Check if there is only one worker giving the selected service
*
* @deprecated since 1.6
*
* @param service: Id of the service for which check will be done
* @since 1.1.1
* @return int|boolean (worker ID if there is one, otherwise false)
*/
function is_single_worker( $service_id ) {
_deprecated_function( __FUNCTION__, '1.6' );
$workers = appointments_get_workers_by_service( $service_id );
if ( 1 === count( $workers ) ) {
return $workers[0]->ID;
}
return false;
}
/**
* Return a row from working hours table, i.e. days/hours we are working or we have break
* @param stat: open (works), or closed (breaks).
* @return object
*/
function get_work_break( $l, $w, $stat ) {
_deprecated_function( __FUNCTION__, '2.0', 'appointments_get_worker_working_hours()' );
$work_break = appointments_get_worker_working_hours( $stat, $w, $l );
if ( false === $work_break ) {
return null;
}
// Backward compatibility
$work_break->hours = maybe_serialize( $work_break->hours );
return $work_break;
}
/**
* Return a row from exceptions table, i.e. days we are working or having holiday
* @deprecated since 1.9.2
* @return object
*/
function get_exception( $l, $w, $stat ) {
_deprecated_function( __FUNCTION__, '1.9.2', 'appointments_get_worker_exceptions()' );
return appointments_get_worker_exceptions( $w, $stat, $l );
}
/**
* Return an appointment given its ID
*
* @deprecated since 1.6
*
* @param app_id: ID of the appointment to be retreived from database
* @since 1.1.8
* @return object
*/
function get_app( $app_id ) {
_deprecated_function( __FUNCTION__, '1.6', 'appointments_get_appointment()' );
return appointments_get_appointment( $app_id );
}
/**
* Return all reserve appointments (i.e. pending, paid, confirmed or reserved by GCal)
* @param week: Optionally appointments only in the number of week in ISO 8601 format (since 1.2.3).
* Weekly gives much better results in RAM usage compared to monthly, with a tolerable, slight increase in number of queries
* @return array of objects
*
* @deprecated since 1.6
*/
function get_reserve_apps( $l, $s, $w, $week=0 ) {
_deprecated_function( __FUNCTION__, '1.6', 'appointments_get_appointments()' );
$args = array(
'location' => $l,
'service' => $s,
'week' => $week,
'worker' => $w
);
return appointments_get_appointments_filtered_by_services( $args );
}
/**
* Return all reserve appointments by worker ID
* @param week: Optionally appointments only in the number of week in ISO 8601 format (since 1.2.3)
* @return array of objects
*/
function get_reserve_apps_by_worker( $l, $w, $week=0 ) {
$apps = wp_cache_get( 'reserve_apps_by_worker_'. $l . '_' . $w . '_' . $week );
if ( false === $apps ) {
$services = appointments_get_services();
if ( $services ) {
$apps = array();
foreach ( $services as $service ) {
$args = array(
'location' => $l,
'service' => $service->ID,
'worker' => $w,
'week' => $week
);
$apps_worker = appointments_get_appointments_filtered_by_services( $args );
if ( $apps_worker )
$apps = array_merge( $apps, $apps_worker );
}
}
wp_cache_set( 'reserve_apps_by_worker_'. $l . '_' . $w . '_' . $week, $apps );
}
return $apps;
}
/**
* Return reserve appointments by service ID
*
* @deprecated since 1.6
*
* @param week: Optionally appointments only in the number of week in ISO 8601 format (since 1.2.3)
* @since 1.1.3
* @return array of objects
*/
function get_reserve_apps_by_service( $l, $s, $week=0 ) {
_deprecated_function( __FUNCTION__, '1.6', 'appointments_get_appointments_filtered_by_services()' );
$args = array(
'location' => $l,
'service' => $s,
'week' => $week
);
return appointments_get_appointments_filtered_by_services( $args );
}
/**
* Find if a user is dummy
* @param user_id: Id of the user who will be checked if he is dummy
* since 1.0.6
* @return bool
*/
function is_dummy( $user_id=0 ) {
global $wpdb, $current_user;
if ( !$user_id )
$user_id = $current_user->ID;
// A dummy should be a worker
$result = appointments_get_worker( $user_id );
if ( ! $result )
return false;
// This is only supported after V1.0.6 and if DB is altered
if ( !$this->db_version )
return false;
if ( $result->dummy )
return true;
return false;
}
/**
*
* @deprecated since 1.6
*
* @param $worker_id
*
* @return bool
*/
public function is_worker( $worker_id ) {
_deprecated_function( __FUNCTION__, '1.6', 'appointments_is_worker()' );
return appointments_is_worker( $worker_id );
}
/**
* Find worker name given his ID
*
* @deprecated since 1.6
*
* @return string
*/
function get_worker_name( $worker=0, $field = true ) {
_deprecated_function( __FUNCTION__, '1.6', 'appointments_get_worker_name()' );
if ( $field ) {
$field = 'default';
}
else {
$field = 'user_login';
}
return appointments_get_worker_name( $worker, $field );
}
/**
* Find worker email given his ID
* since 1.0.6
* @return string
*/
function get_worker_email( $worker=0 ) {
// Real person
if ( !$this->is_dummy( $worker ) ) {
$worker_data = get_userdata( $worker );
if ( $worker_data )
$worker_email = $worker_data->user_email;
else
$worker_email = '';
return apply_filters( 'app_worker_email', $worker_email, $worker );
}
// Dummy
if ( isset( $this->options['dummy_assigned_to'] ) && $this->options['dummy_assigned_to'] ) {
$worker_data = get_userdata( $this->options['dummy_assigned_to'] );
if ( $worker_data )
$worker_email = $worker_data->user_email;
else
$worker_email = '';
return apply_filters( 'app_dummy_email', $worker_email, $worker );
}
// If not set anything, assign to admin
return $this->get_admin_email( );
}
/**
* Return admin email
* since 1.2.7
* @return string
*/
function get_admin_email( ) {
global $current_site;
$admin_email = get_option('admin_email');
if ( !$admin_email )
$admin_email = 'admin@' . $current_site->domain;
return apply_filters( 'app_get_admin_email', $admin_email );
}
/**
* Find service name given its ID
* @return string
*/
function get_service_name( $service=0 ) {
// Safe text if we delete a service
$name = __('Not defined', 'appointments');
$result = appointments_get_service( $service );
if ( $result )
$name = $result->name;
$name = apply_filters( 'app_get_service_name', $name, $service );
return stripslashes( $name );
}
/**
* Find client name given his appointment
* @return string
*/
function get_client_name( $app_id ) {
$name = '';
// This is only used on admin side, so an optimization is not required.
$result = appointments_get_appointment( $app_id );
if ( $result ) {
// Client can be a user
if ( $result->user ) {
$userdata = get_userdata( $result->user );
if ( $userdata ) {
$href = function_exists('bp_core_get_user_domain') && (defined('APP_BP_LINK_TO_PROFILE') && APP_BP_LINK_TO_PROFILE)
? bp_core_get_user_domain($result->user)
: admin_url("user-edit.php?user_id="). $result->user
;
$name = '' .
($result->name && !(defined('APP_USE_LEGACY_ADMIN_USERDATA_OVERRIDES') && APP_USE_LEGACY_ADMIN_USERDATA_OVERRIDES) ? $result->name : $userdata->user_login) .
'';
}
else
$name = $result->name;
}
else {
$name = $result->name;
if ( !$name )
$name = $result->email;
}
}
return apply_filters( 'app_get_client_name', $name, $app_id, $result );
}
/**
* Get price for the current service and worker
* If worker has additional price (optional), it is added to the service price
* @param paypal: If set true, deposit price is calculated
* @return string
*/
function get_price( $paypal=false ) {
global $current_user;
$this->get_lsw();
$price = appointments_get_price( $this->service, $this->worker );
/**
* Filter allows other plugins or integrations to apply a discount to
* the price.
*/
$price = apply_filters( 'app_get_price_prepare', $price, $paypal, $this );
// Discount
if ( $this->is_member() && isset( $this->options["members_discount"] ) && $this->options["members_discount"] ) {
// Special condition: Free for members
if ( 100 == $this->options["members_discount"] )
$price = 0;
else
$price = $price * ( 100 - $this->options["members_discount"] )/100;
}
if ( $paypal ) {
// Deposit
if ( isset( $this->options["percent_deposit"] ) && $this->options["percent_deposit"] )
$price = $price * $this->options["percent_deposit"] / 100;
if ( isset( $this->options["fixed_deposit"] ) && $this->options["fixed_deposit"] )
$price = $this->options["fixed_deposit"];
// It is possible to ask special amounts to be paid
$price = apply_filters( 'app_paypal_amount', $price, $this->service, $this->worker, $current_user->ID );
} else {
$price = apply_filters( 'app_get_price', $price, $this->service, $this->worker, $current_user->ID );
}
// Use number_format right at the end, cause it converts the number to a string.
$price = number_format( $price, 2 );
return $price;
}
/**
* Get deposit given price
* This is required only for manual pricing
* @param price: the full price
* @since 1.0.8
* @return string
*/
function get_deposit( $price ) {
$deposit = 0;
if ( !$price )
return apply_filters( 'app_get_deposit', 0 );
// Discount
if ( $this->is_member() && isset( $this->options["members_discount"] ) && $this->options["members_discount"] ) {
// Special condition: Free for members
if ( 100 == $this->options["members_discount"] )
$price = 0;
else
$price = number_format( $price * ( 100 - $this->options["members_discount"] )/100, 2 );
}
// Deposit
if ( isset( $this->options["percent_deposit"] ) && $this->options["percent_deposit"] )
$deposit = number_format( $price * $this->options["percent_deposit"] / 100, 2 );
if ( isset( $this->options["fixed_deposit"] ) && $this->options["fixed_deposit"] )
$deposit = $this->options["fixed_deposit"];
return apply_filters( 'app_get_deposit', $deposit );
}
/**
* Get the capacity of the current service
* @return integer
*/
function get_capacity( $service_id = false ) {
if ( $service_id && $service = appointments_get_service( $service_id ) ) {
$service_id = $service->ID;
}
else {
$service_id = $this->service;
}
return appointments_get_service_capacity( $service_id );
}
/**
**************************************
* Methods for Specific Content Caching
* Developed especially for this plugin
**************************************
*/
/**
* Get request uri
* @return string
*/
function get_uri() {
// Get rid of # part
if ( strpos( $_SERVER['REQUEST_URI'], '#' ) !== false ) {
$uri_arr = explode( '#', $_SERVER['REQUEST_URI'] );
$uri = $uri_arr[0];
}
else
$uri = $_SERVER['REQUEST_URI'];
return $uri;
}
/**
* Flush both database and object caches
*
*/
function flush_cache( ) {
wp_cache_flush();
appointments_clear_cache();
}
/****************
* General methods
*****************
*/
/**
* Provide options if asked outside the class
* @return array
*/
function get_options() {
return $this->options;
}
/**
* Save a message to the log file
*/
function log( $message='' ) {
if ( $message ) {
$to_put = '['. date_i18n( $this->datetime_format, $this->local_time ) .'] '. $message;
// Prevent multiple messages with same text and same timestamp
if ( !file_exists( $this->log_file ) || strpos( @file_get_contents( $this->log_file ), $to_put ) === false ) {
@file_put_contents( $this->log_file, $to_put . chr(10). chr(13), FILE_APPEND );
}
}
}
/**
* Remove tabs and breaks
*/
function esc_rn( $text ) {
$text = str_replace( array("\t","\n","\r"), "", $text );
return $text;
}
/**
* Converts number of seconds to hours:mins acc to the WP time format setting
* @param integer $secs Seconds
* @param bool $forced_format
* @param bool $do_i18n
* @return bool|int|string
*/
function secs2hours( $secs, $forced_format=false, $do_i18n = true ) {
$min = (int)($secs / 60);
$hours = "00";
if ( $min < 60 )
$hours_min = $hours . ":" . $min;
else {
$hours = (int)($min / 60);
if ( $hours < 10 )
$hours = "0" . $hours;
$mins = $min - $hours * 60;
if ( $mins < 10 )
$mins = "0" . $mins;
$hours_min = $hours . ":" . $mins;
}
if (!empty($forced_format)) $hours_min = strtotime($hours_min . ":00");
else if ($this->time_format) $hours_min = strtotime($hours_min . ":00"); // @TODO: TEST THIS THOROUGHLY!!!!
if( $do_i18n ) {
$hours_min = date_i18n( $this->time_format, $hours_min );
}
elseif ( $forced_format ) {
$hours_min = date( $forced_format, $hours_min );
}
else {
$hours_min = date( $this->time_format, $hours_min );
}
return $hours_min;
}
function secs_to_24h( $secs ) {
$min = (int)($secs / 60);
$hours = "00";
if ( $min < 60 )
$hours_min = $hours . ":" . $min;
else {
$hours = (int)($min / 60);
if ( $hours < 10 )
$hours = "0" . $hours;
$mins = $min - $hours * 60;
if ( $mins < 10 )
$mins = "0" . $mins;
$hours_min = $hours . ":" . $mins;
}
return date( 'H:i', strtotime( $hours_min ) );
}
/**
* Return an array of preset base times, so that strange values are not set
* @return array
*/
function time_base() {
$default = array( 10,15,30,60,90,120 );
$options = appointments_get_options();
$a = $options["additional_min_time"];
// Additional time bases
if ( isset( $a ) && $a && is_numeric( $a ) )
$default[] = $a;
return apply_filters( 'app_time_base', $default );
}
/**
* Return minimum set interval time
* If not set, return a safe time.
*
* @return integer
*/
function get_min_time() {
$options = appointments_get_options();
$min_time = $options['min_time'];
if ( $min_time && $min_time > apply_filters( 'app_safe_min_time', 9 ) ) {
return apply_filters( 'app-time-min_time', absint( $min_time ) );
} else {
return apply_filters( 'app-time-min_time', apply_filters( 'app_safe_time', 10 ) );
}
}
/**
* Number of days that an appointment can be taken
* @return integer
*/
function get_app_limit() {
$options = appointments_get_options();
$app_limit = $options['app_limit'];
if ( $app_limit ) {
return apply_filters( 'app_limit', absint( $app_limit ) );
} else {
return apply_filters( 'app_limit', 365 );
}
}
/**
* Return an array of weekdays
* @return array
*/
function weekdays() {
return array(
__('Sunday', 'appointments') => 'Sunday',
__('Monday', 'appointments') => 'Monday',
__('Tuesday', 'appointments') => 'Tuesday',
__('Wednesday', 'appointments') => 'Wednesday',
__('Thursday', 'appointments') => 'Thursday',
__('Friday', 'appointments') => 'Friday',
__('Saturday', 'appointments') => 'Saturday'
);
}
/**
* Return all available statuses
*
* @deprecated Since version 1.6
*
* @return array
*/
function get_statuses() {
_deprecated_function( __FUNCTION__, '1.6', 'appointments_get_statuses()' );
return appointments_get_statuses();
}
/**
* Return a selected field name to further customize them and make translation easier
* @return string (name of the field)
*/
function get_field_name( $key ) {
$field_names = array(
'name' => __('Name', 'appointments'),
'email' => __('Email', 'appointments'),
'phone' => __('Phone', 'appointments'),
'address' => __('Address', 'appointments'),
'city' => __('City', 'appointments'),
'note' => __('Note', 'appointments')
);
$field_names = apply_filters( 'app_get_field_name', $field_names );
if ( array_key_exists( $key, $field_names ) ) {
return $field_names[ $key ];
} else {
return __( 'Not defined', 'appointments' );
}
}
/**
* Return an array of all available front end box classes
* @return array
*/
function get_classes() {
return apply_filters( 'app_box_class_names',
array(
'free' => __( 'Free', 'appointments' ),
'busy' => __( 'Busy', 'appointments' ),
'notpossible' => __( 'Not possible', 'appointments' )
)
);
}
/**
* Return a default color for a selected box class
* @return string
*/
function get_preset( $class, $set ) {
$presets = array(
1 => array(
'free' => '48c048',
'busy' => 'ffffff',
'notpossible' => 'ffffff'
),
2 => array(
'free' => '73ac39',
'busy' => '616b6b',
'notpossible' => '8f99a3'
),
3 => array(
'free' => '40BF40',
'busy' => '454C54',
'notpossible' => '454C54'
)
);
return isset( $presets[ $set ][ $class ] ) ? $presets[ $set ][ $class ] : '111111';
}
/**
* Change status for a given app ID
*
* @deprecated since 1.6
*
* @return bool
*/
function change_status( $stat, $app_id ) {
_deprecated_function( __FUNCTION__, '1.6', 'appointments_update_appointment_status()' );
return appointments_update_appointment_status( $app_id, $stat );
}
/************************************************************
* Methods for Shortcodes and those related to shortcodes only
*************************************************************
*/
/**
* Generate an excerpt from the selected service/worker page
* Applies custom filter set instead of the default one.
*/
function get_excerpt( $page_id, $thumb_size, $thumb_class, $worker_id=0, $show_thumb_holder = false ) {
$text = '';
if ( !$page_id )
return $text;
$page = get_post( $page_id );
if ( !$page )
return $text;
$text = get_the_excerpt( $page_id );
if ( empty( $text ) ) {
$text = $page->post_content;
}
$text = strip_shortcodes( $text );
$text = apply_filters('app_the_content', $text, $page_id, $worker_id );
$text = str_replace(']]>', ']]>', $text);
$excerpt_length = apply_filters('app_excerpt_length', 55);
$excerpt_more = apply_filters('app_excerpt_more', ' … ' . __( 'More information →', 'appointments' ) . '');
$text = wp_trim_words( $text, $excerpt_length, $excerpt_more );
if ( $show_thumb_holder ) {
// @TODO Little crap :( to avoid so many queries when there are many services
$thumb = '
';
}
else {
$thumb = $this->get_thumbnail( $page_id, $thumb_size, $thumb_class, $worker_id );
}
return apply_filters( 'app_excerpt', $thumb. $text, $page_id, $worker_id );
}
/**
* Fetch content from the selected service/worker page.
* Applies custom filter set instead of the default one.
*/
function get_content( $page_id, $thumb_size, $thumb_class, $worker_id=0, $show_thumb_holder = false ) {
$content = '';
if ( !$page_id )
return $content;
$page = get_post( $page_id );
if ( !$page )
return $content;
if ( $show_thumb_holder ) {
// @TODO Little crap :( to avoid so many queries when there are many services
$thumb = '';
}
else {
$thumb = $this->get_thumbnail( $page_id, $thumb_size, $thumb_class, $worker_id );
}
$app_content = apply_filters( 'app_pre_content', wpautop( $this->strip_app_shortcodes( $page->post_content ), true ) );
return apply_filters( 'app_content', $thumb. $app_content, $page_id, $worker_id );
}
/**
* Clear app shortcodes
* @since 1.1.9
*/
function strip_app_shortcodes( $content ) {
// Don't even try to touch a non string, just in case
if ( !is_string( $content ) )
return $content;
else
return preg_replace( '%\[app_(.*?)\]%is', '', $content );
}
/**
* Get html code for thumbnail or avatar
*/
function get_thumbnail( $page_id, $thumb_size, $thumb_class, $worker_id ) {
if ( $thumb_size && 'none' != $thumb_size ) {
if ( strpos( $thumb_size, 'avatar' ) !== false ) {
if ( strpos( $thumb_size, ',' ) !== false ) {
$size_arr = explode( ",", $thumb_size );
$size = $size_arr[1];
}
else
$size = 96;
$thumb = get_avatar( $worker_id, $size );
if ( $thumb_class ) {
// Dirty, but faster than preg_replace
$thumb = str_replace( "class='", "class='".$thumb_class." ", $thumb );
$thumb = str_replace( 'class="', 'class="'.$thumb_class.' ', $thumb );
}
}
else {
if ( strpos( $thumb_size, ',' ) !== false )
$size = explode( ",", $thumb_size );
else
$size = $thumb_size;
$thumb = get_the_post_thumbnail( $page_id, $size, apply_filters( 'app_thumbnail_attr', array('class'=>$thumb_class) ) );
}
}
else
$thumb = '';
return apply_filters( 'app_thumbnail', $thumb, $page_id, $worker_id );
}
/**
* Build GCal url for GCal Button. It requires UTC time.
* @param start: Timestamp of the start of the app
* @param end: Timestamp of the end of the app
* @param php: If this is called for php. If false, called for js
* @param address: Address of the appointment
* @param city: City of the appointment
* @return string
*/
function gcal( $service, $start, $end, $php=false, $address, $city ) {
// Find time difference from Greenwich as GCal asks UTC
$text = sprintf(__('%s Appointment', 'appointments'), $this->get_service_name($service));
if (!$php) $text = esc_js( $text );
$gmt_start = get_gmt_from_date( date( 'Y-m-d H:i:s', $start ), "Ymd\THis\Z" );
$gmt_end = get_gmt_from_date( date( 'Y-m-d H:i:s', $end ), "Ymd\THis\Z" );
$location = isset($this->options["gcal_location"]) && '' != trim($this->options["gcal_location"])
? esc_js(str_replace(array('ADDRESS', 'CITY'), array($address, $city), $this->options["gcal_location"]))
: esc_js(get_bloginfo('description'))
;
$param = array(
'action' => 'TEMPLATE',
'text' => $text,
'dates' => $gmt_start . "/" . $gmt_end,
'sprop' => 'website:' . home_url(),
'location' => $location
);
return add_query_arg(
apply_filters('app_gcal_variables', $param, $service, $start, $end),
'http://www.google.com/calendar/event'
);
}
/**
* Die showing which field has a problem
*
* @param string $field_name
*/
function json_die( $field_name ) {
die( json_encode( array("error"=>sprintf( __( 'Something wrong about the submitted %s', 'appointments'), $this->get_field_name($field_name)))));
}
/**
* Check for too frequent back to back apps
* return true means no spam
* @return bool
*/
function check_spam() {
$options = appointments_get_options();
if (
! isset( $options["spam_time"] )
|| ! $options["spam_time"]
|| ! Appointments_Sessions::is_visitor_appointments_cookie_set()
) {
return true;
}
$apps = Appointments_Sessions::get_current_visitor_appointments();
if ( empty( $apps ) ) {
return true;
}
$checkdate = date( 'Y-m-d H:i:s', $this->local_time - $options["spam_time"] );
$results = appointments_get_appointments( array(
'app_id' => $apps,
'status' => 'pending',
'date_query' => array(
array(
'field' => 'created',
'compare' => '>',
'value' => $checkdate
)
)
) );
// A recent app is found
if ( $results ) {
return false;
}
return true;
}
/**
* Find timestamp of first day of month for a given time
* @param time: input whose first day will be found
* @param add: how many months to add
* @return integer (timestamp)
* @since 1.0.4
*/
function first_of_month( $time, $add ) {
$year = date( "Y", $time );
$month = date( "n", $time ); // Notice "n"
return mktime( 0, 0, 0, $month+$add, 1, $year );
}
/**
* Helper function to create a monthly schedule
*/
function get_monthly_calendar( $timestamp=false, $class='', $long, $widget ) {
$this->get_lsw();
$args = array(
'service_id' => $this->service,
'worker_id' => $this->worker,
'location_id' => $this->location,
'class' => $class,
'long' => $long,
'echo' => false,
'widget' => $widget
);
return appointments_monthly_calendar( $timestamp, $args );
}
function console_log( $start, $desc = '' ) {
$finish = microtime( true );
?>
worker;
$timetable_key .= '-' . date( 'Ym', $time );
// Calculate step
$start = $end = 0;
if ( $min_max = $this->min_max_wh( 0, 0 ) ) {
$start = $min_max["min"];
$end = $min_max["max"];
}
if ( $start >= $end ) {
$start = 8;
$end = 18;
}
$start = apply_filters( 'app_schedule_starting_hour', $start, $day_start, 'day' );
$end = apply_filters( 'app_schedule_ending_hour', $end, $day_start, 'day' );
$first = $start *3600 + $day_start; // Timestamp of the first cell
$last = $end *3600 + $day_start; // Timestamp of the last cell
$min_step_time = $this->get_min_time() * 60; // Cache min step increment
if (appointments_use_legacy_duration_calculus()) {
$step = $min_step_time; // Timestamp increase interval to one cell ahead
} else {
$service = appointments_get_service($this->service);
$step = (!empty($service->duration) ? $service->duration : $min_step_time) * 60; // Timestamp increase interval to one cell ahead
}
if ( ! appointments_use_legacy_duration_calculus() ) {
$start_result = appointments_get_worker_working_hours( 'open', $this->worker, $this->location );
$start_unpacked_days = isset( $start_result->hours ) ? $start_result->hours : array();
} else {
$start_unpacked_days = array();
}
if ( appointments_use_break_times_padding_calculus() ) {
$break_result = appointments_get_worker_working_hours( 'closed', $this->worker, $this->location );
$break_times = $break_result->hours;
} else {
$break_times = array();
}
// Allow direct step increment manipulation,
// mainly for service duration based calculus start/stop times
$step = apply_filters('app-timetable-step_increment', $step, 'timetable' );
if ( empty( $step ) || ! is_numeric( $step ) ) {
// If step is null/0 etc we can end up with problems
return '';
}
$timetable_key .= '-' . $step . '-' . $this->service;
// Are we looking to today?
// If today is a working day, shows its free times by default
if ( date( 'Ymd', $day_start ) == date( 'Ymd', $time ) )
$style = '';
else
$style = ' style="display:none"';
if ( isset( $this->timetables[ $timetable_key ] ) ) {
$data = $this->timetables[ $timetable_key ];
}
else {
$data = array();
for ( $t=$first; $t<$last; ) {
$ccs = apply_filters('app_ccs', $t); // Current cell starts
$cce = apply_filters('app_cce', $ccs + $step); // Current cell ends
// Fix for service durations calculus and workhours start conflict with different duration services
// Example: http://premium.wpmudev.org/forums/topic/problem-with-time-slots-not-properly-allocating-free-time
$this_day_key = date('l', $t);
if (!empty($start_unpacked_days) && ! appointments_use_legacy_duration_calculus() ) {
if (!empty($start_unpacked_days[$this_day_key])) {
// Check slot start vs opening start
$this_day_opening_timestamp = strtotime(date('Y-m-d ' . $start_unpacked_days[$this_day_key]['start'], $ccs));
if ($t < $this_day_opening_timestamp) {
$t = ($t - $step) + (apply_filters('app_safe_time', 1) * 60);
$t = apply_filters('app_next_time_step', $t+$step, $ccs, $step); //Allows dynamic/variable step increment.
continue;
}
// Check slot end vs opening end - optional, but still applies
//$this_day_closing_timestamp = strtotime(date('Y-m-d ' . $start_unpacked_days[$this_day_key]['end'], $ccs));
//if ($cce > $this_day_closing_timestamp) continue;
}
}
// Breaks are not behaving like paddings, which is to be expected.
// This fix (2) will force them to behave more like paddings
if (!empty($break_times[$this_day_key]['active']) && appointments_use_break_times_padding_calculus() ) {
$active = $break_times[$this_day_key]['active'];
$break_starts = $break_times[$this_day_key]['start'];
$break_ends = $break_times[$this_day_key]['end'];
if (!is_array($active) && 'no' !== $active) {
$break_start_ts = strtotime(date('Y-m-d ' . $break_starts, $ccs));
$break_end_ts = strtotime(date('Y-m-d ' . $break_ends, $ccs));
if ($t == $break_start_ts) {
$t += ($break_end_ts - $break_start_ts) - $step;
$t = apply_filters('app_next_time_step', $t+$step, $ccs, $step); //Allows dynamic/variable step increment.
continue;
}
} else if (is_array($active) && in_array('yes', array_values($active))) {
$has_break_time = false;
for ($idx=0; $idxis_busy( $ccs, $cce, $capacity );
$title = apply_filters('app-schedule_cell-title', date_i18n($this->datetime_format, $ccs), $is_busy, $ccs, $cce, $schedule_key);
$class_name = '';
// Mark now
if ( $local_time > $ccs && $local_time < $cce )
$class_name = 'notpossible now';
// Mark passed hours
else if ( $local_time > $ccs )
$class_name = 'notpossible app_past';
// Then check if this time is blocked
else if ( isset( $this->options["app_lower_limit"] ) && $this->options["app_lower_limit"]
&&( $local_time + $this->options["app_lower_limit"] * 3600) > $cce )
$class_name = 'notpossible app_blocked';
// Check if this is break
else if ( $this->is_break( $ccs, $cce ) )
$class_name = 'notpossible app_break';
// Then look for appointments
else if ( $is_busy )
$class_name = 'busy';
// Then check if we have enough time to fulfill this app
else if ( !$this->is_service_possible( $ccs, $cce, $capacity ) )
$class_name = 'notpossible service_notpossible';
// If nothing else, then it must be free
else {
$class_name = 'free';
// We found at least one timetable cell to be free
}
$class_name = apply_filters( 'app_class_name', $class_name, $ccs, $cce );
$data[] = array(
'class' => $class_name,
'title' => $title,
'hours' => $this->secs2hours( $ccs - $day_start ),
'ccs' => $ccs,
'cce' => $cce
);
$t = apply_filters('app_next_time_step', $t+$step, $t, $step); //Allows dynamic/variable step increment.
}
}
$this->timetables[ $timetable_key ] = $data;
// Save timetables only once at the end of the execution
//add_action( 'shutdown', array( $this, 'regenerate_timetables' ) );
add_action( 'shutdown', array( $this, 'save_timetables' ) );
$ret = '';
$ret .= '