WordPress: Fix Category Exclusions in next_post_link() / previous_post_link()

(UPDATE: Word on the street (see comment 27, below) is that this bug has been fixed in WordPress 2.1.)

I recently came across a known bug in WordPress 1.5.2 (well, at least it’s known to users; I can only assume that the dev community is also aware of it). Just wanted to post my fix here, in case anyone else finds it helpful.

The Bug

Category exclusion is broken. The problem is that the category exclusion parameter (the 4th param) of next_post_link() and previous_post_link() is effectively ignored.

(next_post_link() and previous_post_link() are the new versions of the now-deprecated next_post() and previous_post().)

A quick scan through the WordPress code indicates that the “category exclude” feature isn’t even implemented (at least not in a serious way) for next/previous_post_link. I haven’t spent much time perusing the WordPress code base, but from what I can tell, it seems that the relevant sections of these functions were never upgraded from an earlier WP version–one with a different schema; in particular, one with a different method of storing post-category relationships.

The FixHack

Here is how I fixed it in my installation (see warnings below before trying this at home):

1. Edit wp-includes/template-functions-links.php. At approximately line 253 (in function get_previous_post) [line 221 in WP1.5], you’ll see this:

    $sql_exclude_cats = '';
    if (!empty($excluded_categories)) {
        $blah = explode('and', $excluded_categories);
        foreach($blah as $category) {
            $category = intval($category);
            $sql_exclude_cats .= " AND post_category != $category";
        }
    }

2. Replace that block with the following (beware of copy/paste errors, especially with respect to line breaks):

    $sql_exclude_cats = '';
    if (!empty($excluded_categories)) {
### ronr hack BEGIN ###
        # find ids of all posts in excluded cats
        $query =
            "SELECT post_id
             FROM $wpdb->post2cat
             WHERE category_id IN ("
             . str_replace(',', ',', $excluded_categories)
             . ')';
        $excluded_post_rows = $wpdb->get_results($query);

        # build IN clause from those ids
        if (count($excluded_post_rows) > 0) {
            $sql_exclude_cats =
                ' AND ID NOT IN ('
                . intval($excluded_post_rows[0]->post_id);
            for ($i = 1; $i < (count($excluded_post_rows)); $i++) {
                $sql_exclude_cats .=
                    ','
                    . intval($excluded_post_rows[$i]->post_id);
            }
            $sql_exclude_cats .= " )";
        }
### ronr hack END ###
    }

(Note: instead of copy/pasting the above code, you can download it here.)

3. Now, make the same replacement in the corresponding block in get_next_post, which is a little further down in the file.

Disclaimers

Presumably, this bug will be fixed properly in an upcoming release, so now that I’ve got it working (albeit jury-rigged), I’m not going to spend any more time on it. If it doesn’t get fixed soon, perhaps I’ll clean it up and fold it into a plug-in (perhaps even as Asides plug-in).

My code does no error checking, and it assumes that the value of the exclude_categories parameter is not screwy. It’s provided as-is. I would never release code like this if it wasn’t intended to be used solely by me. If I wrote code like this at work, I’d deserve to be fired. Constructive suggestions for improvements are always welcome, but please don’t bitch to me about how it didn’t work for you, broke your blog, crashed your server, ruined your marriage, etc. Capisce?

Note too that I’ve changed the original interface; instead of separating excluded category ids with the word “and” (odd), it now expects a comma (not odd).

Anyway, good luck. And if you happen to find this useful, please let me know.

Ron

https://www.ronrothman.com/public/about+me.shtml

View Comments

  • Personally, I had a whole array of problems with this and I only had 4 categories. The solution is still dodgy in Wordpress 2.3, but I got it working perfectly by:


    <?php next_post_link($format='%link »', $link='%title', $in_same_cat
    = false, $excluded_categories = 'X and X and X' ); ?>

    It's explained in better detail on the wordpress codex here

    As you will no doubt notice, the third parameter '$in_same_cat' is bogus and doesn't work if set to 'true.'

  • Wait, I just figured out what the error is. For WP 2.1.3 the code in get_next_post and get_previous_post hasn't been tested, but most of the mistakes are harmless (for example $sql_exclude_cats is declared and never used).

    There's a one line fix for WP2.1.3 to get this code to work. In both functions, this line (lines 294 and 331 in link-template.php):

    $sql_cat_ids = " OR pc.category_ID = '$category'";

    should be changed to

    $sql_cat_ids .= " OR pc.category_ID = '$category'";

    (changed = to .=)

    Where do I submit WP bug reports & fixes. Can I submit a patch myself?

  • I'm trying to get this feature (the fourth parameter to get_next_post) to work in WP 2.1.3 and it still seems to be broken. I looked at the WP code (now in wp-includes/link-template.php) and they've changed it, but it still doesn't work for me.

    If I use

    get_next_post('%link', '%title', FALSE, '1 and 2');

    then posts in category 2 are ignored, but not those in 1. It's quite annoying!

    I looked at the code, but at cursory inspection I can't see what's wrong with it. Have you managed to get this feature to work in v2.1.3?

  • Hi Ron,

    Thanks for the "hack" - it really works like a charm and has been ever since I implemented the Sideblog plugin on top of WP2.03. Would you know whether the faulty core code has been fixed in WP2.1?

    Thanks,
    Oliver

Share
Published by
Ron

Recent Posts

Python 3 Rounding Surprise

I discovered this subtle change when one of my unit tests mysteriously failed once I…

4 years ago

Python 3 Exception Chaining

Exception chaining solves two problems in Python 2. 1. Swallowed Exceptions If, while handling exception…

4 years ago

Safe Password Storage – How Websites Get It Wrong

Here's the recording of my talk "15 minutes w/ Beeswax: Safe Password Storage - How…

4 years ago

Python at Scale: Concurrency at Beeswax

My presentation from the NYC Python Meetup: Python at Scale; Concurrency at Beeswax. Concurrent Python…

4 years ago

Python Dependencies The Right Way

My BazelCon 2019 lightning talk, Python Dependencies The Right Way*, has been posted. Please excuse…

4 years ago

Python 3 f-strings

One of my favorite features of Python 3 is f-strings (Formatted String Literals). Each of…

4 years ago

This website uses cookies.