This is what I went with. It creates a second archive for past events. It shows upcoming events in the main event archive, and old events in the past events page. Sorting is ascending for the main archive (so you see the next upcoming event first), and descending for the past events page, so you see the most recent event first. It allows for paging on the past events page. Note that is does not modify the query in the admin system.
new My_Events_Are_Special;
class My_Events_Are_Special {
function __construct() {
add_filter('rewrite_rules_array', array($this, 'insert_rewrite_rules'));
add_filter('query_vars', array($this, 'insert_query_vars'));
add_action('wp_loaded', array($this, 'flush_rules'));
add_filter('posts_join', array($this, 'posts_join'));
add_filter('posts_where', array($this, 'posts_where'));
add_filter('posts_orderby', array($this, 'posts_orderby'));
}
function can_modify_query() {
return !is_admin() && is_post_type_archive('event');
}
// create rules for the archived events page
var $rewrite_rules = array(
'events/archive$' => 'index.php?post_type=event&archive_type=archive',
'events/archive/page/([0-9]+)$' => 'index.php?post_type=event&archive_type=archive&paged=$matches[1]',
);
// insert rules into rewrite system
function insert_rewrite_rules($rules) {
return $this->rewrite_rules + $rules;
}
// add special query var to system
function insert_query_vars($vars) {
array_push($vars, 'archive_type');
return $vars;
}
// flush rules if any are new
function flush_rules() {
$rules = get_option('rewrite_rules');
$flush = false;
foreach ($this->rewrite_rules as $rule => $rewrite) {
if (!isset($rules[$rule])) {
global $wp_rewrite;
$wp_rewrite->flush_rules();
break;
}
}
}
// add start and end type to query for events (not in admin)
function posts_join($join) {
global $wpdb;
if ($this->can_modify_query()) {
$join .= " JOIN $wpdb->postmeta starts on ($wpdb->posts.ID = starts.post_id AND starts.meta_key = '_starts') ";
$join .= " JOIN $wpdb->postmeta ends on ($wpdb->posts.ID = ends.post_id AND ends.meta_key = '_ends') ";
}
return $join;
}
// only show future events for the main archive, only past events for the "archive archive"
function posts_where($where) {
global $wpdb;
if ($this->can_modify_query()) {
$compare = get_query_var('archive_type') == 'archive' ? '<' : '>';
$where .= " AND ends.meta_value $compare ".time();
}
return $where;
}
// main archive is ordered ascending on event start date, "archive archive" is ordered descending
function posts_orderby($orderby) {
global $wpdb;
if ($this->can_modify_query()) {
$order = get_query_var('archive_type') == 'archive' ? 'DESC' : 'ASC';
$orderby = "starts.meta_value $order, $wpdb->posts.post_date $order";
}
return $orderby;
}
}