?
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
new ga_appointments_post_type();
class ga_appointments_post_type {
public function __construct() {
// Post type
add_action( 'wp_loaded', array($this, 'ga_appointments_init') );
// New Statuses
add_action( 'wp_loaded', array($this,'ga_appointments_new_statuses') );
// Appointments Details Options
add_action( 'cmb2_admin_init', array($this,'cmb2_ga_appointments_details_metaboxes') );
// Rename post statuses
add_filter( "views_edit-ga_appointments", array($this, 'rename_ga_appointments_post_statuses') );
// Appointments post type new submit widget
add_action( 'cmb2_admin_init', array($this, 'cmb2_ga_appointments_submitdiv_metabox' ) );
// Appointment row columns
add_filter( "manage_edit-ga_appointments_columns", array($this,'manage_ga_appointments_columns') ); // column names
add_action( "manage_ga_appointments_posts_custom_column", array($this,'manage_ga_appointments_posts_custom_column'), 10, 2 ); // html column
// Add post type filters
add_action( 'restrict_manage_posts', array($this, 'add_service_provider_filter_to_posts_administration') );
add_action( 'pre_get_posts', array($this, 'add_service_provider_filter_to_posts_query') );
// Remove Date Drop Filter
add_action('admin_head', array($this,'remove_date_drop'));
}
/**
* Remove Date Drop Filter
*/
public function remove_date_drop(){
global $post_type, $pagenow;
if( $pagenow == 'edit.php' && $post_type == 'ga_appointments') {
add_filter('months_dropdown_results', '__return_empty_array');
}
}
/**
* Appointments Post Type
*/
public function ga_appointments_init() {
$questions_labels = array(
'name' => _x('Appointments', 'post type general name'),
'singular_name' => _x('Appointment', 'post type singular name'),
'all_items' => __('Appointments'),
'add_new' => _x('Add new', 'ga_booking_appointments'),
'add_new_item' => __('Add new appointment'),
'edit_item' => __('Edit appointment'),
'new_item' => __('New appointment'),
'view_item' => __('View appointment'),
'search_items' => __('Search appointments'),
'not_found' => __('You do not have any appointments!'),
'not_found_in_trash' => __('Nothing found in trash'),
'parent_item_colon' => ''
);
$args = array(
'labels' => $questions_labels,
'public' => false,
'publicly_queryable' => false,
'has_archive' => false,
'show_ui' => true,
'show_in_menu' => 'ga_appointments_settings',
'query_var' => true,
'rewrite' => false,
'rewrite' => array('slug' => 'contact'),
'capability_type' => 'post',
'hierarchical' => false,
'menu_position' => 5,
'supports' => array(''), // nothing supports
);
register_post_type('ga_appointments', $args);
}
/**
* Appointment Post Type New Statuses
*/
public function ga_appointments_new_statuses() {
register_post_status( 'completed', array(
'label' => _x( 'Completed', 'post' ),
'public' => true,
'exclude_from_search' => true,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Completed <span class="count">(%s)</span>', 'Completed <span class="count">(%s)</span>' ),
) );
register_post_status( 'payment', array(
'label' => _x( 'Pending Payment', 'post' ),
'public' => true,
'exclude_from_search' => true,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Pending Payment <span class="count">(%s)</span>', 'Pending Payment <span class="count">(%s)</span>' ),
) );
register_post_status( 'cancelled', array(
'label' => _x( 'Cancelled', 'post' ),
'public' => true,
'exclude_from_search' => true,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Cancelled <span class="count">(%s)</span>', 'Cancelled <span class="count">(%s)</span>' ),
) );
}
/**
* 1. Add custom filters to appointments post type
*/
public function add_service_provider_filter_to_posts_administration() {
//execute only on the 'post' content type
global $post_type, $pagenow;
//if we are currently on the edit screen of the post type listings
if($pagenow == 'edit.php' && $post_type == 'ga_appointments') {
$services_args = array(
'show_option_all' => 'Filter by service',
'name' => 'service_filter',
'selected' => '0',
);
$providers_args = array(
'show_option_all' => 'Filter by provider',
'name' => 'provider_filter',
'selected' => '-1',
);
$date_filter_args = array(
'name' => 'ga_date_filter',
'selected' => '',
);
// Filter by service
if( isset($_GET['service_filter'])) {
//set the selected value to the value of the author
$services_args['selected'] = absint( $_GET['service_filter'] );
}
echo '<select name="'.$services_args['name'].'" id="'.$services_args['name'].'">';
echo '<option value="0">'.$services_args['show_option_all'].'</option>';
if( get_ga_appointment_services() ) {
foreach( get_ga_appointment_services() as $_id => $service_title ) {
$selected = $services_args['selected'] == $_id ? ' selected' : '';
echo '<option value="'.$_id.'"'.$selected.'>'.$service_title.'</option>';
}
}
echo '</select>';
// Filter by provider
if( isset($_GET['provider_filter']) ) {
// set the selected value to the value of the author
$providers_args['selected'] = (int) $_GET['provider_filter'];
}
echo '<select name="'.$providers_args['name'].'" id="'.$providers_args['name'].'">';
echo '<option value="-1">'.$providers_args['show_option_all'].'</option>';
if( get_ga_appointment_providers() ) {
foreach( get_ga_appointment_providers() as $_id => $service_title ) {
$selected = $providers_args['selected'] == $_id ? ' selected' : '';
echo '<option value="'.$_id.'"'.$selected.'>'.$service_title.'</option>';
}
}
echo '</select>';
// Filter by input date
if( isset($_GET['ga_date_filter']) ) {
// set the selected value to the value of the author
$date_filter_args['selected'] = esc_html($_GET['ga_date_filter']);
}
echo '<input type="text" class="ga-date-picker" name="'.$date_filter_args['name'].'" id="'.$date_filter_args['name'].'" value="'.$date_filter_args['selected'].'" placeholder="Filter by date">';
}
}
/**
* 2. Add custom filters to appointments post type
*/
public function add_service_provider_filter_to_posts_query($query){
global $post_type, $pagenow;
//if we are currently on the edit screen of the post type listings
if($pagenow == 'edit.php' && $post_type == 'ga_appointments' && $query->is_main_query()) {
$filters = array();
// Sort Appointment Order
$filters[] = array(
'relation' => 'AND',
'date' => array( 'key' => 'ga_appointment_date', 'type' => 'DATE' ),
'time' => array( 'key' => 'ga_appointment_time', 'compare' => 'BETWEEN', 'type' => 'TIME' ),
array(
// A nested set of conditions for when the above condition is false.
array(
'relation' => 'OR',
'date' => array( 'key' => 'ga_appointment_date', 'compare' => '=', 'value' => '',),
'date' => array( 'key' => 'ga_appointment_date', 'compare' => 'NOT EXISTS', ),
'date' => array( 'key' => 'ga_appointment_date', 'type' => 'DATE' ),
)
),
);
$orderby = array(
'date' => 'ASC',
'time' => 'ASC',
);
// Sort Appointment Order
if(isset($_GET['service_filter'])) {
//set the query variable for 'author' to the desired value
$service_id = sanitize_text_field($_GET['service_filter']);
//if the author is not 0 (meaning all)
if($service_id != 0) {
$filters[] = array(
'key' => 'ga_appointment_service',
'value' => $service_id,
'type' => 'numeric',
);
}
}
if(isset($_GET['provider_filter'])) {
$provider_id = sanitize_text_field($_GET['provider_filter']);
if($provider_id != '-1') {
$filters[] = array(
'key' => 'ga_appointment_provider',
'value' => $provider_id,
'type' => 'numeric',
);
}
}
// Filter by input date
if( isset($_GET['ga_date_filter']) && ga_valid_date_format($_GET['ga_date_filter']) ) {
$date = $_GET['ga_date_filter'];
$filters[] = array(
'key' => 'ga_appointment_date',
'value' => $date,
);
}
$query->set( 'meta_query', $filters);
$query->set( 'orderby', $orderby);
}
}
/**
* Appointments Details Options
*/
public function cmb2_ga_appointments_details_metaboxes() {
// Start with an underscore to hide fields from custom fields list
$prefix = 'ga_appointment_';
/**
* Initiate the metabox
*/
$cmb = new_cmb2_box( array(
'id' => 'ga_appointment_details',
'title' => __( 'Appointment Details', 'cmb2' ),
'object_types' => array( 'ga_appointments' ), // Post type
'context' => 'normal',
'priority' => 'high',
'show_names' => true, // Show field names on the left
) );
// Type
$cmb->add_field( array(
'name' => 'Type',
'desc' => 'Interval or bookable date appointment',
'id' => $prefix . 'type',
'type' => 'select',
'default' => 'time_slot',
'options' => array(
'time_slot' => __( 'Time slot', 'cmb2' ),
'date' => __( 'Bookable date', 'cmb2' ),
),
) );
// Duration
$cmb->add_field( array(
'name' => 'Duration',
'desc' => 'Duration is how long the appointment lasts',
'id' => $prefix . 'duration',
'type' => 'select',
'classes_cb' => array($this, 'show_on_time_slot_type'),
'default' => '30', // 30 minutes
'options_cb' => 'ga_service_duration_options',
'sanitization_cb' => 'ga_sanitize_service_duration_options', // function should return a sanitized value
) );
// Date
$cmb->add_field( array(
'name' => 'Date',
'desc' => 'Format: year, month, day',
'id' => $prefix . 'date',
'type' => 'text_date',
'timezone_meta_key' => ga_time_zone(),
'date_format' => 'Y-m-j',
'sanitization_cb' => 'sanitize_get_ga_services_date', // function should return a sanitized value
) );
// Time
$cmb->add_field( array(
'name' => 'Time',
'desc' => 'Start time',
'id' => $prefix . 'time',
'type' => 'select',
'classes_cb' => array($this, 'show_on_time_slot_type'),
'default' => '09:00', // 30 minutes
'options_cb' => 'get_ga_appointment_time',
'sanitization_cb' => 'sanitize_get_ga_appointment_time', // function should return a sanitized value
) );
// Provider
$cmb->add_field( array(
'name' => 'Provider',
'desc' => 'Select no provider if you don\'t want any provider',
'id' => $prefix . 'provider',
'type' => 'select',
'options_cb' => 'get_ga_appointment_providers',
'sanitization_cb' => 'sanitize_get_ga_appointment_providers', // function should return a sanitized value
) );
// Service
$cmb->add_field( array(
'name' => 'Service',
'desc' => 'Select a service',
'id' => $prefix . 'service',
'type' => 'select',
'options_cb' => 'get_ga_appointment_services',
'sanitization_cb' => 'sanitize_ga_appointment_services', // function should return a sanitized value
) );
// Client
$cmb->add_field( array(
'name' => 'Client',
'desc' => 'Select a registered user or add new client',
'id' => $prefix . 'client',
'type' => 'select',
'options_cb' => 'get_ga_appointment_users',
'sanitization_cb' => 'sanitize_get_ga_appointment_users', // function should return a sanitized value
) );
// Client
$cmb->add_field( array(
'name' => 'Client Information',
'desc' => 'Name, Email & Phone',
'id' => $prefix . 'new_client',
'type' => 'text',
'render_row_cb' => 'get_ga_appointment_new_client_render_row',
'sanitization_cb' => 'sanitize_get_ga_appointment_new_client', // function should return a sanitized value
) );
// GF Entry ID
$cmb->add_field( array(
'name' => 'Payment Order Details',
'desc' => 'GravityForms Entry ID. This field is necessary to update appointment status after completed or failed payment. Modify this field only if you know what you\'re doing.',
'id' => $prefix . 'gf_entry_id',
'type' => 'select',
'show_option_none' => 'Select entry',
'options_cb' => 'get_gravity_form_entries_ids',
'sanitization_cb' => 'sanitize_get_gravity_form_entries_ids', // function should return a sanitized value
) );
// Appointment IP
$cmb->add_field( array(
'name' => 'User IP',
'desc' => 'Clients who want to book more than 1 time per time slot will be blocked if it\'s enabled service capacity',
'id' => $prefix . 'ip',
'type' => 'text',
'sanitization_cb' => 'sanitize_ga_appointment_ip', // function should return a sanitized value
) );
}
/**
* Show on time slot type
*/
public function show_on_time_slot_type( $field_args, $field ) {
// Appointment Type
$type = (string) get_post_meta($field->object_id, 'ga_appointment_type', true);
// Time Slots Mode
if( $type == 'time_slot' || $type == '' ) {
return '';
} else {
return 'cmb2-hidden';
}
}
/**
* Appointments New Submit Widget
*/
public function cmb2_ga_appointments_submitdiv_metabox() {
// Start with an underscore to hide fields from custom fields list
$prefix = 'ga_appointment_';
/**
* Initiate the metabox
*/
$cmb = new_cmb2_box( array(
'id' => 'ga_appointment_submitdiv',
'title' => __( 'Save Appointment', 'cmb2' ),
'object_types' => array( 'ga_appointments' ), // Post type
'context' => 'side',
'priority' => 'high',
'show_names' => true, // Show field names on the left
) );
// New Submit Widget
$cmb->add_field( array(
'name' => 'Save Appointment',
'id' => $prefix . 'submitdiv',
'type' => 'select',
'render_row_cb' => array($this, 'ga_appointment_submitdiv_render_row'),
) );
}
/**
* Appointments New Submit Widget
*/
public function rename_ga_appointments_post_statuses( $views ) {
if( isset( $views['publish'] ) )
$views['publish'] = str_replace( 'Published ', 'Confirmed ', $views['publish'] );
return $views;
}
/**
* Appointments Custom Columns
*/
public function manage_ga_appointments_columns( $columns ) {
$columns = array(
'cb' => '<input type="checkbox" />', // needed for checking/selecting multiple rows
//'title' => __( 'Title' ),
//'date' => __( 'Date' ),
'date_on' => __( 'Appointment Date' ),
'time' => __( 'Time' ),
'time_end' => __( 'Ends' ),
'duration' => __( 'Duration' ),
'provider' => __( 'Provider' ),
'service' => __( 'Service' ),
'client' => __( 'Client' ),
'payment' => __( 'Payment' ),
'status' => __( 'Status' ),
);
return $columns;
}
/**
* Appointments Custom Columns HTML
*/
public function manage_ga_appointments_posts_custom_column( $column, $post_id ) {
//global $post;
switch( $column ) {
case 'date_on' :
$post = get_post( $post_id );
// DATE
$app_date = (string) get_post_meta( $post_id, 'ga_appointment_date', true );
$date = ga_valid_date_format($app_date) ? new DateTime($app_date) : false;
$app_date_text = $date ? $date->format('F j, Y') : 'Date not defined';
// Date-Time Link
echo '<a href="' . get_edit_post_link( $post->ID, true ) . '" title="' . esc_attr( __( 'Edit this appointment' ) ) . '">'. $app_date_text .'</a>';
//***** ROW ACTIONS *******//
// First set up some variables
$actions = array();
$post_type_object = get_post_type_object( $post->post_type );
$can_edit_post = current_user_can( $post_type_object->cap->edit_post, $post->ID );
// Actions to edit
if ( $can_edit_post && 'trash' != $post->post_status ) {
$actions['edit'] = '<a href="' . get_edit_post_link( $post->ID, true ) . '" title="' . esc_attr( __( 'Edit this appointment' ) ) . '">' . __( 'Edit' ) . '</a>';
}
// Actions to delete/trash
if ( current_user_can( $post_type_object->cap->delete_post, $post->ID ) ) {
if ( 'trash' == $post->post_status ) {
$_wpnonce = wp_create_nonce( 'untrash-post_' . $post_id );
$actions['untrash'] = "<a title='" . esc_attr( __( 'Restore this item from the Trash' ) ) . "' href='" . admin_url( 'post.php?post=' . $post->ID . '&action=untrash&_wpnonce=' . $_wpnonce ) . "'>" . __( 'Restore' ) . "</a>";
} elseif ( EMPTY_TRASH_DAYS ) {
$actions['trash'] = "<a class='submitdelete' title='" . esc_attr( __( 'Move this item to the Trash' ) ) . "' href='" . get_delete_post_link( $post->ID ) . "'>" . __( 'Delete' ) . "</a>";
}
if ( 'trash' == $post->post_status || !EMPTY_TRASH_DAYS ) {
$actions['delete'] = "<a class='submitdelete' title='" . esc_attr( __( 'Delete this item permanently' ) ) . "' href='" . get_delete_post_link( $post->ID, '', true ) . "'>" . __( 'Delete Permanently' ) . "</a>";
}
}
//***** END - ROW ACTIONS *******//
echo '<div class="row-actions">';
foreach( $actions as $key => $action ) {
$sep = $key == 'edit' || $key == 'untrash' ? ' | ' : '';
echo '<span class="'.$key.'">'. $action . $sep .'<span>';
}
echo '</div>';
echo '<button type="button" class="toggle-row"><span class="screen-reader-text">Show more details</span></button>';
break;
case 'time' :
// Time
$app_time = (string) get_post_meta( $post_id, 'ga_appointment_time', true );
$time = ga_valid_time_format($app_time) ? new DateTime($app_time) : false;
$app_time_text = $time ? $time->format('g:i a') : 'Time not defined';
// Date Slots Mode
$app_type = get_post_meta($post_id, 'ga_appointment_type', true);
if( $app_type == 'date' ) {
echo 'Full day';
} else {
// Time Slots Mode
echo $app_time_text;
}
break;
case 'time_end' :
// Time
$app_time = (string) get_post_meta( $post_id, 'ga_appointment_time_end', true );
$time = ga_valid_time_format($app_time) ? new DateTime($app_time) : false;
$app_time_text = $time ? $time->format('g:i a') : 'Time not defined';
// Date Slots Mode
$app_type = get_post_meta($post_id, 'ga_appointment_type', true);
if( $app_type == 'date' ) {
echo 'Full day';
} else {
// Time Slots Mode
if( $time && $time->format('H:i') == '23:59' || $app_time == '24:00' ) {
echo '12:00 am';
} else {
echo $app_time_text;
}
}
break;
case 'duration' :
// Date Slots Mode
$app_type = get_post_meta($post_id, 'ga_appointment_type', true);
if( $app_type == 'date' ) {
echo 'Full day';
} else {
// Time Slots Mode
$duration = (int) get_post_meta( $post_id, 'ga_appointment_duration', true );
echo convertToHoursMins($duration);
}
break;
case 'client' :
$client = (string) get_post_meta( $post_id, 'ga_appointment_client', true );
if( $client == 'new_client' ) {
$new_client = get_post_meta( $post_id, 'ga_appointment_new_client', true );
$client = isset($new_client['name']) ? esc_html($new_client['name']) : '';
if( isset($new_client['email']) && !empty($new_client['email']) ) {
$client .= '<div class="ga_client_email">(' . esc_html($new_client['email']) . ')</div>';
}
if( isset($new_client['phone']) && !empty($new_client['phone']) ) {
$client .= '<div class="ga_client_phone">(' . esc_html($new_client['phone']) . ')</div>';
}
echo $client;
} else {
$user_info = get_userdata($client);
if( $user_info ) {
echo '<a href="'.get_edit_user_link( $user_info->ID ).'" target="_blank">'.$user_info->user_login.'</a>';
} else {
echo 'Not defined';
}
}
break;
case 'service' :
$service_id = get_post_meta( $post_id, 'ga_appointment_service', true );
if( 'ga_services' == get_post_type($service_id) ) {
echo esc_html( get_the_title( $service_id ) );
} else {
echo 'Not defined';
}
break;
case 'provider' :
$provider_id = (int) get_post_meta( $post_id, 'ga_appointment_provider', true );
if( 'ga_providers' == get_post_type($provider_id) ) {
echo esc_html( get_the_title( $provider_id ) );
} else {
echo 'No provider';
}
break;
case 'payment' :
$app_entry_id = (int) get_post_meta( $post_id, 'ga_appointment_gf_entry_id', true );
if( RGFormsModel::get_lead($app_entry_id) ) {
$entry_obj = RGFormsModel::get_lead($app_entry_id);
$form_id = $entry_obj['form_id'];
$entry_id = $entry_obj['id'];
$entry_url = get_bloginfo( 'wpurl' ) . '/wp-admin/admin.php?page=gf_entries&view=entry&id=' . $form_id . '&lid=' . $entry_id;
$text = str_replace( '{entry_url}', urlencode( $entry_url ), $entry_url );
echo '<a href="'.esc_url($text).'" target="_blank">View Order Details</a>';
} else {
echo '<span>Not Available</span>';
}
break;
case 'status' :
$post_obj = get_post($post_id);
if( isset($post_obj->post_status) ) {
if( array_key_exists($post_obj->post_status, ga_appointment_statuses()) ) {
$statuses = ga_appointment_statuses();
echo $statuses[$post_obj->post_status];
} elseif( $post_obj->post_status == 'trash' ) {
echo 'In trash';
} else {
echo 'Unknown';
}
}
break;
/* Just break out of the switch statement for everything else. */
default :
break;
}
}
/**
* Appointments Submit Widget HTML
*/
public function ga_appointment_submitdiv_render_row() {
global $post;
$post_status = isset($post->post_status) ? $post->post_status : '';
$appointment_statuses = ga_appointment_statuses();
echo '<div class="cmb-submitdiv">';
echo '<select id="post-status" class="" name="post_status">';
foreach($appointment_statuses as $key => $status) {
$selected = $post_status == $key ? ' selected' : '';
echo '<option value="'.$key.'"'.$selected.'>'.$status.'</option>';
}
echo '</select>';
echo '<input type="submit" class="button button-ga right" value="Update">'; // button-primary
echo '<div class="clear"></div>';
echo '<textarea name="ga_cancel_message" class="ga_cancel_message cmb2-hidden" placeholder="Optional message"></textarea>';
echo '</div>';
}
} // end class