2013-10-16 06:22:09 +02:00
< ? php
/**
* Twenty Fourteen Featured Content
*
2013-11-11 00:38:10 +01:00
* This module allows you to define a subset of posts to be displayed in the
* theme ' s Featured Content area .
2013-10-16 06:22:09 +02:00
*
2013-11-11 00:38:10 +01:00
* For maximum compatibility with different methods of posting users will
* designate a featured post tag to associate posts with . Since this tag now
* has special meaning beyond that of a normal tags , users will have the
* ability to hide it from the front - end of their site .
2013-10-16 06:22:09 +02:00
*/
class Featured_Content {
/**
2013-11-11 00:38:10 +01:00
* The maximum number of posts that a Featured Content area can contain . We
* define a default value here but themes can override this by defining a
* " max_posts " entry in the second parameter passed in the call to
2013-10-16 06:22:09 +02:00
* add_theme_support ( 'featured-content' ) .
*
* @ see Featured_Content :: init ()
*/
public static $max_posts = 15 ;
/**
* Instantiate
*
* All custom functionality will be hooked into the " init " action .
*/
public static function setup () {
2013-10-16 20:29:09 +02:00
add_action ( 'init' , array ( __CLASS__ , 'init' ), 30 );
2013-10-16 06:22:09 +02:00
}
/**
* Conditionally hook into WordPress
*
* Theme must declare that they support this module by adding
* add_theme_support ( 'featured-content' ); during after_setup_theme .
*
2013-11-11 00:38:10 +01:00
* If no theme support is found there is no need to hook into WordPress .
* We ' ll just return early instead .
2013-10-16 06:22:09 +02:00
*
* @ uses Featured_Content :: $max_posts
*/
public static function init () {
$theme_support = get_theme_support ( 'featured-content' );
// Return early if theme does not support Featured Content.
if ( ! $theme_support )
return ;
/*
2013-11-11 00:38:10 +01:00
* An array of named arguments must be passed as the second parameter
* of add_theme_support () .
2013-10-16 06:22:09 +02:00
*/
if ( ! isset ( $theme_support [ 0 ] ) )
return ;
// Return early if "featured_content_filter" has not been defined.
if ( ! isset ( $theme_support [ 0 ][ 'featured_content_filter' ] ) )
return ;
$filter = $theme_support [ 0 ][ 'featured_content_filter' ];
// Theme can override the number of max posts.
if ( isset ( $theme_support [ 0 ][ 'max_posts' ] ) )
self :: $max_posts = absint ( $theme_support [ 0 ][ 'max_posts' ] );
2013-11-07 21:25:09 +01:00
add_filter ( $filter , array ( __CLASS__ , 'get_featured_posts' ) );
add_action ( 'customize_register' , array ( __CLASS__ , 'customize_register' ), 9 );
add_action ( 'admin_init' , array ( __CLASS__ , 'register_setting' ) );
add_action ( 'save_post' , array ( __CLASS__ , 'delete_transient' ) );
add_action ( 'delete_post_tag' , array ( __CLASS__ , 'delete_post_tag' ) );
add_action ( 'customize_controls_enqueue_scripts' , array ( __CLASS__ , 'enqueue_scripts' ) );
add_action ( 'pre_get_posts' , array ( __CLASS__ , 'pre_get_posts' ) );
2013-11-11 00:38:10 +01:00
add_action ( 'wp_loaded' , array ( __CLASS__ , 'wp_loaded' ) );
}
2013-10-16 06:22:09 +02:00
2013-11-11 00:38:10 +01:00
/**
* Hide " featured " tag from the front - end .
*
* Has to run on wp_loaded so that the preview filters of the customizer
* have a chance to alter the value .
*/
public static function wp_loaded () {
2013-10-16 06:22:09 +02:00
if ( self :: get_setting ( 'hide-tag' ) ) {
2013-11-07 21:25:09 +01:00
add_filter ( 'get_terms' , array ( __CLASS__ , 'hide_featured_term' ), 10 , 2 );
2013-10-16 06:22:09 +02:00
add_filter ( 'get_the_terms' , array ( __CLASS__ , 'hide_the_featured_term' ), 10 , 3 );
}
}
/**
* Get featured posts
*
* @ uses Featured_Content :: get_featured_post_ids ()
*
2013-11-07 21:25:09 +01:00
* @ return array
2013-10-16 06:22:09 +02:00
*/
public static function get_featured_posts () {
$post_ids = self :: get_featured_post_ids ();
// No need to query if there is are no featured posts.
if ( empty ( $post_ids ) )
return array ();
$featured_posts = get_posts ( array (
'include' => $post_ids ,
2013-11-07 21:25:09 +01:00
'posts_per_page' => count ( $post_ids ),
2013-10-16 06:22:09 +02:00
) );
return $featured_posts ;
}
/**
* Get featured post IDs
*
* This function will return the an array containing the
* post IDs of all featured posts .
*
* Sets the " featured_content_ids " transient .
*
2013-11-07 21:25:09 +01:00
* @ return array Array of post IDs
2013-10-16 06:22:09 +02:00
*/
public static function get_featured_post_ids () {
// Return array of cached results if they exist.
$featured_ids = get_transient ( 'featured_content_ids' );
if ( ! empty ( $featured_ids ) )
return array_map ( 'absint' , ( array ) $featured_ids );
2013-11-11 00:38:10 +01:00
$settings = self :: get_setting ();
// Return sticky post ids if no tag name is set.
$term = get_term_by ( 'name' , $settings [ 'tag-name' ], 'post_tag' );
if ( $term )
$tag = $term -> term_id ;
else
return self :: get_sticky_posts ();
2013-10-16 06:22:09 +02:00
// Query for featured posts.
$featured = get_posts ( array (
'numberposts' => $settings [ 'quantity' ],
'tax_query' => array (
array (
'field' => 'term_id' ,
'taxonomy' => 'post_tag' ,
'terms' => $tag ,
),
),
) );
2013-11-07 21:25:09 +01:00
// Return array with sticky posts if no Featured Content exists.
2013-10-16 06:22:09 +02:00
if ( ! $featured )
2013-11-07 21:25:09 +01:00
return self :: get_sticky_posts ();
2013-10-16 06:22:09 +02:00
// Ensure correct format before save/return.
$featured_ids = wp_list_pluck ( ( array ) $featured , 'ID' );
$featured_ids = array_map ( 'absint' , $featured_ids );
set_transient ( 'featured_content_ids' , $featured_ids );
return $featured_ids ;
}
2013-11-07 21:25:09 +01:00
/**
* Returns an array with IDs of posts maked as sticky .
*
* @ return array
*/
public static function get_sticky_posts () {
$settings = self :: get_setting ();
return array_slice ( get_option ( 'sticky_posts' , array () ), 0 , $settings [ 'quantity' ] );
}
2013-10-16 06:22:09 +02:00
/**
* Delete transient
*
* Hooks in the " save_post " action .
* @ see Featured_Content :: validate_settings () .
*/
public static function delete_transient () {
delete_transient ( 'featured_content_ids' );
}
/**
* Exclude featured posts from the home page blog query
*
* Filter the home page posts , and remove any featured post ID ' s from it . Hooked
* onto the 'pre_get_posts' action , this changes the parameters of the query
* before it gets any posts .
*
* @ uses Featured_Content :: get_featured_post_ids ();
* @ param WP_Query $query
* @ return WP_Query Possibly modified WP_query
*/
public static function pre_get_posts ( $query = false ) {
// Bail if not home, not a query, not main query.
if ( ! is_home () || ! is_a ( $query , 'WP_Query' ) || ! $query -> is_main_query () )
return ;
$page_on_front = get_option ( 'page_on_front' );
// Bail if the blog page is not the front page.
if ( ! empty ( $page_on_front ) )
return ;
$featured = self :: get_featured_post_ids ();
// Bail if no featured posts.
if ( ! $featured )
return ;
// We need to respect post ids already in the blacklist.
$post__not_in = $query -> get ( 'post__not_in' );
if ( ! empty ( $post__not_in ) ) {
$featured = array_merge ( ( array ) $post__not_in , $featured );
$featured = array_unique ( $featured );
}
$query -> set ( 'post__not_in' , $featured );
}
/**
* Reset tag option when the saved tag is deleted
*
* It ' s important to mention that the transient needs to be deleted , too .
* While it may not be obvious by looking at the function alone , the transient
* is deleted by Featured_Content :: validate_settings () .
*
* Hooks in the " delete_post_tag " action .
* @ see Featured_Content :: validate_settings () .
*
2013-11-11 00:38:10 +01:00
* @ param int $tag_id The term_id of the tag that has been deleted .
2013-10-16 06:22:09 +02:00
* @ return void
*/
public static function delete_post_tag ( $tag_id ) {
$settings = self :: get_setting ();
2013-11-07 21:25:09 +01:00
if ( empty ( $settings [ 'tag-id' ] ) || $tag_id != $settings [ 'tag-id' ] )
2013-10-16 06:22:09 +02:00
return ;
$settings [ 'tag-id' ] = 0 ;
$settings = self :: validate_settings ( $settings );
update_option ( 'featured-content' , $settings );
}
/**
* Hide featured tag from displaying when global terms are queried from the front - end
*
* Hooks into the " get_terms " filter .
*
* @ param array $terms A list of term objects . This is the return value of get_terms () .
* @ param array $taxonomies An array of taxonomy slugs .
* @ return array $terms
*
* @ uses Featured_Content :: get_setting ()
*/
public static function hide_featured_term ( $terms , $taxonomies ) {
// This filter is only appropriate on the front-end.
if ( is_admin () )
return $terms ;
// We only want to hide the featured tag.
if ( ! in_array ( 'post_tag' , $taxonomies ) )
return $terms ;
// Bail if no terms were returned.
if ( empty ( $terms ) )
return $terms ;
foreach ( $terms as $order => $term ) {
if ( self :: get_setting ( 'tag-id' ) == $term -> term_id && 'post_tag' == $term -> taxonomy )
2013-11-07 21:25:09 +01:00
unset ( $terms [ $order ] );
2013-10-16 06:22:09 +02:00
}
return $terms ;
}
/**
* Hide featured tag from display when terms associated with a post object are queried from the front - end
*
* Hooks into the " get_the_terms " filter .
*
* @ param array $terms A list of term objects . This is the return value of get_the_terms () .
* @ param int $id The ID field for the post object that terms are associated with .
* @ param array $taxonomy An array of taxonomy slugs .
* @ return array $terms
*
* @ uses Featured_Content :: get_setting ()
*/
public static function hide_the_featured_term ( $terms , $id , $taxonomy ) {
// This filter is only appropriate on the front-end.
if ( is_admin () )
return $terms ;
// Make sure we are in the correct taxonomy.
2013-11-07 21:25:09 +01:00
if ( 'post_tag' != $taxonomy )
2013-10-16 06:22:09 +02:00
return $terms ;
// No terms? Return early!
if ( empty ( $terms ) )
return $terms ;
foreach ( $terms as $order => $term ) {
if ( self :: get_setting ( 'tag-id' ) == $term -> term_id )
2013-11-07 21:25:09 +01:00
unset ( $terms [ $term -> term_id ] );
2013-10-16 06:22:09 +02:00
}
return $terms ;
}
/**
* Register custom setting on the Settings -> Reading screen
*
* @ uses Featured_Content :: render_form ()
* @ uses Featured_Content :: validate_settings ()
*
* @ return void
*/
public static function register_setting () {
2013-11-07 21:25:09 +01:00
register_setting ( 'featured-content' , 'featured-content' , array ( __CLASS__ , 'validate_settings' ) );
2013-10-16 06:22:09 +02:00
}
/**
2013-11-07 21:25:09 +01:00
* Add settings to the Customizer .
2013-10-16 06:22:09 +02:00
*
2013-11-07 21:25:09 +01:00
* @ param WP_Customize_Manager $wp_customize Theme Customizer object .
2013-10-16 06:22:09 +02:00
*/
2013-11-07 21:25:09 +01:00
public static function customize_register ( $wp_customize ) {
$wp_customize -> add_section ( 'featured_content' , array (
'title' => __ ( 'Featured Content' , 'twentyfourteen' ),
2013-11-13 20:02:09 +01:00
'description' => sprintf ( __ ( 'Easily feature all posts with the <a href="%1$s">"featured" tag</a> or a tag of your choice; if no posts match the tag, <a href="%2$s">"sticky" posts</a> will be displayed instead.' , 'twentyfourteen' ), admin_url ( '/edit.php?tag=featured' ), admin_url ( '/edit.php?show_sticky=1' ) ),
2013-11-07 21:25:09 +01:00
'priority' => 130 ,
'theme_supports' => 'featured-content' ,
) );
2013-10-16 06:22:09 +02:00
2013-11-07 21:25:09 +01:00
// Add Featured Content settings.
$wp_customize -> add_setting ( 'featured-content[tag-name]' , array (
2013-11-11 00:38:10 +01:00
'default' => 'featured' ,
'type' => 'option' ,
'sanitize_js_callback' => array ( __CLASS__ , 'delete_transient' ),
2013-11-07 21:25:09 +01:00
) );
$wp_customize -> add_setting ( 'featured-content[hide-tag]' , array (
2013-11-11 00:38:10 +01:00
'default' => true ,
'type' => 'option' ,
'sanitize_js_callback' => array ( __CLASS__ , 'delete_transient' ),
2013-11-07 21:25:09 +01:00
) );
2013-10-16 20:27:09 +02:00
2013-11-07 21:25:09 +01:00
// Add Featured Content controls.
$wp_customize -> add_control ( 'featured-content[tag-name]' , array (
'label' => __ ( 'Tag name' , 'twentyfourteen' ),
'section' => 'featured_content' ,
'priority' => 20 ,
) );
$wp_customize -> add_control ( 'featured-content[hide-tag]' , array (
'label' => __ ( 'Hide tag from displaying in post meta and tag clouds.' , 'twentyfourteen' ),
'section' => 'featured_content' ,
'type' => 'checkbox' ,
'priority' => 30 ,
) );
2013-10-16 06:22:09 +02:00
}
2013-11-07 21:25:09 +01:00
/**
* Enqueue the tag suggestion script .
*
* @ since Twenty Fourteen 1.0
*/
public static function enqueue_scripts () {
wp_enqueue_script ( 'featured-content-suggest' , get_template_directory_uri () . '/js/featured-content-admin.js' , array ( 'jquery' , 'suggest' ), '20131022' , true );
}
2013-10-16 06:22:09 +02:00
/**
* Get settings
*
* Get all settings recognized by this module . This function will return
* all settings whether or not they have been stored in the database yet .
* This ensures that all keys are available at all times .
*
* In the event that you only require one setting , you may pass its name
* as the first parameter to the function and only that value will be returned .
*
2013-11-07 21:25:09 +01:00
* @ uses Featured_Content :: sanitize_quantity ()
2013-10-16 06:22:09 +02:00
*
* @ param string $key The key of a recognized setting .
* @ return mixed Array of all settings by default . A single value if passed as first parameter .
*/
public static function get_setting ( $key = 'all' ) {
$saved = ( array ) get_option ( 'featured-content' );
$defaults = array (
'hide-tag' => 1 ,
'quantity' => 6 ,
'tag-id' => 0 ,
2013-11-11 00:38:10 +01:00
'tag-name' => 'featured' ,
2013-10-16 06:22:09 +02:00
);
$options = wp_parse_args ( $saved , $defaults );
$options = array_intersect_key ( $options , $defaults );
$options [ 'quantity' ] = self :: sanitize_quantity ( $options [ 'quantity' ] );
2013-11-07 21:25:09 +01:00
if ( 'all' != $key )
return isset ( $options [ $key ] ) ? $options [ $key ] : false ;
2013-10-16 06:22:09 +02:00
return $options ;
}
/**
* Validate settings
*
* Make sure that all user supplied content is in an
* expected format before saving to the database . This
* function will also delete the transient set in
* Featured_Content :: get_featured_content () .
*
* @ uses Featured_Content :: self :: sanitize_quantity ()
* @ uses Featured_Content :: self :: delete_transient ()
*
* @ param array $input
* @ return array $output
*/
public static function validate_settings ( $input ) {
$output = array ();
2013-11-11 00:38:10 +01:00
if ( empty ( $input [ 'tag-name' ] ) ) {
$output [ 'tag-id' ] = 0 ;
} else {
$new_tag = wp_create_tag ( $input [ 'tag-name' ] );
if ( ! is_wp_error ( $new_tag ) && isset ( $new_tag [ 'term_id' ] ) ) {
$output [ 'tag-id' ] = $new_tag [ 'term_id' ];
2013-11-07 21:25:09 +01:00
} else {
2013-11-11 00:38:10 +01:00
$term = get_term_by ( 'name' , $input [ 'tag-name' ], 'post_tag' );
$output [ 'tag-id' ] = $term ? $term -> term_id : 0 ;
2013-11-07 21:25:09 +01:00
}
2013-11-11 00:38:10 +01:00
$output [ 'tag-name' ] = $input [ 'tag-name' ];
2013-10-16 06:22:09 +02:00
}
if ( isset ( $input [ 'quantity' ] ) )
$output [ 'quantity' ] = self :: sanitize_quantity ( $input [ 'quantity' ] );
2013-11-07 21:25:09 +01:00
$output [ 'hide-tag' ] = isset ( $input [ 'hide-tag' ] ) && $input [ 'hide-tag' ] ? 1 : 0 ;
2013-10-16 06:22:09 +02:00
self :: delete_transient ();
return $output ;
}
/**
* Sanitize quantity
*
* @ param int $input The value to sanitize .
* @ return int A number between 1 and FeaturedContent :: $max_posts .
*
* @ uses Featured_Content :: $max_posts
*/
public static function sanitize_quantity ( $input ) {
$quantity = absint ( $input );
if ( $quantity > self :: $max_posts )
$quantity = self :: $max_posts ;
else if ( 1 > $quantity )
$quantity = 1 ;
return $quantity ;
}
}
Featured_Content :: setup ();