Календарь в стиле iCal для архивов в wordpress

Давно пылится в черновиках красивый примерчик для календаря. В оригинале календарь под хтмл, я приладил для вордпресса, на страницу архивов по месяцам.
Вот тут можно посмотреть календарь в действии, скрин
calendar
Сам календарь можно взять отсюда.
Как поставить себе.
Открываем файл functions.php для вашей темы, добавляем функцию

function MyCal(){
 global $wpdb, $m, $monthnum, $year, $wp_locale, $posts;

 $cache = array();
 $key = md5( $m . $monthnum . $year );
 if ( $cache = wp_cache_get( 'get_calendar', 'calendar' ) ) {
 if ( is_array($cache) && isset( $cache[ $key ] ) ) {
 echo $cache[ $key ];
 return;
 }
 }

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

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

 ob_start();
 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 = '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
 WHERE post_date >    '$thisyear-$thismonth-01'
 AND MONTH( post_date ) != MONTH( '$thisyear-$thismonth-01' )
 AND post_type = 'post' 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');
 echo '<table id="wp-calendar" summary="' . __('Calendar') . '" cellspacing="0">
 <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".'<th colspan="3"><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($previous->month) . '</a></th>';
 } else {
 echo "\n\t\t".'<th colspan="3">&nbsp;</th>';
 }

 echo "\n\t\t".'<th>&nbsp;</th>';

 if ( $next ) {
 echo "\n\t\t".'<th colspan="3" style="border-right: 1px solid #9DABCE;"><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($next->month) . ' &raquo;</a></th>';
 } else {
 echo "\n\t\t".'<th colspan="3" style="border-right: 1px solid #9DABCE;">&nbsp;</th>';
 }

 echo '
 </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 = 'post' 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 || 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 "
 ."WHERE YEAR(post_date) = '$thisyear' "
 ."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 = esc_attr( apply_filters( 'the_title', $ak_post_title->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"] = "<li><span class=\"title\">".$post_title."</span></li>";
 else
 $ak_titles_for_day["$ak_post_title->dom"] .= "<li><span class=\"title\">".$post_title."</span></li>";
 }
 }

 // 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.'">&nbsp;</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 ( in_array($day, $daywithpost) ){ // any posts today?
 echo "<td class=\"date_has_event\"><a href=\"".get_day_link($thisyear, $thismonth, $day)."\" class=\"date_has_event\">$day</a><div class=\"events\">
 <ul>
 $ak_titles_for_day[$day]
 </ul>

 </div>";
 } 

 if(!in_array($day, $daywithpost)){
 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>'.$day;}  else {            
echo '<td>'.$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.'">&nbsp;</td>';
 echo '    <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>';

 echo "\n\t</tr>\n\t</tbody>\n\t</table>";

 $output = ob_get_contents();
 ob_end_clean();
 echo $output;
 $cache[ $key ] = $output;
 wp_cache_set( 'get_calendar', $cache, 'calendar' );
}

Далее открываем файл header.php, добавляем следующее:

<script src="<?php bloginfo('template_directory'); ?>/js/coda.js" type="text/javascript"> </script>
<link rel="stylesheet" href="<?php bloginfo('template_directory'); ?>/cal.css" type="text/css" media="screen" charset="utf-8" />

Теперь создаем файл cal.css:

* {
	border: 0px;
	padding: 0px;
}
.capt {
	padding-top: 20px;
	font-size: 22px;
	color: #335;
	text-align:center;
}
table {
	border-collapse: separate;
	border: 1px solid #9DABCE;
	border-width: 0px 0px 1px 1px;
	margin: 10px auto;
	font-size: 20px;
}
td, th {
	width: 81px;
	height: 81px;
	text-align: center;
	vertical-align: middle;
	background: url(img/cells.png);
	color: #444;
	position: relative;
}
th {
	height: 30px;
	font-weight: bold;
	font-size: 14px;
}
td:hover, th:hover {
	background-position: 0px -81px;
	color: #222;
}
td.date_has_event {
	background-position: 162px 0px;
	color: white;
}
a.date_has_event {
	background-position: 162px 0px;
	color: white;
	text-decoration:none;
}
td.date_has_event:hover {
	background-position: 162px -81px;
}
td.padding {
	background: url(img/calpad.jpg);
}
td.today {
	background-position: 81px 0px;
	color: white;
}
td.today:hover {
	background-position: 81px -81px;
}
.events {
	position: relative;
}
.events ul {
	text-align: left;
	position: absolute;
	display: none;
	z-index: 1000;
	padding: 15px;
	background: #E7ECF2 url(img/popup.png) no-repeat;
	color: white;
	border: 1px solid white;
	font-size: 15px;
	width: 200px;
	-moz-border-radius: 3px;
	-khtml-border-radius: 3px;
	-webkit-border-radius: 3px;
	-border-radius: 3px;
	list-style: none;
	color: #444444;
	-webkit-box-shadow: 0px 8px 8px #333;
}
.events li {
	padding-bottom: 5px;
}
.events li span {
	display: block;
	font-size: 12px;
	text-align: justify;
	color: #555;
}
.events li span.title {
	font-weight: bold;
	color: #222;
}
a.next_month {
	background-position: 162px 0px;
	color: #444;
	text-decoration:none;
}
th.month{
      background: none
}
tfoot {
	background: url(img/tfoot.gif);
}
th.padding{
      background: transparent;
}

И правим файл archive.php так, чтобы при is_month выводился только календарь, else все остальное. Мой archive.php:

