Including Sticky Posts in Recent Posts Shortcode

Hello, I’ve got a particular issue I’m working through.

I’m using the “Classic Recent Posts” shortcode to return the latest post in several areas on my site using offsets to show the right post in the right place. In talking through what we’d like to do, we also would like to use sticky posts to bump highlighted posts to the beginning of the list. However, sticky posts don’t behave as we would like them to behave. Instead of replacing the latest post and bumping the rest down the line according to the offset setting, instead it appends the sticky note in addition to the latest post. Is there a way for a sticky post to behave like normal posts in the wordpress loop so that they are actually the first post in the list and not just added before the first post?

So, to clarify what I want, here is an example

[recent_posts count=1]
[recent_posts count=1 offset=1]
[recent_posts count=1 offset=2]

This works great and as expected without sticky posts and returns this:

[ post 1 ]
[ post 2 ]
[ post 3 ]

However, when I make a post sticky, here is what happens:

[ sticky 1 ]
[ post 1 ]
[ sticky 1 ]
[ post 2 ]
[ sticky 1 ]
[ post 3 ]

What I want to happen is:

[ sticky 1 ]
[ post 1 ]
[ post 2 ]

Do you have any suggestions on how to do this?

Hello @bobbybosler

To achieve that, please add this code to your child theme functions.php file:

// Setting "no-sticky" to false
// =============================================================================

function x_shortcode_recent_posts_v2( $atts ) {
  extract( shortcode_atts( array(
    'id'           => '',
    'class'        => '',
    'style'        => '',
    'type'         => 'post',
    'count'        => '',
    'category'     => '',
    'offset'       => '',
    'orientation'  => '',
    'show_excerpt' => 'true',
    'no_sticky'    => '',
    'no_image'     => '',
    'fade'         => ''
  ), $atts, 'x_recent_posts' ) );

  $allowed_post_types = apply_filters( 'cs_recent_posts_post_types', array( 'post' => 'post' ) );
  $type = ( isset( $allowed_post_types[$type] ) ) ? $allowed_post_types[$type] : 'post';

  $id            = ( $id           != ''     ) ? 'id="' . esc_attr( $id ) . '"' : '';
  $class         = ( $class        != ''     ) ? 'x-recent-posts cf ' . esc_attr( $class ) : 'x-recent-posts cf';
  $style         = ( $style        != ''     ) ? 'style="' . $style . '"' : '';
  $count         = ( $count        != ''     ) ? $count : 3;
  $category      = ( $category     != ''     ) ? $category : '';
  $category_type = ( $type         == 'post' ) ? 'category_name' : 'portfolio-category';
  $offset        = ( $offset       != ''     ) ? $offset : 0;
  $orientation   = ( $orientation  != ''     ) ? ' ' . $orientation : ' horizontal';
  $show_excerpt  = ( $show_excerpt == 'true' );
  $no_sticky     = ( $no_sticky    == 'false' );
  $no_image      = ( $no_image     == 'true' ) ? $no_image : '';
  $fade          = ( $fade         == 'true' ) ? $fade : 'false';

  $js_params = array(
    'fade' => ( $fade == 'true' )
  );

  $data = cs_generate_data_attributes( 'recent_posts', $js_params );

  $output = "<div {$id} class=\"{$class}{$orientation}\" {$style} {$data} data-fade=\"{$fade}\" >";

    $q = new WP_Query( array(
      'orderby'             => 'date',
      'post_type'           => "{$type}",
      'posts_per_page'      => "{$count}",
      'offset'              => "{$offset}",
      "{$category_type}"    => "{$category}",
      'ignore_sticky_posts' => $no_sticky
    ) );

    if ( $q->have_posts() ) : while ( $q->have_posts() ) : $q->the_post();

      if ( $no_image == 'true' ) {
        $image_output       = '';
        $image_output_class = 'no-image';
      } else {
        $image              = wp_get_attachment_image_src( get_post_thumbnail_id(), 'thumbnail' );
        $bg_image           = ( $image[0] != '' ) ? ' style="background-image: url(' . $image[0] . ');"' : '';
        $image_output       = '<div class="x-recent-posts-img"' . $bg_image . '></div>';
        $image_output_class = 'with-image';
      }

      $excerpt = ( $show_excerpt ) ? '<div class="x-recent-posts-excerpt"><p>' . preg_replace('/<a.*?more-link.*?<\/a>/', '', cs_get_raw_excerpt() ) . '</p></div>' : '';

      $output .= '<a class="x-recent-post' . $count . ' ' . $image_output_class . '" href="' . get_permalink( get_the_ID() ) . '" title="' . esc_attr( sprintf( csi18n('shortcodes.recent-posts-permalink'), the_title_attribute( 'echo=0' ) ) ) . '">'
                 . '<article id="post-' . get_the_ID() . '" class="' . implode( ' ', get_post_class() ) . '">'
                   . '<div class="entry-wrap">'
                     . $image_output
                     . '<div class="x-recent-posts-content">'
                       . '<h3 class="h-recent-posts">' . get_the_title() . '</h3>'
		               . '<span class="recent-posts-author">' .the_author() . '</span>'
                       . '<span class="x-recent-posts-date">' . get_the_date() . '</span>'
                        . $excerpt
                     . '</div>'
                   . '</div>'
                 . '</article>'
               . '</a>';

    endwhile; endif; wp_reset_postdata();

  $output .= '</div>';

  return $output;

}

