How To List All Posts Of An Archive, A Category Or A Search Result

Archive pages are usually paged according to your settings in options/reading. Sometimes you may want to offer a page with all posts for an archive (time, category, search result).

You need:

  • a separate address for the unpaged archive,
  • a filter for the internal WordPress query and
  • a link to your ‘all posts’ page.

We put everything into a class to avoid name collisions and to keep the global namespace clean.
We name the file class.View_All_Posts.php.

Let’s start with the class, the parameter and a checker, this is easy:

class View_All_Posts
{
    /**
     * GET parameter to trigger a complete, not paged archive.
     * @var string
     */
    protected $all_param = 'all';

    /**
     * Are we there already?
     *
     * @return bool
     */
    public function is_all_posts()
    {
        return isset ( $_GET[$this->all_param] );
    }
}

For the address I use a very simple approach: a GET parameter named all. You may change the name here; just stay with ASCII chars from a—z. все_сообщения will get you in trouble!

Next we need a constructor that manages our work:

    public function __construct()
    {
        /* Register the query argument. */
        add_filter('query_vars', array ( $this, 'add_query_arg') );

        /* Hook into the query. */
        add_action('pre_get_posts', array ( $this, 'view_all_posts') );
    }

The constructor references two internal functions – add_query_arg() and view_all_posts(), which we build next:

    /**
     * Registers the query arg in WordPress.
     * Otherwise it will be unset.
     *
     * @param  array $vars Already registered query args.
     * @return array
     */
    public function add_query_arg( array $vars )
    {
        return array_merge( $vars, array ( $this->query_arg ) );
    }

    /**
     * Alters the query to remove the paging.
     * @return void
     */
    public function view_all_posts()
    {
        if ( ! $this->is_all_posts() )
        {
            return;
        }

        $GLOBALS['wp_query']->query_vars['nopaging'] = TRUE;

        return;
    }

The first function just registers our GET parameter in WordPress. The second alters the query to the database and removes the paging.

We are almost done. A template tag for the link would be nice, wouldn’t it?

    /**
     * Creates the markup for the link.
     *
     * Usage in archive.php, category.php or search.php:
     * $GLOBALS['view_all_posts']->get_allposts_link();
     *
     * @param  string $text Linktext
     * @param  bool   $print echo or return
     * @return string|void
     */
    public function get_allposts_link(
        $text   = 'View all posts'
    ,   $before = '<p class="allpostslink">'
    ,   $after  = '</p>;'
    ,   $print  = TRUE
    )
    {
        if ( $this->is_all_posts()
         or $GLOBALS['wp_query']->found_posts <= get_option('posts_per_page')
        )
        {   // No link needed.
            return;
        }

        if ( isset ( $_SERVER['QUERY_STRING'] )
            && ! empty ( $_SERVER['QUERY_STRING'] )
        )
        {
            /* We have already visible GET parameters: /?hello=world. */
            $new_url = $_SERVER['REQUEST_URI'] . '&';
        }
        else
        {
            /* Note the difference: REQUEST_URL doesn't include
             * the query string while REQUEST_URI does. */
            $new_url = $_SERVER['REQUEST_URL'] . '?';
        }

        $link = "$before<a href='$new_url$this->all_param'>$text</a>$after";

        if ( $print )
        {
            print $link;
            return;
        }
        return $link;
    }

Note: $GLOBALS['wp_query']->found_posts holds the sum of all posts for a given query, not just for the current page. Useful if you want to print out the total number on a paged archive.

If you want to avoid duplicate content, hide the full archives from search engines in your header:

    /**
     * Prevents indexing from search engines.
     *
     * Add this as an action to 'wp_head'.
     *
     * @return void
     */
    public function meta_noindex()
    {
        if ( $this->is_all_posts() )
        {
             print '<meta name="robots" content="noindex">';
        }
    }

Our class is complete. Now we put an object of the class into the global namespace …

