Home / Admin / Automatically Trash Past Events
Duplicate Snippet

Embed Snippet on Your Site

Automatically Trash Past Events

Sites that have been running for years build up thousands of finished events. They stay in the admin lists, the search results and every calendar query, and browsing back
through old months gets slow. This moves events to the trash once they have been over for a set number of days. It does nothing until you pick that number. Out of the box, it is set to 0, which means off.

Customizing

Change the 0 in sc_snippets_auto_trash_days() to the number of days you want to keep
finished events for. 90 is a reasonable starting point.

function sc_snippets_auto_trash_days() {
return 90;
}

Code Preview
php
<?php
/**
 * Move past events to the trash on a daily schedule.
 *
 * Sites that have run for years accumulate thousands of finished events. They
 * stay in every admin view and every query, and past a few hundred per month
 * the calendar views get slow. This trashes events that finished more than N
 * days ago, a batch at a time.
 *
 * Nothing happens until you set a number: sc_snippets_auto_trash_days()
 * returns 0, which means off. Change it to 90 and events that ended more than
 * 90 days ago start going to the trash.
 *
 * Trash, never delete. A trashed event keeps its wp_sc_events row and can be
 * restored from Events > Trash. Permanently deleting an event drops that row
 * along with its RSVP responses and registration answers, which is not
 * something a scheduled job should ever do on its own.
 *
 * Recurring events are skipped. A recurring event's end date is the end of its
 * FIRST occurrence, so a weekly series running since 2019 looks five years
 * past. Trashing on that would kill live series.
 *
 * If the event uses a Zoom or other online meeting, trashing it deletes the
 * meeting on the provider's side. That is Sugar Calendar's normal behaviour on
 * trash, not something this snippet adds.
 *
 * To keep certain events, see sc_snippets_auto_trash_should_trash below.
 *
 * If you disable this snippet, the scheduled job is left behind. Clear it with:
 *   wp cron event delete sc_snippets_auto_trash_past_events
 */
/**
 * How many days after an event ends before it is trashed.
 *
 * 0 turns the snippet off. There is no default number on purpose: a snippet
 * that trashes events the moment it is pasted in is a bad snippet.
 *
 * @return int Days, or 0 for off.
 */
if ( ! function_exists( 'sc_snippets_auto_trash_days' ) ) :
function sc_snippets_auto_trash_days() {
	return 0;
}
endif;
/**
 * How many events to trash per run.
 *
 * WP-Cron runs inside a normal page request, so a run that trashes thousands
 * of posts will time out. The job picks up where it left off the next day.
 * Raise this only if you know the site can take it.
 *
 * @return int
 */
if ( ! function_exists( 'sc_snippets_auto_trash_batch' ) ) :
function sc_snippets_auto_trash_batch() {
	return 100;
}
endif;
/**
 * Decide whether one event should be trashed.
 *
 * Return false to keep it. Copy this into your own snippet to protect events
 * that still matter, for example ones that sold tickets:
 *
 *   add_filter( 'sc_snippets_auto_trash_should_trash', function ( $trash, $event ) {
 *       if ( function_exists( 'Sugar_Calendar\AddOn\Ticketing\Common\Functions\get_event_tickets' ) ) {
 *           $sold = \Sugar_Calendar\AddOn\Ticketing\Common\Functions\get_event_tickets( $event->id );
 *           if ( ! empty( $sold ) ) {
 *               return false;
 *           }
 *       }
 *       return $trash;
 *   }, 10, 2 );
 *
 * @param bool                  $trash Whether to trash. Default true.
 * @param \Sugar_Calendar\Event $event The event.
 *
 * @return bool
 */
/**
 * Schedule the daily job, or clear it when the snippet is switched off.
 *
 * Runs on every request because that is how WPCode executes a snippet. Both
 * branches are no-ops once the schedule matches the setting.
 */
if ( ! function_exists( 'sc_snippets_auto_trash_schedule' ) ) :
function sc_snippets_auto_trash_schedule() {
	$days = (int) sc_snippets_auto_trash_days();
	$next = wp_next_scheduled( 'sc_snippets_auto_trash_past_events' );
	if ( $days > 0 && empty( $next ) ) {
		wp_schedule_event( time() + HOUR_IN_SECONDS, 'daily', 'sc_snippets_auto_trash_past_events' );
	} elseif ( $days < 1 && ! empty( $next ) ) {
		wp_unschedule_event( $next, 'sc_snippets_auto_trash_past_events' );
	}
}
endif;
add_action( 'init', 'sc_snippets_auto_trash_schedule' );
/**
 * Trash one batch of finished events.
 *
 * The cutoff is built with gmdate() because Sugar Calendar stores and compares
 * the start and end columns in UTC. sugar_calendar_get_request_time() does the
 * same thing for core's own past/upcoming queries.
 *
 * @return int How many events were trashed.
 */
if ( ! function_exists( 'sc_snippets_auto_trash_run' ) ) :
function sc_snippets_auto_trash_run() {
	$days = (int) sc_snippets_auto_trash_days();
	if ( $days < 1 || ! function_exists( 'sugar_calendar_get_events' ) ) {
		return 0;
	}
	$batch  = max( 1, (int) sc_snippets_auto_trash_batch() );
	$cutoff = gmdate( 'Y-m-d H:i:s', time() - ( $days * DAY_IN_SECONDS ) );
	// Ask for more rows than the batch, because recurring events are dropped
	// below and would otherwise use up the batch without trashing anything.
	$events = sugar_calendar_get_events(
		array(
			'object_type' => 'post',
			'status'      => 'publish',
			'orderby'     => 'end',
			'order'       => 'ASC',
			'number'      => $batch * 3,
			'end_query'   => array(
				'inclusive' => true,
				'before'    => $cutoff,
			),
		)
	);
	if ( empty( $events ) ) {
		return 0;
	}
	$trashed = 0;
	foreach ( $events as $event ) {
		if ( $trashed >= $batch ) {
			break;
		}
		// Recurring: the end column describes the first occurrence only.
		if ( ! empty( $event->recurrence ) ) {
			continue;
		}
		if ( empty( $event->object_id ) || get_post_status( $event->object_id ) !== 'publish' ) {
			continue;
		}
		/** This filter is documented above sc_snippets_auto_trash_schedule(). */
		if ( ! apply_filters( 'sc_snippets_auto_trash_should_trash', true, $event ) ) {
			continue;
		}
		if ( wp_trash_post( $event->object_id ) ) {
			$trashed++;
		}
	}
	return $trashed;
}
endif;
add_action( 'sc_snippets_auto_trash_past_events', 'sc_snippets_auto_trash_run' );

Comments

Add a Comment