add_filter('wp_head', 'custom_recent_posts');

function custom_recent_posts() {
  remove_shortcode( 'x_recent_posts' );
  remove_shortcode( 'recent_posts' );
  add_shortcode( 'x_recent_posts', 'x_shortcode_recent_posts_v2' );
  add_shortcode( 'recent_posts', 'x_shortcode_recent_posts_v2' );
}

Then you can use this shortcode: [recent_posts count=1] which will show the sticky post + the second recent post, which you can control with offset parameter.

If you don’t know how to create and activate child themes, please check this guide:

Thanks.

Great. That helps.

One more follow up question. Is there a way to disable sticky posts from appearing out of order on the blog page? In other words, I’d like for them to appear like normal posts on the blog page and not at the top.

EDIT:
In my investigation, it seems that adding the following code to my functions.php should accomplish what I want, but it is not doing that. The Sticky posts are still at the top of the page, when they should be in chronological order below.

function bcm_exclude_sticky_posts( $query ) {  

if ( $query->is_home() && $query->is_main_query() )  
$query->set( 'ignore_sticky_posts', 1 );  
}  
add_action( 'pre_get_posts', 'bcm_exclude_sticky_posts' );

Hi @bobbybosler

Try this one instead:

function ignore_sticky_posts_in_blog( $query ) {
	if ( $query->is_home() && $query->is_main_query() ) {
		$query->set( 'ignore_sticky_posts', 'true' );
	}
}
add_action( 'pre_get_posts', 'ignore_sticky_posts_in_blog' );

I confirmed it’s working fine but your blog page must be set as “Posts Page” under (WordPress > Settings > Reading):

Thanks.

Thanks.

I’ve done all these things, but for some reason, my sticky posts keep appearing at the top of the “News & Updates” page. I’ll include a login via a secure note so you can take a look-see.

It could be some sort of conflict between other codes added in functions.php file or those files in integrity folder in the child theme directory, I suggest taking a backup of these files, then remove them one by one to investigate this issue.

It could be also a caching issue, try to set these posts to be non-sticky then reload the blog page, then try to put them sticky again and clear cache.

Let me know how it goes,
Thanks.

Got it!

Somewhere along the way in investigating this issue, I installed the “Post Glue” plugin. Once I deactivated it, everything worked as expected.

Thanks so much!

Thanks for updating the thread, I’m glad you managed to get this one sorted out, you are welcome :slight_smile:

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.