Entry header, first paragraph, featured Image above the remaining content and sidebar on single Posts in Genesis

Posted on

In Genesis Facebook group, a user asked an interesting question:

How would you do that semantically correct with genesis?

While this looks simple on the surface, it is actually challenging especially with the condition that text below the title is first paragraph of the Post’s content and that text below the featured image should not include the first para.

Method 1: Short and Recommended

(Added on January 19, 2016)

The benefit of using this approach is that structured data like headline and date. Published will remain intact. This can be tested at https://developers.google.com/structured-data/testing-tool/. See this comment for the background.

The idea is to insert featured image and Primary sidebar after the first paragraph.

Step 1

Add the following in child theme’s functions.php:

// Register a custom image size for featured images on single Posts
add_image_size( 'post-single', 1080, 270, true );

Regenerate thumbnails.

Step 2

Create a file named single-post.php in child theme directory having the following:

// Add "has-featured-image" body class if the post has a featured image
add_filter( 'body_class', 'sk_single_post_body_class' );
/**
* Adds a css class to the body element
*
* @param array $classes the current body classes
* @return array $classes modified classes
*/
function sk_single_post_body_class( $classes ) {

if ( has_post_thumbnail() ) {
$classes[] = 'has-featured-image';
}

return $classes;
}

add_action( 'get_header', 'sk_layout' );
function sk_layout() {

// if the post does not have a featured image, abort.
if ( ! has_post_thumbnail() ) {
return;
}

// force full width content
add_filter( 'genesis_pre_get_option_site_layout', '__genesis_return_full_width_content' );

// insert featured image and Primary sidebar
add_filter( 'the_content', sk_featured_image_sidebar, 20 );

}

// Function to insert featured image and Primary sidebar
function sk_featured_image_sidebar( $content ) {

// store the Primary sidebar's output in a variable using output buffering
ob_start();
get_sidebar(); // include the sidebar.php template file
$sidebar = ob_get_clean();

// insert featured image and Primary sidebar after the first paragraph
$content = preg_replace( '/<\/p>/', '</p>' . get_the_post_thumbnail( $post->ID, 'post-single' ) . $sidebar, $content, 1 );

return $content;

}

genesis();

Step 3

Add the following in child theme’s style.css:

.attachment-post-single {
	margin-bottom: 22px;
}

.single-post.has-featured-image .sidebar-primary {
	margin-left: 30px;
}

.single-post.has-featured-image .sidebar .widget {
	padding-top: 0;
	padding-bottom: 0;
}

.entry-content .sidebar ul {
	margin-left: 0;
}

.entry-content .sidebar ul > li {
	list-style-type: none;
	list-style-image: none;
}

That’s it.

Single Posts that do not have featured images will continue to appear normally (with a right sidebar).

Method 2: Long and Not Recommended

Here’s the plan of action:

  • Create a custom shortcode in functions file to output the URL of author page for the entry author outside the loop. This is needed because otherwise linked author will not appear as we are going to show the post info outside the loop.
  • Create a template for single Posts in which
    1. entry header having entry title and post info are repositioned before content
    2. a custom function that prints the first paragraph of the current Post is hooked before content
    3. featured image is set to appear before content
    4. regex is used to find the first para in the post’s content and replace it with an empty string
    5. use genesis_post_info filter hook to replace the default post author posts link shortcode with post_author_posts_link_outside_loop custom shortcode

While the tutorial has been written for Genesis Sample child theme it should work with minor adjustments in any Genesis child theme.

Step 1

Add the following in child theme’s functions.php:

// Register a custom image size for featured images on single Posts
add_image_size( 'post-single', 1200, 300, true );

