<?php
/**
* Event Manager. Listing and organization of events.
*
* @category Events
* @package My Calendar
* @author Joe Dolson
* @license GPLv2 or later
* @link https://www.joedolson.com/my-calendar/
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Handle a bulk action.
*
* @param string $action type of action.
* @param array $events Optional. Array of event IDs to act on.
*
* @return string bulk action details.
*/
function mc_bulk_action( $action, $events = array() ) {
global $wpdb;
$events = ( empty( $events ) ) ? $_POST['mass_edit'] : $events;
$i = 0;
$total = 0;
$ids = array();
$prepare = array();
$sql = '';
foreach ( $events as $value ) {
$value = (int) $value;
$total = count( $events );
if ( 'delete' === $action ) {
$result = $wpdb->get_results( $wpdb->prepare( 'SELECT event_author FROM ' . my_calendar_table() . ' WHERE event_id = %d', $value ), ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
if ( mc_can_edit_event( $value ) ) {
$occurrences = 'DELETE FROM ' . my_calendar_event_table() . ' WHERE occur_event_id = %d';
$wpdb->query( $wpdb->prepare( $occurrences, $value ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$ids[] = (int) $value;
$prepare[] = '%d';
++$i;
}
}
if ( 'delete' !== $action && current_user_can( 'mc_approve_events' ) ) {
$ids[] = (int) $value;
$prepare[] = '%d';
++$i;
}
}
$prepared = implode( ',', $prepare );
switch ( $action ) {
case 'delete':
$sql = 'DELETE FROM ' . my_calendar_table() . ' WHERE event_id IN (' . $prepared . ')';
break;
case 'unarchive':
$sql = 'UPDATE ' . my_calendar_table() . ' SET event_status = 1 WHERE event_id IN (' . $prepared . ')';
break;
case 'archive':
$sql = 'UPDATE ' . my_calendar_table() . ' SET event_status = 0 WHERE event_id IN (' . $prepared . ')';
break;
case 'approve': // Synonymous with publish.
$sql = 'UPDATE ' . my_calendar_table() . ' SET event_approved = 1 WHERE event_id IN (' . $prepared . ')';
break;
case 'draft':
$sql = 'UPDATE ' . my_calendar_table() . ' SET event_approved = 0 WHERE event_id IN (' . $prepared . ')';
break;
case 'trash':
$sql = 'UPDATE ' . my_calendar_table() . ' SET event_approved = 2 WHERE event_id IN (' . $prepared . ')';
break;
case 'unspam':
$sql = 'UPDATE ' . my_calendar_table() . ' SET event_flagged = 0 WHERE event_id IN (' . $prepared . ')';
// send notifications.
foreach ( $ids as $id ) {
$post_ID = mc_get_event_post( $id );
$submitter = get_post_meta( $post_ID, '_submitter_details', true );
if ( is_array( $submitter ) && ! empty( $submitter ) ) {
$name = $submitter['first_name'] . ' ' . $submitter['last_name'];
$email = $submitter['email'];
/**
* Run action when a publically submitted event is un-spammed.
*
* @hook mcs_complete_submission
*
* @param {string} $name Submitter's name.
* @param {string} $email Submitter's email.
* @param {int} $id Event ID.
* @param {string} $action Action performed ('edit').
*/
do_action( 'mcs_complete_submission', $name, $email, $id, 'edit' );
}
}
break;
}
/**
* Add custom bulk actions.
*
* @hook mc_bulk_actions
*
* @param {string} $action Declared action.
* @param {array} $ids Array of event IDs being requested.
*/
do_action( 'mc_bulk_actions', $action, $ids );
$result = ( '' !== $sql ) ? $wpdb->query( $wpdb->prepare( $sql, $ids ) ) : false; // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
mc_update_count_cache();
$results = array(
'count' => $i,
'total' => $total,
'ids' => $ids,
'result' => $result,
);
return mc_bulk_message( $results, $action );
}
/**
* Generate a notification for bulk actions.
*
* @param array $results of bulk action.
* @param string $action Type of action.
*
* @return string message
*/
function mc_bulk_message( $results, $action ) {
$count = $results['count'];
$total = $results['total'];
$ids = $results['ids'];
$result = $results['result'];
$error = '';
$success = '';
switch ( $action ) {
case 'delete':
// Translators: Number of events deleted, number selected.
$success = __( '%1$d events deleted successfully out of %2$d selected.', 'my-calendar' );
$error = __( 'Your events have not been deleted. Please investigate.', 'my-calendar' );
break;
case 'trash':
// Translators: Number of events trashed, number of events selected.
$success = __( '%1$d events trashed successfully out of %2$d selected.', 'my-calendar' );
$error = __( 'Your events have not been trashed. Please investigate.', 'my-calendar' );
break;
case 'approve':
// Translators: Number of events published, number of events selected.
$success = __( '%1$d events published out of %2$d selected.', 'my-calendar' );
$error = __( 'Your events have not been published. Were these events already published? Please investigate.', 'my-calendar' );
break;
case 'draft':
// Translators: Number of events converted to draft, number of events selected.
$success = __( '%1$d events switched to drafts out of %2$d selected.', 'my-calendar' );
$error = __( 'Your events have not been switched to drafts. Were these events already drafts? Please investigate.', 'my-calendar' );
break;
case 'archive':
// Translators: Number of events archived, number of events selected.
$success = __( '%1$d events archived successfully out of %2$d selected.', 'my-calendar' );
$error = __( 'Your events have not been archived. Please investigate.', 'my-calendar' );
break;
case 'unarchive':
// Translators: Number of events removed from archive, number of events selected.
$success = __( '%1$d events removed from archive successfully out of %2$d selected.', 'my-calendar' );
$error = __( 'Your events have not been removed from the archive. Were these events already archived? Please investigate.', 'my-calendar' );
break;
case 'unspam':
// Translators: Number of events removed from archive, number of events selected.
$success = __( '%1$d events successfully unmarked as spam out of %2$d selected.', 'my-calendar' );
$error = __( 'Your events were not removed from spam. Please investigate.', 'my-calendar' );
break;
}
if ( 0 !== $result && false !== $result ) {
$diff = 0;
if ( $result < $count ) {
$diff = ( $count - $result );
// Translators: Sprintf as a 3rd argument if this string is appended to prior error. # of unchanged events.
$success .= ' ' . _n( '%3$d event was not changed in that update.', '%3$d events were not changed in that update.', $diff, 'my-calendar' );
}
/**
* Dynamic action executed when a group of events is bulk modified.
*
* @hook mc_mass_{$action}_events
*
* @param {array} $ids Array of event IDs being handled.
*/
do_action( 'mc_mass_' . $action . '_events', $ids );
$message = mc_show_notice( sprintf( $success, $result, $total, $diff ), true, false, 'success' );
} else {
$message = mc_show_error( $error, false );
}
return $message;
}
/**
* Generate form for listing events that are editable by current user
*/
function my_calendar_manage() {
my_calendar_check();
global $wpdb;
if ( isset( $_GET['mode'] ) && 'delete' === $_GET['mode'] ) {
$event_id = ( isset( $_GET['event_id'] ) ) ? absint( $_GET['event_id'] ) : false;
$result = $wpdb->get_results( $wpdb->prepare( 'SELECT event_title, event_author FROM ' . my_calendar_table() . ' WHERE event_id=%d', $event_id ), ARRAY_A ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
if ( mc_can_edit_event( $event_id ) ) {
if ( isset( $_GET['date'] ) ) {
$event_instance = (int) $_GET['date'];
$inst = $wpdb->get_var( $wpdb->prepare( 'SELECT occur_begin FROM ' . my_calendar_event_table() . ' WHERE occur_id=%d', $event_instance ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$instance_date = '(' . mc_date( 'Y-m-d', mc_strtotime( $inst ), false ) . ')';
} else {
$instance_date = '';
} ?>
<div class="error">
<form action="<?php echo esc_url( admin_url( 'admin.php?page=my-calendar-manage' ) ); ?>" method="post">
<p><strong><?php esc_html_e( 'Delete Event', 'my-calendar' ); ?>:</strong> <?php esc_html_e( 'Are you sure you want to delete this event?', 'my-calendar' ); ?>
<input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce( 'my-calendar-nonce' ); ?>"/>
<input type="hidden" value="delete" name="event_action" />
<?php
if ( ! empty( $_GET['date'] ) ) {
?>
<input type="hidden" name="event_instance" value="<?php echo (int) $_GET['date']; ?>"/>
<?php
}
if ( isset( $_GET['ref'] ) ) {
?>
<input type="hidden" name="ref" value="<?php echo esc_url( $_GET['ref'] ); ?>" />
<?php
}
?>
<input type="hidden" name="event_id" value="<?php echo esc_attr( $event_id ); ?>"/>
<?php
$event_info = ' "' . stripslashes( $result[0]['event_title'] ) . "" $instance_date";
// Translators: Title & date of event to delete.
$delete_text = sprintf( __( 'Delete %s', 'my-calendar' ), $event_info );
?>
<input type="submit" name="submit" class="button-secondary delete" value="<?php echo esc_attr( $delete_text ); ?>"/>
</form>
</div>
<?php
} else {
mc_show_error( __( 'You do not have permission to delete that event.', 'my-calendar' ) );
}
}
// Approve and show an Event ...originally by Roland.
if ( isset( $_GET['mode'] ) && 'publish' === $_GET['mode'] ) {
$mcnonce = wp_verify_nonce( $_GET['_mcnonce'], 'mcnonce' );
if ( $mcnonce ) {
if ( current_user_can( 'mc_approve_events' ) ) {
$event_id = absint( $_GET['event_id'] );
// Publish the event.
$wpdb->get_results( $wpdb->prepare( 'UPDATE ' . my_calendar_table() . ' SET event_approved = 1 WHERE event_id=%d', $event_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
// Remove spam flag if present.
$wpdb->get_results( $wpdb->prepare( 'UPDATE ' . my_calendar_table() . ' SET event_flagged = 0 WHERE event_id=%d', $event_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$event = mc_get_event_core( $event_id );
$private = mc_private_event( $event, false );
$status = ( $private ) ? 'private' : 'publish';
wp_update_post(
array(
'ID' => mc_get_event_post( $event_id ),
'post_status' => $status,
)
);
mc_update_count_cache();
} else {
mc_show_error( __( 'You do not have permission to approve that event.', 'my-calendar' ) );
}
} else {
mc_show_error( __( 'Invalid security check; please try again!', 'my-calendar' ) );
}
}
// Reject and hide an Event ...by Roland.
if ( isset( $_GET['mode'] ) && 'reject' === $_GET['mode'] ) {
$mcnonce = wp_verify_nonce( $_GET['_mcnonce'], 'mcnonce' );
if ( $mcnonce ) {
if ( current_user_can( 'mc_approve_events' ) ) {
$event_id = absint( $_GET['event_id'] );
$wpdb->get_results( $wpdb->prepare( 'UPDATE ' . my_calendar_table() . ' SET event_approved = 2 WHERE event_id=%d', $event_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
wp_update_post(
array(
'ID' => mc_get_event_post( $event_id ),
'post_status' => 'trash',
)
);
mc_update_count_cache();
} else {
mc_show_error( __( 'You do not have permission to trash that event.', 'my-calendar' ) );
}
} else {
mc_show_error( __( 'Invalid security check; please try again!', 'my-calendar' ) );
}
}
if ( ! empty( $_POST['mc_bulk_actions'] ) ) {
$nonce = $_REQUEST['_wpnonce'];
if ( ! wp_verify_nonce( $nonce, 'my-calendar-nonce' ) ) {
wp_die( 'My Calendar: Security check failed' );
}
if ( isset( $_POST['mc_bulk_actions'] ) ) {
$action = sanitize_text_field( $_POST['mc_bulk_actions'] );
$results = '';
switch ( $action ) {
case 'mass_delete':
mc_bulk_action( 'delete' );
break;
case 'mass_trash':
mc_bulk_action( 'trash' );
break;
case 'mass_publish':
mc_bulk_action( 'approve' );
break;
case 'mass_draft':
mc_bulk_action( 'draft' );
break;
case 'mass_archive':
mc_bulk_action( 'archive' );
break;
case 'mass_undo_archive':
mc_bulk_action( 'unarchive' );
break;
case 'mass_not_spam':
mc_bulk_action( 'unspam' );
break;
}
echo wp_kses_post( $results );
}
}
?>
<div class='wrap my-calendar-admin'>
<h1 id="mc-manage" class="wp-heading-inline"><?php esc_html_e( 'Events', 'my-calendar' ); ?></h1>
<a href="<?php echo esc_url( admin_url( 'admin.php?page=my-calendar' ) ); ?>" class="page-title-action"><?php esc_html_e( 'Add New', 'my-calendar' ); ?></a>
<hr class="wp-header-end">
<div class="mc-tablinks">
<a href="#my-calendar-admin-table" aria-current="page"><?php esc_html_e( 'My Events', 'my-calendar' ); ?></strong>
<a href="<?php echo esc_url( admin_url( 'admin.php?page=my-calendar-manage&groups=true' ) ); ?>"><?php esc_html_e( 'Event Groups', 'my-calendar' ); ?></a>
</div>
<div class="postbox-container jcd-wide">
<div class="metabox-holder">
<div class="ui-sortable meta-box-sortables">
<div class="postbox">
<h2 class="mc-heading-inline"><?php esc_html_e( 'My Events', 'my-calendar' ); ?></h2>
<?php
$grid = ( 'grid' === mc_get_option( 'default_admin_view' ) ) ? true : false;
$grid_url = admin_url( 'admin.php?page=my-calendar-manage&view=grid' );
$list_url = admin_url( 'admin.php?page=my-calendar-manage&view=list' );
?>
<ul class="mc-admin-mode">
<li><span class="dashicons dashicons-calendar" aria-hidden="true"></span><a <?php echo ( $grid ) ? 'aria-current="true"' : ''; ?> href="<?php echo esc_url( $grid_url ); ?>"><?php esc_html_e( 'Grid View', 'my-calendar' ); ?></a></li>
<li><span class="dashicons dashicons-list-view" aria-hidden="true"></span><a <?php echo ( $grid ) ? '' : 'aria-current="true"'; ?> href="<?php echo esc_url( $list_url ); ?>"><?php esc_html_e( 'List View', 'my-calendar' ); ?></a></li>
</ul>
<div class="inside">
<?php
// If any subsidiary post actions (e.g. delete an event), handle before display.
mc_handle_post();
if ( $grid ) {
$calendar = array(
'name' => 'admin',
'format' => 'calendar',
'category' => 'all',
'time' => 'month',
'id' => 'mc-admin-view',
'below' => 'categories,locations,access',
'above' => 'nav,jump,search',
);
if ( mc_count_locations() > 200 ) {
$calendar['below'] = 'categories,access';
}
/**
* Filter arguments used to display the calendar grid view for admins.
*
* @hook mc_filter_admin_grid_args
*
* @param {array} $calendar Calendar display arguments.
*
* @return {array}
*/
apply_filters( 'mc_filter_admin_grid_args', $calendar );
echo my_calendar( $calendar );
} else {
mc_list_events();
}
?>
</div>
</div>
</div>
</div>
</div>
<?php
$problems = mc_list_problems();
mc_show_sidebar( '', $problems );
?>
</div>
<?php
}
/**
* Generate screens for editing and managing events & event groups.
*/
function my_calendar_manage_screen() {
if ( ! isset( $_GET['groups'] ) ) {
my_calendar_manage();
} else {
my_calendar_group_edit();
}
}
/**
* Show bulk actions dropdown in event manager.
*
* @return string
*/
function mc_show_bulk_actions() {
$bulk_actions = array(
'mass_publish' => __( 'Publish', 'my-calendar' ),
'mass_not_spam' => __( 'Not spam', 'my-calendar' ),
'mass_draft' => __( 'Switch to Draft', 'my-calendar' ),
'mass_trash' => __( 'Trash', 'my-calendar' ),
'mass_archive' => __( 'Archive', 'my-calendar' ),
'mass_undo_archive' => __( 'Remove from Archive', 'my-calendar' ),
'mass_delete' => __( 'Delete', 'my-calendar' ),
);
if ( ! current_user_can( 'mc_approve_events' ) || isset( $_GET['limit'] ) && 'published' === $_GET['limit'] ) {
unset( $bulk_actions['mass_publish'] );
}
if ( ! current_user_can( 'mc_manage_events' ) || isset( $_GET['limit'] ) && 'trashed' === $_GET['limit'] ) {
unset( $bulk_actions['mass_trash'] );
}
if ( isset( $_GET['limit'] ) && 'draft' === $_GET['limit'] ) {
unset( $bulk_actions['mass_draft'] );
}
if ( isset( $_GET['restrict'] ) && 'archived' === $_GET['restrict'] ) {
unset( $bulk_actions['mass_archive'] );
} else {
unset( $bulk_actions['mass_undo_archive'] );
}
if ( ! ( isset( $_GET['restrict'] ) && 'flagged' === $_GET['restrict'] ) ) {
unset( $bulk_actions['mass_not_spam'] );
}
/**
* Filter Event manager bulk actions.
*
* @hook mc_bulk_actions
*
* @param {array} $bulk_actions Array of bulk actions currently available.
*
* @return {array}
*/
$bulk_actions = apply_filters( 'mc_bulk_actions', $bulk_actions );
$options = '';
foreach ( $bulk_actions as $action => $label ) {
$options .= '<option value="' . $action . '">' . $label . '</option>';
}
return $options;
}
/**
* Handle event list/grid POST actions.
*
* @return void
*/
function mc_handle_post() {
$action = ! empty( $_POST['event_action'] ) ? $_POST['event_action'] : '';
$event_id = ! empty( $_POST['event_id'] ) ? $_POST['event_id'] : '';
if ( 'delete' === $action ) {
$verify = wp_verify_nonce( $_POST['_wpnonce'], 'my-calendar-nonce' );
if ( ! $verify ) {
wp_die( 'My Calendar: Could not verify your request.', 'my-calendar' );
} else {
$message = mc_delete_event( $event_id );
echo wp_kses_post( $message );
}
}
}
/**
* Get the current sorting characteristics for the admin event list.
*
* @return array
*/
function mc_get_event_list_sorting() {
$user_direction = get_user_meta( wp_get_current_user()->ID, '_mc_default_direction', true );
$default_direction = ( '' !== $user_direction ) ? $user_direction : mc_get_option( 'default_direction' );
$user_sort = get_user_meta( wp_get_current_user()->ID, '_mc_default_sort', true );
$default_sort = ( '' !== $user_sort ) ? $user_sort : mc_get_option( 'default_sort' );
if ( isset( $_GET['order'] ) ) {
$sortbydirection = ( isset( $_GET['order'] ) && 'ASC' === $_GET['order'] ) ? 'ASC' : $default_direction;
$sortbydirection = ( isset( $_GET['order'] ) && 'DESC' === $_GET['order'] ) ? 'DESC' : $sortbydirection;
update_user_meta( wp_get_current_user()->ID, '_mc_default_direction', $sortbydirection );
} else {
$sortbydirection = $default_direction;
}
if ( isset( $_GET['sort'] ) ) {
$sortby = absint( $_GET['sort'] );
update_user_meta( wp_get_current_user()->ID, '_mc_default_sort', $sortby );
} else {
$sortby = $default_sort;
}
if ( empty( $sortby ) ) {
$sortbyvalue = 'event_begin';
} else {
switch ( $sortby ) {
case 1:
$sortbyvalue = "event_ID $sortbydirection";
break;
case 2:
case 3:
$sortbyvalue = "event_title $sortbydirection";
break;
case 4:
$sortbyvalue = "event_begin $sortbydirection, event_time $sortbydirection";
break;
case 5:
$sortbyvalue = "event_author $sortbydirection";
break;
case 6:
$sortbyvalue = "event_category $sortbydirection";
break;
case 7:
$sortbyvalue = "event_label $sortbydirection";
break;
default:
$sortbyvalue = "event_begin $sortbydirection, event_time $sortbydirection";
}
}
$sort = ( 'DESC' === $sortbydirection ) ? 'ASC' : 'DESC';
return array(
'sort' => $sortbyvalue,
'direction' => $sort,
'sortby' => $sortby,
);
}
/**
* Get event list limits.
*
* @return string
*/
function mc_get_event_status_limit() {
$status = ( isset( $_GET['limit'] ) ) ? sanitize_text_field( $_GET['limit'] ) : '';
// Filter by status.
switch ( $status ) {
case 'all':
$limit = '';
break;
case 'draft':
$limit = 'WHERE event_approved = 0';
break;
case 'published':
$limit = 'WHERE event_approved = 1';
break;
case 'trashed':
$limit = 'WHERE event_approved = 2';
break;
default:
$limit = 'WHERE event_approved != 2';
}
return $limit;
}
/**
* Event list search form output.
*
* @param string $context String to differentiate for/ID attributes if on page twice.
*/
function mc_admin_event_search( $context = '' ) {
$search_text = ( isset( $_POST['mcs'] ) ) ? sanitize_text_field( $_POST['mcs'] ) : '';
$args = map_deep( $_GET, 'sanitize_text_field' );
?>
<div class='mc-search'>
<form action="<?php echo esc_url( add_query_arg( $args, admin_url( 'admin.php' ) ) ); ?>" method="post" role='search'>
<div><input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce( 'my-calendar-nonce' ); ?>"/>
</div>
<div>
<label for="mc_search<?php echo esc_attr( $context ); ?>" class='screen-reader-text'><?php esc_html_e( 'Search Events', 'my-calendar' ); ?></label>
<input type='text' name='mcs' id='mc_search<?php echo esc_attr( $context ); ?>' value='<?php echo esc_attr( $search_text ); ?>' />
<input type='submit' value='<?php echo esc_attr( __( 'Search', 'my-calendar' ) ); ?>' class='button-secondary'/>
</div>
</form>
</div>
<?php
}
/**
* Get query limit pagination values for DB query.
*/
function mc_get_query_limit() {
$current = empty( $_GET['paged'] ) ? 1 : intval( $_GET['paged'] );
$user = get_current_user_id();
$screen = get_current_screen();
$option = $screen->get_option( 'per_page', 'option' );
$items_per_page = get_user_meta( $user, $option, true );
if ( empty( $items_per_page ) || $items_per_page < 1 ) {
$items_per_page = $screen->get_option( 'per_page', 'default' );
}
$query_limit = ( ( $current - 1 ) * $items_per_page );
return array(
'query' => $query_limit,
'current' => $current,
'items_per_page' => $items_per_page,
);
}
/**
* Get filter type and filtered value.
*
* @return array
*/
function mc_get_filter() {
$restrict = ( isset( $_GET['restrict'] ) ) ? sanitize_text_field( $_GET['restrict'] ) : 'all';
switch ( $restrict ) {
case 'all':
$filter = '';
break;
case 'where':
$filter = ( isset( $_GET['filter'] ) ) ? absint( $_GET['filter'] ) : '';
$restrict = 'event_location';
break;
case 'author':
$filter = ( isset( $_GET['filter'] ) ) ? (int) $_GET['filter'] : '';
$restrict = 'event_author';
break;
case 'category':
$filter = ( isset( $_GET['filter'] ) ) ? (int) $_GET['filter'] : '';
$restrict = 'event_category';
break;
case 'flagged':
$filter = ( isset( $_GET['filter'] ) ) ? (int) $_GET['filter'] : '';
$restrict = 'event_flagged';
break;
default:
$filter = '';
}
return array(
'filter' => $filter,
'restrict' => $restrict,
);
}
/**
* Used on the manage events admin page to display a list of events
*/
function mc_list_events() {
global $wpdb;
if ( current_user_can( 'mc_approve_events' ) || current_user_can( 'mc_manage_events' ) || current_user_can( 'mc_add_events' ) ) {
// Check current user's last sort.
$sort = mc_get_event_list_sorting();
$sortbyvalue = $sort['sort'];
$sortbydirection = $sort['direction'];
$sortby = $sort['sortby'];
$limit = mc_get_event_status_limit();
$filters = mc_get_filter();
$filter = $filters['filter'];
$restrict = $filters['restrict'];
$allow_filters = true;
if ( ! current_user_can( 'mc_manage_events' ) && ! current_user_can( 'mc_approve_events' ) ) {
$restrict = 'event_author';
$filter = get_current_user_id();
$allow_filters = false;
}
// Set up filter format for location names.
if ( 'event_label' === $restrict ) {
$filter = "'$filter'";
}
$join = '';
// Set up standard filter limits - normal database fields.
if ( '' === $limit && '' !== $filter ) {
$limit = "WHERE $restrict = $filter";
} elseif ( '' !== $limit && '' !== $filter ) {
$limit .= " AND $restrict = $filter";
}
// Set up filter format for categories.
if ( 'event_category' === $restrict ) {
$cat_limit = mc_select_category( $filter );
$join = ( isset( $cat_limit[0] ) ) ? $cat_limit[0] : '';
$select_category = ( isset( $cat_limit[1] ) ) ? $cat_limit[1] : '';
$limit .= ' ' . $select_category;
}
// Define default limits if none otherwise set.
if ( '' === $limit ) {
$limit .= ( 'event_flagged' !== $restrict ) ? ' WHERE event_flagged = 0' : '';
} else {
$limit .= ( 'event_flagged' !== $restrict ) ? ' AND event_flagged = 0' : '';
}
// Define search query parameters.
if ( isset( $_POST['mcs'] ) || isset( $_GET['mcs'] ) ) {
$query = $_REQUEST['mcs'];
$limit .= mc_prepare_search_query( $query );
}
// Get page and pagination values.
$query = mc_get_query_limit();
// Set event status limits.
$limit .= ( 'archived' !== $restrict ) ? ' AND e.event_status = 1' : ' AND e.event_status = 0';
// Toggle query type depending on whether we're limiting categories, which requires a join.
if ( 'event_category' !== $sortbyvalue ) {
$events = $wpdb->get_results( $wpdb->prepare( 'SELECT e.event_id FROM ' . my_calendar_table() . " AS e $join $limit ORDER BY $sortbyvalue " . 'LIMIT %d, %d', $query['query'], $query['items_per_page'] ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
$found_rows = $wpdb->get_col( 'SELECT COUNT(*) FROM ' . my_calendar_table() . " AS e $join $limit ORDER BY $sortbyvalue" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
} else {
$limit = str_replace( array( 'WHERE ' ), '', $limit );
$limit = ( strpos( $limit, 'AND' ) === 0 ) ? $limit : 'AND ' . $limit;
$events = $wpdb->get_results( $wpdb->prepare( 'SELECT DISTINCT e.event_id FROM ' . my_calendar_table() . ' AS e ' . $join . ' JOIN ' . my_calendar_categories_table() . " AS c WHERE e.event_category = c.category_id $limit ORDER BY c.category_name $sortbydirection " . 'LIMIT %d, %d', $query['query'], $query['items_per_page'] ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
$found_rows = $wpdb->get_col( 'SELECT COUNT(*) FROM ' . my_calendar_table() . ' AS e ' . $join . ' JOIN ' . my_calendar_categories_table() . " AS c WHERE e.event_category = c.category_id $limit ORDER BY c.category_name $sortbydirection" ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQL.NotPrepared
}
$items = $found_rows[0];
$num_pages = ceil( $items / $query['items_per_page'] );
if ( $num_pages > 1 ) {
$page_links = paginate_links(
array(
'base' => add_query_arg( 'paged', '%#%' ),
'format' => '',
'prev_text' => __( '« Previous<span class="screen-reader-text"> Events</span>', 'my-calendar' ),
'next_text' => __( 'Next<span class="screen-reader-text"> Events</span> »', 'my-calendar' ),
'total' => $num_pages,
'current' => $query['current'],
'mid_size' => 2,
)
);
$nav_label = esc_attr( __( 'Events Pagination', 'my-calendar' ) );
printf( "<nav class='tablenav' aria-label='$nav_label'><div class='tablenav-pages'>%s</div></nav>", $page_links );
}
// Display a link to clear filters if set.
$filtered = '';
if ( '' !== $filter && $allow_filters ) {
$filtered = "<a class='mc-clear-filters' href='" . admin_url( 'admin.php?page=my-calendar-manage' ) . "'><span class='dashicons dashicons-no' aria-hidden='true'></span> " . __( 'Clear filters', 'my-calendar' ) . '</a>';
}
?>
<nav class="mc-admin-header" aria-label="<?php esc_attr_e( 'Search and Filter', 'my-calendar' ); ?>">
<?php
// Display links to different statuses.
echo wp_kses( mc_status_links( $allow_filters ), mc_kses_elements() );
// Display event search.
mc_admin_event_search();
?>
</nav>
<?php
if ( ! empty( $events ) ) {
?>
<form action="<?php echo esc_url( add_query_arg( $_GET, admin_url( 'admin.php' ) ) ); ?>" method="post">
<input type="hidden" name="_wpnonce" value="<?php echo wp_create_nonce( 'my-calendar-nonce' ); ?>" />
<div class='mc-actions'>
<label for="mc_bulk_actions" class="screen-reader-text"><?php esc_html_e( 'Bulk actions', 'my-calendar' ); ?></label>
<select name="mc_bulk_actions" id="mc_bulk_actions">
<option value=""><?php esc_html_e( 'Bulk actions', 'my-calendar' ); ?></option>
<?php echo mc_show_bulk_actions(); ?>
</select>
<input type="submit" class="button-secondary" value="<?php echo esc_attr( __( 'Apply', 'my-calendar' ) ); ?>" />
<div><input type='checkbox' class='selectall' id='mass_edit' data-action="mass_edit" /> <label for='mass_edit'><?php esc_html_e( 'Check all', 'my-calendar' ); ?></label></div>
</div>
<table class="widefat striped mc-responsive-table mc-events-table wp-list-table" id="my-calendar-admin-table">
<caption class="screen-reader-text"><?php esc_html_e( 'Event list. Use column headers to sort.', 'my-calendar' ); ?></caption>
<thead>
<tr>
<?php
$admin_url = admin_url( "admin.php?page=my-calendar-manage&order=$sortbydirection&paged=" . $query['current'] );
$admin_url = isset( $_GET['limit'] ) ? add_query_arg( 'limit', sanitize_text_field( $_GET['limit'] ), $admin_url ) : $admin_url;
$url = add_query_arg( 'sort', '1', $admin_url );
$col_head = mc_table_header( __( 'ID', 'my-calendar' ), $sortbydirection, $sortby, '1', $url );
$url = add_query_arg( 'sort', '2', $admin_url );
$col_head .= mc_table_header( __( 'Title', 'my-calendar' ), $sortbydirection, $sortby, '2', $url );
$url = add_query_arg( 'sort', '7', $admin_url );
$col_head .= mc_table_header( __( 'Location', 'my-calendar' ), $sortbydirection, $sortby, '7', $url );
$url = add_query_arg( 'sort', '4', $admin_url );
$col_head .= mc_table_header( __( 'Date/Time', 'my-calendar' ), $sortbydirection, $sortby, '4', $url );
$url = add_query_arg( 'sort', '5', $admin_url );
$col_head .= mc_table_header( __( 'Author', 'my-calendar' ), $sortbydirection, $sortby, '5', $url );
$url = add_query_arg( 'sort', '6', $admin_url );
$col_head .= mc_table_header( __( 'Category', 'my-calendar' ), $sortbydirection, $sortby, '6', $url );
echo mc_kses_post( $col_head );
?>
</tr>
</thead>
<tbody>
<?php mc_admin_events_table( $events ); ?>
</tbody>
</table>
<div class="mc-actions">
<label for="mc_bulk_actions_footer" class="screen-reader-text"><?php esc_html_e( 'Bulk actions', 'my-calendar' ); ?></label>
<select name="mc_bulk_actions" id="mc_bulk_actions_footer">
<option value=""><?php esc_html_e( 'Bulk actions', 'my-calendar' ); ?></option>
<?php echo mc_show_bulk_actions(); ?>
</select>
<input type="submit" class="button-secondary" value="<?php echo esc_attr( __( 'Apply', 'my-calendar' ) ); ?>" />
<input type='checkbox' class='selectall' id='mass_edit_footer' data-action="mass_edit" /> <label for='mass_edit_footer'><?php esc_html_e( 'Check all', 'my-calendar' ); ?></label>
</div>
</form>
<?php
if ( $num_pages > 1 ) {
printf( "<nav class='tablenav' aria-label='$nav_label'><div class='tablenav-pages'>%s</div></nav>", $page_links );
}
?>
<div class='mc-admin-footer'>
<?php
$status_links = mc_status_links( $allow_filters );
echo wp_kses( $status_links . $filtered, mc_kses_elements() );
mc_admin_event_search( '_footer' );
?>
</div>
<?php
} else {
if ( isset( $_POST['mcs'] ) ) {
echo '<p>' . esc_html__( 'No results found for your search query.', 'my-calendar' ) . '</p>';
}
if ( ! isset( $_GET['restrict'] ) && ( ! isset( $_GET['limit'] ) || isset( $_GET['limit'] ) && 'all' === $_GET['limit'] ) ) {
?>
<p class='mc-create-event'><a href="<?php echo esc_url( admin_url( 'admin.php?page=my-calendar' ) ); ?>" class="button button-hero"><?php esc_html_e( 'Create an event', 'my-calendar' ); ?></a></p>
<?php
} else {
?>
<p class='mc-none'><?php esc_html_e( 'No events found.', 'my-calendar' ); ?></p>
<?php
}
}
}
}
/**
* Output event table data for My Calendar admin events.
*
* @param array $events Array of objects representing events.
*/
function mc_admin_events_table( $events ) {
global $wpdb;
$class = '';
foreach ( array_keys( $events ) as $key ) {
$e =& $events[ $key ];
$event = mc_get_first_event( $e->event_id );
$invalid = false;
if ( ! is_object( $event ) ) {
$event = $wpdb->get_row( $wpdb->prepare( 'SELECT * FROM ' . my_calendar_table() . ' WHERE event_id = %d', $e->event_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$invalid = true;
}
$class = ( $invalid ) ? 'invalid' : '';
$pending = ( 0 === (int) $event->event_approved ) ? 'pending' : '';
$trashed = ( 2 === (int) $event->event_approved ) ? 'trashed' : '';
$author = ( 0 !== (int) $event->event_author ) ? get_userdata( $event->event_author ) : 'Public Submitter';
if ( 1 === (int) $event->event_flagged && ( isset( $_GET['restrict'] ) && 'flagged' === $_GET['restrict'] ) ) {
$spam = 'spam';
$pending = '';
$spam_label = '<strong>' . esc_html__( 'Possible spam', 'my-calendar' ) . ':</strong> ';
} else {
$spam = '';
$spam_label = '';
}
$trash = ( '' !== $trashed ) ? ' - ' . __( 'Trash', 'my-calendar' ) : '';
$draft = ( '' !== $pending ) ? ' - ' . __( 'Draft', 'my-calendar' ) : '';
$inv = ( $invalid ) ? ' - ' . __( 'Invalid Event', 'my-calendar' ) : '';
$limit = ( isset( $_GET['limit'] ) ) ? sanitize_text_field( $_GET['limit'] ) : 'all';
$private = ( mc_private_event( $event, false ) ) ? ' - ' . __( 'Private', 'my-calendar' ) : '';
$check = mc_test_occurrence_overlap( $event, true );
$problem = ( '' !== $check ) ? 'problem' : '';
$edit_url = admin_url( "admin.php?page=my-calendar&mode=edit&event_id=$event->event_id" );
$copy_url = admin_url( "admin.php?page=my-calendar&mode=copy&event_id=$event->event_id" );
$view_url = ( $invalid ) ? '' : mc_get_permalink( $event );
$group_url = admin_url( "admin.php?page=my-calendar-manage&groups=true&mode=edit&event_id=$event->event_id&group_id=$event->event_group_id" );
$mcnonce = wp_create_nonce( 'mcnonce' );
$delete_url = add_query_arg( '_mcnonce', $mcnonce, admin_url( "admin.php?page=my-calendar-manage&mode=delete&event_id=$event->event_id" ) );
$can_edit = mc_can_edit_event( $event );
if ( current_user_can( 'mc_manage_events' ) || current_user_can( 'mc_approve_events' ) || $can_edit ) {
?>
<tr class="<?php echo sanitize_html_class( "$class $spam $pending $trashed $problem" ); ?>">
<th scope="row">
<input type="checkbox" value="<?php echo absint( $event->event_id ); ?>" name="mass_edit[]" id="mc<?php echo $event->event_id; ?>" aria-describedby='event<?php echo absint( $event->event_id ); ?>' />
<label for="mc<?php echo absint( $event->event_id ); ?>">
<?php
// Translators: Event ID.
printf( __( "<span class='screen-reader-text'>Select event </span>%d", 'my-calendar' ), absint( $event->event_id ) );
?>
</label>
</th>
<td>
<strong>
<?php
if ( $can_edit ) {
?>
<a href="<?php echo esc_url( $edit_url ); ?>" class='edit'><span class="dashicons dashicons-edit" aria-hidden="true"></span>
<?php
}
echo $spam_label;
echo '<span id="event' . absint( $event->event_id ) . '">' . esc_html( stripslashes( $event->event_title ) ) . '</span>';
if ( $can_edit ) {
echo '</a>';
if ( '' !== $check ) {
// Translators: URL to edit event.
echo wp_kses_post( '<br /><strong class="error">' . sprintf( __( 'There is a problem with this event. <a href="%s">Edit</a>', 'my-calendar' ), esc_url( $edit_url ) ) . '</strong>' );
}
}
echo wp_kses_post( $private . $trash . $draft . $inv );
?>
</strong>
<div class='row-actions'>
<?php
if ( mc_event_published( $event ) ) {
?>
<a href="<?php echo esc_url( $view_url ); ?>" class='view' aria-describedby='event<?php echo absint( $event->event_id ); ?>'><?php esc_html_e( 'View', 'my-calendar' ); ?></a> |
<?php
} elseif ( current_user_can( 'mc_manage_events' ) ) {
?>
<a href="<?php echo esc_url( add_query_arg( 'preview', 'true', $view_url ) ); ?>" class='view' aria-describedby='event<?php echo absint( $event->event_id ); ?>'><?php esc_html_e( 'Preview', 'my-calendar' ); ?></a> |
<?php
}
if ( $can_edit ) {
?>
<a href="<?php echo esc_url( $copy_url ); ?>" class='copy' aria-describedby='event<?php echo absint( $event->event_id ); ?>'><?php esc_html_e( 'Copy', 'my-calendar' ); ?></a>
<?php
}
if ( $can_edit ) {
if ( mc_event_is_grouped( $event->event_group_id ) ) {
?>
| <a href="<?php echo esc_url( $group_url ); ?>" class='edit group' aria-describedby='event<?php echo absint( $event->event_id ); ?>'><?php esc_html_e( 'Edit Group', 'my-calendar' ); ?></a>
<?php
}
?>
| <a href="<?php echo esc_url( $delete_url ); ?>" class="delete" aria-describedby='event<?php echo absint( $event->event_id ); ?>'><?php esc_html_e( 'Delete', 'my-calendar' ); ?></a>
<?php
} else {
_e( 'Not editable.', 'my-calendar' );
}
?>
|
<?php
if ( current_user_can( 'mc_approve_events' ) && $can_edit ) {
if ( 1 === (int) $event->event_approved ) {
$mo = 'reject';
$te = __( 'Trash', 'my-calendar' );
} else {
$mo = 'publish';
$te = __( 'Publish', 'my-calendar' );
}
?>
<a href="<?php echo esc_url( add_query_arg( '_mcnonce', $mcnonce, admin_url( "admin.php?page=my-calendar-manage&mode=$mo&limit=$limit&event_id=$event->event_id" ) ) ); ?>" class='<?php echo esc_attr( $mo ); ?>' aria-describedby='event<?php echo absint( $event->event_id ); ?>'><?php echo esc_html( $te ); ?></a>
<?php
} else {
switch ( $event->event_approved ) {
case 1:
_e( 'Published', 'my-calendar' );
break;
case 2:
_e( 'Trashed', 'my-calendar' );
break;
default:
_e( 'Awaiting Approval', 'my-calendar' );
}
}
?>
</div>
</td>
<td>
<?php
$elabel = '';
if ( property_exists( $event, 'location' ) && is_object( $event->location ) ) {
$filter = $event->event_location;
$elabel = $event->location->location_label;
}
if ( '' !== $elabel ) {
?>
<a class='mc_filter' href='<?php echo esc_url( mc_admin_url( 'admin.php?page=my-calendar-manage&filter=' . urlencode( $filter ) . '&restrict=where' ) ); ?>'><span class="screen-reader-text"><?php esc_html_e( 'Show only: ', 'my-calendar' ); ?></span><?php echo esc_html( stripslashes( $elabel ) ); ?></a>
<?php
}
?>
</td>
<td>
<?php
if ( '23:59:59' !== $event->event_endtime ) {
$event_time = date_i18n( mc_time_format(), mc_strtotime( $event->event_time ) );
} else {
$event_time = mc_notime_label( $event );
}
$begin = date_i18n( mc_date_format(), mc_strtotime( $event->event_begin ) );
echo esc_html( "$begin, $event_time" );
?>
<div class="recurs">
<?php echo wp_kses_post( mc_recur_string( $event ) ); ?>
</div>
</td>
<?php
$auth = ( is_object( $author ) ) ? $author->ID : 0;
$filter = mc_admin_url( "admin.php?page=my-calendar-manage&filter=$auth&restrict=author" );
$author = ( is_object( $author ) ? $author->display_name : $author );
?>
<td>
<a class='mc_filter' href="<?php echo esc_url( $filter ); ?>">
<span class="screen-reader-text"><?php esc_html_e( 'Show only: ', 'my-calendar' ); ?></span><?php echo esc_html( $author ); ?>
</a>
</td>
<td>
<?php echo mc_admin_category_list( $event ); ?>
</td>
</tr>
<?php
}
}
}
/**
* Get next available group ID
*
* @return int
*/
function mc_group_id() {
global $wpdb;
$result = $wpdb->get_var( 'SELECT MAX(event_id) FROM ' . my_calendar_table() ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$next = $result + 1;
return $next;
}
/**
* Check whether an event is a member of a group
*
* @param int $group_id Event Group ID.
*
* @return boolean
*/
function mc_event_is_grouped( $group_id ) {
global $wpdb;
if ( 0 === (int) $group_id ) {
return false;
} else {
$value = $wpdb->get_var( $wpdb->prepare( 'SELECT count( event_group_id ) FROM ' . my_calendar_table() . ' WHERE event_group_id = %d', $group_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
if ( $value > 1 ) {
return true;
} else {
return false;
}
}
}
/**
* Can the current user edit this category?
*
* @param int $category Category ID.
* @param int $user User ID.
*
* @return boolean
*/
function mc_can_edit_category( $category, $user ) {
$permissions = ( '' === get_user_meta( $user, 'mc_user_permissions', true ) ) ? array() : get_user_meta( $user, 'mc_user_permissions', true );
/**
* Filter permissions for users editing a category.
*
* @hook mc_user_permissions
*
* @param {array} $permissions User meta data for this user's category permissions.
* @param {int} $category Category ID.
* @param {int} $user User ID.
*
* @return {array} Array of categories this user can edit.
*/
$permissions = apply_filters( 'mc_user_permissions', $permissions, $category, $user );
if ( ( ! $permissions || empty( $permissions ) ) || in_array( 'all', $permissions, true ) || in_array( $category, $permissions, true ) || current_user_can( 'manage_options' ) ) {
return true;
}
return false;
}
/**
* Unless an admin, authors can only edit their own events if they don't have mc_manage_events capabilities.
*
* @param object|boolean|int $event Event object or event ID.
* @param string $datatype 'event' or 'instance'.
*
* @return boolean
*/
function mc_can_edit_event( $event = false, $datatype = 'event' ) {
global $wpdb;
if ( ! $event ) {
return false;
}
/**
* Filter permissions to edit an event via the My Calendar Pro REST API..
*
* @hook mc_api_can_edit_event
*
* @param {bool} $return True if API user can edit this event.
* @param {object|int} $event The ID of the current event or an event object.
*
* @return {bool}
*/
$api = apply_filters( 'mc_api_can_edit_event', false, $event );
if ( $api ) {
return $api;
}
if ( ! is_user_logged_in() ) {
return false;
}
if ( is_object( $event ) ) {
$event_id = $event->event_id;
$event_author = $event->event_author;
} else {
$event_id = absint( $event );
if ( ! $event_id ) {
return false;
}
if ( 'event' === $datatype ) {
$event = mc_get_first_event( $event );
if ( ! is_object( $event ) ) {
$event = $wpdb->get_row( $wpdb->prepare( 'SELECT * FROM ' . my_calendar_table() . ' WHERE event_id=%d LIMIT 1', $event_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
}
} else {
$event = mc_get_event( $event_id );
}
$event_author = $event->event_author;
}
$current_user = wp_get_current_user();
$user = $current_user->ID;
$categories = mc_get_categories( $event );
$has_permissions = true;
if ( is_array( $categories ) ) {
foreach ( $categories as $cat ) {
// If user doesn't have access to all relevant categories, prevent editing.
if ( ! $has_permissions ) {
continue;
}
$has_permissions = mc_can_edit_category( $cat, $user );
}
}
$return = false;
if ( ( current_user_can( 'mc_manage_events' ) && $has_permissions ) || ( $user === (int) $event_author ) ) {
$return = true;
}
/**
* Filter permissions to edit an event.
*
* @hook mc_can_edit_event
*
* @param {bool} $return True if user can edit this event.
* @param {int} $event_id The ID of the current event.
*
* @return {bool}
*/
return apply_filters( 'mc_can_edit_event', $return, $event_id );
}
/**
* Determine max values to increment
*
* @param string $recur Type of recurrence.
*/
function _mc_increment_values( $recur ) {
switch ( $recur ) {
case 'S': // Single.
return 0;
case 'D': // Daily.
return 500;
case 'E': // Weekdays.
return 400;
case 'W': // Weekly.
return 240;
case 'B': // Biweekly.
return 240;
case 'M': // Monthly.
case 'U':
return 240;
case 'Y':
return 50;
default:
return false;
}
}
/**
* Deletes all instances of an event without deleting the event details. Sets stage for rebuilding event instances.
*
* @param int $id Event ID.
*/
function mc_delete_instances( $id ) {
global $wpdb;
$id = (int) $id;
$wpdb->query( $wpdb->prepare( 'DELETE FROM ' . my_calendar_event_table() . ' WHERE occur_event_id = %d', $id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
// After bulk deletion, optimize table.
$wpdb->query( 'OPTIMIZE TABLE ' . my_calendar_event_table() ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
}
/**
* Check for events with known occurrence overlap problems.
*/
function mc_list_problems() {
$events = get_posts(
array(
'post_type' => 'mc-events',
'meta_key' => '_occurrence_overlap',
'meta_value' => 'false',
)
);
$list = array();
$problems = array();
if ( is_array( $events ) && count( $events ) > 0 ) {
foreach ( $events as $event ) {
$event_id = get_post_meta( $event->ID, '_mc_event_id', true );
$event_url = admin_url( 'admin.php?page=my-calendar&mode=edit&event_id=' . absint( $event_id ) );
$list[] = '<a href="' . esc_url( $event_url ) . '">' . esc_html( $event->post_title ) . '</a>';
}
}
if ( ! empty( $list ) ) {
$problems = array( 'Problem Events' => '<ul><li>' . implode( '</li><li>', $list ) . '</li></ul>' );
}
return $problems;
}