A tag archive for «calendar»

Annual blog calendar

For quite some time, I had been meaning to create something like this on my website: an annual calendar (both for the current year and for each of the past years) in which, for every date, there is a link leading to a collection of blog posts published on that very day (if anything was actually published). On LiveJournal (and other blogging platforms), such a calendar existed and was very convenient as a tool for finding old articles and/or analysing blog activity (one’s own or that of others). In WordPress, however, such a calendar does not exist and, if I remember correctly, it never has (or perhaps it did exist so long ago that I have completely forgotten about it): there is only a similar calendar based on individual months, which is decidedly inconvenient for most searches and analyses.
To create something like this on my own, I needed a bit of free time: at last, I managed to find it! Even more importantly (and interestingly), that time was not wasted: I succeeded in creating the calendar archive. A working example is available via the link below, while in this article I will describe all the features of my calendar and provide the basic PHP and CSS code.
Features of my annual blog calendar for WordPress:
— full-year calendar (all 12 months are visible, regardless of the current date at the time of viewing);
— the ability to view past years (though not earlier than the year of the blog’s first public post);
— days with published posts are automatically turned into links leading to the corresponding dates in the article archive;
— when hovering over a linked day, a tooltip appears showing the number of posts published on that date;
— in calendars for past years, the current date («today») is highlighted with a clearly visible border;
— cells corresponding to days on which at least one post was published change their background colour;
— the calendar features a responsive design: on large and medium screens, a 3×4 grid of months is displayed, while on smaller screens the months are arranged in a column;
— database queries are optimised so that the calendar loads quickly even on blogs with tens of thousands of posts.
And now we move on to the calendar code (I hope you have read all the text above—it may help you find your way around).
The PHP code, which you can test directly as it is: simply copy and paste it into your page layout:

<?php
global $wpdb;


