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");



1 Answer 1


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:


class YourSite_CategoryCalendar {
  var $category;
  var $initial;
  var $echo;
  static function on_load() {
    global $wp_rewrite;
    $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() {
      '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() {
    $calendar = ob_get_clean();
    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
      $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;


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"]
  • Hi Mike, this looks extremely useful (not only for this project, but as an approach for future projects). Many thanks for taking the time to share your knowledge - I shouldn't come up against any problems using it with a custom post type right? I intend to use it with an 'Events' post type with a hierarchical custom taxonomy...
    – Osu
    Commented Jan 24, 2011 at 8:52
  • @Osu - You'll have to search the query for post_type = 'post' and replace with ` post_type = 'event'` but other than that, no problem (btw, don't forgot to vote up and select a best answer when people go to the effort to help you here. :) Commented Jan 24, 2011 at 8:57
  • @MikeSchinkel - thanks for this, just voted for the answer. I just tried this out, but using this: <?php $cc = new YourSite_Category_Calendar('news'); echo $cc->get_calendar(); ?> came up with both Blog and News category posts in the calendar (on my localhost, I only have these two categories for posts). Any idea why?
    – Osu
    Commented Jan 24, 2011 at 9:14
  • @Osu - I know it's obvious but I have to ask, are the categories exclusive or to have have posts that have both categories? Also, are you using any plugins that may be conflicting with this? Commented Jan 24, 2011 at 9:45
  • @MikeSchinkel - No, posts aren't sharing a category. This is a vanilla install with only WP Dummy Content installed to see how content looks in the site. I'll see if that's conflicting, but I doubt it is considering it only adds content and a custom field
    – Osu
    Commented Jan 24, 2011 at 9:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.