~canonical-sysadmins/wordpress/4.7.2

« back to all changes in this revision

Viewing changes to wp-content/themes/twentyfourteen/inc/featured-content.php

  • Committer: Jacek Nykis
  • Date: 2015-01-05 16:17:05 UTC
  • Revision ID: jacek.nykis@canonical.com-20150105161705-w544l1h5mcg7u4w9
Initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/**
 
3
 * Twenty Fourteen Featured Content
 
4
 *
 
5
 * This module allows you to define a subset of posts to be displayed
 
6
 * in the theme's Featured Content area.
 
7
 *
 
8
 * For maximum compatibility with different methods of posting users
 
9
 * will designate a featured post tag to associate posts with. Since
 
10
 * this tag now has special meaning beyond that of a normal tags, users
 
11
 * will have the ability to hide it from the front-end of their site.
 
12
 */
 
13
class Featured_Content {
 
14
 
 
15
        /**
 
16
         * The maximum number of posts a Featured Content area can contain.
 
17
         *
 
18
         * We define a default value here but themes can override
 
19
         * this by defining a "max_posts" entry in the second parameter
 
20
         * passed in the call to add_theme_support( 'featured-content' ).
 
21
         *
 
22
         * @see Featured_Content::init()
 
23
         *
 
24
         * @since Twenty Fourteen 1.0
 
25
         *
 
26
         * @static
 
27
         * @access public
 
28
         * @var int
 
29
         */
 
30
        public static $max_posts = 15;
 
31
 
 
32
        /**
 
33
         * Instantiate.
 
34
         *
 
35
         * All custom functionality will be hooked into the "init" action.
 
36
         *
 
37
         * @static
 
38
         * @access public
 
39
         * @since Twenty Fourteen 1.0
 
40
         */
 
41
        public static function setup() {
 
42
                add_action( 'init', array( __CLASS__, 'init' ), 30 );
 
43
        }
 
44
 
 
45
        /**
 
46
         * Conditionally hook into WordPress.
 
47
         *
 
48
         * Theme must declare that they support this module by adding
 
49
         * add_theme_support( 'featured-content' ); during after_setup_theme.
 
50
         *
 
51
         * If no theme support is found there is no need to hook into WordPress.
 
52
         * We'll just return early instead.
 
53
         *
 
54
         * @static
 
55
         * @access public
 
56
         * @since Twenty Fourteen 1.0
 
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
 
 
66
                /*
 
67
                 * An array of named arguments must be passed as the second parameter
 
68
                 * of add_theme_support().
 
69
                 */
 
70
                if ( ! isset( $theme_support[0] ) ) {
 
71
                        return;
 
72
                }
 
73
 
 
74
                // Return early if "featured_content_filter" has not been defined.
 
75
                if ( ! isset( $theme_support[0]['featured_content_filter'] ) ) {
 
76
                        return;
 
77
                }
 
78
 
 
79
                $filter = $theme_support[0]['featured_content_filter'];
 
80
 
 
81
                // Theme can override the number of max posts.
 
82
                if ( isset( $theme_support[0]['max_posts'] ) ) {
 
83
                        self::$max_posts = absint( $theme_support[0]['max_posts'] );
 
84
                }
 
85
 
 
86
                add_filter( $filter,                              array( __CLASS__, 'get_featured_posts' )    );
 
87
                add_action( 'customize_register',                 array( __CLASS__, 'customize_register' ), 9 );
 
88
                add_action( 'admin_init',                         array( __CLASS__, 'register_setting'   )    );
 
89
                add_action( 'switch_theme',                       array( __CLASS__, 'delete_transient'   )    );
 
90
                add_action( 'save_post',                          array( __CLASS__, 'delete_transient'   )    );
 
91
                add_action( 'delete_post_tag',                    array( __CLASS__, 'delete_post_tag'    )    );
 
92
                add_action( 'customize_controls_enqueue_scripts', array( __CLASS__, 'enqueue_scripts'    )    );
 
93
                add_action( 'pre_get_posts',                      array( __CLASS__, 'pre_get_posts'      )    );
 
94
                add_action( 'wp_loaded',                          array( __CLASS__, 'wp_loaded'          )    );
 
95
        }
 
