plugin = $plugin; $this->hooks(); } /** * Initiate our hooks. * * @since 0.0.3 */ public function hooks() { add_shortcode( 'ssa_booking', array( $this, 'ssa_booking' ) ); add_shortcode( 'tec_ssa_booking', array( $this, 'tec_ssa_booking' ) ); add_shortcode( 'mepr_ssa_booking', array( $this, 'mepr_ssa_booking' ) ); add_shortcode( 'ssa_past_appointments', array( $this, 'ssa_past_appointments' ) ); add_shortcode( 'ssa_upcoming_appointments', array( $this, 'ssa_upcoming_appointments' ) ); add_shortcode( 'ssa_admin_upcoming_appointments', array( $this, 'ssa_admin_upcoming_appointments' ) ); add_shortcode( 'ssa_admin', array( $this, 'ssa_admin' ) ); add_shortcode( 'ssa_confirmation', array( $this, 'ssa_confirmation' ) ); add_action( 'init', array( $this, 'store_enqueued_styles_scripts' ), 1 ); add_action( 'wp_enqueue_scripts', array( $this, 'register_styles' ) ); add_action( 'init', array( $this, 'register_scripts' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_styles' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'maybe_enqueue_scripts_sitewide' ), 0 ); add_action( 'wp_enqueue_scripts', array( $this, 'disable_third_party_scripts' ), 9999999 ); add_action( 'wp_enqueue_scripts', array( $this, 'disable_third_party_styles' ), 9999999 ); add_action( 'init', array( $this, 'custom_rewrite_basic' ) ); add_action('init', array( $this, 'prevent_breakdance_conflict_with_appointment_edit_url' ) ); add_action( 'query_vars', array( $this, 'register_query_var' ) ); add_filter( 'template_include', array( $this, 'hijack_booking_page_template' ) ); add_filter( 'template_include', array( $this, 'hijack_embedded_page' ), 9999999 ); add_filter( 'template_include', array( $this, 'hijack_appointment_edit_page' ), 9999999 ); add_filter( 'template_include', array( $this, 'hijack_appointment_edit_url' ), 9999999 ); add_action( 'template_redirect', array( $this, 'prevent_thrive_themes_conflict_with_appointment_edit_url' ), 8 ); // REST API Endpoint to pull generate output from shortcode add_action( 'rest_api_init', array( $this, 'register_rest_routes' ) ); } /** * Withou this, BreakDance would force the appointment edit URL to load the home page. * */ public function prevent_breakdance_conflict_with_appointment_edit_url(){ if ( empty( $_GET['appointment_action'] ) || 'edit' !== $_GET['appointment_action'] || empty( $_GET['appointment_token'] ) ) { return; } if (has_filter('template_include', 'Breakdance\ActionsFilters\template_include')) { remove_filter('template_include', 'Breakdance\ActionsFilters\template_include', 1000000); } } public function prevent_thrive_themes_conflict_with_appointment_edit_url() { if ( empty( $_GET['appointment_action'] ) || 'edit' !== $_GET['appointment_action'] || empty( $_GET['appointment_token'] ) ) { return; } remove_action( 'template_redirect', 'tcb_custom_editable_content', 9 ); // this is ThriveTheme's nuclear function that removes *all* template_include filters } public function custom_rewrite_basic() { add_rewrite_rule( '^ssa-embed?', 'index.php?ssa_embed=yes', 'top' ); } public function register_query_var( $vars ) { $vars[] = 'ssa_embed'; return $vars; } public function store_enqueued_styles_scripts() { if ( is_admin() ) { return; } global $wp_scripts; if ( empty( $wp_scripts->queue ) ) { $this->script_handle_whitelist = array(); } else { $this->script_handle_whitelist = $wp_scripts->queue; } } public function disable_third_party_scripts() { if ( ! $this->disable_third_party_scripts ) { return; } global $wp_scripts; if ( ! empty( $wp_scripts->queue ) ) { foreach ( $wp_scripts->queue as $key => $handle ) { if ( strpos( $handle, 'ssa-' ) === 0 ) { continue; } if ( in_array( $handle, $this->script_handle_whitelist ) || in_array( $handle, $this->custom_script_handle_whitelist ) ) { continue; } wp_dequeue_script( $handle ); } } } public function disable_third_party_styles() { if ( ! $this->disable_third_party_styles ) { return; } global $wp_styles; if ( ! empty( $wp_styles->queue ) ) { foreach ( $wp_styles->queue as $key => $handle ) { if ( strpos( $handle, 'ssa-' ) === 0 ) { continue; } if ( in_array( $handle, $this->style_handle_whitelist ) || in_array( $handle, $this->custom_script_handle_whitelist ) ) { continue; } wp_dequeue_style( $handle ); } } } public function is_booking_page() { $page_id = get_queried_object_id(); $settings = $this->plugin->settings->get(); if ( empty( $page_id ) || $page_id != $settings['global']['booking_post_id'] ) { return false; } return true; } public function get_appointment_edit_permalink() { return apply_filters( 'ssa/booking/appointment_edit_permalink', 'appointments/change' ); } public function is_embedded_page() { $query_var = get_query_var( 'ssa_embed' ); if ( ! empty( $query_var ) ) { return true; } if ( ! empty( $_GET['ssa_embed'] ) ) { return true; } global $wp; if ( ! empty( $wp->matched_rule ) && $wp->matched_rule === '^ssa-embed?' ) { return true; } if ( $wp->request === 'ssa-embed' ) { return true; } return false; } public function hijack_embedded_page( $template ) { if ( ! $this->is_embedded_page() ) { return $template; } return $this->plugin->dir( 'booking-app-new/iframe-inner.php' ); } public function hijack_booking_page_template( $template ) { if ( ! $this->is_booking_page() ) { return $template; } return $this->plugin->dir( 'booking-app-new/fullscreen-page.php' ); } public function hijack_appointment_edit_page( $template ) { $appointment_edit_permalink = $this->get_appointment_edit_permalink(); global $wp; $uri = sanitize_text_field( $wp->request ); if ( strpos( $uri, $appointment_edit_permalink ) !== 0 ) { return $template; } $uri = str_replace( $appointment_edit_permalink, '', $uri ); $uri = ltrim( $uri, '/' ); $uri = rtrim( $uri, '/' ); $parts = explode( '-', $uri ); $provided_hash = $parts['0']; $provided_hash = substr( $uri, 0, 32 ); $appointment_id = substr( $uri, 32 ); if ( ! $this->plugin->appointment_model->verify_id_token( $appointment_id, $provided_hash ) ) { die( 'An error occurred, please check the URL' ); // phpcs:ignore } add_filter( 'show_admin_bar', '__return_false' ); global $ssa_current_appointment_id; $ssa_current_appointment_id = $appointment_id; return $this->plugin->dir( 'booking-app-new/page-appointment-edit.php' ); } public function hijack_appointment_edit_url( $template ) { // If Appointment Action is not edit if ( empty( $_GET['appointment_action'] ) || 'edit' !== $_GET['appointment_action'] || empty( $_GET['appointment_token'] ) ) { return $template; } /** * Prevent hijacking the PAGE if this is a Gravity Forms custom confirmation * The URL is already hijacked by now */ if( ! empty( $_GET['ssa_gf_custom_confirmation'] ) ) { return $template; } $settings_global = ssa()->settings->get()['global']; $edit_appointment_page_id = $settings_global['edit_appointment_page_id']; // If Edit Appointment Page is found and has the shortcode ssa_confirmation if ( ! empty ( $edit_appointment_page_id ) && get_queried_object_id() == $edit_appointment_page_id ) { $post = get_post($edit_appointment_page_id); if ($post && has_shortcode( $post->post_content, 'ssa_confirmation' )) { /** * Allow hijacking the page if the admin is editing as customer from within SSA admin app * @see admin-app/src/components/Appointment/Appointment.vue */ if ( empty( $_GET['redirect_from'] ) || $_GET['redirect_from'] !== 'ssa_admin' ) { return $template; } } } else if ( !is_front_page() && ! is_home() ) { return $template; } $this->disable_third_party_scripts = true; $this->disable_third_party_styles = true; $appointment_token = sanitize_text_field( $_GET['appointment_token'] ); $provided_hash = substr( $appointment_token, 0, 32 ); $appointment_id = substr( $appointment_token, 32 ); if ( ! $this->plugin->appointment_model->verify_id_token( $appointment_id, $provided_hash ) ) { die( 'An error occurred, please check the URL' ); // phpcs:ignore } if ( isset( $_GET['admin'] ) && current_user_can( 'ssa_manage_site_settings' ) ) { wp_redirect( ssa()->appointment_model->get_admin_edit_url( $appointment_id ), 302 ); exit; } add_filter( 'show_admin_bar', '__return_false' ); global $ssa_current_appointment_id; $ssa_current_appointment_id = $appointment_id; return $this->plugin->dir( 'booking-app-new/page-appointment-edit.php' ); } public function body_class( $classes ) { $classes = "$classes ssa-admin-app"; return $classes; } public function register_scripts() { wp_register_script( 'ssa-iframe-inner', $this->plugin->url( 'assets/js/iframe-inner.js' ), array(), Simply_Schedule_Appointments::VERSION, true ); wp_register_script( 'ssa-iframe-outer', $this->plugin->url( 'assets/js/iframe-outer.js' ), array(), Simply_Schedule_Appointments::VERSION, true ); wp_register_script( 'ssa-tracking', $this->plugin->url( 'assets/js/ssa-tracking.js' ), array(), Simply_Schedule_Appointments::VERSION, true ); wp_register_script( 'ssa-form-embed', $this->plugin->url( 'assets/js/ssa-form-embed.js' ), array( 'jquery' ), Simply_Schedule_Appointments::VERSION, true ); wp_register_script( 'ssa-unsupported-script', $this->plugin->url( 'assets/js/unsupported.js' ), array(), Simply_Schedule_Appointments::VERSION ); } public function register_styles() { wp_register_style( 'ssa-booking-material-icons', $this->plugin->url( 'assets/css/material-icons.css' ), array(), Simply_Schedule_Appointments::VERSION ); wp_register_style( 'ssa-booking-style', $this->plugin->url( 'booking-app/dist/static/css/app.css' ), array(), Simply_Schedule_Appointments::VERSION ); wp_register_style( 'ssa-booking-roboto-font', $this->plugin->url( 'assets/css/roboto-font.css' ), array(), Simply_Schedule_Appointments::VERSION ); wp_register_style( 'ssa-iframe-inner', $this->plugin->url( 'assets/css/iframe-inner.css' ), array(), Simply_Schedule_Appointments::VERSION ); wp_register_style( 'ssa-styles', $this->plugin->url( 'assets/css/ssa-styles.css' ), array(), Simply_Schedule_Appointments::VERSION ); wp_register_style( 'ssa-unsupported-style', $this->plugin->url( 'assets/css/unsupported.css' ), array(), Simply_Schedule_Appointments::VERSION ); wp_register_style( 'ssa-upcoming-appointments-card-style', $this->plugin->url( 'assets/css/upcoming-appointments.css' ), array(), Simply_Schedule_Appointments::VERSION ); } public function enqueue_styles() { global $post; if ( $this->is_embedded_page() ) { wp_enqueue_style( 'ssa-unsupported-style' ); wp_enqueue_style( 'ssa-booking-material-icons' ); wp_enqueue_style( 'ssa-booking-style' ); wp_enqueue_style( 'ssa-booking-roboto-font' ); wp_enqueue_style( 'ssa-iframe-inner' ); } elseif ( is_a( $post, 'WP_Post' ) ) { wp_enqueue_style( 'ssa-upcoming-appointments-card-style' ); wp_enqueue_style( 'ssa-styles' ); } } public function maybe_enqueue_scripts_sitewide() { $developer_settings = $this->plugin->developer_settings->get(); if ( empty( $developer_settings['enqueue_everywhere'] ) ) { return; } if ( $this->is_embedded_page() ) { return; } wp_enqueue_script( 'ssa-iframe-outer' ); wp_enqueue_script( 'ssa-tracking' ); wp_enqueue_script( 'ssa-form-embed' ); } public function tec_ssa_booking( $atts = array() ) { // sanitize all atts $atts = array_map( 'sanitize_text_field', $atts ); $post_id = get_the_ID(); if ( ! function_exists( 'tribe_is_event' ) || ! tribe_is_event( $post_id ) ) { return $this->ssa_booking( $atts ); } $event = tribe_get_event( $post_id ); if ( empty( $atts ) ) { $atts = array(); } $atts = array_merge( array( 'availability_start_date' => $event->dates->start_utc->format( 'Y-m-d H:i:s' ), 'availability_end_date' => $event->dates->end_utc->format( 'Y-m-d H:i:s' ), ), $atts ); return $this->ssa_booking( $atts ); } public function get_ssa_booking_arg_defaults() { return array( 'integration' => '', // internal use only for integrations 'type' => '', 'label' => '', 'types' => '', 'edit' => '', 'view' => '', 'payment_provider' => '', // use to indicate to the frontend what payment provider was used 'ssa_locale' => SSA_Translation::get_locale(), 'ssa_is_rtl' => SSA_Translation::is_rtl(), 'sid' => sha1( gmdate( 'Ymd' ) . get_current_user_id() ), // busts full-page caching so each URL is user-specific (daily) and doesn't leak sensitive data 'availability_start_date' => '', 'availability_end_date' => '', 'suggest_first_available' => '', 'suggest_first_available_within_minutes' => '', 'flow' => '', 'fallback_flow' => '', 'time_view' => '', 'date_view' => '', 'appointment_types_view' => '', 'version' => '', 'accent_color' => '', 'background' => '', 'padding' => '', 'font' => '', 'booking_url' => urlencode( get_permalink() ), 'booking_post_id' => urlencode( get_the_ID() ), 'booking_title' => urlencode( get_the_title() ), '_wpnonce' => wp_create_nonce( 'wp_rest' ), 'redirect_post_id' => '', // MemberPress Integration 'mepr_membership_id' => '' ); } public function get_passed_args() { $passed_args = array_diff_key( $_GET, $this->get_ssa_booking_arg_defaults() ); return $passed_args; } public function ssa_confirmation() { $appointment_token = isset($_GET['appointment_token']) ? esc_attr($_GET['appointment_token']): (isset($_POST["appointment_token"]) ? esc_attr( $_POST["appointment_token"]): ''); $error_message = ''; $paypal_success = isset($_GET['paypal_success']) ? esc_attr($_GET['paypal_success']): ''; $paypal_cancel = isset($_GET['paypal_cancel']) ? esc_attr($_GET['paypal_cancel']): ''; if(empty($appointment_token)) { if ( current_user_can( 'ssa_manage_site_settings' ) ) { $error_message = '
' . sprintf( __('The specified appointment types \'%1$s\' can\'t be found %2$s (this message is only viewable to site administrators)', 'simply-schedule-appointments'),
$types,
'' );
}
return $error_message;
}
}
// Check for atts['label'] if set, and convert it to atts['types']
if( ! empty( $atts['label'] ) ) {
$label = sanitize_text_field( esc_attr( $atts['label'] ) );
$ids = $this->convert_label_to_appt_types_ids( $label );
if ( empty( $ids ) ) {
$error_message = '' . sprintf( __('The specified appointment type label \'%1$s\' can\'t be found, or has no appointment types available %2$s (this message only viewable to site administrators)', 'simply-schedule-appointments'),
$label,
'' );
}
return $error_message;
} else {
$atts['types'] = $ids;
}
}
$appointment_type = '';
if ( ! empty( $atts['type'] ) ) {
if ( $atts['type'] == (string) (int) $atts['type'] ) {
// integer ID provided
$appointment_type_id = (int) sanitize_text_field( $atts['type'] );
} else {
// slug provided
$appointment_types = $this->plugin->appointment_type_model->query(
array(
'slug' => sanitize_text_field( $atts['type'] ),
'status' => 'publish',
)
);
if ( ! empty( $appointment_types['0']['id'] ) ) {
$appointment_type_id = $appointment_types['0']['id'];
}
if ( empty( $appointment_type_id ) ) {
$type = sanitize_text_field( esc_attr( $atts['type'] ) );
$error_message = '' . sprintf( __('The specified appointment type \'%1$s\' can\'t be found %2$s (this message only viewable to site administrators)', 'simply-schedule-appointments'),
$type,
'' );
}
return $error_message;
}
}
if ( ! empty( $appointment_type_id ) ) {
$appointment_type = $this->plugin->appointment_type_model->get( $appointment_type_id );
if ( empty( $atts['types'] ) ) {
$atts['types'] = $appointment_type_id;
}
}
}
if ( $is_embedded_page || $this->is_embedded_page() ) {
// wp_localize_script( 'ssa-booking-app', 'ssaBookingParams', array(
// 'apptType' => $appointment_type,
// 'translatedStrings' => array(
// ),
// ) );
wp_localize_script( 'ssa-booking-app', 'ssa', $this->plugin->bootstrap->get_api_vars() );
wp_localize_script( 'ssa-booking-app', 'ssa_translations', $this->get_translations() );
wp_enqueue_script( 'ssa-unsupported-script' );
wp_enqueue_script( 'ssa-booking-manifest' );
wp_enqueue_script( 'ssa-booking-vendor' );
wp_enqueue_script( 'ssa-booking-app' );
wp_enqueue_script( 'ssa-iframe-inner' );
return '
' . __('The MemberPress plugin is not available or not activated. Please ensure that the plugin is installed and activated.', 'simply-schedule-appointments') . '';
$error_message .= '' . __('(this message is only viewable to site administrators)', 'simply-schedule-appointments') . '';
}
$atts['error_message'] = $error_message;
return $atts;
}
// Invalid input membership_id
if ( is_numeric( $membership_id ) ) {
$membership_id = (int) $membership_id;
} else {
$error_message .= '' . __('The membership ID provided is not valid.', 'simply-schedule-appointments') . '';
$error_message .= '' . __('(this message is only viewable to site administrators)', 'simply-schedule-appointments') . '';
}
$atts['error_message'] = $error_message;
return $atts;
}
$current_user = wp_get_current_user();
// We don't have a logged in user
if ( empty( $current_user ) || empty( $current_user->ID ) ) {
$must_login_err_msg = apply_filters( 'ssa/mepr/shortcode/must_login_msg', __( 'You must be logged in to schedule an appointment. Please log in or register.', 'simply-schedule-appointments' ) );
$error_message .= '' . __('The provided membership ID does not have any associated appointment types. Please ensure that the membership ID is correct and associated with valid appointment types.', 'simply-schedule-appointments') . '';
$error_message .= '' . __('(this message is only viewable to site administrators)', 'simply-schedule-appointments') . '';
}
$atts['error_message'] = $error_message;
return $atts;
}
if ( empty( $atts['types'] ) && empty( $atts['type'] ) ) {
$atts['types'] = implode( ',', $appointment_type_ids );
}
return $atts;
}
/**
* MemberPress Integration
* The SSA - mepr shortcode wrapper [mepr_ssa_booking]
*
* @return string
*/
public function mepr_ssa_booking() {
$is_admin = current_user_can( 'ssa_manage_site_settings' );
$error_message = '';
if ( ! class_exists( 'SSA_Memberpress') || ! $this->plugin->memberpress->is_ssa_mepr_integration_active() ) {
$error_message .= '' . __('The MemberPress plugin is not available or not activated. Please ensure that the plugin is installed and activated.', 'simply-schedule-appointments') . '';
$error_message .= '' . __('(this message is only viewable to site administrators)', 'simply-schedule-appointments') . '';
}
return $error_message;
}
$current_user = wp_get_current_user();
// We don't have a logged in user
if ( empty( $current_user ) || empty( $current_user->ID ) ) {
$must_login_err_msg = apply_filters( 'ssa/mepr/shortcode/must_login_msg', __( 'You must be logged in to schedule an appointment. Please log in or register.', 'simply-schedule-appointments' ) );
return '