Wordpress offset archive hack
Dec11th
2008
••
One issue I have found with Wordpress is that is doesn’t have a class of “offset” for the archives. While most of the time this isn’t an issue, I am currently working on a template that needed just this feature. It took me a while to get it going the way I needed as documentation on altering the general-template.php file for Wordpress is not readily available unlike most hacks. I was trying to list my archives in the footer of this template. But, like most blogs, that list will get incredibly long as time goes by thus resulting in a very unattractive footer, not to mention insanely tall. What I was looking for was a way to only show the six most recent months and then use jQuery to show the rest onclick.
With some help from an old post by Otto42 on the WP forums and some trial an error, I got what I needed to accomplished.
I’ll run through the jQuery part rather quickly as it’s pretty basic. You will need two files for this. jQuery itself and the Dimensions plugin. Then using the technique described at Learning jQuery, we need to make a call to show and hide the list items:
<script type="text/javascript">
$(document).ready(function() {
$('div.footdivsample:eq(0)> ul li.hideli').hide();
$('div.footdivsample:eq(0)> ul a.more').click(function() {
$('div.footdivsample:eq(0)> ul li.hideli').slideToggle('fast');
$('div.footdivsample:eq(0)> ul li.morelink').remove();
});
});
</script>
Some thanks go to The CSS Guy for the remove();.
A quick break down on what this does. It finds the first div with a class footdivsample and then hides all <li class=”hideli”> inside of it. Then the function does two things. On clicking a link with the class of “more” it toggles the hidden list items and then removes the selected link parent list item from the DOM. That’s the easy part. The HTML looks like this, don’t worry too much yet about all the PHP inside of it as I will explain later:
<div class="footdivsample">
<h2>Archives with 'offset' and 'limit' applied with some jQuery</h2>
<ul>
<?php wp_get_archives_with_offset('type=monthly&limit=5&before=<li>&after=</li>'); ?>
<?php echo "<li class="morelink">"."<a href="#x" class="more">more »</a>"."</li>"."n"; ?>
<?php wp_get_archives_with_offset('type=monthly&offset=5&limit=9999999&before=<li class="hideli">&after=</li>'); ?>
</ul>
</div>
What this is doing is already taking advantage of the new modifications to the themes functions.php file. That’s a little tricky so I’ll go over what each line does for starters.
<?php wp_get_archives_with_offset('type=monthly&limit=5&before=<li>&after=</li>'); ?>
The first line uses the new class of wp_get_archives_with_offset, limits the results to the most recent 5 and then wraps the result in a list. By default, the wp_get_archives already builds the list. Later I’ll show how I took them off and why I needed to.
<?php echo "<li class="morelink">"."<a href="#x" class="more">more »</a>"."</li>"."n"; ?>
The next line prints out the “more” list item and link to activate the jQuery. Because the first line specifies 5, this list item will be printed as the sixth list item.
<?php wp_get_archives_with_offset('type=monthly&offset=5&limit=9999999&before=<li class="hideli">&after=</li>'); ?>
In the last line we grab the archives again but this time we use “offset” set to five to skip the first 5 list items and then print the rest. In the code in the new wp_get_archives_with_offset function, you have to declare the “limit” as well. Not really an issue. Just set it some big number, in the case 9999999. Divide that by 12 and you’ll notice it’s way past the Mayan Doomsday date so, whatever.
Now the “hard part”
If you look into your sites “wp-includes” folder you’ll find a file called “general-template.php”. At around line 350, there will be the whole chunk of following code. It is the entire wp_get_archives. Safest way to do this is to copy it out and then open your themes functions.php file and paste it right after the opening <?php tag.
function wp_get_archives($args = '') {
global $wpdb, $wp_locale; $defaults = array(
'type' => 'monthly', 'limit' => '',
'format' => 'html', 'before' => '',
'after' => '', 'show_post_count' => false
);
$r = wp_parse_args( $args, $defaults );
extract( $r, EXTR_SKIP );
if ( '' == $type )
$type = 'monthly';
if ( '' != $limit ) {
$limit = absint($limit);
$limit = ' LIMIT '.$limit;
}
// this is what will separate dates on weekly archive links
$archive_week_separator = '–';
// over-ride general date format ? 0 = no: use the date format set in Options, 1 = yes: over-ride
$archive_date_format_over_ride = 0;
// options for daily archive (only if you over-ride the general date format)
$archive_day_date_format = 'Y/m/d';
// options for weekly archive (only if you over-ride the general date format)
$archive_week_start_date_format = 'Y/m/d';
$archive_week_end_date_format = 'Y/m/d';
if ( !$archive_date_format_over_ride ) {
$archive_day_date_format = get_option('date_format');
$archive_week_start_date_format = get_option('date_format');
$archive_week_end_date_format = get_option('date_format');
}
//filters
$where = apply_filters('getarchives_where', "WHERE post_type = 'post' AND post_status = 'publish'", $r );
$join = apply_filters('getarchives_join', "", $r);
if ( 'monthly' == $type ) {
$query = "SELECT DISTINCT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts FROM $wpdb->posts
$join $where GROUP BY YEAR(post_date), MONTH(post_date) ORDER BY post_date DESC $limit";
$key = md5($query);
$cache = wp_cache_get( 'wp_get_archives' , 'general');
if ( !isset( $cache[ $key ] ) ) {
$arcresults = $wpdb->get_results($query);
$cache[ $key ] = $arcresults;
wp_cache_add( 'wp_get_archives', $cache, 'general' );
} else {
$arcresults = $cache[ $key ];
}
if ( $arcresults ) {
$afterafter = $after;
foreach ( $arcresults as $arcresult ) {
$url = get_month_link($arcresult->year, $arcresult->month);
$text = sprintf(__('%1$s %2$d'), $wp_locale->get_month($arcresult->month), $arcresult->year);
if ( $show_post_count )
$after = ' ('.$arcresult->posts.')' . $afterafter;
echo get_archives_link($url, $text, $format, $before, $after);
}
}
} elseif ('yearly' == $type) {
$query = "SELECT DISTINCT YEAR(post_date) AS `year`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY
YEAR(post_date) ORDER BY post_date DESC $limit";
$key = md5($query);
$cache = wp_cache_get( 'wp_get_archives' , 'general');
if ( !isset( $cache[ $key ] ) ) {
$arcresults = $wpdb->get_results($query);
$cache[ $key ] = $arcresults;
wp_cache_add( 'wp_get_archives', $cache, 'general' );
} else {
$arcresults = $cache[ $key ];
}
if ($arcresults) {
$afterafter = $after;
foreach ($arcresults as $arcresult) {
$url = get_year_link($arcresult->year);
$text = sprintf('%d', $arcresult->year);
if ($show_post_count)
$after = ' ('.$arcresult->posts.')' . $afterafter;
echo get_archives_link($url, $text, $format, $before, $after);
}
}
} elseif ( 'daily' == $type ) {
$query = "SELECT DISTINCT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, DAYOFMONTH(post_date) AS
`dayofmonth`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date), DAYOFMONTH(post_date) ORDER
BY post_date DESC $limit";
$key = md5($query);
$cache = wp_cache_get( 'wp_get_archives' , 'general');
if ( !isset( $cache[ $key ] ) ) {
$arcresults = $wpdb->get_results($query);
$cache[ $key ] = $arcresults;
wp_cache_add( 'wp_get_archives', $cache, 'general' );
} else {
$arcresults = $cache[ $key ];
}
if ( $arcresults ) {
$afterafter = $after;
foreach ( $arcresults as $arcresult ) {
$url = get_day_link($arcresult->year, $arcresult->month, $arcresult->dayofmonth);
$date = sprintf('%1$d-%2$02d-%3$02d 00:00:00', $arcresult->year, $arcresult->month,
$arcresult->dayofmonth);
$text = mysql2date($archive_day_date_format, $date);
if ($show_post_count)
$after = ' ('.$arcresult->posts.')'.$afterafter;
echo get_archives_link($url, $text, $format, $before, $after);
}
}
} elseif ( 'weekly' == $type ) {
$start_of_week = get_option('start_of_week');
$query = "SELECT DISTINCT WEEK(post_date, $start_of_week) AS `week`, YEAR(post_date) AS yr, DATE_FORMAT(post_date,
'%Y-%m-%d') AS yyyymmdd, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY WEEK(post_date, $start_of_week), YEAR(post_date)
ORDER BY post_date DESC $limit";
$key = md5($query);
$cache = wp_cache_get( 'wp_get_archives' , 'general');
if ( !isset( $cache[ $key ] ) ) {
$arcresults = $wpdb->get_results($query);
$cache[ $key ] = $arcresults;
wp_cache_add( 'wp_get_archives', $cache, 'general' );
} else {
$arcresults = $cache[ $key ];
}
$arc_w_last = '';
$afterafter = $after;
if ( $arcresults ) {
foreach ( $arcresults as $arcresult ) {
if ( $arcresult->week != $arc_w_last ) {
$arc_year = $arcresult->yr;
$arc_w_last = $arcresult->week;
$arc_week = get_weekstartend($arcresult->yyyymmdd, get_option('start_of_week'));
$arc_week_start = date_i18n($archive_week_start_date_format, $arc_week['start']);
$arc_week_end = date_i18n($archive_week_end_date_format, $arc_week['end']);
$url = sprintf('%1$s/%2$s%3$sm%4$s%5$s%6$sw%7$s%8$d', get_option('home'), '', '?',
'=', $arc_year, '&', '=', $arcresult->week);
$text = $arc_week_start . $archive_week_separator . $arc_week_end;
if ($show_post_count)
$after = ' ('.$arcresult->posts.')'.$afterafter;
echo get_archives_link($url, $text, $format, $before, $after);
}
}
}
} elseif ( ( 'postbypost' == $type ) || ('alpha' == $type) ) {
('alpha' == $type) ? $orderby = "post_title ASC " : $orderby = "post_date DESC ";
$query = "SELECT * FROM $wpdb->posts $join $where ORDER BY $orderby $limit";
$key = md5($query);
$cache = wp_cache_get( 'wp_get_archives' , 'general');
if ( !isset( $cache[ $key ] ) ) {
$arcresults = $wpdb->get_results($query);
$cache[ $key ] = $arcresults;
wp_cache_add( 'wp_get_archives', $cache, 'general' );
} else {
$arcresults = $cache[ $key ];
}
if ( $arcresults ) {
foreach ( $arcresults as $arcresult ) {
if ( $arcresult->post_date != '0000-00-00 00:00:00' ) {
$url = get_permalink($arcresult);
$arc_title = $arcresult->post_title;
if ( $arc_title )
$text = strip_tags(apply_filters('the_title', $arc_title));
else
$text = $arcresult->ID;
echo get_archives_link($url, $text, $format, $before, $after);
}
}
}
}
}
This might look and sound scary, and it is if you’re like me and close files randomly by accident and save a lot of wrong code. The relevant section of this code is this:
function wp_get_archives($args = '') {
global $wpdb, $wp_locale;
$defaults = array(
'type' => 'monthly', 'limit' => '',
'format' => 'html', 'before' => '',
'after' => '', 'show_post_count' => false
); $r = wp_parse_args( $args, $defaults );
extract( $r, EXTR_SKIP );
if ( '' == $type )
$type = 'monthly';
if ( '' != $limit ) {
$limit = absint($limit);
$limit = ' LIMIT '.$limit;
}
We are going to change that to look like this:
function wp_get_archives_with_offset($args = '') {
global $wpdb, $wp_locale; $defaults = array(
'type' => 'monthly', 'limit' => '',
'offset' => '', 'before' => '',
'after' => '', 'show_post_count' => false
);
$r = wp_parse_args( $args, $defaults );
extract( $r, EXTR_SKIP );
if ( '' == $type )
$type = 'monthly';
if ( '' != $limit ) {
$limit = (int) $limit;
if (!empty($offset)) {
$offset = (int) $offset;
$limit = ' LIMIT '.$offset.','.$limit;
} else {
$limit = ' LIMIT '.$limit;
}
}
A little line-by-line break down as best I can.
function wp_get_archives_with_offset($args = '') {
Here we are just naming a new function call. If we left it the same as wp_get_archives then WP would throw an error as it was already defined. Plus this way we can still use the original function as well as our new function.
$defaults = array(
'type' => 'monthly', 'limit' => '',
'offset' => '', 'before' => '',
'after' => '', 'show_post_count' => false
);
On this we are removing the ’format’ => html, and replacing it with ’offset’ => ‘’,. This does two things. First by removing the format line, it removes the default wrapping <li>’s thus allowing us to make our custom ones with class names. Second, it “turns on” the “offset” class. Woot! Two birds – 0; One stone – 1.
if ( '' != $limit ) {
$limit = (int) $limit;
if (!empty($offset)) {
$offset = (int) $offset;
$limit = ' LIMIT '.$offset.','.$limit;
} else {
$limit = ' LIMIT '.$limit;
}
}
Now what I think is going on here is that we are basically combining “LIMIT” and “OFFSET” into one class. So if “limit” is not empty look to see if “offset” isn’t empty. If both of them have a value then “limit” is now “offset+limit”. This means it needs to be written in that order as well. If only “limit” has value, then “limit” remains just as “limit”.
Have a look at the example page: Wordpress archive offset hack













2008
••
Hi Alan, thanks for the mention. One note: if you’re using jQuery version 1.2.6 or later, you won’t need to include the Dimensions plugin, since that has been rolled into the core jQuery file.
Cheers.
2008
••
Thanks Karl for the heads up.
I have been using 1.2.3.min for the most part. I have 1.2.6 as well in the same spot just never realized I could save the step.
-Alan
2010
••
Hi,
thanks for the awesome resource, exactly what i’ve been looking for!
BUT… I’m a complete newbie and not sure what i did wrong, but i’m getting this error:
Parse error: syntax error, unexpected T_STRING, expecting ‘,’ or ‘;’ in /home/tabnews.co.za/public_html/test/wp-content/themes/default/sidebar.php on line 65
which points to the line where you create the more link… PLEASE HELP!