96
 
 
97
        /**
 
98
         * Hide "featured" tag from the front-end.
 
99
         *
 
100
         * Has to run on wp_loaded so that the preview filters of the customizer
 
101
         * have a chance to alter the value.
 
102
         *
 
103
         * @static
 
104
         * @access public
 
105
         * @since Twenty Fourteen 1.0
 
106
         */
 
107
        public static function wp_loaded() {
 
108
                if ( self::get_setting( 'hide-tag' ) ) {
 
109
                        add_filter( 'get_terms',     array( __CLASS__, 'hide_featured_term'     ), 10, 3 );
 
110
                        add_filter( 'get_the_terms', array( __CLASS__, 'hide_the_featured_term' ), 10, 3 );
 
111
                }
 
112
        }
 
113
 
 
114
        /**
 
115
         * Get featured posts.
 
116
         *
 
117
         * @static
 
118
         * @access public
 
119
         * @since Twenty Fourteen 1.0
 
120
         *
 
121
         * @return array Array of featured posts.
 
122
         */
 
123
        public static function get_featured_posts() {
 
124
                $post_ids = self::get_featured_post_ids();
 
125
 
 
126
                // No need to query if there is are no featured posts.
 
127
                if ( empty( $post_ids ) ) {
 
128
                        return array();
 
129
                }
 
130
 
 
131
                $featured_posts = get_posts( array(
 
132
                        'include'        => $post_ids,
 
133
                        'posts_per_page' => count( $post_ids ),
 
134
                ) );
 
135
 
 
136
                return $featured_posts;
 
137
        }
 
138
 
 
139
        /**
 
140
         * Get featured post IDs
 
141
         *
 
142
         * This function will return the an array containing the
 
143
         * post IDs of all featured posts.
 
144
         *
 
145
         * Sets the "featured_content_ids" transient.
 
146
         *
 
147
         * @static
 
148
         * @access public
 
149
         * @since Twenty Fourteen 1.0
 
150
         *
 
151
         * @return array Array of post IDs.
 
152
         */
 
153
        public static function get_featured_post_ids() {
 
154
                // Get array of cached results if they exist.
 
155
                $featured_ids = get_transient( 'featured_content_ids' );
 
156
 
 
157
                if ( false === $featured_ids ) {
 
158
                        $settings = self::get_setting();
 
159
                        $term     = get_term_by( 'name', $settings['tag-name'], 'post_tag' );
 
160
 
 
161
                        if ( $term ) {
 
162
                                // Query for featured posts.
 
163
                                $featured_ids = get_posts( array(
 
164
                                        'fields'           => 'ids',
 
165
                                        'numberposts'      => self::$max_posts,
 
166
                                        'suppress_filters' => false,
 
167
                                        'tax_query'        => array(
 
168
                                                array(
 
169
                                                        'field'    => 'term_id',
 
170
                                                        'taxonomy' => 'post_tag',
 
171
                                                        'terms'    => $term->term_id,
 
172
                                                ),
 
173
                                        ),
 
174
                                ) );
 
175
                        }
 
176
 
 
177
                        // Get sticky posts if no Featured Content exists.
 
178
                        if ( ! $featured_ids ) {
 
179
                                $featured_ids = self::get_sticky_posts();
 
180
                        }
 
181
 
 
182
                        set_transient( 'featured_content_ids', $featured_ids );
 
183
                }
 
184
 
 
185
                // Ensure correct format before return.
 
186
                return array_map( 'absint', $featured_ids );
 
187
        }
 
188
 
 
189
        /**
 
190
         * Return an array with IDs of posts maked as sticky.
 
191
         *
 
192
         * @static
 
193
         * @access public
 
194
         * @since Twenty Fourteen 1.0
 
195
         *
 
196
         * @return array Array of sticky posts.
 
197
         */
 
198
        public static function get_sticky_posts() {
 
199
                return array_slice( get_option( 'sticky_posts', array() ), 0, self::$max_posts );
 
200
        }
 
201
 
 
202
        /**
 
203
         * Delete featured content ids transient.
 
204
         *
 
205
         * Hooks in the "save_post" action.
 
206
         *
 
207
         * @see Featured_Content::validate_settings().
 
208
         *
 
209
         * @static
 
210
         * @access public
 
211
         * @since Twenty Fourteen 1.0
 
212
         */
 
213
        public static function delete_transient() {
 
214
                delete_transient( 'featured_content_ids' );
 
215
        }
 
