Skip to content

Instantly share code, notes, and snippets.

@panoslyrakis
Last active February 15, 2017 18:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save panoslyrakis/5b7f6d488f4f11701e01ac3ebf1226b9 to your computer and use it in GitHub Desktop.
Save panoslyrakis/5b7f6d488f4f11701e01ac3ebf1226b9 to your computer and use it in GitHub Desktop.
Moves latest completed and removed appointments in A+ to a history table in db so the appointments table is lighter
<?php
/*
Plugin Name: Appointments+ History
Description: Moves latest completed and removed appointments to a history table in db so the appointments table is lighter
Plugin URI: https://gist.github.com/panoslyrakis/5b7f6d488f4f11701e01ac3ebf1226b9
Version: 1.0.0
Author: Panos Lyrakis ( WPMUDEV )
Author URI: https://premium.wpmudev.org/profile/panoskatws
*/
//Suggested to use as a mu-plugin, but should work as a normal plugin too
if( ! class_exists('WPMUDEV_Apps_History') ){
class WPMUDEV_Apps_History{
var $history_table = '';
var $apps_limit = ''; //To be moved
var $page_limit = ''; //To be shown
private function __construct(){
global $wpdb;
$this->history_table = apply_filters( 'wpudev_apps_history/history_table', $wpdb->prefix . 'apps_history' );
$this->apps_limit = apply_filters( 'wpudev_apps_history/apps_limit', 20 );
$this->page_limit = apply_filters( 'wpudev_apps_history/page_limit', 20 );
add_action( 'admin_menu', array( $this, 'admin_menu' ) );
add_action( 'wp_ajax_wpmudev_apps_hist_create_table', array( $this, 'create_table' ) );
//The Cron actions
add_filter( 'cron_schedules', array( $this, 'cron_intervals' ) );
add_action('init', array( $this, 'shcedule_history' ) );
add_action('wpmudev_apps_history', array( $this, 'run_history' )) ;
}
static public function get_instance(){
static $Inst = null;
if( null == $Inst ){
$Inst = new self();
}
}
public function cron_intervals( $schedules ){
// Add a 'weekly' interval
$schedules['weekly'] = array(
'interval' => 604800,
'display' => __('Once a week')
);
return $schedules;
}
public function shcedule_history(){
$interval = apply_filters( 'wpudev_apps_history/shcedule_interval', 'weekly' );
if( !wp_next_scheduled( 'wpmudev_apps_history' ) ) {
wp_schedule_event( time(), $interval, 'wpmudev_apps_history' );
}
}
public function run_history(){
if( ! class_exists( 'Appointments' ) || ! $this->_history_table_exists() ) return;
// Select and move completed and removed to history table
global $wpdb;
$apps_table = appointments_get_table( 'appointments' );
$hist_table = $this->history_table;
$to_move_apps = $wpdb->get_results( "SELECT * FROM {$apps_table} WHERE status='completed' OR status='removed' ORDER BY ID ASC LIMIT {$this->apps_limit}" );
foreach( $to_move_apps as $app ){
$insert = array(
'prev_ID' => $app->ID,
'created' => $app->created,
'user' => $app->user,
'name' => $app->name,
'email' => $app->email,
'phone' => $app->phone,
'address' => $app->address,
'city' => $app->city,
'location' => $app->location,
'service' => $app->service,
'worker' => $app->worker,
'price' => $app->price,
'status' => $app->status,
'start' => $app->start,
'end' => $app->end,
'sent' => $app->sent,
'sent_worker' => $app->sent_worker,
'note' => $app->note,
'gcal_ID' => $app->gcal_ID,
'gcal_updated' => $app->gcal_updated,
);
$insert_wildcards = array(
'%d',
'%s',
'%d',
'%s',
'%s',
'%s',
'%s',
'%s',
'%d',
'%d',
'%d',
'%d',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s',
'%s'
);
$result = $wpdb->insert( $hist_table, $insert, $insert_wildcards );
if ( ! $result )
return false;
$app_id = $wpdb->insert_id;
//Now we can DELETE $app_id from apps table
$wpdb->query( $wpdb->prepare( "DELETE FROM {$apps_table} WHERE ID=%d", $app_id ) );
}
}
public function admin_menu(){
add_menu_page( 'Appointments History', 'Appointments History', 'manage_options', 'appointments-history', array( $this, 'admin_page' ), 'dashicons-calendar-alt', 100 );
}
public function admin_page(){
echo '<h1>Appointments History</h1>';
if( ! $this->_history_table_exists() ){
echo $this->_history_table_warning();
return;
}
$args = array();
$allowed_sorting_vars = array( 'asc', 'desc' );
$allowed_orderby_vars = array( 'ID', 'date', 'start' );
$allowed_status_vars = array( 'removed', 'completed' );
$sorting = isset( $_GET['order'] ) && in_array( $_GET['order'], $allowed_sorting_vars ) ? $_GET['order'] : false;
$orderby = isset( $_GET['orderby'] ) && in_array( $_GET['orderby'], $allowed_orderby_vars ) ? $_GET['orderby'] : false;
$status = isset( $_GET['status'] ) && in_array( $_GET['status'], $allowed_status_vars ) ? $_GET['status'] : false;
$paged = isset( $_GET['paged'] ) ? (int)$_GET['paged'] : 1;
if( $orderby ) $args['orderby'] = $orderby;
if( $sorting ) $args['order'] = $sorting;
if( $status ) $args['status'] = $status;
if( $paged ) $args['page'] = $paged;
$args['per_page'] = $this->page_limit;
$apps = $this->fetch_appointments( $args );
$args['count'] = true;
$total = $this->fetch_appointments( $args );
$columns = array(
//'cb' => '<input type="checkbox" />',
'app_ID' => __('ID','appointments'),
'date' => __('Appointment Date','appointments'),
'user' => __('Client','appointments'),
'service' => __('Service','appointments'),
'worker' => __('Provider','appointments'),
'created' => __('Created on','appointments'),
'status' => __('Status','appointments')
);
$default_columns = $columns;
$columns = apply_filters( 'wpudev_apps_history/list_columns', $columns );
$sortable_columns = array( 'created' => 'ID', 'date' => 'start' );
$sortables = array();
foreach ( $sortable_columns as $col => $field ) {
$sortables[ $col ] = array( 'field' => $field );
if ( isset( $_GET['orderby'] ) && $_GET['orderby'] === $field ) {
$sortables[ $col ]['order'] = 'ASC' === $sorting ? 'ASC' : 'DESC';
$sortables[ $col ]['sorting'] = true;
}
else {
$sortables[ $col ]['order'] = 'ASC';
$sortables[ $col ]['sorting'] = false;
}
}
$pagination_args = array(
'total' => $total,
'total_pages' => ceil( $total / $this->page_limit ),
'current' => $paged
);
echo '<div style="width: 100; text-align: right; padding-right: 20px;">';
self::pagination( $pagination_args, 'top' );
echo '</div>';
?>
<form method="post" >
<h2 class="screen-reader-text"><?php _e( 'Appointments list', 'appointments' ); ?>></h2>
<table class="wp-list-table widefat fixed stripped appointments">
<thead>
<tr>
<?php foreach ( $columns as $key => $title ): ?>
<?php if ( $key === 'cb' ): ?>
<td id="cb" class="manage-column column-cb check-column">
<label class="screen-reader-text" for="cb-select-all-1"><?php _e( 'Select All', 'appointments' ); ?></label>
<input id="cb-select-all-1" type="checkbox">
</td>
<?php else: ?>
<?php
$class = '';
$is_sortable = false;
if ( array_key_exists( $key , $sortables ) ) {
$is_sortable = true;
$order = strtolower( $sortables[ $key ]['order'] );
if ( $sortables[ $key ]['sorting'] ) {
$class .= 'sorted ' . $order;
}
else {
$class .= 'sortable ' . $order;
}
$sort_url = add_query_arg( array(
'orderby' => $sortables[ $key ]['field'],
'order' => $order == 'desc' ? 'asc' : 'desc'
) );
}
?>
<th scope="col" class="manage-column column-<?php echo esc_attr($key); ?> app-column-<?php echo esc_attr($key); ?> <?php echo $class; ?>" id="<?php echo esc_attr($key); ?>">
<?php if ( $is_sortable ): ?>
<a href="<?php echo esc_url( $sort_url ); ?>">
<span><?php echo $title; ?></span>
<span class="sorting-indicator"></span>
</a>
<?php else: ?>
<?php echo $title; ?>
<?php endif; ?>
</th>
<?php endif; ?>
<?php endforeach; ?>
</tr>
</thead>
<tbody id="the-list">
<?php if ( $apps ): ?>
<?php $i = 0; ?>
<?php /** @var Appointments_Appointment $app */ ?>
<?php foreach($apps as $key => $app): ?>
<?php
$client_info = get_userdata( $app->user );
$service_info = appointments_get_service( $app->service );
?>
<?php $i++; ?>
<tr id="app-<?php echo $app->ID; ?>" class="app-tr <?php echo $i % 2 ? 'alternate' : ''; ?>">
<?php foreach ( $columns as $key => $value ): ?>
<?php if ( $key === 'cb' ): ?>
<th id="cb" class="column-delete check-column app-check-column" scope="row">
<label class="screen-reader-text" for="cb-select-<?php echo $app->ID; ?>"></label>
<input type="checkbox" name="app[]" id="cb-select-<?php echo $app->ID; ?>" value="<?php echo esc_attr($app->ID);?>" />
</th>
<?php elseif ( array_key_exists( $key, $default_columns ) ): ?>
<td class="column-<?php echo $key; ?>">
<?php if ( 'app_ID' === $key ): ?>
<span class="span_app_ID"><?php echo $app->ID;?></span>
<?php elseif ('user' === $key ): ?>
<?php //echo stripslashes( $app->get_client_name() ); ?>
<?php echo is_object( $client_info ) && isset( $client_info->display_name ) ? $client_info->display_name : ''; ?>
<?php elseif ('date' === $key ): ?>
<?php echo mysql2date( 'Y/m/d', $app->start );//echo $app->get_formatted_start_date(); ?>
<div class="row-actions">
<a href="javascript:void(0)" class="app-inline-edit" data-app-id="<?php echo $app->ID; ?>">
<?php //echo 'reserved' == $app->status ? __('See Details (Cannot be edited)', 'appointments') : __('See Details and Edit', 'appointments') ?>
</a>
<img class="waiting" style="display:none;" src="<?php echo admin_url('images/spinner.gif')?>" alt="">
</div>
<?php elseif ('service' === $key ): ?>
<?php echo $service_info->name; //echo $app->get_service_name(); ?>
<?php elseif ('worker' === $key ): ?>
<?php echo appointments_get_worker_name( $app->worker ); ?>
<?php elseif ('status' === $key ): ?>
<?php echo appointments_get_status_name( $app->status ); ?>
<?php elseif ('created' === $key ): ?>
<?php echo mysql2date( 'Y/m/d', $app->created ); ?><br/>
<?php echo mysql2date( 'H:i:s', $app->created ); ?>
<?php endif; ?>
</td>
<?php endif; ?>
<?php do_action( 'appointments_my_appointments_list_row', $app ); ?>
<?php endforeach; ?>
</tr>
<?php endforeach; ?>
<?php else: ?>
<tr class="no-items">
<td class="colspanchange" colspan="<?php echo count( $columns ); ?>">
<?php _e('No appointments have been found.','appointments'); ?>
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</form>
<?php
echo '<div style="width: 100; text-align: right; padding-right: 20px;">';
self::pagination( $pagination_args, 'bottom' );
echo '</div>';
}
public function fetch_appointments( $args = array() ){
global $wpdb;
if( ! class_exists( 'Appointments' ) ) return;
$table = $this->history_table;
$defaults = array(
'worker' => false,
'service' => false,
'location' => false,
'user' => false,
'date_query' => array(),
'app_id' => array(),
'status' => false,
'per_page' => -1,
'page' => 1,
's' => false,
'orderby' => 'ID',
'order' => 'ASC',
'count' => false // Will return only the number of rows found
);
$args = wp_parse_args( $args, $defaults );
$where = array();
if( $args['count'] ){
if( $args[ 'status' ] ){
$where[] = $wpdb->prepare( "status = '%s'", $args['status'] );
}
if ( ! empty( $where ) ) {
$where = "WHERE " . implode( " AND ", $where );
}
else {
$where = '';
}
return $wpdb->get_var( "SELECT COUNT(*) FROM {$table} {$where}" );
}
if ( false !== $args['worker'] && $worker = appointments_get_worker( $args['worker'] ) ) {
$where[] = $wpdb->prepare( "worker = %d", $worker->ID );
}
if ( false !== $args['location'] ) {
$where[] = $wpdb->prepare( "location = %d", $args['location'] );
}
if ( false !== $args['user'] ) {
$where[] = $wpdb->prepare( "user = %d", $args['user'] );
}
if ( false !== $args['service'] && ! is_array( $args['service'] ) ) {
// Only one service, let's make it an array
$args['service'] = array( $args['service'] );
}
if ( ! empty( $args['service'] ) && is_array( $args['service'] ) ) {
$where_services = array();
foreach ( $args['service'] as $service_id ) {
$service = appointments_get_service( $service_id );
if ( ! $service ) {
continue;
}
$where_services[] = absint( $service_id );
}
if ( ! empty( $where_services ) ) {
$where[] = 'service IN (' . implode( ',', $where_services ) . ')';
}
}
if ( ! empty( $args['date_query'] ) && is_array( $args['date_query'] ) ) {
$date_query_where = array();
$date_queries = $args['date_query'];
// Set the date queries conditions
$allowed_conditions = array( 'AND', 'OR' );
if ( ! isset( $args['date_query']['condition'] ) ) {
$condition = 'AND';
}
else {
$condition = strtoupper( $args['date_query']['condition'] );
}
if ( ! in_array( $condition, $allowed_conditions ) ) {
$condition = 'AND';
}
// Parse every Date query
foreach ( $date_queries as $key => $date_query ) {
if ( 'condition' === $key ) {
continue;
}
$date_query = _appointments_parse_date_query( $date_query );
if ( $date_query ) {
$date_query_where[] = $wpdb->prepare( $date_query['field'] . $date_query['compare'] . "%s", $date_query['value'] );
}
}
if ( $date_query_where ) {
$where[] = '(' . implode( " " . $condition . " ", $date_query_where ) . ')';
}
}
if ( ! empty( $args['app_id'] ) && is_array( $args['app_id'] ) ) {
$args['app_id'] = array_map( 'absint', $args['app_id'] );
$where[] = 'ID IN ( ' . implode( ',', $args['app_id'] ) . ' )';
}
if ( $args['status'] ) {
$statuses = array();
if ( is_array( $args['status'] ) ) {
foreach ( $args['status'] as $status ) {
if ( array_key_exists( $status, appointments_get_statuses() ) ) {
$statuses[] = $status;
}
}
}
elseif ( is_string( $args['status'] ) && array_key_exists( $args['status'], appointments_get_statuses() ) ) {
$statuses = array( $args['status'] );
}
$where[] = 'status IN ("' . implode( '","', $statuses ) . '")';
}
if ( false !== $args['s'] ) {
$args['s'] = trim( $args['s'] );
if ( $args['s'] ) {
// Search by user name
$where[] = $wpdb->prepare(
"( name LIKE %s OR email LIKE %s OR user IN ( SELECT ID FROM $wpdb->users WHERE user_login LIKE %s OR user_nicename LIKE %s OR display_name LIKE %s OR user_email LIKE %s ) )",
'%' . $args['s'] . '%',
'%' . $args['s'] . '%',
'%' . $args['s'] . '%',
'%' . $args['s'] . '%',
'%' . $args['s'] . '%',
'%' . $args['s'] . '%'
);
}
}
if ( ! empty( $where ) ) {
$where = "WHERE " . implode( " AND ", $where );
}
else {
$where = '';
}
$allowed_orderby = array( 'ID', 'created', 'user', 'name', 'email', 'location',
'service', 'worker', 'price', 'status', 'start', 'end' );
$allowed_order = array( 'DESC', 'ASC', '' );
$order_query = '';
$args['order'] = strtoupper( $args['order'] );
if ( in_array( $args['orderby'], $allowed_orderby ) && in_array( $args['order'], $allowed_order ) ) {
$orderby = $args['orderby'];
$order = $args['order'];
$order_query = "ORDER BY $orderby $order";
}
$limit = '';
if ( $args['per_page'] > 0 ) {
$limit = $wpdb->prepare( "LIMIT %d, %d", intval( ( $args['page'] - 1 ) * $args['per_page'] ), intval( $args['per_page'] ) );
}
$found_rows = '';
if ( $args['count'] ) {
$found_rows = 'SQL_CALC_FOUND_ROWS';
}
$query = "SELECT $found_rows * FROM $table $where $order_query $limit";
$results = $wpdb->get_results( $query );
if ( $args['count'] ) {
$results = $wpdb->get_var( "SELECT FOUND_ROWS()" );
}
return $results;
}
public static function pagination( $args, $which = 'top' ) {
$total_items = $args['total'];
$total_pages = $args['total_pages'];
if ( $total_pages > 1 ) {
echo '<h2 class="screen-reader-text">' . __( 'Appointments list navigation', 'appointments' ) . '</h2>';
}
$output = '<span class="displaying-num">' . sprintf( _n( '%s item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
$current = $args['current'];
$current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
$current_url = remove_query_arg( array( 'hotkeys_highlight_last', 'hotkeys_highlight_first' ), $current_url );
$page_links = array();
$total_pages_before = '<span class="paging-input">';
$total_pages_after = '</span>';
$disable_first = $disable_last = $disable_prev = $disable_next = false;
if ( $current == 1 ) {
$disable_first = true;
$disable_prev = true;
}
if ( $current == 2 ) {
$disable_first = true;
}
if ( $current == $total_pages ) {
$disable_last = true;
$disable_next = true;
}
if ( $current == $total_pages - 1 ) {
$disable_last = true;
}
if ( $disable_first ) {
$page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&laquo;</span>';
} else {
$page_links[] = sprintf( "<a class='first-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
esc_url( remove_query_arg( 'paged', $current_url ) ),
__( 'First page' ),
'&laquo;'
);
}
if ( $disable_prev ) {
$page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&lsaquo;</span>';
} else {
$page_links[] = sprintf( "<a class='prev-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ),
__( 'Previous page' ),
'&lsaquo;'
);
}
if ( 'bottom' === $which ) {
$html_current_page = $current;
$total_pages_before = '<span class="screen-reader-text">' . __( 'Current Page' ) . '</span><span id="table-paging" class="paging-input">';
} else {
$html_current_page = sprintf( "%s<input class='current-page' id='current-page-selector' type='text' name='paged' value='%s' size='%d' aria-describedby='table-paging' />",
'<label for="current-page-selector" class="screen-reader-text">' . __( 'Current Page' ) . '</label>',
$current,
strlen( $total_pages )
);
}
$html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
$page_links[] = $total_pages_before . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . $total_pages_after;
if ( $disable_next ) {
$page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&rsaquo;</span>';
} else {
$page_links[] = sprintf( "<a class='next-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ),
__( 'Next page' ),
'&rsaquo;'
);
}
if ( $disable_last ) {
$page_links[] = '<span class="tablenav-pages-navspan" aria-hidden="true">&raquo;</span>';
} else {
$page_links[] = sprintf( "<a class='last-page' href='%s'><span class='screen-reader-text'>%s</span><span aria-hidden='true'>%s</span></a>",
esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
__( 'Last page' ),
'&raquo;'
);
}
$pagination_links_class = 'pagination-links';
$output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';
if ( $total_pages ) {
$page_class = $total_pages < 2 ? ' one-page' : '';
} else {
$page_class = ' no-pages';
}
$output = "<div class='tablenav-pages{$page_class}'>$output</div>";
$output_form = '<form method="get" action="" class="search-form">';
$output_form .= '<input type="hidden" value="'. $_GET['page'] .'" name="page">';
$output_form .= '<input type="hidden" value="'. $_GET['orderby'] .'" name="orderby">';
$output_form .= '<input type="hidden" value="'. $_GET['order'] .'" name="order">';
$output = $output_form . $output . '</form>';
echo $output;
}
private function _history_table_exists(){
global $wpdb;
return $wpdb->get_var("SHOW TABLES LIKE '$this->history_table'") == $this->history_table;
}
private function _history_table_warning(){
ob_start();
?>
<div class="wpmudev_apps_history_warning">
<h3 class="header">It seems that the history table doesn't exists</h3>
<div class="content">
<p>
Please click on the "Create History Table" button so that the table gets crated in your db.
</p>
<p>
<a class="button button-primary" id="wpmudev_apps_history_table_create_button">Create History Table</a>
</p>
</div>
<script type="text/javascript">
(function($){
$( document ).ready(function(){
$( '#wpmudev_apps_history_table_create_button' ).on( 'click', function(e){
e.preventDefault();
var data = {
action: 'wpmudev_apps_hist_create_table',
security: '<?php echo wp_create_nonce( "wpmudev_apps_hist_create_table" ); ?>',
};
$.ajax({
url:ajaxurl,
type:"POST",
data: data,
dataType: "json",
success:function(response){
if( response.status == "_SUCCESS_" ){
alert( 'Table Created!' );
location.reload()
}
else{
alert( 'Something went wrong :(' );
console.log( response.message );
}
},
error: function( xhr, status, error ){
console.log(xhr.responseText);
}
});
});
});
})(jQuery);
</script>
<?php
return ob_get_clean();
}
public function create_table(){
check_ajax_referer( 'wpmudev_apps_hist_create_table', 'security' );
global $wpdb;
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
$charset_collate = '';
if (!empty($wpdb -> charset)) $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
if (!empty($wpdb -> collate)) $charset_collate .= " COLLATE $wpdb->collate";
$query = "CREATE TABLE IF NOT EXISTS `{$this->history_table}` (
`ID` bigint(20) unsigned NOT NULL auto_increment,
`prev_ID` bigint(20) NOT NULL default '0',
`created` datetime,
`user` bigint(20) NOT NULL default '0',
`name` varchar(250) default NULL,
`email` varchar(250) default NULL,
`phone` varchar(250) default NULL,
`address` varchar(250) default NULL,
`city` varchar(250) default NULL,
`location` bigint(20) NOT NULL default '0',
`service` bigint(20) NOT NULL default '0',
`worker` bigint(20) NOT NULL default '0',
`price` bigint(20) default NULL,
`status` varchar(35) default NULL,
`start` datetime default NULL,
`end` datetime default NULL,
`sent` text,
`sent_worker` text,
`note` text,
`gcal_ID` varchar(250) default NULL,
`gcal_updated` datetime,
PRIMARY KEY (`ID`)
)
$charset_collate;";
$return = array();
if( dbDelta( sprintf( $query, $this->history_table ) ) ){
$return['status'] = '_SUCCESS_';
}
else{
$return['status'] = '_FAIL_';
$return['message'] = $wpdb->show_errors();
}
wp_send_json($return);
}
}
add_action( 'plugins_loaded', function(){
WPMUDEV_Apps_History::get_instance();
} );
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment