WordPress has post sticky option only for posts
post type. So if you are going to create a new custom post type, there is no native way for adding support for sticky posts. This feature was considered to bring to the core, but didn’t happen sadly.
In my case, I’m using a plugin Sticky Custom Post Types to add sticky post support for my custom post type. But the problem is, the sticky posts doesn’t stick in the post type archive. If you are registering custom post type and want to display all posts from that custom post type in a page without using any page template, custom post type archive comes handy in this case. So, if you have a post type named place
, WordPress will search for a page archive-place.php
to display all the posts from place
post type (if it supports archive. e.g: 'has_archive' => true
).
But the problem arises here, you won’t see your sticky posts at the top of the post list. Because WordPress doesn’t process sticky posts on other pages except home (e.g: is_home()
). So here’s the handy hack to move all the sticky posts to the top of the archive post list.
[php]
/**
* Put sticky posts to top at custom post archives
*
* WordPress doesn’t do any processing for sticky posts in custom post type archives.
* It process sticky posts in homepage only (is_home()). This function processes
* sticky posts at custom post archive page and puts them to the top of list.
*
* @author Tareq Hasan (http://tareq.weDevs.com)
*
* @param array $posts array of queried posts
* @return array
*/
function wedevs_cpt_sticky_at_top( $posts ) {
// apply the magic on post archive only
if ( is_main_query() && is_post_type_archive() ) {
global $wp_query;
$sticky_posts = get_option( ‘sticky_posts’ );
$num_posts = count( $posts );
$sticky_offset = 0;
// loop through the post array and find the sticky post
for ($i = 0; $i < $num_posts; $i++) {
// Put sticky posts at the top of the posts array
if ( in_array( $posts[$i]->ID, $sticky_posts ) ) {
$sticky_post = $posts[$i];
// Remove sticky from current position
array_splice( $posts, $i, 1 );
// Move to front, after other stickies
array_splice( $posts, $sticky_offset, 0, array($sticky_post) );
$sticky_offset++;
// Remove post from sticky posts array
$offset = array_search($sticky_post->ID, $sticky_posts);
unset( $sticky_posts[$offset] );
}
}
// Fetch sticky posts that weren’t in the query results
if ( !empty( $sticky_posts) ) {
$stickies = get_posts( array(
‘post__in’ => $sticky_posts,
‘post_type’ => $wp_query->query_vars[‘post_type’],
‘post_status’ => ‘publish’,
‘nopaging’ => true
) );
foreach ( $stickies as $sticky_post ) {
array_splice( $posts, $sticky_offset, 0, array( $sticky_post ) );
$sticky_offset++;
}
}
}
return $posts;
}
add_filter( ‘the_posts’, ‘wedevs_cpt_sticky_at_top’ );
[/php]
So, grab the plugin mentioned earlier for adding sticky support to custom post type and use this code snippet to bring the sticky posts at the top.
Tareq, my question is this. I am using a commercial framework to create a Membership Real Estate listing site. Users can submit their own property listings after registering. This Framework adds custom functions and a User Front End that make sure that some of the prescribed values are contained in dropdowns or checkbox selections.
Like most frameworks it keeps the Registered User from seeing and using the Wp Admin panel to make and edit the own posts.
But, using this “glorified” FORM approach means that the User can not use or even see a Plugin or plugins written specifically to give every User with an Author or Subscriber role more ways to add content to their own Post/Listing.
For example, I have an Availability Calendar plugin that after the Administrator installs and activates it then installs a User Menu that is seen in the WP Admin Panel in a Twenty-Eleven install.
The Calendar plugin gives all logged in registered Users a manage_options permission for their own versions of the master Calendar. A User who has been assigned an AUTHOR role now will see a menu of different options they can create and save to create one separate instance of this Calendar for each listing/Post. Each Calendar then will have its own date ranges of Open and Closed days.
BUT, in a Framework Front End it cannot show or access that Plugin for Users because FORMS cannot display PLUGINS and their Options.
The Framework Front End is just a posting form, not a true WP Admin environment.
Tareq, is there a way to allow a Framework to have both the “pretty Front End for User interface” but ALSO allow this Form to integrate “parts” of the WP User Admin — specifically the full Text Editor with any tools that a Plugin would install AND the Plugin Options Menu as seen in the Wp Admin view if the User were in the default WP Admin logged in view?
Can a Framework Front End be a HYBRID of both simplified User Interface and the more sophisticated User Administration of a for-User Plugin?
Thanks for considering this lengthy question!
Yeah, thats obviously possible. Creating a backend area and frontend area for maintaining things is a twice pain, cause you’ve to deal with both area. It’s painful, but possible.
Hi Tareq,
Thanks for this post. It’s quite useful.
One short-coming of this function that I think I’m seeing is that if the sticky post does not appear in the first “page” as determined by the ‘posts_per_page’ setting of the query, then the_posts filter is too late to add it.
For example, if I am listing the three most-recent posts and the fourth most-recent post is sticky, then it seems not to work.
Did you run into this and can you think of how you might handle it. My current thought is to actually query for all the sticky posts and then add each post object onto the front of $posts object while popping off on from the end each time. However, if one needed page (I don’t, but still…) then I don’t think this would work.
Yeah, you got it right. The trick I used here is,
the_post
returns the current posts. I get the list of sticky posts, match them with the posts and pop out the found one from their current position and place them to front. I think WordPress does that same thing.It actually depends on the situation. If you really need to display all the sticky posts at the top,
pre_get_posts
filter would be ideal to deal with besides of this trick.Hmm… I also need the functionality of grabbing posts on pages other than the current one. Simply changing ‘the_posts’ to ‘pre_get_posts’ doesn’t work for me, though. Are there additional implied steps that I’m missing?
May be it’s not working because you are doing it on a page. If you check the code, it’s using
is_main_query()
to determine if it’s the main loop. In a page, the main loop only will return one post. So using the same code won’t work there. In that case, may be you need to run a newWP_Query
instance and remove theis_main_query()
checking.I misspoke—I’m running this on a custom post type archives page. It’s really the exact same predicament Mark describes. When I said “on pages other than the current one,” I meant to describe the pagination.
Till now I thought, if you set the pagination to 10 posts per page, WP will show only 10 posts in a page including the sticky posts. And I was wrong. Simply WP pulls the sticky posts and prepends to the front.
So we get the idea, now code is updated 🙂
Hey Tareq,
Thanks for this very useful code. I have a question though. On the home page the sticky posts usually have class .sticky or .featured-post added by WordPress. Can we get them in this hack or how do you suggest that we should style the sticky posts differently?
I think it’ll add the sticky class automatically, just you need to make sure that you are using the
post_class()
function in your themes template file.yes, I am using default twenty twelve theme which uses post_class.
<article id="post-" >
which outputs these classes:
post-6 movie_reviews type-movie_reviews status-publish hentry
but not sticky.
Oh you are right, I missed the a part on the
get_post_class()
function. Seems like it’s adding thesticky
class only on the homepage. So you need to add a filter onpost_class
to add the sticky css class.Thanks, I used this to add sticky class:
[php]
function cpt_sticky_class($classes) {
if ( is_sticky() ) :
$classes[] = ‘sticky’;
return $classes;
endif;
return $classes;
}
add_filter(‘post_class’, ‘cpt_sticky_class’);
[/php]
Tareq,
many thanks for this snippet – it is really helpful and easy to understand.
I am considering one small problem though – if you put this code to your functions.php and (for whatever reason) want your posts to be displayed in a random order, how do you make the sticky posts stay at the top and randomize the regular ones?
Adding query_posts($query_string . ‘&orderby=rand’); to my archive file randomizes the order of all the posts, so the sticky posts won’t stay at the top.
Kind regards
That works nice, however not so easy for non tech guy like me. Tried the Advanced Post Types Order http://www.nsp-code.com/premium-plugins/wordpress-plugins/advanced-post-types-order/ and work much easier, through a drag and drop interface i can set certain posts for sticky in certain position.