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 datePublished 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 ); |
Step 2
Create a file named single-post.php in child theme directory having the following:
<?php | |
// 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
- entry header having entry title and post info are repositioned before content
- a custom function that prints the first paragraph of the current Post is hooked before content
- featured image is set to appear before content
- regex is used to find the first para in the post’s content and replace it with an empty string
- use
genesis_post_info
filter hook to replace the default post author posts link shortcode withpost_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 ); | |
} |
Step 2
Create a file named single-post.php in the child theme directory having the following code:
<?php | |
// 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:
From DEVANZ Slack chat:
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; | |
} | |
} |
References:
http://wordpress.stackexchange.com/a/61280/14380
https://gist.github.com/banago/4023646
http://wordpress.stackexchange.com/questions/51741/get-post-content-from-outside-the-loop
http://www.codecheese.com/2013/11/get-the-first-paragraph-as-an-excerpt-for-wordpress/
http://www.regular-expressions.info/modifiers.html
https://sridharkatakam.com/post-authors-link-outside-the-loop-in-genesis/
Would it be possible to implement this with Sixteen Nine Pro Theme using ‘content-sidebar’? I’ve attempted; but haven’t had luck.
Yes. I just tested Method 1 and it works fine in Sixteen Nine Pro.
https://www.youtube.com/watch?v=TBdbBox5wOE
You just need to adjust the CSS: http://pastebin.com/raw/Uf34SmVi
How would I accomplish this using the secondary sidebar? I tried swapping in get_sidebar( ‘sidebar-alt’ ); but no luck
Oops – it’s supposed to be get_sidebar( ‘alt’ );