Series): list of series, "edit this and future", delete, and the uninstall choice.
* Flow: form (nonce field) -> admin-post.php -> recurkit_handle_*() -> redirect with a status flag -> notice.
*
* @package recurkit
*/
defined( 'ABSPATH' ) || exit;
/** Capability TEC gives editors and administrators for other people's events. */
const RECURKIT_CAP = 'edit_others_tribe_events';
/** Where the Pro add-on is sold. Shown as one link on the Series screen. */
const RECURKIT_PRO_URL = 'https://brightlabs.app/apps/recurkit/';
/**
* Admin notice for a status flag. Unknown flags print nothing.
*
* @param string $status Status flag from the redirect.
*/
function recurkit_render_status_notice( string $status ): void {
$notices = array(
'updated' => array( 'notice-success', __( 'The occurrences were updated.', 'recurkit' ) ),
'deleted' => array( 'notice-success', __( 'The series was deleted.', 'recurkit' ) ),
'saved' => array( 'notice-success', __( 'Settings saved.', 'recurkit' ) ),
'unchanged' => array( 'notice-info', __( 'Nothing to change: the form matched the chosen occurrence.', 'recurkit' ) ),
'invalid' => array( 'notice-error', __( 'Nothing was changed: check the event, the times and the venue.', 'recurkit' ) ),
);
if ( ! isset( $notices[ $status ] ) ) {
return;
}
printf(
'
',
esc_attr( $notices[ $status ][0] ),
esc_html( $notices[ $status ][1] )
);
}
/** Renders the Series screen. */
function recurkit_render_admin_page(): void {
if ( ! current_user_can( RECURKIT_CAP ) ) {
wp_die( esc_html__( 'You do not have permission to view this page.', 'recurkit' ), '', array( 'response' => 403 ) );
}
// Display-only values: the status is checked against an allowlist, the event id is checked below. Nothing changes.
$status = isset( $_GET['recurkit_status'] ) ? sanitize_key( wp_unslash( $_GET['recurkit_status'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$event = isset( $_GET['event'] ) ? absint( wp_unslash( $_GET['event'] ) ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
?>
= $today ) {
return (int) $id;
}
}
return (int) end( $ids );
}
/**
* "Edit this and future" form, plus a link to edit each occurrence on its own.
*
* @param int $event Occurrence to start from.
*/
function recurkit_render_edit_form( int $event ): void {
$ids = recurkit_series_event_ids( (string) get_post_meta( $event, '_recurkit_series', true ) );
$post = get_post( $event );
$start = (string) get_post_meta( $event, '_EventStartDate', true );
$end = (string) get_post_meta( $event, '_EventEndDate', true );
$venue = (int) get_post_meta( $event, '_EventVenueID', true );
$venues = get_posts(
array(
'post_type' => 'tribe_venue',
'post_status' => 'publish',
'numberposts' => 200,
'orderby' => 'title',
'order' => 'ASC',
)
);
?>
403 ) );
}
check_admin_referer( 'recurkit_update', 'recurkit_nonce' );
$event = isset( $post['recurkit_event'] ) ? absint( $post['recurkit_event'] ) : 0;
if ( ! $event || '' === (string) get_post_meta( $event, '_recurkit_series', true ) ) {
return 'invalid';
}
foreach ( recurkit_series_event_ids( (string) get_post_meta( $event, '_recurkit_series', true ) ) as $id ) {
if ( ! current_user_can( 'edit_post', $id ) ) {
wp_die( esc_html__( 'You do not have permission to change these events.', 'recurkit' ), '', array( 'response' => 403 ) );
}
}
// Only fields the user changed against the picked occurrence are applied, so later occurrences edited on their own
// keep their own text, venue and times.
$picked = get_post( $event );
$fields = array();
if ( isset( $post['recurkit_title'] ) ) {
$title = sanitize_text_field( wp_unslash( $post['recurkit_title'] ) );
if ( $title !== $picked->post_title ) {
$fields['title'] = $title;
}
}
if ( isset( $post['recurkit_content'] ) ) {
$raw = (string) wp_unslash( $post['recurkit_content'] );
if ( $raw !== $picked->post_content ) {
$fields['content'] = current_user_can( 'unfiltered_html' ) ? $raw : wp_kses_post( $raw );
}
}
$venue = isset( $post['recurkit_venue'] ) ? absint( $post['recurkit_venue'] ) : 0;
if ( $venue === (int) get_post_meta( $event, '_EventVenueID', true ) ) {
$venue = 0;
}
if ( $venue ) {
if ( 'tribe_venue' !== get_post_type( $venue ) ) {
return 'invalid';
}
$fields['venue'] = $venue;
}
$start = isset( $post['recurkit_start_time'] ) ? sanitize_text_field( wp_unslash( $post['recurkit_start_time'] ) ) : '';
$end = isset( $post['recurkit_end_time'] ) ? sanitize_text_field( wp_unslash( $post['recurkit_end_time'] ) ) : '';
$was_start = substr( (string) get_post_meta( $event, '_EventStartDate', true ), 11, 5 );
$was_end = substr( (string) get_post_meta( $event, '_EventEndDate', true ), 11, 5 );
if ( ( '' !== $start || '' !== $end ) && ( $start !== $was_start || $end !== $was_end ) ) {
if ( ! recurkit_is_hm( $start ) || ! recurkit_is_hm( $end ) ) {
return 'invalid';
}
$fields['start_time'] = $start;
$fields['end_time'] = $end;
}
if ( ! $fields ) {
return 'unchanged';
}
return recurkit_update_from( $event, $fields ) ? 'updated' : 'invalid';
}
/**
* Deletes a series. Order: capability, nonce, sanitize, per-event rights, delete.
*
* @param array $post Raw POST data.
* @return string Status flag.
*/
function recurkit_process_delete( array $post ): string {
if ( ! current_user_can( RECURKIT_CAP ) ) {
wp_die( esc_html__( 'You do not have permission to delete events.', 'recurkit' ), '', array( 'response' => 403 ) );
}
check_admin_referer( 'recurkit_delete', 'recurkit_nonce' );
$series = isset( $post['recurkit_series'] ) ? sanitize_text_field( wp_unslash( $post['recurkit_series'] ) ) : '';
if ( ! preg_match( '/^[0-9a-f-]{36}$/', $series ) ) {
return 'invalid';
}
$ids = recurkit_series_event_ids( $series );
foreach ( $ids as $id ) {
if ( ! current_user_can( 'delete_post', $id ) ) {
wp_die( esc_html__( 'You do not have permission to delete these events.', 'recurkit' ), '', array( 'response' => 403 ) );
}
}
return ( $ids && recurkit_delete_series( $series ) ) ? 'deleted' : 'invalid';
}
/**
* Stores the uninstall choice.
*
* @param array $post Raw POST data.
* @return string Status flag.
*/
function recurkit_process_settings( array $post ): string {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'You do not have permission to change these settings.', 'recurkit' ), '', array( 'response' => 403 ) );
}
check_admin_referer( 'recurkit_settings', 'recurkit_nonce' );
$mode = isset( $post['recurkit_uninstall'] ) ? sanitize_key( wp_unslash( $post['recurkit_uninstall'] ) ) : '';
recurkit_set_uninstall_mode( 'delete' === $mode ? 'delete' : 'keep' );
return 'saved';
}
/**
* Sends the browser back to the Series screen with a status flag.
*
* @param string $status Status flag.
*/
function recurkit_back( string $status ): void {
wp_safe_redirect( add_query_arg( 'recurkit_status', $status, admin_url( 'edit.php?post_type=tribe_events&page=recurkit-series' ) ) );
exit;
}
/** The admin-post.php entry point for "edit this and future". */
function recurkit_handle_update(): void {
recurkit_back( recurkit_process_update( $_POST ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified inside.
}
add_action( 'admin_post_recurkit_update', 'recurkit_handle_update' );
/** The admin-post.php entry point for deleting a series. */
function recurkit_handle_delete(): void {
recurkit_back( recurkit_process_delete( $_POST ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified inside.
}
add_action( 'admin_post_recurkit_delete', 'recurkit_handle_delete' );
/** The admin-post.php entry point for the uninstall choice. */
function recurkit_handle_settings(): void {
recurkit_back( recurkit_process_settings( $_POST ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- verified inside.
}
add_action( 'admin_post_recurkit_settings', 'recurkit_handle_settings' );