<?php /* Plugin Name: Date-based Taxonomy Archives Plugin URI: https://ethitter.com/plugins/date-based-taxonomy-archives/ Description: Add support for date-based taxonomy archives. Render an unordered list of years with months, linked to corresponding date-based taxonomy archive, nested therein. Author: Erick Hitter Version: 0.3.1 Author URI: https://ethitter.com/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ class Date_Based_Taxonomy_Archives { /** * Singleton */ private static $__instance = null; /** * Class variables */ private $defaults = array( 'taxonomies' => false, 'show_post_count' => false, 'limit' => '', 'before' => '', 'after' => '', 'echo' => true, ); private $cache_key_incrementor = 'incrementor'; private $cache_group = 'date_based_taxonomy_archives'; private $filter_archive_links = false; /** * Silence is golden! */ private function __construct() {} /** * Singleton implementation * * @uses self::setup * @return object */ public static function get_instance() { if ( ! is_a( self::$__instance, __CLASS__ ) ) { self::$__instance = new self; self::$__instance->setup(); } return self::$__instance; } /** * Magic getter to provide access to class variables that were public prior to v0.3. * * @param string $name * @return mixed */ public function __get( $name ) { if ( property_exists( $this, $name ) ) return $this->$name; else return null; } /** * Register actions and filters * * @uses add_action * @uses add_filter * @return null */ private function setup() { add_filter( 'date_based_taxonomy_archives_where', array( $this, 'filter_date_based_taxonomy_archives_where' ), 10, 2 ); add_filter( 'date_based_taxonomy_archives_join', array( $this, 'filter_date_based_taxonomy_archives_join' ), 10, 2 ); add_filter( 'get_archives_link', array( $this, 'filter_get_archives_link' ) ); add_action( 'generate_rewrite_rules', array( $this, 'action_generate_rewrite_rules' ) ); add_action( 'transition_post_status', array( $this, 'action_transition_post_status' ), 50, 2 ); } /** * Render unordered lists of monthly archive links grouped by year * * @global $wpdb * @global $wp_locale * @param array $args * @uses apply_filters * @uses wp_parse_args * @uses absint * @uses this::get_incrementor * @uses wp_cache_get * @uses wp_cache_set * @uses number_format_i18n * @uses get_month_link * @uses get_archives_link * @return string or false */ function get_archives( $args = array() ) { global $wpdb, $wp_locale; $args = apply_filters( 'date_based_taxonomy_archives_args', $args ); $args = wp_parse_args( $args, $this->defaults ); extract( $args ); // Build query $where = apply_filters( 'date_based_taxonomy_archives_where', "WHERE post_type = 'post' AND post_status = 'publish'", $args ); $join = apply_filters( 'date_based_taxonomy_archives_join', '', $args ); if ( is_numeric( $limit ) ) $limit = ' LIMIT ' . absint( $limit ); $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date DESC $limit"; // Generate cache key, check cache, query DB if necessary and cache results $cache_key = $this->get_incrementor() . md5( $query ); if ( ! $results = wp_cache_get( $cache_key, $this->cache_group ) ) { $results = $wpdb->get_results( $query ); wp_cache_set( $cache_key, $results, $this->cache_group ); } // Bail out if necessary data isn't available if ( ! is_array( $results ) || empty( $results ) ) return false; // Render archive $output = '<ul>'; $cy = false; // Alias $after for use inside of foreach $_after = $after; foreach ( $results as $result ) { if ( $cy !== false && $cy != $result->year ) $output .= '</ul></li>'; if ( $cy === false || $cy != $result->year ) { $cy = $result->year; $output .= '<li><span>' . absint( $result->year ) . '</span>'; $output .= '<ul>'; } $url = get_month_link( $result->year, $result->month ); $text = $wp_locale->get_month( $result->month ); $after = $show_post_count ? ' (' . number_format_i18n( absint( $result->posts ) ) . ')' . $_after : $_after; $output .= get_archives_link( $url, $text, 'html', $before, $after ); } if ( $cy == $result->year ) $output .= '</ul></li>'; $output .= '</ul>'; // Reset archive links filter indicator $this->filter_archive_links = false; if ( $echo ) echo $output; else return $output; } /** * Filter where clause used in this::get_archives * * @global $wpdb * @param string $where * @param array $args * @uses apply_filters * @uses wp_parse_args * @uses is_category * @uses is_tag * @uses is_tax * @uses get_queried_object * @filter date_based_taxonomy_archives_where * @return string */ function filter_date_based_taxonomy_archives_where( $where, $args ) { global $wpdb; $args = apply_filters( 'date_based_taxonomy_archives_args', $args ); $args = wp_parse_args( $args, $this->defaults ); extract( $args ); if ( ( $taxonomies == 'all' && ( is_category() || is_tag() || is_tax() ) ) || ( $taxonomies == 'custom' && ! is_category() && ! is_tag() && is_tax() ) ) { $queried_object = get_queried_object(); if ( is_object( $queried_object ) && property_exists( $queried_object, 'term_taxonomy_id' ) ) $where .= $wpdb->prepare( ' AND dbtrtr.term_taxonomy_id = %d', $queried_object->term_taxonomy_id ); } elseif ( is_array( $taxonomies ) ) { $queried_object = get_queried_object(); if ( is_object( $queried_object ) && property_exists( $queried_object, 'term_taxonomy_id' ) && property_exists( $queried_object, 'taxonomy' ) && in_array( $queried_object->taxonomy, $taxonomies ) ) $where .= $wpdb->prepare( ' AND dbtrtr.term_taxonomy_id = %d', $queried_object->term_taxonomy_id ); } return $where; } /** * Filter join clause used in this::get_archives * * @global $wpdb * @param string $join * @param array $args * @uses apply_filters * @uses wp_parse_args * @uses is_category * @uses is_tag * @uses is_tax * @filter date_based_taxonomy_archives_join * @return string */ function filter_date_based_taxonomy_archives_join( $join, $args ) { global $wpdb; $args = apply_filters( 'date_based_taxonomy_archives_args', $args ); $args = wp_parse_args( $args, $this->defaults ); extract( $args ); if ( ( $taxonomies == 'all' && ( is_category() || is_tag() || is_tax() ) ) || ( $taxonomies == 'custom' && ! is_category() && ! is_tag() && is_tax() ) || is_array( $taxonomies ) ) { $join .= " INNER JOIN {$wpdb->term_relationships} AS dbtrtr on dbtrtr.object_id = {$wpdb->posts}.ID"; $this->filter_archive_links = true; } return $join; } /** * Filter get_archives_link output to inject taxonomy and term slugs * * @global $wp_rewrite * @param string $link_html * @uses get_queried_object * @uses is_wp_error * @uses path_join * @uses trailingslashit * @uses home_url * @uses get_taxonomy * @uses esc_url * @uses add_query_arg * @filter get_archives_link * @return string */ function filter_get_archives_link( $link_html ) { if ( $this->filter_archive_links ) { global $wp_rewrite; $queried_object = get_queried_object(); if ( is_object( $queried_object ) && ! is_wp_error( $queried_object ) ) { $exploded = explode( "'", $link_html ); if ( $wp_rewrite->using_permalinks() && array_key_exists( $queried_object->taxonomy, $wp_rewrite->extra_permastructs ) ) { $term_rewrite = preg_replace( '#%[^%]+%#i', $queried_object->slug, $wp_rewrite->extra_permastructs[$queried_object->taxonomy]['struct'] ); $term_rewrite = substr( $term_rewrite, 1 ); // Drop leading slash, otherwise path_join misinterprets request $term_rewrite = path_join( $term_rewrite, 'date' ); $exploded[1] = str_replace( trailingslashit( home_url() ), trailingslashit( path_join( home_url(), $term_rewrite ) ), $exploded[1] ); } else { $taxonomy = get_taxonomy( $queried_object->taxonomy ); if ( is_object( $taxonomy ) && ! is_wp_error( $taxonomy ) ) $exploded[1] = esc_url( add_query_arg( $taxonomy->query_var, $queried_object->slug, $exploded[1] ) ); } $link_html = implode( "'", $exploded ); } } return $link_html; } /** * Add rewrite rules to support [taxonomy]/[term]/date/[year]/[month]/page/[number] * * @param object $wp_rewrite * @uses get_taxonomies * @action generate_rewrite_rules * @return null */ function action_generate_rewrite_rules( $wp_rewrite ) { $taxonomies = get_taxonomies( null, 'objects' ); if ( is_array( $taxonomies ) && ! empty( $taxonomies ) ) { $rules = array(); foreach ( $taxonomies as $taxonomy ) $rules[$taxonomy->rewrite['slug'] . '/(.+?)/date/([0-9]{4})/([0-9]{1,2})/?(page/?([0-9]{1,})/?)?$'] = 'index.php?' . $taxonomy->query_var . '=$matches[1]&year=$matches[2]&monthnum=$matches[3]&paged=$matches[5]'; $wp_rewrite->rules = $rules + $wp_rewrite->rules; } } /** * Return cache incrementor. To invalidate caches, incrementor is deleted via this::action_transition_post_status. * * @uses wp_cache_get * @uses wp_cache_set * @return int */ function get_incrementor() { $incrementor = wp_cache_get( $this->cache_key_incrementor, $this->cache_group ); if ( ! is_numeric( $incrementor ) ) { $incrementor = time(); wp_cache_set( $this->cache_key_incrementor, $incrementor, $this->cache_group ); } return (int) $incrementor; } /** * Invalidate caches when posts are published or published posts are updated * * @param string $new_status * @param string $old_status * @uses wp_cache_delete * @action transition_post_status * @return null */ function action_transition_post_status( $new_status, $old_status ) { if ( $new_status == 'publish' || $old_status == 'publish' ) wp_cache_delete( $this->cache_key_incrementor, $this->cache_group ); } } Date_Based_Taxonomy_Archives::get_instance(); /** * Alias global variable used to hold class prior to singleton implementation in v0.3. */ $GLOBALS['date_based_taxonomy_archives'] = Date_Based_Taxonomy_Archives::get_instance(); /** * Render unordered lists of monthly archive links grouped by year * * @param array $args * @uses Date_Based_Taxonomy_Archives::get_instance * @return string or false */ function date_based_taxonomy_archives( $args = array() ) { return Date_Based_Taxonomy_Archives::get_instance()->get_archives( $args ); }