Featured_Content

Featured Content.

Defined (1)

The class is defined in the following location(s).

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