/* ======================================
   1. THE FIRST YEAR OF BLOG
====================================== */
$first_post_date = $wpdb->get_var("
SELECT post_date
FROM $wpdb->posts
WHERE post_status='publish'
AND post_type='post'
ORDER BY post_date ASC
LIMIT 1
");

$first_year = date('Y', strtotime($first_post_date));
$current_year = date('Y');
$year = isset($_GET['yr']) ? intval($_GET['yr']) : $current_year;

if ($year < $first_year) $year = $first_year;
if ($year > $current_year) $year = $current_year;


/* ======================================
   2. POST DATES (OPTIMISED SQL QUERY + CACHE)
====================================== */
$cache_key = 'calendar_'.$year;
$post_dates = get_transient($cache_key);
if ($post_dates === false){
	$post_dates = [];
	$results = $wpdb->get_results($wpdb->prepare("
	SELECT DATE(post_date) as post_day, COUNT(ID) as total
	FROM $wpdb->posts
	WHERE post_status='publish'
	AND post_type='post'
	AND YEAR(post_date)=%d
	GROUP BY post_day
	", $year));
	foreach ($results as $row){
		$post_dates[$row->post_day] = $row->total;
	}
	set_transient($cache_key,$post_dates,12*HOUR_IN_SECONDS);
	}


/* ======================================
   3. BROWSE BY YEAR
====================================== */
echo '<div class="calendar-nav">';
for ($y=$first_year; $y<=$current_year; $y++){
	if ($y==$year){
		echo '<span class="current-year">'.$y.'</span>';
	}else{
		echo '<a href="?yr='.$y.'">'.$y.'</a> ';
	}
}
echo '</div>';


/* ======================================
   4. ‘TODAY IN THE PAST YEARS’ COMPARISON
====================================== */
$today_month = date('m');
$today_day = date('d');
$today_key = $year.'-'.$today_month.'-'.$today_day;
$today_has_posts = isset($post_dates[$today_key]);


/* ======================================
   5. YEAR CONTAINER
====================================== */
echo '<div class="calendar-year">';


/* ======================================
   6. MONTHLY CYCLE
====================================== */
for ($month=1; $month<=12; $month++) {
	echo '<div class="calendar-month-block">';
	$month_name = date_i18n('F', mktime(0,0,0,$month,1,$year));
	$month_has_posts = false;
	foreach ($post_dates as $date => $count){
		if (strpos($date,$year.'-'.sprintf('%02d',$month))===0){
			$month_has_posts = true;
			break;
		}
	}

/* month title */
if ($month_has_posts){
	echo '<h3 class="month-title">
	<a href="'.get_month_link($year,$month).'">'.$month_name.'</a>
	</h3>';
	}else{
	echo '<h2 class="month-title">'.$month_name.'</h2>';
	}


/* ======================================
   7. CALENDAR TABLE
====================================== */
echo '<table class="calendar-month">';
echo '<thead><tr>';
$weekdays = ['M','T','W','T','F','S','S']; //to change manually to your language
foreach ($weekdays as $w){
	echo '<th>'.$w.'</th>';
	}
echo '</tr></thead>';
echo '<tbody><tr>';

/* first day of the month */
$first_day = date('N', strtotime("$year-$month-01"));
for ($i=1;$i<$first_day;$i++){
	echo '<td class="empty"></td>';
	}
$days = cal_days_in_month(CAL_GREGORIAN,$month,$year);
$weekday = $first_day;


/* ======================================
   8. CYCLE DAYS
====================================== */
for ($day=1;$day<=$days;$day++){
	$date = $year.'-'.sprintf('%02d',$month).'-'.sprintf('%02d',$day);
	$is_today_past = false;
	/* ‘today in the past years’ comparison */
	if ($year < $current_year && $month==$today_month && $day==$today_day){
		$is_today_past = true;
	}
	$classes = &#91;&#93;;
	if ($is_today_past) $classes&#91;&#93; = 'today-past';
	if (isset($post_dates&#91;$date&#93;)){
		$classes&#91;&#93; = 'has-posts';
	} else {
		$classes&#91;&#93; = 'no-posts';
	}
	echo '<td class="'.implode(' ', $classes).'">';
	if (isset($post_dates[$date])){
		$count = $post_dates[$date];
		$title = ($count==1) ? '1 post' : $count.' posts';
		echo '<a class="day-link" href="'.get_day_link($year,$month,$day).'" title="'.$title.'">'.$day.'</a>';
	}else{
		echo '<span class="day-number">'.$day.'</span>';
	}
	echo '</td>';
	if ($weekday==7){
		echo '</tr><tr>';
		$weekday=1;
	}else{
		$weekday++;
	}
	}

/* final table cells */
while ($weekday<=7){
	echo '<td class="empty"></td>';
	$weekday++;
	}
echo '</tr></tbody></table>';
echo '</div>';
}

echo '</div>';
?>

Just to be clear, here’s something obvious: with the PHP code shown above, you need to create a template (a *.php file, for example calendarblog.php), upload it to your WordPress site’s theme, and then use it to create the calendar page via the admin panel.
The CSS code for displaying the calendar: it can be copied into the theme’s style.css file or included separately:

/* ===== navigation by year ===== */
.calendar-nav{
	text-align:center;
	margin:40px 0;
	font-size:22px;
	line-height:2;
}
.calendar-nav a{
	margin:0 10px;
	text-decoration:none;
}
.current-year{
	font-weight:bold;
	margin:0 15px;
}
/* ===== monthly layout ===== */
.calendar-year{ display:block; }
.calendar-month-block{ margin-bottom:40px; }
/* ===== month title ===== */
.month-title{
	text-align:center;
	margin-bottom:10px;
}
/* ===== table ===== */
.calendar-month{
	width:100%;
	border-collapse:collapse;
}
.calendar-month th{
	padding:5px;
	text-align:center;
	font-weight:bold;
}
.calendar-month td{
	padding:0;
	height:32px;
	text-align:center;
}
/* ===== days ===== */
.day-number{
	display:block;
	padding:6px;
}
/* table cells with items */
.calendar-month td.has-posts{ background:#C5C5C5; }
/* the link fills the entire cell */
.day-link{
	display:block;
	width:100%;
	height:100%;
	padding:6px;
	text-decoration:none;
	font-weight:bold;
	background:transparent; /* VERY IMPORTANT */
}
/* table cells without items */
.calendar-month td.no-posts{ background:#FFFFFF; }
/* non-hyperlinked numbers */
.day-number{
	display:block;
	padding:6px;
	background:#FFFFFF; /* guarantees full white */
}
.today-past{ outline:2px dashed #FF9800; }
/* hover */
.day-link:hover{ background:#C5C5C5; }
/* empty table cells */
.empty{ background:#FFFFFF; }
/* ===== layout desktop ===== */
@media (min-width:1024px){
.calendar-year{
	display:grid;
	grid-template-columns:repeat(3,1fr);
	gap:30px;
	align-items:start;
}
.calendar-month-block{ margin-bottom:0; }
}

In this form, with these features, this is exactly the blog calendar I needed. What could or should be added? In fact, I am not entirely sure myself. If something comes to mind (or if someone gives me a suggestion), I will publish a second version and announce it.