Adding Custom Post Types To get_calendar() And The Calendar Widget

By default or design, the WordPress function `get_calendar()`[^1] does not handle post types other than `post`. I’ve updated it as the standalone function `ucc_get_calendar()` to allow it to accept a `$post_types` array; the included filter function `ucc_get_calendar_filter()` will allow for seamless integration via `functions.php` without requiring additional editing of Theme templates. (Note that the filter will also apply to the Calendar Widget’s output.)

[php]
/* ucc_get_calendar() :: Extends get_calendar() by including custom post types.
* Derived from get_calendar() code in /wp-includes/general-template.php.
*/

function ucc_get_calendar( $post_types = ” , $initial = true , $echo = true ) {
global $wpdb, $m, $monthnum, $year, $wp_locale, $posts;

if ( empty( $post_types ) || !is_array( $post_types ) ) {
$args = array(
‘public’ => true ,
‘_builtin’ => false
);
$output = ‘names’;
$operator = ‘and’;

$post_types = get_post_types( $args , $output , $operator );
$post_types = array_merge( $post_types , array( ‘post’ ) );
} else {
/* Trust but verify. */
$my_post_types = array();
foreach ( $post_types as $post_type ) {
if ( post_type_exists( $post_type ) )
$my_post_types[] = $post_type;
}
$post_types = $my_post_types;
}
$post_types_key = implode( ” , $post_types );
$post_types = "’" . implode( "’ , ‘" , $post_types ) . "’";

$cache = array();
$key = md5( $m . $monthnum . $year . $post_types_key );
if ( $cache = wp_cache_get( ‘get_calendar’ , ‘calendar’ ) ) {
if ( is_array( $cache ) && isset( $cache[$key] ) ) {
remove_filter( ‘get_calendar’ , ‘ucc_get_calendar_filter’ );
$output = apply_filters( ‘get_calendar’, $cache[$key] );
add_filter( ‘get_calendar’ , ‘ucc_get_calendar_filter’ );
if ( $echo ) {
echo $output;
return;
} else {
return $output;
}
}
}

if ( !is_array( $cache ) )
$cache = array();

// Quick check. If we have no posts at all, abort!
if ( !$posts ) {
$sql = "SELECT 1 as test FROM $wpdb->posts WHERE post_type IN ( $post_types ) AND post_status = ‘publish’ LIMIT 1";
$gotsome = $wpdb->get_var( $sql );
if ( !$gotsome ) {
$cache[$key] = ”;
wp_cache_set( ‘get_calendar’ , $cache , ‘calendar’ );
return;
}
}

if ( isset( $_GET[‘w’] ) )
$w = ” . intval( $_GET[‘w’] );

// week_begins = 0 stands for Sunday
$week_begins = intval( get_option( ‘start_of_week’ ) );

// Let’s figure out when we are
if ( !empty( $monthnum ) && !empty( $year ) ) {
$thismonth = ” . zeroise( intval( $monthnum ) , 2 );
$thisyear = ”.intval($year);
} elseif ( !empty( $w ) ) {
// We need to get the month from MySQL
$thisyear = ” . intval( substr( $m , 0 , 4 ) );
$d = ( ( $w – 1 ) * 7 ) + 6; //it seems MySQL’s weeks disagree with PHP’s
$thismonth = $wpdb->get_var( "SELECT DATE_FORMAT( ( DATE_ADD( ‘${thisyear}0101’ , INTERVAL $d DAY ) ) , ‘%m’ ) " );
} elseif ( !empty( $m ) ) {
$thisyear = ” . intval( substr( $m , 0 , 4 ) );
if ( strlen( $m ) < 6 )
$thismonth = ’01’;
else
$thismonth = ” . zeroise( intval( substr( $m , 4 , 2 ) ) , 2 );
} else {
$thisyear = gmdate( ‘Y’ , current_time( ‘timestamp’ ) );
$thismonth = gmdate( ‘m’ , current_time( ‘timestamp’ ) );
}

$unixmonth = mktime( 0 , 0 , 0 , $thismonth , 1 , $thisyear);

// Get the next and previous month and year with at least one post
$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 IN ( $post_types ) 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 IN ( $post_types ) AND post_status = ‘publish’
ORDER BY post_date ASC
LIMIT 1" );

/* translators: Calendar caption: 1: month name, 2: 4-digit year */
$calendar_caption = _x( ‘%1$s %2$s’ , ‘calendar caption’ );
$calendar_output = ‘<table id="wp-calendar" summary="’ . esc_attr__( ‘Calendar’ ) . ‘">
<caption>’ . sprintf( $calendar_caption , $wp_locale->get_month( $thismonth ) , date( ‘Y’ , $unixmonth ) ) . ‘</caption>
<thead>
<tr>’;

$myweek = array();

for ( $wdcount = 0 ; $wdcount <= 6 ; $wdcount++ ) {
$myweek[] = $wp_locale->get_weekday( ( $wdcount + $week_begins ) % 7 );
}

foreach ( $myweek as $wd ) {
$day_name = ( true == $initial ) ? $wp_locale->get_weekday_initial( $wd ) : $wp_locale->get_weekday_abbrev( $wd );
$wd = esc_attr( $wd );
$calendar_output .= "\n\t\t<th scope=\"col\" title=\"$wd\">$day_name</th>";
}

$calendar_output .= ‘
</tr>
</thead>

<tfoot>
<tr>’;

if ( $previous ) { $calendar_output .= "\n\t\t" . ‘<td colspan="3" id="prev"><a href="’ . get_month_link( $previous->year , $previous->month ) . ‘" title="’ . sprintf( __( ‘View posts for %1$s %2$s’ ) , $wp_locale->get_month( $previous->month ) , date( ‘Y’ , mktime( 0 , 0 , 0 , $previous->month , 1 , $previous->year ) ) ) . ‘">&laquo; ‘ . $wp_locale->get_month_abbrev( $wp_locale->get_month( $previo
us->month ) ) . ‘</a></td>’;
} else {
$calendar_output .= "\n\t\t" . ‘<td colspan="3" id="prev" class="pad">&nbsp;</td>’;
}

$calendar_output .= "\n\t\t" . ‘<td class="pad">&nbsp;</td>’;

if ( $next ) { $calendar_output .= "\n\t\t" . ‘<td colspan="3" id="next"><a href="’ . get_month_link( $next->year , $next->month ) . ‘" title="’ . esc_attr( sprintf( __( ‘View posts for %1$s %2$s’ ) , $wp_locale->get_month( $next->month ) , date( ‘Y’ , mktime( 0 , 0 , 0 , $next->month , 1 , $next->year ) ) ) ) . ‘">’ . $wp_locale->get_month_abbrev( $wp_locale->get_month( $next->month ) ) . ‘ &r
aquo;</a></td>’;
} else {
$calendar_output .= "\n\t\t" . ‘<td colspan="3" id="next" class="pad">&nbsp;</td>’;
}

$calendar_output .= ‘
</tr>
</tfoot>

<tbody>
<tr>’;

// Get days with posts
$dayswithposts = $wpdb->get_results( "SELECT DISTINCT DAYOFMONTH( post_date )
FROM $wpdb->posts WHERE MONTH( post_date ) = ‘$thismonth’
AND YEAR( post_date ) = ‘$thisyear’
AND post_type IN ( $post_types ) AND post_status = ‘publish’
AND post_date < ‘" . current_time( ‘mysql’ ) . ‘\”, ARRAY_N );
if ( $dayswithposts ) {
foreach ( (array) $dayswithposts as $daywith ) {
$daywithpost[] = $daywith[0];
}
} else {
$daywithpost = array();
}

if ( strpos( $_SERVER[‘HTTP_USER_AGENT’] , ‘MSIE’ ) !== false || stripos( $_SERVER[‘HTTP_USER_AGENT’] , ‘camino’ ) !== false || stripos( $_SERVER[‘HTTP_USER_AGENT’] , ‘safari’ ) !== false )
$ak_title_separator = "\n";
else
$ak_title_separator = ‘, ‘;

$ak_titles_for_day = array();
$ak_post_titles = $wpdb->get_results( "SELECT ID, post_title, DAYOFMONTH( post_date ) as dom "
. "FROM $wpdb->posts "
. "WHERE YEAR( post_date ) = ‘$thisyear’ "
. "AND MONTH( post_date ) = ‘$thismonth’ "
. "AND post_date < ‘" . current_time( ‘mysql’ ) . "’ "
. "AND post_type IN ( $post_types ) AND post_status = ‘publish’"
);
if ( $ak_post_titles ) {
foreach ( (array) $ak_post_titles as $ak_post_title ) {

$post_title = esc_attr( apply_filters( ‘the_title’ , $ak_post_title->post_title , $ak_post_title->ID ) );

if ( empty( $ak_titles_for_day[‘day_’ . $ak_post_title->dom] ) )
$ak_titles_for_day[‘day_’.$ak_post_title->dom] = ”;
if ( empty( $ak_titles_for_day["$ak_post_title->dom"] ) ) // first one
$ak_titles_for_day["$ak_post_title->dom"] = $post_title;
else
$ak_titles_for_day["$ak_post_title->dom"] .= $ak_title_separator . $post_title;
}
}

// See how much we should pad in the beginning
$pad = calendar_week_mod( date( ‘w’ , $unixmonth ) – $week_begins );
if ( 0 != $pad )
$calendar_output .= "\n\t\t" . ‘<td colspan="’ . esc_attr( $pad ) . ‘" class="pad">&nbsp;</td>’;

$daysinmonth = intval( date( ‘t’ , $unixmonth ) );
for ( $day = 1 ; $day <= $daysinmonth ; ++$day ) {
if ( isset( $newrow ) && $newrow )
$calendar_output .= "\n\t</tr>\n\t<tr>\n\t\t";
$newrow = false;

if ( $day == gmdate( ‘j’ , current_time( ‘timestamp’ ) ) && $thismonth == gmdate( ‘m’ , current_time( ‘timestamp’ ) ) && $thisyear == gmdate( ‘Y’ , current_time( ‘timestamp’ ) ) )
$calendar_output .= ‘<td id="today">’;
else
$calendar_output .= ‘<td>’;

if ( in_array( $day , $daywithpost ) ) // any posts today?
$calendar_output .= ‘<a href="’ . get_day_link( $thisyear , $thismonth , $day ) . "\" title=\"" . esc_attr( $ak_titles_for_day[$day] ) . "\">$day</a>";
else
$calendar_output .= $day;
$calendar_output .= ‘</td>’;

if ( 6 == calendar_week_mod( date( ‘w’ , mktime( 0 , 0 , 0 , $thismonth , $day , $thisyear ) ) – $week_begins ) )
$newrow = true;
}

$pad = 7 – calendar_week_mod( date( ‘w’ , mktime( 0 , 0 , 0 , $thismonth , $day , $thisyear ) ) – $week_begins );
if ( $pad != 0 && $pad != 7 )
$calendar_output .= "\n\t\t" . ‘<td class="pad" colspan="’ . esc_attr( $pad ) . ‘">&nbsp;</td>’;

$calendar_output .= "\n\t</tr>\n\t</tbody>\n\t</table>";

$cache[$key] = $calendar_output;
wp_cache_set( ‘get_calendar’ , $cache, ‘calendar’ );

remove_filter( ‘get_calendar’ , ‘ucc_get_calendar_filter’ );
$output = apply_filters( ‘get_calendar’, $calendar_output );
add_filter( ‘get_calendar’ , ‘ucc_get_calendar_filter’ );

if ( $echo )
echo $output;
else
return $output;
}

function ucc_get_calendar_filter( $content ) {
$output = ucc_get_calendar( ” , ” , false );
return $output;
}
add_filter( ‘get_calendar’ , ‘ucc_get_calendar_filter’ , 10 , 2 );
[/php]

## References

[^1]: [Function Reference/get calendar](http://codex.wordpress.org/Function_Reference/get_calendar)

31 thoughts on “Adding Custom Post Types To get_calendar() And The Calendar Widget

  1. I don’t understand why you’ve chosen to post the PHP code like this – would you mind bundling it up in a .php file and upload it somewhere? When copy-pasting this, I get a lot of PHP code inside weird comments and other poor formatting.

  2. Sorry about that, I recently swapped out code formatting plugins and have not finished going through my archives. I’m working on moving the code to github as well.

  3. Hello!

    I also had several broken-code issues with the copy-pasting the code. Nevertheless, the code works like a charm now, just what I was looking for. I was browsing through the core source code myself to see if the SQL can be tweaked, but apparently it is hardcoded. Thank you very much for this time-saver.

  4. Just to follow up.

    I’m doing some site speed-ups and have discovered that your approach with filter doubles the calendar work and calendar database calls. When you add the filter to ‘get_calendar’, it first calls the generation of the original calendar (received in $content variable, which is unused) and then you call once again the generation of your custom calendar. Not that it takes a lot of time, but maybe it matters to someone. That’s why I’ve decided to migrate your code directly to the default widgets file and change the original calendar widget. It now calls your function directly instead of the original.

    I’ve also removed the first mysql query which checks if there are any published posts. That call might matter when you first start with your blog, but it doesn’t seem to matter after the first published post, so there’s one more speed-up.

    Cheers!

  5. Hi Jennifer,

    I receive a t-string syntax error right on the starting line of your code when I paste this into functions.php
    I am not an experienced php coder, but I can troubleshoot and resolve with a helping hand.
    I did insure this code is inside the close tag. I am running wp 3.32
    Any ideas?
    Thanks!

  6. I really like your usage here. I was able to implement with one of my custom post types. But could you direct me to the lines of code I could edit to have the calendar put the links on the days of the specific value (date in the form of: 9/11/2012) of one of my meta fields that the user inputs instead of just links on the days the post was created.

  7. Hey Man,

    Thanks for the code, but I am having an issue with one of the sites I am working on. I am actually stepping in for the original developer which makes things a little more difficult since I am not sure how he went about using your code.

    This code has been altered to show events from the Timely All In One Event Calendar plugin. It is able to pull in all the events (except the events set to reoccurring, but once I figure this out I will work on that), however it is displaying events on the wrong dates. I found this happening is because this calendar is saying the events are happening 5 hours ahead of what they are actually set to be. (Ex. Click on Nov. 16th, the event is at 7:30pm on Nov. 15th, but the calendar says it’s happening at 12:30am Nov. 16th.)

    I am assuming that this is a timezone issue, but I have been unable to figure out how to change the timezone in this code (or just subtract 5 hours). Is there an easy way to go about this?

  8. Hi any possibility of getting a raw pastable version of this code? Every time I try copy/pasting directly from this site it causes my theme to whitescreen regardless of correctly opening/closing my php tags. And THANK YOU for the solution!
    Best,
    Shaun

Comments are closed.