<?php get_header(); ?>

 <div id="posts">

 <?php if (is_month()) { ?>
 <h1>Записи за <?php the_time('F, Y'); ?></h1>
<?php MyCal(); ?>
 </div>

<?php get_sidebar(); ?>

<?php get_footer(); ?>

<?php } else {?>
 <?php /* If this is a category archive */ if (is_category()) { ?>
 <h1>Posts in &#8216;<?php echo single_cat_title(); ?>&#8217;</h1>

 <?php /* If this is a daily archive */ } elseif (is_day()) { ?>
 <h1>Posts from <?php the_time('F jS, Y'); ?></h1>

 <?php /* If this is a yearly archive */ } elseif (is_year()) { ?>
 <h1>Posts from <?php the_time('Y'); ?></h1>

 <?php /* If this is an author archive */ } elseif (is_author()) { ?>
 <h1>Author Archive</h1>

 <?php /* If this is a paged archive */ } elseif (isset($_GET['paged']) && !empty($_GET['paged'])) { ?>
 <h1>Публикации</h1>

 <?php } ?>

 <?php if (have_posts()) : ?>

 <?php $post = $posts[0]; // Hack. Set $post so that the_date() works. ?>
 <?php while (have_posts()) : the_post(); ?>

 <div id="post-<?php the_ID(); ?>">
 <h2><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title(); ?>"><?php the_title(); ?></a></h2>
 <p><?php the_time('M d') ?></p>
 <div>
 <?php the_content('read all &raquo;'); ?>
 </div>
 <div>
 <span><?php the_category(', ') ?></span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span><?php comments_popup_link('0 Comments', '1 Comment', '% Comments'); ?></span> <?php edit_post_link('Edit', '&laquo;&raquo; ', ''); ?>
 </div>
 </div>

 <?php endwhile; ?>

 <div>
 <div><?php next_posts_link('&laquo; Previous Entries') ?></div>
 <div><?php previous_posts_link('Next Entries &raquo;') ?></div>
 </div>

 <?php else : ?>

 <h2>Not Found</h2>
 <?php include (TEMPLATEPATH . '/searchform.php'); ?>

 <?php endif; ?>

 </div>

<?php get_sidebar(); ?>

<?php get_footer(); ?>
<?php } ?>

Если кому-то лень заморачиваться с правкой файлов, можно скачать архив со всеми нужными файлами. Скачать iCal.tar

5.00 avg. rating (90% score) - 1 vote

15 комментариев

  • Красивый календарь. Такой весь айфоновский )))
    Но в сентябре глючит… 10 число продублировалось и поэтому 13 сентября находится во временной анамалии )))
    А так, да — тема! )))

    • какой браузер? Скрин не покажешь?

    • Все поправил, спасибо за багрепорт

  • Календарь в стиле iCal для архивов в wordpress…

    Thank you for submitting this cool story — Trackback from progg.ru…

  • Поправил

  • А насколько сложно сделать из всего этого кода плагин?

    • Даже не знаю, не делал никогда плагины

  • Как-то экспериментировал с плагинами. Например, если взять код известного плагина rus-to-lat и вставить его в functions.php, то он сразу работает в данной теме оформления. Аналогично работает и russify comments. То есть таким образом можно расширить функциональность темы без необходимости дополнительной установки плагинов. Уверен, что также будут работать многие другие плагины.
    А по вашему примеру можно сделать вывод, что от кода в functions.php до кода плагина один шаг 🙂

  • >>То есть таким образом можно расширить функциональность темы без необходимости дополнительной установки плагинов

     

    Что я и сделал, расширил функциональность самой темы без всяких плагинов. Я вообще плагины не очень люблю и стараюст их не использовать. Всякие примочки для твитера, схожие посты, еще что-то, не помню точно, все сделано без плагинов. Поясню почему не люблю плагины:

    Плагин, даже не активный, проверяется на обновления. Даже хеллоу долли. Это лишние затраты.

    Выполнение плагина вклинивается между функциями ядра и выполнением темы. Т.о. это лишняя прослойка. + плагины добавляют хуки. Тоже лишнее время генерации.

    Плагин — это лишний подключаемый файл. Даже если просто подключить echo «privet mir», это сожрет мин ресурс, выделенный под процесс пхп (если пхп установлен как mod_php у апача)

    Из всего этого бывают случаи, когда плагины просто завешивали блог. Поэтому я стараюсь их не использовать. Да, это удобно. Но истинные рыцари джедай не ищут легких путей ))

  • Ну, чтобы блокировать запрос на обновления, есть плагин от Lecactus «Блокировка запросов на новые версии». Но это плагин…
    На самом деле, плагин — это отличный способ расширить функциональность программы, не вникая в ее код. Это очень важно для людей, далеких от программирования. Это как раз случай WordPress, ведь большинство блоггеров не программисты.

  • В 6 и 7 осле не работает. Есть стили для 6 и 7?

    • Нет, для них нет

  • Уважаемый автор, вы нарисоали весьма симпатичный календарь. Есть вопрос, который меня давно мучает и ответа на него я пока найти не могу (ибо новичок): можно ли настроить календарь так, чтобы он начинался не стекущей даты (месяца), а даты давно прошедшей и настроить количество перелистываем месяцев (два-три-четыре).

    Например, надо сделать навигацию по давнишним (3 года) записям относящимся к трем месяца. Календарь — очень удобный стиль отображения этого архива, но начинаться он должен естественно с даты 3-летней давности, а не с сегодняшнего числа.

    • думаю, у вордпресса есть возможность через параметры вызова функции задать начальную дату

css.php