216
 
 
217
        /**
 
218
         * Exclude featured posts from the home page blog query.
 
219
         *
 
220
         * Filter the home page posts, and remove any featured post ID's from it.
 
221
         * Hooked onto the 'pre_get_posts' action, this changes the parameters of
 
222
         * the query before it gets any posts.
 
223
         *
 
224
         * @static
 
225
         * @access public
 
226
         * @since Twenty Fourteen 1.0
 
227
         *
 
228
         * @param WP_Query $query WP_Query object.
 
229
         * @return WP_Query Possibly-modified WP_Query.
 
230
         */
 
231
        public static function pre_get_posts( $query ) {
 
232
 
 
233
                // Bail if not home or not main query.
 
234
                if ( ! $query->is_home() || ! $query->is_main_query() ) {
 
235
                        return;
 
236
                }
 
237
 
 
238
                $page_on_front = get_option( 'page_on_front' );
 
239
 
 
240
                // Bail if the blog page is not the front page.
 
241
                if ( ! empty( $page_on_front ) ) {
 
242
                        return;
 
243
                }
 
244
 
 
245
                $featured = self::get_featured_post_ids();
 
246
 
 
247
                // Bail if no featured posts.
 
248
                if ( ! $featured ) {
 
249
                        return;
 
250
                }
 
251
 
 
252
                // We need to respect post ids already in the blacklist.
 
253
                $post__not_in = $query->get( 'post__not_in' );
 
254
 
 
255
                if ( ! empty( $post__not_in ) ) {
 
256
                        $featured = array_merge( (array) $post__not_in, $featured );
 
257
                        $featured = array_unique( $featured );
 
258
                }
 
259
 
 
260
                $query->set( 'post__not_in', $featured );
 
261
        }
 
262
 
 
263
        /**
 
264
         * Reset tag option when the saved tag is deleted.
 
265
         *
 
266
         * It's important to mention that the transient needs to be deleted,
 
267
         * too. While it may not be obvious by looking at the function alone,
 
268
         * the transient is deleted by Featured_Content::validate_settings().
 
269
         *
 
270
         * Hooks in the "delete_post_tag" action.
 
271
         *
 
272
         * @see Featured_Content::validate_settings().
 
273
         *
 
274
         * @static
 
275
         * @access public
 
276
         * @since Twenty Fourteen 1.0
 
277
         *
 
278
         * @param int $tag_id The term_id of the tag that has been deleted.
 
279
         */
 
280
        public static function delete_post_tag( $tag_id ) {
 
281
                $settings = self::get_setting();
 
282
 
 
283
                if ( empty( $settings['tag-id'] ) || $tag_id != $settings['tag-id'] ) {
 
284
                        return;
 
285
                }
 
286
 
 
287
                $settings['tag-id'] = 0;
 
288
                $settings = self::validate_settings( $settings );
 
289
                update_option( 'featured-content', $settings );
 
290
        }
 
291
 
 
292
        /**
 
293
         * Hide featured tag from displaying when global terms are queried from the front-end.
 
294
         *
 
295
         * Hooks into the "get_terms" filter.
 
296
         *
 
297
         * @static
 
298
         * @access public
 
299
         * @since Twenty Fourteen 1.0
 
300
         *
 
301
         * @param array $terms      List of term objects. This is the return value of get_terms().
 
302
         * @param array $taxonomies An array of taxonomy slugs.
 
303
         * @return array A filtered array of terms.
 
304
         *
 
305
         * @uses Featured_Content::get_setting()
 
306
         */
 
307
        public static function hide_featured_term( $terms, $taxonomies, $args ) {
 
308
 
 
309
                // This filter is only appropriate on the front-end.
 
310
                if ( is_admin() ) {
 
311
                        return $terms;
 
312
                }
 
313
 
 
314
                // We only want to hide the featured tag.
 
315
                if ( ! in_array( 'post_tag', $taxonomies ) ) {
 
316
                        return $terms;
 
317
                }
 
318
 
 
319
                // Bail if no terms were returned.
 
320
                if ( empty( $terms ) ) {
 
321
                        return $terms;
 
322
                }
 
323
 
 
324
                // Bail if term objects are unavailable.
 
325
                if ( 'all' != $args['fields'] ) {
 
326
                        return $terms;
 
327
                }
 
328
 
 
329
                $settings = self::get_setting();
 
330
                foreach( $terms as $order => $term ) {
 
331
                        if ( ( $settings['tag-id'] === $term->term_id || $settings['tag-name'] === $term->name ) && 'post_tag' === $term->taxonomy ) {
 
332
                                unset( $terms[ $order ] );
 
333
                        }
 
334
                }
 
335
 
 
336
                return $terms;
 
337
        }
 