$GLOBALS['view_all_posts'] = new View_All_Posts;

… add an action to wp_head

add_action(
    'wp_head'
,   array ( $GLOBALS['view_all_posts'], 'meta_noindex' )
);

… and include the file into the functions.php of our theme:

require_once dirname(__FILE__) . DIRECTORY_SEPARATOR
    . 'class.View_All_Posts.php';

In our archive templates (archive.php, category.php, search.php) we print the link:

$GLOBALS['view_all_posts']->get_allposts_link();

Done.

Oh, wait … maybe you want to see the full code? And a download link?

Here’s the link: Download class.View_All_Posts.php

The complete code:

/**
 * Adds a view all posts page to any query.
 * @author Thomas Scholz http://toscho.de
 * @version 1.1
 */
class View_All_Posts
{
    /**
     * GET parameter to trigger a complete, not paged archive.
     * @var string
     */
    protected $all_param = 'all';

    public function __construct()
    {
        /* Register the query argument. */
        add_filter('query_vars', array ( $this, 'add_query_arg') );

        /* Hook into the query. */
        add_action('pre_get_posts', array ( $this, 'view_all_posts') );
    }

    /**
     * Registers the query arg in WordPress.
     * Otherwise it will be unset.
     *
     * @param  array $vars Already registered query args.
     * @return array
     */
    public function add_query_arg( array $vars )
    {
        return array_merge( $vars, array ( $this->query_arg ) );
    }

    /**
     * Alters the query to remove the paging.
     * @return void
     */
    public function view_all_posts()
    {
        if ( ! $this->is_all_posts() )
        {
            return;
        }

        $GLOBALS['wp_query']->query_vars['nopaging'] = TRUE;

        return;
    }

    /**
     * Are we there already?
     *
     * @return bool
     */
    public function is_all_posts()
    {
        return isset ( $_GET[$this->all_param] );
    }

    /**
     * Creates the markup for the link.
     *
     * Usage in archive.php, category.php or search.php:
     * $GLOBALS['view_all_posts']->get_allposts_link();
     *
     * @param  string $text Linktext
     * @param  bool   $print echo or return
     * @return string|void
     */
    public function get_allposts_link(
        $text   = 'View all posts'
    ,   $before = '<p class="allpostslink">'
    ,   $after  = '</p>'
    ,   $print  = TRUE
    )
    {
        if ( $this->is_all_posts()
        or $GLOBALS['wp_query']->found_posts <= get_option('posts_per_page')
        )
        {   // No link needed.
            return;
        }

        if ( isset ( $_SERVER['QUERY_STRING'] )
            && ! empty ( $_SERVER['QUERY_STRING'] )
        )
        {
            /* We have already visible GET parameters: /?hello=world. */
            $new_url = $_SERVER['REQUEST_URI'] . '&';
        }
        else
        {
            /* Note the difference: REQUEST_URL doesn't include
             * the query string while REQUEST_URI does. */
            $new_url = $_SERVER['REQUEST_URL'] . '?';
        }

         $link = "$before<a href='$new_url$this->all_param'>$text</a>$after";

        if ( $print )
        {
            print $link;
            return;
        }
        return $link;
    }
}

$GLOBALS['view_all_posts'] = new View_All_Posts;

Mission completed. Any suggestions?

Guest Post

Thomas ScholzThis post is written by Thomas Scholz toscho.de, a good friend of us and a web designer from Halle, Germany.

Thank you very much from our part to Thomas.

WordPress 3.0 Menu Update

The new menus WordPress 3.0 just got an update, provided by Ptah Dunbar. Thanks Ptah, cool work! I checked out the new version. But first a screenshot of the backend:

WordPress 3.0 Menu Option Page

As you can see, you can build your menu from modules. Right now these are pages, posts, custom links, categories, tags and media. You can sort the entries in your menu via drag & drop. Here an edit menu item screenshot:

Wordpress 3.0 edit menu item screenshot