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. By default or design, the WordPress function get_calendar()1 does not handle post types other than post.

    Some more info on the subject can be found here:
    http://core.trac.wordpress.org/ticket/7155

    Your function works as expected, just great!

    All your articles considering custom post types/taxonomies are very usefull. I have just one custom post type but it has been causing huge confusion for me. I start to wonder why aren’t functions like yours in core?

  2. Hello,

    Would you know why this would cause the site to be blank after putting your code in functions.php? I can’t see what it is conflicting with. Its so very annoying that custom post types don’t appear on the calendar by default. I am trying to use custom posts for events using future publish dates. It works fine but just not on the calendar….

    cheers

    Mark

  3. It sounds like the file is no longer parsing correctly in PHP–might you have pasted the code outside of the <?php?> tags in the functions.php file?

  4. Hi!

    Your code makes the calendar widget shows links for the days that I have any custom post type published on. However, it doesn’t actually archive the custom post types them-selves. So I found nothing if I click on any of those links.

    I’ve tried to install and activate Simple Custom Post Types Archive plugin, but the links remain pointing to nothing.

    Could you please help me? Also, is there a way I can limit the types of posts to show on the calendar?

  5. You can use this code to include custom post types in your archive pages.

    To limit the types of posts shown in the calendar, change this line:
    $post_types = array_merge( $post_types , array( 'post' ) );
    to:
    $post_types = array( 'post' , 'event' , 'tweet' );
    where your custom post types are listed in the array.

  6. The code in that page doesn’t include my custom post types in the archive pages. Are you sure you meant that code, because I see it unrelated thread: Custom Post Types In The Loop: Using pre_get_posts, Refined

  7. I did mean Custom Post Types In The Loop: Using pre_get_posts, Refined. This will include custom post types in The Loop, which is what displays posts on archive pages.

  8. Oh, it works after deactivating Custom Post Type Archives, which I installed a while ago trying to get that working.

    Thanks, J.M Dodd!

  9. Last question: Can I create a template for a custom post type archive, or I have to use the same template for all archive pages?

    Thanks!

Comments are closed.