[WordPress] redefining the default calendar

So I had a customer that needed a very interesting change in their default calendar.  It’s really two parts and I’ll show you how I overcame both.  Normally I would write a plugin myself, but being that the customer is trying to stick to a budget I decided to find one, unfortunately it only did half the job.

Part One:  Make calendar pull only from one category

/* code to put in your functions.php file */
 
function widget_customCalendar($args) {
	extract($args);
	echo $before_widget;
	echo get_calendar_custom(7); /* 7 is the category id you want to limit posts to */
	echo $after_widget;
}
 
if ( function_exists('register_sidebar_widget') ) {
	register_sidebar_widget('Customized Calendar', 'widget_customCalendar');
}
 
function get_calendar_custom($catid,$initial = true) {
	global $wpdb, $m, $monthnum, $year, $wp_locale, $posts;
 
	$key = md5( $m . $monthnum . $year );
	if ( $cache = wp_cache_get( 'get_calendar_custom', 'calendar_custom' ) ) {
		if ( isset( $cache[ $key ] ) ) {
			echo $cache[ $key ];
			return;
		}
	}
 
	ob_start();
	// Quick check. If we have no posts at all, abort!
	if ( !$posts ) {
		$gotsome = $wpdb->get_var("SELECT ID from $wpdb->posts WHERE post_type = 'post' AND post_status = 'publish' ORDER BY post_date DESC LIMIT 1");
		if ( !$gotsome )
			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
		LEFT JOIN $wpdb->term_relationships ON($wpdb->posts.ID = $wpdb->term_relationships.object_id)
		LEFT JOIN $wpdb->term_taxonomy ON($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id) 
 
		WHERE post_date < '$thisyear-$thismonth-01'
 
		AND $wpdb->term_taxonomy.term_id IN ($catid)
		AND $wpdb->term_taxonomy.taxonomy = 'category' 
 
		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
		LEFT JOIN $wpdb->term_relationships ON($wpdb->posts.ID = $wpdb->term_relationships.object_id)
		LEFT JOIN $wpdb->term_taxonomy ON($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)
 
		WHERE post_date >	'$thisyear-$thismonth-01'
 
		AND $wpdb->term_taxonomy.term_id IN ($catid)
		AND $wpdb->term_taxonomy.taxonomy = 'category' 
 
		AND MONTH( post_date ) != MONTH( '$thisyear-$thismonth-01' )
		AND post_type = 'post' AND post_status = 'publish'
			ORDER	BY post_date ASC
			LIMIT 1");
 
	echo '<div id="calendar_wrap">
	<table id="wp-calendar" summary="' . __('Calendar') . '">
	<caption>' . sprintf(_c('%1$s %2$s|Used as a 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);
		echo "\n\t\t<th abbr=\"$wd\" scope=\"col\" title=\"$wd\">$day_name</th>";
	}
 
	echo '
	</tr>
	</thead>
 
	<tfoot>
	<tr>';
 
	if ( $previous ) {
		echo "\n\t\t".'<td abbr="' . $wp_locale->get_month($previous->month) . '" 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))) . '">« ' . $wp_locale->get_month_abbrev($wp_locale->get_month($previous->month)) . '</a></td>';
	} else {
		echo "\n\t\t".'<td colspan="3" id="prev"> </td>';
	}
 
	echo "\n\t\t".'<td> </td>';
 
	if ( $next ) {
		echo "\n\t\t".'<td abbr="' . $wp_locale->get_month($next->month) . '" colspan="3" id="next"><a href="' .
		get_month_link($next->year, $next->month) . '" title="' . 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)) . ' »</a></td>';
	} else {
		echo "\n\t\t".'<td colspan="3" id="next"> </td>';
	}
 
	echo '
	</tr>
	</tfoot>
 
	<tbody>
	<tr>';
 
	// Get days with posts
	$dyp_sql = "SELECT DISTINCT DAYOFMONTH(post_date)
		FROM $wpdb->posts 
 
		LEFT JOIN $wpdb->term_relationships ON($wpdb->posts.ID = $wpdb->term_relationships.object_id)
		LEFT JOIN $wpdb->term_taxonomy ON($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id) 
 
		WHERE MONTH(post_date) = '$thismonth' 
 
		AND $wpdb->term_taxonomy.term_id IN ($catid)
		AND $wpdb->term_taxonomy.taxonomy = 'category' 
 
		AND YEAR(post_date) = '$thisyear'
		AND post_type = 'post' AND post_status = 'publish'
		AND post_date < '" . current_time('mysql') . "'";
 
	$dayswithposts = $wpdb->get_results($dyp_sql, ARRAY_N);
 
	if ( $dayswithposts ) {
		foreach ( (array) $dayswithposts as $daywith ) {
			$daywithpost[] = $daywith[0];
		}
	} else {
		$daywithpost = array();
	}
 
	if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false || strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'camino') !== false || strpos(strtolower($_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 post_title, DAYOFMONTH(post_date) as dom "
		."FROM $wpdb->posts "
 
		."LEFT JOIN $wpdb->term_relationships ON($wpdb->posts.ID = $wpdb->term_relationships.object_id) "
		."LEFT JOIN $wpdb->term_taxonomy ON($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id) "
 
		."WHERE YEAR(post_date) = '$thisyear' "
 
		."AND $wpdb->term_taxonomy.term_id IN ($catid) "
		."AND $wpdb->term_taxonomy.taxonomy = 'category' "
 
		."AND MONTH(post_date) = '$thismonth' "
		."AND post_date < '".current_time('mysql')."' "
		."AND post_type = 'post' AND post_status = 'publish'"
	);
	if ( $ak_post_titles ) {
		foreach ( (array) $ak_post_titles as $ak_post_title ) {
 
				$post_title = apply_filters( "the_title", $ak_post_title->post_title );
				$post_title = str_replace('"', '"', wptexturize( $post_title ));
 
				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 )
		echo "\n\t\t".'<td colspan="'.$pad.'"> </td>';
 
	$daysinmonth = intval(date('t', $unixmonth));
	for ( $day = 1; $day <= $daysinmonth; ++$day ) {
		if ( isset($newrow) && $newrow )
			echo "\n\t</tr>\n\t<tr>\n\t\t";
		$newrow = false;
 
		if ( $day == gmdate('j', (time() + (get_option('gmt_offset') * 3600))) && $thismonth == gmdate('m', time()+(get_option('gmt_offset') * 3600)) && $thisyear == gmdate('Y', time()+(get_option('gmt_offset') * 3600)) )
			echo '<td id="today">';
		else
			echo '<td>';
 
		if ( in_array($day, $daywithpost) ) // any posts today?
				echo '<a href="' . get_day_link($thisyear, $thismonth, $day) . "\" title=\"$ak_titles_for_day[$day]\">$day</a>";
		else
			echo $day;
		echo '</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 )
		echo "\n\t\t".'<td colspan="'.$pad.'"> </td>';
 
	echo "\n\t</tr>\n\t</tbody>\n\t</table></div>";
 
	$output = ob_get_contents();
	ob_end_clean();
	echo $output;
	$cache[ $key ] = $output;
	wp_cache_set( 'get_calendar_custom', $cache, 'calendar_custom' );
}

Part Two: Allow this new calendar to show future posts (to be used as an events listing)

To make this happen we need to tell the wordpress queries within our calendar function to also get posts with post_status=’future’ .   I’ll give one example and then you will copy the way the example is for all the corresponding spots (I’ll give you the line numbers based on the code above)

Original: Line #30

$gotsome = $wpdb->get_var("SELECT ID from $wpdb->posts WHERE post_type = 'post' AND post_status = 'publish' ORDER BY post_date DESC LIMIT 1");

We will replace the post_status = ‘publish’ with (post_status = ‘publish’ || post_status = ‘future’) as shown below. This tells the query to find posts that are either published or scheduled in the future.

$gotsome = $wpdb->get_var("SELECT ID from $wpdb->posts WHERE post_type = 'post' AND (post_status = 'publish' || post_status = 'future') ORDER BY post_date DESC LIMIT 1");

The following line numbers will also need to be replaced:
#74, #89, #155 and #187.

You can also see the pastie here: http://pastie.org/491698

Hope this helps you!

7 thoughts on “[WordPress] redefining the default calendar

  1. James M says:

    Great post!! Excellent addition to wp!

    Can you please detail how to added ‘Future’ as a Publish status option when creating / editing a post in wp-admin? Thanks!

  2. James M says:

    Ahh I did not see you can set the publish date into the future when editing a post. So if it’s an event you just set the post to the date of the event. Very sneaky :p And now I see in the database the post-status field is ‘future’

    So the post will not show up in the blog till that date. Any idea on how to get the best of both worlds? So the post can be set as future AND show up in the blog prior to the event. Guessing there would need to be a custom solution similar to the SQL statements you created. Thanks for this great post!!

  3. Mannie says:

    Shoot , the code disappeared (as a suspected it might).

    I left out the php tags, but here’s how you query future posts:

    query_posts(‘post_status=future&showposts=-1&order=ASC’);

    (Using “-1″ shows all posts.)

  4. snails says:

    Hi Tony
    I’ve been looking for something similar to this. Would it be possible to use a custom field to store the date of an event, and then use something similar to show future posts but using the date that is stored in the custom field??
    So in other words, query a custom field (or multiple fields) instead of ‘future posts’.
    Thanks

  5. Tony Tony says:

    Yes Snails, that is possible. It would require rewriting some of the query, particularly replacing the parts that use post_date to use whatever custom field you create.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>