Overriding default calendar to show posts from a category

I'm trying to modify the default WP calendar (get_calendar()) so that it shows posts from a specific category and then use that as a shortcode.

Here's what I've done:

Copied across the get_calendar() function from the core (from wp-includes) into my child theme's functions.php file. In order to make it an 'independent' calendar, I changed the name of the get_calendar() function to osu_get_calendar() and have successfully hardcoded the calendar into my template using osu_get_calendar();

Now I'm trying to get my hands a bit dirtier and do the following:

1) Restrict the posts shown in the calendar to a specific category (while maintaining the previous and next navigation so visitors can search through those posts by month)

2) Make it into a shortcode so my client can essentially embed a calendar with posts from a category they choose at the bottom of their posts.

I'm stuck on 1) at the moment as it looks like I need to modify the SQL queries so that it pulls posts from a specific category. Looking at the code below from the get_calendar() function, how would I filter the posts pulled from the database so that they are from a specific category? There are other SQL queries in there, but hopefully someone can help me with this one for now:

$previous = $wpdb-get_row("SELECT DISTINCT MONTH(post_date) AS month, YEAR(post_date) AS year
        FROM $wpdb-posts
        WHERE post_date  '$thisyear-$thismonth-01'
        AND post_type = 'post' AND post_status = 'publish'
            ORDER BY post_date DESC
            LIMIT 1");
    $next = $wpdb-get_row("SELECT  DISTINCT MONTH(post_date) AS month, YEAR(post_date) AS year
        FROM $wpdb-posts
        WHERE post_date    '$thisyear-$thismonth-01'
        AND MONTH( post_date ) != MONTH( '$thisyear-$thismonth-01' )
        AND post_type = 'post' AND post_status = 'publish'
            ORDER   BY post_date ASC
            LIMIT 1");

Thanks,

osu

Topic calendar filters categories query Wordpress sql

Category Web


You were starting on the right track but you had several bits of WordPress hookage you still needed to add. An approach I like to use is to encapsulate a call to get_calendar() in a class so we can use the low-level 'query' hook but only use for it for the one call.

So I wrote up an example which you can drop into your theme's functions.php file (or in a .php file for a plugin you might be writing) using a class I named YourSite_Category_Calendar(), and here is how you'd call it in place of your call to get_calendar():

$cc = new YourSite_Category_Calendar('your-category');
echo $cc->get_calendar();

And here's the code for the class:

<?php 

class YourSite_CategoryCalendar {
  var $category;
  var $initial;
  var $echo;
  static function on_load() {
    add_shortcode('category-calendar',array(__CLASS__,'shortcode'));
    add_action('init',array(__CLASS__,'init'));
    global $wp_rewrite;
    $wp_rewrite->add_rule('^events/([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})/([^/]+)/?$',
      'index.php?post_type=event&year=$matches[1]&monthnum=$matches[2]&day=$matches[3]&category_name=$matches[4]',
      'top');
    $wp_rewrite->flush_rules(false);  // Remove this after you've got it working
  }
  static function shortcode($attributes) {
    $attributes = wp_parse_args($attributes,array(
      'category' => false,
    ));
    $cc = new YourSite_CategoryCalendar($attributes['category']);
    echo $cc->get_calendar();
  }
  static function init() {
    register_post_type('event',array(
      'hierarchical'    => true,
      'label'          => 'Events',
      'public'          => true,
      'show_ui'         => true,
      'query_var'       => 'event',
      'rewrite'         => array('slug' => 'events'),
      'supports'        => array('title','editor','custom-fields'),
      'taxonomies'      => array('category'),
    ));
  }
  function __construct($category,$initial=true,$echo=true) {
    $this->category = $category;
    $this->initial = $initial;
    $this->echo = $echo;
  }
  function get_calendar() {
    add_filter('query',array(&$this,'query'));
    ob_start();
    get_calendar($this->category,$this->initial,$this->echo);
    $calendar = ob_get_clean();
    remove_filter('query',array(&$this,'query'));
    list($header,$body) = explode('<tbody>',$calendar);
    $find = '#(href="http://[^/]+)(/[0-9]{4}/[0-9]{1,2}/[0-9]{1,2}/)#';
    $replace = '$1/events$2'.$this->category.'/"';
    $body = preg_replace($find,$replace,$body);
    return "{$header}<tbody>{$body}";
  }
  function query($query) {
    if ($this->category) {
      global $wpdb;
      $find = "FROM {$wpdb->posts}\\s+WHERE";
      $add =<<<SQL
INNER JOIN {$wpdb->term_relationships} calendar_term_relationship ON calendar_term_relationship.object_id={$wpdb->posts}.ID
INNER JOIN {$wpdb->term_taxonomy} calendar_term_taxonomy ON calendar_term_taxonomy.term_taxonomy_id=calendar_term_relationship.term_taxonomy_id
INNER JOIN {$wpdb->terms} calendar_term ON calendar_term.term_id=calendar_term_taxonomy.term_id
WHERE calendar_term_taxonomy.taxonomy='category' AND calendar_term.slug='%s' AND
SQL;
      $replace = "FROM {$wpdb->posts} {$add} ";
      $query = preg_replace("#{$find}#Us",$replace,$query);
      $query = preg_replace("#post_type\s*=\s*'post'#","post_type='event'",$query);
      $query = $wpdb->prepare($query,$this->category);
    }
    return $query;
  }
}
YourSite_CategoryCalendar::on_load();

UPDATE

Based on the comments I've added the URLs rewriting needed as well as a shortcode you'd call like this:

[category-calendar category="party"]

About

Geeks Mental is a community that publishes articles and tutorials about Web, Android, Data Science, new techniques and Linux security.