338
 
 
339
        /**
 
340
         * Hide featured tag from display when terms associated with a post object
 
341
         * are queried from the front-end.
 
342
         *
 
343
         * Hooks into the "get_the_terms" filter.
 
344
         *
 
345
         * @static
 
346
         * @access public
 
347
         * @since Twenty Fourteen 1.0
 
348
         *
 
349
         * @param array $terms    A list of term objects. This is the return value of get_the_terms().
 
350
         * @param int   $id       The ID field for the post object that terms are associated with.
 
351
         * @param array $taxonomy An array of taxonomy slugs.
 
352
         * @return array Filtered array of terms.
 
353
         *
 
354
         * @uses Featured_Content::get_setting()
 
355
         */
 
356
        public static function hide_the_featured_term( $terms, $id, $taxonomy ) {
 
357
 
 
358
                // This filter is only appropriate on the front-end.
 
359
                if ( is_admin() ) {
 
360
                        return $terms;
 
361
                }
 
362
 
 
363
                // Make sure we are in the correct taxonomy.
 
364
                if ( 'post_tag' != $taxonomy ) {
 
365
                        return $terms;
 
366
                }
 
367
 
 
368
                // No terms? Return early!
 
369
                if ( empty( $terms ) ) {
 
370
                        return $terms;
 
371
                }
 
372
 
 
373
                $settings = self::get_setting();
 
374
                foreach( $terms as $order => $term ) {
 
375
                        if ( ( $settings['tag-id'] === $term->term_id || $settings['tag-name'] === $term->name ) && 'post_tag' === $term->taxonomy ) {
 
376
                                unset( $terms[ $term->term_id ] );
 
377
                        }
 
378
                }
 
379
 
 
380
                return $terms;
 
381
        }
 
382
 
 
383
        /**
 
384
         * Register custom setting on the Settings -> Reading screen.
 
385
         *
 
386
         * @static
 
387
         * @access public
 
388
         * @since Twenty Fourteen 1.0
 
389
         */
 
390
        public static function register_setting() {
 
391
                register_setting( 'featured-content', 'featured-content', array( __CLASS__, 'validate_settings' ) );
 
392
        }
 
393
 
 
394
        /**
 
395
         * Add settings to the Customizer.
 
396
         *
 
397
         * @static
 
398
         * @access public
 
399
         * @since Twenty Fourteen 1.0
 
400
         *
 
401
         * @param WP_Customize_Manager $wp_customize Theme Customizer object.
 
402
         */
 
403
        public static function customize_register( $wp_customize ) {
 
404
                $wp_customize->add_section( 'featured_content', array(
 
405
                        'title'          => __( 'Featured Content', 'twentyfourteen' ),
 
406
                        'description'    => sprintf( __( 'Use a <a href="%1$s">tag</a> to feature your posts. If no posts match the tag, <a href="%2$s">sticky posts</a> will be displayed instead.', 'twentyfourteen' ),
 
407
                                esc_url( add_query_arg( 'tag', _x( 'featured', 'featured content default tag slug', 'twentyfourteen' ), admin_url( 'edit.php' ) ) ),
 
408
                                admin_url( 'edit.php?show_sticky=1' )
 
409
                        ),
 
410
                        'priority'       => 130,
 
411
                        'theme_supports' => 'featured-content',
 
412
                ) );
 
413
 
 
414
                // Add Featured Content settings.
 
415
                $wp_customize->add_setting( 'featured-content[tag-name]', array(
 
416
                        'default'              => _x( 'featured', 'featured content default tag slug', 'twentyfourteen' ),
 
417
                        'type'                 => 'option',
 
418
                        'sanitize_js_callback' => array( __CLASS__, 'delete_transient' ),
 
419
                ) );
 
420
                $wp_customize->add_setting( 'featured-content[hide-tag]', array(
 
421
                        'default'              => true,
 
422
                        'type'                 => 'option',
 
423
                        'sanitize_js_callback' => array( __CLASS__, 'delete_transient' ),
 
424
                ) );
 
425
 
 
426
                // Add Featured Content controls.
 
427
                $wp_customize->add_control( 'featured-content[tag-name]', array(
 
428
                        'label'    => __( 'Tag Name', 'twentyfourteen' ),
 
429
                        'section'  => 'featured_content',
 
430
                        'priority' => 20,
 
431
                ) );
 
432
                $wp_customize->add_control( 'featured-content[hide-tag]', array(
 
433
                        'label'    => __( 'Don&rsquo;t display tag on front end.', 'twentyfourteen' ),
 
434
                        'section'  => 'featured_content',
 
435
                        'type'     => 'checkbox',
 
436
                        'priority' => 30,
 
437
                ) );
 
438
        }
 