// Create a shortcode that outputs the URL of author page for the entry author outside the loop
add_shortcode( 'post_author_posts_link_outside_loop', 'sk_post_author_posts_link_shortcode' );
/**
* Produces the author of the post (link to author archive).
*
* Supported shortcode attributes are:
* after (output after link, default is empty string),
* before (output before link, default is empty string).
*
* Output passes through 'genesis_post_author_posts_link_shortcode' filter before returning.
*
* @since 1.1.0
*
* @param array|string $atts Shortcode attributes. Empty string if no attributes.
* @return string Shortcode output
*/
function sk_post_author_posts_link_shortcode( $atts ) {

if ( ! is_singular() ) {
return;
}

$defaults = array(
'after' => '',
'before' => '',
);

$atts = shortcode_atts( $defaults, $atts, 'post_author_posts_link_outside_loop' );

global $post;
$author_id = $post->post_author;
$author = get_the_author_meta( 'display_name', $author_id );
$url = get_author_posts_url( $author_id );

if ( genesis_html5() ) {
$output = sprintf( '<span %s>', genesis_attr( 'entry-author' ) );
$output .= $atts['before'];
$output .= sprintf( '<a href="%s" %s>', $url, genesis_attr( 'entry-author-link' ) );
$output .= sprintf( '<span %s>', genesis_attr( 'entry-author-name' ) );
$output .= esc_html( $author );
$output .= '</span></a>' . $atts['after'] . '</span>';
} else {
$link = sprintf( '<a href="%s" rel="author">%s</a>', esc_url( $url ), esc_html( $author ) );
$output = sprintf( '<span class="author vcard">%2$s<span class="fn">%1$s</span>%3$s</span>', $link, $atts['before'], $atts['after'] );
}

return apply_filters( 'genesis_post_author_posts_link_shortcode', $output, $atts );

}

Regenerate thumbnails.

Step 2

Create a file named single-post.php in the child theme directory having the following code:

// Add opening div.single-post-top for entry header + first para
add_action( 'genesis_before_content', 'sk_single_post_top_opening' );
function sk_single_post_top_opening() {
echo '<div class="single-post-top">';
}

// Add entry header before content
add_action( 'genesis_before_content', 'genesis_entry_header_markup_open' );
add_action( 'genesis_before_content', 'genesis_do_post_title' );
add_action( 'genesis_before_content', 'genesis_post_info' );
add_action( 'genesis_before_content', 'genesis_entry_header_markup_close' );

// Show first para before content
add_action( 'genesis_before_content', 'get_first_paragraph' );
/**
* Echo first paragraph from the current post.
*
*/
function get_first_paragraph() {

global $post;

$content = wpautop( $post->post_content );

$str = substr( $content, 0, strpos( $content, '</p>' ) + 4 );

// To remove images from the first paragraph
// $str = preg_replace( '/<img[^>]+\>/i', '', $str );

echo '<div class="first-para">' . $str . '</div></div>';

}

// If featured image is present, show it before content
add_action( 'genesis_before_content', 'sk_featured_image' );
function sk_featured_image() {

if ( $image = genesis_get_image( 'format=url&size=post-single' ) ) {
printf( '<div class="featured-image-single"><img src="%s" alt="%s" /></div>', $image, the_title_attribute( 'echo=0' ) );

}

}

// Remove first para from the content
add_filter( 'the_content', sk_remove_first_para, 20 );
function sk_remove_first_para( $content ) {

$content = preg_replace( '/<p.*?<\/p>/s', '', $content, 1 );

return $content;

}

// Remove entry header from its default location
remove_action( 'genesis_entry_header', 'genesis_entry_header_markup_open', 5 );
remove_action( 'genesis_entry_header', 'genesis_entry_header_markup_close', 15 );
remove_action( 'genesis_entry_header', 'genesis_do_post_title' );
remove_action( 'genesis_entry_header', 'genesis_post_info', 12 );

// Customize entry meta in the entry header
add_filter( 'genesis_post_info', 'sp_post_info_filter' );
function sp_post_info_filter( $post_info ) {
$post_info = '[post_date] by [post_author_posts_link_outside_loop] [post_comments] [post_edit]';
return $post_info;
}

genesis();

The following image should help you understand how the regex is set to match the first para:

Step 3

Add the following in child theme’s style.css:

.single-post-top {
	padding: 50px 50px 22px 50px;
	overflow: hidden;
}

.single-post .content-sidebar-wrap {
	background-color: #fff;
	overflow: hidden;
	margin-bottom: 40px;
}

.featured-image-single img {
	vertical-align: top;
}

@media only screen and (max-width: 800px) {

	.single-post-top {
		padding: 0;
	}

	.featured-image-single {
		margin-bottom: 28px;
	}

}