Архив метки «календарь»

Давно хотел сделать на сайте такую штуку – календарь года (текущего и каждого из прошлых), в котором с каждой даты ссылка ведёт на подборку соответствующих постов в блоге (если в тот день что-то было опубликовано) – то есть, «календарь года как в ЖЖ». В ЖЖ такой календарь существовал и был очень удобен в качестве одного из способов поиска старых записей и/или анализа активности блога (своего или чужого). А вот, например, в WordPress такого календаря нет и, если я правильно помню, не было никогда (ну или был настолько давно, что я уже полностью забыл о его существовании) – есть только аналогичный календарь по месяцам, что совсем неудобно для большинства видов поиска и анализа.
На реализацию такой штуки своими силами нужно было некоторое количество свободного времени – и я наконец-то его нашёл! Что ещё более важно и интересно, это время я потратил не зря: сделать календарь-архив у меня получилось. Работающий пример можно посмотреть по ссылке, а в данном посте я перечислю словами все характеристики и приведу базовые PHP и CSS коды.
Характеристики моего годового календаря блога для WordPress:
– полный календарь года (видны все 12 месяцев, какая бы дата ни была на дворе на момент просмотра);
– возможность просматривать прошлые годы (но не ранее, чем год самой первой публичной записи в блоге);
– дни с публичными записями автоматически превращаются в ссылки на соответствующие даты в архиве записей;
– при наведении курсора на день ссылку всплывает подсказка с числом записей за соответствующую дату;
– в календарях за прошлые годы дата просмотра («сегодняшняя») выделяется заметной рамкой;
– у ячеек с датами, в которые была опубликована хотя бы одна запись, меняется фоновый цвет;
– у календаря – адаптивный дизайн: на больших и средних экранах показывается сетка 3×4 месяца, а на маленьких экранах месяцы года выстроены в столбик;
– запросы к базе данных оптимизированы таким образом, чтобы календарь не «тормозил» даже в блогах с десятками тысяч записей.
Ну а теперь – сами коды календаря (надеюсь, вы прочитали весь текст выше – он может вам помочь сориентироваться).
PHP-код, который можно испытывать прямо в таком виде – его легко вставить в свой макет страницы обыкновенным копипастом:

<?php
global $wpdb;


/* ======================================
   1. PRIMO ANNO DEL 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. DATE DEI POST (QUERY SQL OTTIMIZZATA + 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. NAVIGAZIONE CON TUTTI GLI ANNI
====================================== */
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. CONTROLLO "OGGI NEGLI ANNI PASSATI"
====================================== */
$today_month = date('m');
$today_day = date('d');
$today_key = $year.'-'.$today_month.'-'.$today_day;
$today_has_posts = isset($post_dates[$today_key]);


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


/* ======================================
   6. CICLO MESI
====================================== */
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;
		}
	}

/* titolo mese */
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. TABELLA CALENDARIO
====================================== */
echo '<table class="calendar-month">';
echo '<thead><tr>';
$weekdays = ['L','M','M','G','V','S','D']; //da cambiare per le lingue diverse da quella italiana
foreach ($weekdays as $w){
	echo '<th>'.$w.'</th>';
	}
echo '</tr></thead>';
echo '<tbody><tr>';

/* primo giorno mese */
$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. CICLO GIORNI
====================================== */
for ($day=1;$day<=$days;$day++){
	$date = $year.'-'.sprintf('%02d',$month).'-'.sprintf('%02d',$day);
	$is_today_past = false;
	/* controllo "oggi negli anni passati" */
	if ($year < $current_year && $month==$today_month && $day==$today_day){
		$is_today_past = true;
	}
	$classes = [];
	if ($is_today_past) $classes[] = 'today-past';
	if (isset($post_dates[$date])){
		$classes[] = 'has-posts';
	} else {
		$classes[] = 'no-posts';
	}
	echo '<td class="'.implode(' ', $classes).'">';
	if (isset($post_dates[$date])){
		$count = $post_dates[$date];
		$title = ($count==1) ? '1 articolo' : $count.' articoli';
		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++;
	}
	}

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

echo '</div>';
?>

CSS-код визуализации календаря – скопируйте его в файл style.css вашей темы или подключите отдельно:

/* ===== navigazione anni ===== */
.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;
}
/* ===== layout mesi ===== */
.calendar-year{ display:block; }
.calendar-month-block{ margin-bottom:40px; }
/* ===== titolo mese ===== */
.month-title{
	text-align:center;
	margin-bottom:10px;
}
/* ===== tabella ===== */
.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;
}
/* ===== giorni ===== */
.day-number{
	display:block;
	padding:6px;
}
/* celle con articoli */
.calendar-month td.has-posts{ background:#C5C5C5; }
/* link riempie tutta la cella */
.day-link{
	display:block;
	width:100%;
	height:100%;
	padding:6px;
	text-decoration:none;
	font-weight:bold;
	background:transparent; /* IMPORTANTISSIMO */
}
/* celle senza articoli */
.calendar-month td.no-posts{ background:#FFFFFF; }
/* numeri non linkati */
.day-number{
	display:block;
	padding:6px;
	background:#FFFFFF; /* garantisce bianco pieno */
}
.today-past{ outline:2px dashed #FF9800; }
/* hover */
.day-link:hover{ background:#C5C5C5; }
/* celle vuote */
.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; }
}

В таком виде, с таким функционалом – это именно тот календарь блога, который мне был нужен. Что к нему можно или нужно добавить – даже и не знаю. Если придумаю (или кто-то подскажет) – выпущу вторую версию и о ней сообщу.


Вечный календарь

Случается со мной – хотя и очень-очень редко – сильно раздражающая ситуация: знаю, какой предмет мне нужен, всё знаю о его желаемых характеристиках и внешнем виде, а вот официального названия – не знаю. А так как эксперта по интересующему предмету может поблизости не оказаться, приходится мне мучать поисковики своими длинными словесными описаниями – в надежде на то, что рано или поздно вылезет нужная картинка с подписью (бесплатная версия Чата ЖэПэТэ в таких вопросах пока часто ошибается или вообще сообщает о собственной некомпетентности).
К счастью, иногда – но совсем редко – желаемый предмет физически находится быстрее, чем я узнаю его название. Вот, например, «вечный календарь», показывающий день месяца, название месяца и день недели (последняя функция, оказывается, есть не у всех моделей).

Для смены дня недели на этой конкретной модели нужно крутить на себя чёрную ручку. Для смены дня месяца нужно крутить от себя по горизонтальной оси металлическую коробочку. Названия месяцев написаны Продолжить чтение этого поста »