439
 
 
440
        /**
 
441
         * Enqueue the tag suggestion script.
 
442
         *
 
443
         * @static
 
444
         * @access public
 
445
         * @since Twenty Fourteen 1.0
 
446
         */
 
447
        public static function enqueue_scripts() {
 
448
                wp_enqueue_script( 'featured-content-suggest', get_template_directory_uri() . '/js/featured-content-admin.js', array( 'jquery', 'suggest' ), '20131022', true );
 
449
        }
 
450
 
 
451
        /**
 
452
         * Get featured content settings.
 
453
         *
 
454
         * Get all settings recognized by this module. This function
 
455
         * will return all settings whether or not they have been stored
 
456
         * in the database yet. This ensures that all keys are available
 
457
         * at all times.
 
458
         *
 
459
         * In the event that you only require one setting, you may pass
 
460
         * its name as the first parameter to the function and only that
 
461
         * value will be returned.
 
462
         *
 
463
         * @static
 
464
         * @access public
 
465
         * @since Twenty Fourteen 1.0
 
466
         *
 
467
         * @param string $key The key of a recognized setting.
 
468
         * @return mixed Array of all settings by default. A single value if passed as first parameter.
 
469
         */
 
470
        public static function get_setting( $key = 'all' ) {
 
471
                $saved = (array) get_option( 'featured-content' );
 
472
 
 
473
                $defaults = array(
 
474
                        'hide-tag' => 1,
 
475
                        'tag-id'   => 0,
 
476
                        'tag-name' => _x( 'featured', 'featured content default tag slug', 'twentyfourteen' ),
 
477
                );
 
478
 
 
479
                $options = wp_parse_args( $saved, $defaults );
 
480
                $options = array_intersect_key( $options, $defaults );
 
481
 
 
482
                if ( 'all' != $key ) {
 
483
                        return isset( $options[ $key ] ) ? $options[ $key ] : false;
 
484
                }
 
485
 
 
486
                return $options;
 
487
        }
 
488
 
 
489
        /**
 
490
         * Validate featured content settings.
 
491
         *
 
492
         * Make sure that all user supplied content is in an expected
 
493
         * format before saving to the database. This function will also
 
494
         * delete the transient set in Featured_Content::get_featured_content().
 
495
         *
 
496
         * @static
 
497
         * @access public
 
498
         * @since Twenty Fourteen 1.0
 
499
         *
 
500
         * @param array $input Array of settings input.
 
501
         * @return array Validated settings output.
 
502
         */
 
503
        public static function validate_settings( $input ) {
 
504
                $output = array();
 
505
 
 
506
                if ( empty( $input['tag-name'] ) ) {
 
507
                        $output['tag-id'] = 0;
 
508
                } else {
 
509
                        $term = get_term_by( 'name', $input['tag-name'], 'post_tag' );
 
510
 
 
511
                        if ( $term ) {
 
512
                                $output['tag-id'] = $term->term_id;
 
513
                        } else {
 
514
                                $new_tag = wp_create_tag( $input['tag-name'] );
 
515
 
 
516
                                if ( ! is_wp_error( $new_tag ) && isset( $new_tag['term_id'] ) ) {
 
517
                                        $output['tag-id'] = $new_tag['term_id'];
 
518
                                }
 
519
                        }
 
520
 
 
521
                        $output['tag-name'] = $input['tag-name'];
 
522
                }
 
523
 
 
524
                $output['hide-tag'] = isset( $input['hide-tag'] ) && $input['hide-tag'] ? 1 : 0;
 
525
 
 
526
                // Delete the featured post ids transient.
 
527
                self::delete_transient();
 
528
 
 
529
                return $output;
 
530
        }
 
531
} // Featured_Content
 
532
 
 
533
Featured_Content::setup();