Make WordPress Core

Ticket #25549: 25549.diff

File 25549.diff, 16.4 KB (added by obenland, 11 years ago)
  • wp-content/themes/twentyfourteen/functions.php

     
    102102        ) ) );
    103103
    104104        /*
     105         * Add support for featured content.
     106         */
     107        add_theme_support( 'featured-content', array(
     108                'featured_content_filter' => 'twentyfourteen_get_featured_posts',
     109                'max_posts' => 6,
     110        ) );
     111
     112        /*
    105113         * This theme uses its own gallery styles.
    106114         */
    107115        add_filter( 'use_default_gallery_style', '__return_false' );
     
    126134 * Getter function for Featured Content Plugin.
    127135 *
    128136 * @since Twenty Fourteen 1.0
     137 *
     138 * @return array An array of WP_Post objects.
    129139 */
    130140function twentyfourteen_get_featured_posts() {
    131         return apply_filters( 'twentyfourteen_get_featured_posts', false );
     141        return apply_filters( 'twentyfourteen_get_featured_posts', array() );
    132142}
    133143
    134144/**
    135  * A helper conditional function that returns a boolean value
    136  * So that we can use a condition like
    137  * if ( twentyfourteen_has_featured_posts( 1 ) )
     145 * A helper conditional function that returns a boolean value.
    138146 *
    139147 * @since Twenty Fourteen 1.0
     148 *
     149 * @return bool Whether there are featured posts.
    140150 */
    141 function twentyfourteen_has_featured_posts( $minimum = 1 ) {
    142         if ( is_paged() )
    143                 return false;
    144 
    145                 $featured_posts = apply_filters( 'twentyfourteen_get_featured_posts', array() );
    146 
    147         return is_array( $featured_posts ) && count( $featured_posts ) > absint( $minimum );
     151function twentyfourteen_has_featured_posts() {
     152        return ! is_paged() && (bool) apply_filters( 'twentyfourteen_get_featured_posts', false );
    148153}
    149154
    150155/**
     
    578583
    579584// Add Theme Customizer functionality.
    580585require get_template_directory() . '/inc/customizer.php';
     586
     587/*
     588 * Adds featured content functionality.
     589 *
     590 * To overwrite in a plugin, define your own Featured_Content class on or
     591 * before the 'setup_theme' hook.
     592 */
     593if ( ! class_exists( 'Featured_Content' ) && 'plugins.php' != $GLOBALS['pagenow'] )
     594        require get_template_directory() . '/inc/featured-content.php';
  • wp-content/themes/twentyfourteen/inc/featured-content.php

     
     1<?php
     2
     3/**
     4 * Featured Content.
     5 *
     6 * This module will allow users to define a subset of posts
     7 * to be displayed in a theme-designated featured content area.
     8 *
     9 * This feature will only be activated for themes that declare
     10 * that they support it. This can be done by adding code similar
     11 * to the following during the "after_setup_theme" action:
     12 *
     13 * add_theme_support( 'featured-content', array(
     14 *     'featured_content_filter' => 'mytheme_get_featured_content',
     15 *     'description' => 'Describe the featured content area.',
     16 *     'max_posts' => 20,
     17 * ) );
     18 *
     19 * For maximum compatibility with different methods of posting
     20 * users will designate a featured post tag to associate posts
     21 * to. Since this tag now has special meaning beyond that of a
     22 * normal tags, users will have the ability to hide it from the
     23 * front-end of their site.
     24 */
     25class Featured_Content {
     26
     27        /**
     28         * The maximum number of posts that a featured content
     29         * area can contain. We define a default value here but
     30         * themes can override this by defining a "max_posts"
     31         * entry in the second parameter passed in the call to
     32         * add_theme_support( 'featured-content' ).
     33         *
     34         * @see Featured_Content::init()
     35         */
     36        public static $max_posts = 15;
     37
     38        /**
     39         * Instantiate.
     40         *
     41         * All custom functionality will be hooked into the "init" action.
     42         */
     43        public static function setup() {
     44                add_action( 'init', array( __class__, 'init' ), 30 );
     45        }
     46
     47        /**
     48         * Conditionally Hook into WordPress.
     49         *
     50         * Themes must declare that they support this module by adding
     51         * add_theme_support( 'featured-content' ); during after_setup_theme.
     52         *
     53         * If no theme support is found there is no need to hook into
     54         * WordPress. We'll just return early instead.
     55         *
     56         * @uses Featured_Content::$max_posts
     57         */
     58        public static function init() {
     59                $theme_support = get_theme_support( 'featured-content' );
     60
     61                // Return early if theme does not support featured content.
     62                if ( ! $theme_support )
     63                        return;
     64
     65                // An array of named arguments must be passed as
     66                // the second parameter of add_theme_support().
     67                if ( ! isset( $theme_support[0] ) )
     68                        return;
     69
     70                // Return early if "featured_content_filter" has not been defined.
     71                if ( ! isset( $theme_support[0]['featured_content_filter'] ) )
     72                        return;
     73
     74                $filter = $theme_support[0]['featured_content_filter'];
     75
     76                // Themes can override the number of max posts.
     77                if ( isset( $theme_support[0]['max_posts'] ) )
     78                        self::$max_posts = absint( $theme_support[0]['max_posts'] );
     79
     80                add_filter( $filter,                 array( __CLASS__, 'get_featured_posts' ) );
     81                add_action( 'admin_init',            array( __CLASS__, 'register_setting' ) );
     82                add_action( 'save_post',             array( __CLASS__, 'delete_transient' ) );
     83                add_action( 'delete_post_tag',       array( __CLASS__, 'delete_post_tag' ) );
     84                add_action( 'pre_get_posts',         array( __CLASS__, 'pre_get_posts' ) );
     85
     86                // Hide "featured" tag from the front-end.
     87                if ( self::get_setting( 'hide-tag' ) ) {
     88                        add_filter( 'get_terms',     array( __CLASS__, 'hide_featured_term' ), 10, 2 );
     89                        add_filter( 'get_the_terms', array( __CLASS__, 'hide_the_featured_term' ), 10, 3 );
     90                }
     91        }
     92
     93        /**
     94         * Get Featured Posts.
     95         *
     96         * This method is not intended to be called directly.
     97         * Theme developers should place a filter directly in
     98         * their theme and then pass its name as a value of the
     99         * "featured_content_filter" key in the array passed as
     100         * the $args parameter during the call to:
     101         * add_theme_support( 'featured-content', $args ).
     102         *
     103         * @uses Featured_Content::get_featured_post_ids()
     104         *
     105         * @return array|bool
     106         */
     107        public static function get_featured_posts() {
     108                $post_ids = self::get_featured_post_ids();
     109
     110                // User has disabled Featured Content.
     111                if ( false === $post_ids )
     112                        return false;
     113
     114                // No need to query if there is are no featured posts.
     115                if ( empty( $post_ids ) )
     116                        return array();
     117
     118                $featured_posts = get_posts( array(
     119                        'include'        => $post_ids,
     120                        'posts_per_page' => count( $post_ids )
     121                ) );
     122
     123                return $featured_posts;
     124        }
     125
     126        /**
     127         * Get Featured Post IDs.
     128         *
     129         * This function will return the an array containing the
     130         * post IDs of all featured posts.
     131         *
     132         * Sets the "featured_content_ids" transient.
     133         *
     134         * @return array|false Array of post IDs. false if user has disabled this feature.
     135         */
     136        public static function get_featured_post_ids() {
     137                $settings = self::get_setting();
     138
     139                // Return false if the user has disabled this feature.
     140                $tag = $settings['tag-id'];
     141                if ( empty( $tag ) )
     142                        return false;
     143
     144                // Return array of cached results if they exist.
     145                $featured_ids = get_transient( 'featured_content_ids' );
     146                if ( ! empty( $featured_ids ) )
     147                        return array_map( 'absint', (array) $featured_ids );
     148
     149                // Query for featured posts.
     150                $featured = get_posts( array(
     151                        'numberposts' => $settings['quantity'],
     152                        'tax_query'   => array(
     153                                array(
     154                                        'field'    => 'term_id',
     155                                        'taxonomy' => 'post_tag',
     156                                        'terms'    => $tag,
     157                                ),
     158                        ),
     159                ) );
     160
     161                // Return empty array if no featured content exists.
     162                if ( ! $featured )
     163                        return array();
     164
     165                // Ensure correct format before save/return.
     166                $featured_ids = wp_list_pluck( (array) $featured, 'ID' );
     167                $featured_ids = array_map( 'absint', $featured_ids );
     168
     169                set_transient( 'featured_content_ids', $featured_ids );
     170
     171                return $featured_ids;
     172        }
     173
     174        /**
     175         * Delete Transient.
     176         *
     177         * Hooks in the "save_post" action.
     178         * @see Featured_Content::validate_settings().
     179         */
     180        public static function delete_transient() {
     181                delete_transient( 'featured_content_ids' );
     182        }
     183
     184        /**
     185         * Exclude featured posts from the blog query when the blog is the front-page.
     186         *
     187         * Filter the home page posts, and remove any featured post ID's from it. Hooked
     188         * onto the 'pre_get_posts' action, this changes the parameters of the query
     189         * before it gets any posts.
     190         *
     191         * @uses Featured_Content::get_featured_post_ids();
     192         * @param WP_Query $query
     193         * @return WP_Query Possibly modified WP_query
     194         */
     195        public static function pre_get_posts( $query = false ) {
     196
     197                // Bail if not home, not a query, not main query.
     198                if ( ! is_home() || ! is_a( $query, 'WP_Query' ) || ! $query->is_main_query() )
     199                        return;
     200
     201                $page_on_front = get_option( 'page_on_front' );
     202
     203                // Bail if the blog page is not the front page.
     204                if ( ! empty( $page_on_front ) )
     205                        return;
     206
     207                $featured = self::get_featured_post_ids();
     208
     209                // Bail if no featured posts.
     210                if ( ! $featured )
     211                        return;
     212
     213                // We need to respect post ids already in the blacklist.
     214                $post__not_in = $query->get( 'post__not_in' );
     215
     216                if ( ! empty( $post__not_in ) ) {
     217                        $featured = array_merge( (array) $post__not_in, $featured );
     218                        $featured = array_unique( $featured );
     219                }
     220
     221                $query->set( 'post__not_in', $featured );
     222        }
     223
     224        /**
     225         * Reset tag option when the saved tag is deleted.
     226         *
     227         * It's important to mention that the transient needs
     228         * to be deleted too. While it may not be obvious by
     229         * looking at the function alone, the transient is
     230         * deleted by Featured_Content::validate_settings().
     231         *
     232         * @param int $tag_id the term_id of the tag that has been deleted.
     233         *
     234         * Hooks in the "delete_post_tag" action.
     235         * @see Featured_Content::validate_settings().
     236         */
     237        public static function delete_post_tag( $tag_id ) {
     238                $settings = self::get_setting();
     239
     240                if ( empty( $settings['tag-id'] ) )
     241                        return;
     242
     243                if ( $tag_id != $settings['tag-id'] )
     244                        return;
     245
     246                $settings['tag-id'] = 0;
     247                $settings = self::validate_settings( $settings );
     248                update_option( 'featured-content', $settings );
     249        }
     250
     251        /**
     252         * Hide featured tag from displaying when global terms are queried from the front-end.
     253         *
     254         * Hooks into the "get_terms" filter.
     255         *
     256         * @param array $terms A list of term objects. This is the return value of get_terms().
     257         * @param array $taxonomies An array of taxonomy slugs.
     258         *
     259         * @uses Featured_Content::get_setting()
     260         */
     261        public static function hide_featured_term( $terms, $taxonomies ) {
     262
     263                // This filter is only appropriate on the front-end.
     264                if ( is_admin() )
     265                        return $terms;
     266
     267                // We only want to hide the featured tag.
     268                if ( ! in_array( 'post_tag', $taxonomies ) )
     269                        return $terms;
     270
     271                // Bail if no terms were returned.
     272                if ( empty( $terms ) )
     273                        return $terms;
     274
     275                foreach( $terms as $order => $term ) {
     276                        if ( self::get_setting( 'tag-id' ) == $term->term_id && 'post_tag' == $term->taxonomy )
     277                                unset( $terms[$order] );
     278                }
     279
     280                return $terms;
     281        }
     282
     283        /**
     284         * Hide featured tag from displaying when terms associated with
     285         * a post object are queried from the front-end.
     286         *
     287         * Hooks into the "get_the_terms" filter.
     288         *
     289         * @param array $terms A list of term objects. This is the return value of get_the_terms().
     290         * @param int $id The ID field for the post object that terms are associated with.
     291         * @param array $taxonomy An array of taxonomy slugs.
     292         *
     293         * @uses Featured_Content::get_setting()
     294         */
     295        public static function hide_the_featured_term( $terms, $id, $taxonomy ) {
     296
     297                // This filter is only appropriate on the front-end.
     298                if ( is_admin() )
     299                        return $terms;
     300
     301                // Make sure we are in the correct taxonomy.
     302                if ( ! 'post_tag' == $taxonomy )
     303                        return $terms;
     304
     305                // No terms? Return early!
     306                if ( empty( $terms ) )
     307                        return $terms;
     308
     309                foreach( $terms as $order => $term ) {
     310                        if ( self::get_setting( 'tag-id' ) == $term->term_id )
     311                                unset( $terms[$term->term_id] );
     312                }
     313
     314                return $terms;
     315        }
     316
     317        /**
     318         * Register custom setting on the Settings -> Reading screen.
     319         *
     320         * @uses Featured_Content::render_form()
     321         * @uses Featured_Content::validate_settings()
     322         */
     323        public static function register_setting() {
     324                add_settings_field( 'featured-content', __( 'Featured content', 'twentyfourteen' ), array( __class__, 'render_form' ), 'reading' );
     325                register_setting( 'reading', 'featured-content', array( __class__, 'validate_settings' ) );
     326        }
     327
     328        /**
     329         * Renders all form fields on the Settings -> Reading screen.
     330         */
     331        public static function render_form() {
     332                $settings = self::get_setting();
     333
     334                $tag_name = '';
     335                if ( ! empty( $settings['tag-id'] ) ) {
     336                        $tag = get_term( $settings['tag-id'], 'post_tag' );
     337                        if ( ! is_wp_error( $tag ) && isset( $tag->name ) )
     338                                $tag_name = $tag->name;
     339                }
     340                ?>
     341                <div id="featured-content-ui">
     342                        <p>
     343                                <label for="featured-content-tag-name"><?php echo _e( 'Tag name:', 'twentyfourteen' ); ?></label>
     344                                <input type="text" id="featured-content-tag-name" name="featured-content[tag-name]" value="<?php echo esc_attr( $tag_name ); ?>">
     345                                <input type="hidden" id="featured-content-tag-id" name="featured-content[tag-id]" value="<?php echo esc_attr( $settings['tag-id'] ); ?>">
     346                        </p>
     347                        <p>
     348                                <label for="featured-content-quantity"><?php _e( 'Number of posts:', 'twentyfourteen' ); ?></label>
     349                                <input class="small-text" type="number" step="1" min="1" max="<?php echo esc_attr( self::$max_posts ); ?>" id="featured-content-quantity" name="featured-content[quantity]" value="<?php echo esc_attr( $settings['quantity'] ); ?>">
     350                        </p>
     351                        <p>
     352                                <input type="checkbox" id="featured-content-hide-tag" name="featured-content[hide-tag]" <?php checked( $settings['hide-tag'], 1 ); ?>">
     353                                <label for="featured-content-hide-tag"><?php _e( 'Hide tag from displaying in post meta and tag clouds.', 'twentyfourteen' ); ?></label>
     354                        </p>
     355                </div>
     356                <?php
     357        }
     358
     359        /**
     360         * Get Settings.
     361         *
     362         * Get all settings recognized by this module. This
     363         * function will return all settings whether or not
     364         * they have been stored in the database yet. This
     365         * ensures that all keys are available at all times.
     366         *
     367         * In the event that you only require one setting, you
     368         * may pass its name as the first parameter to the function
     369         * and only that value will be returned.
     370         *
     371         * @uses Featured_Content::self::sanitize_quantity()
     372         *
     373         * @param string $key The key of a recognized setting.
     374         * @return mixed Array of all settings by default. A single value if passed as first parameter.
     375         */
     376        public static function get_setting( $key = 'all' ) {
     377                $saved = (array) get_option( 'featured-content' );
     378
     379                $defaults = array(
     380                        'hide-tag' => 1,
     381                        'quantity' => 5,
     382                        'tag-id'   => 0,
     383                );
     384
     385                $options = wp_parse_args( $saved, $defaults );
     386                $options = array_intersect_key( $options, $defaults );
     387                $options['quantity'] = self::sanitize_quantity( $options['quantity'] );
     388
     389                if ( 'all' != $key ) {
     390                        if ( isset( $options[$key] ) )
     391                                return $options[$key];
     392                        else
     393                                return false;
     394                }
     395
     396                return $options;
     397        }
     398
     399        /**
     400         * Validate Settings.
     401         *
     402         * Make sure that all user supplied content is in an
     403         * expected format before saving to the database. This
     404         * function will also delete the transient set in
     405         * Featured_Content::get_featured_content().
     406         *
     407         * @uses Featured_Content::self::sanitize_quantity()
     408         * @uses Featured_Content::self::delete_transient()
     409         */
     410        public static function validate_settings( $input ) {
     411                $output = array();
     412
     413                if ( isset( $input['tag-id'] ) )
     414                        $output['tag-id'] = absint( $input['tag-id'] );
     415
     416                if ( isset( $input['tag-name'] ) && ! empty( $input['tag-name'] ) ) {
     417                        $new_tag = wp_create_tag( $input['tag-name'] );
     418                        if ( ! is_wp_error( $new_tag ) && isset( $new_tag['term_id'] ) )
     419                                $tag = get_term( $new_tag['term_id'], 'post_tag' );
     420                        if ( isset( $tag->term_id ) )
     421                                $output['tag-id'] = $tag->term_id;
     422                } else {
     423                        unset( $output['tag-id'] );
     424                }
     425
     426                if ( isset( $input['quantity'] ) )
     427                        $output['quantity'] = self::sanitize_quantity( $input['quantity'] );
     428
     429                $output['hide-tag'] = ( isset( $input['hide-tag'] ) ) ? 1 : 0;
     430
     431                self::delete_transient();
     432
     433                return $output;
     434        }
     435
     436        /**
     437         * Sanitize Quantity
     438         *
     439         * @param int $input The value to sanitize.
     440         * @output int A number between 1 and FeaturedContent::$max_posts.
     441         *
     442         * @uses Featured_Content::$max_posts
     443         */
     444        public static function sanitize_quantity( $input ) {
     445                $quantity = absint( $input );
     446
     447                if ( $quantity > self::$max_posts )
     448                        $quantity = self::$max_posts;
     449                else if ( 1 > $quantity )
     450                        $quantity = 1;
     451
     452                return $quantity;
     453        }
     454}
     455
     456Featured_Content::setup();