/modules/custom-post-types/testimonial.php

  1. <?php 
  2.  
  3. class Jetpack_Testimonial { 
  4. const CUSTOM_POST_TYPE = 'jetpack-testimonial'; 
  5. const OPTION_NAME = 'jetpack_testimonial'; 
  6. const OPTION_READING_SETTING = 'jetpack_testimonial_posts_per_page'; 
  7.  
  8. public $version = '0.1'; 
  9.  
  10. static function init() { 
  11. static $instance = false; 
  12.  
  13. if ( ! $instance ) { 
  14. $instance = new Jetpack_Testimonial; 
  15.  
  16. return $instance; 
  17.  
  18. /** 
  19. * Conditionally hook into WordPress. 
  20. * 
  21. * Setup user option for enabling CPT. 
  22. * If user has CPT enabled, show in admin. 
  23. */ 
  24. function __construct() { 
  25. // Make sure the post types are loaded for imports 
  26. add_action( 'import_start', array( $this, 'register_post_types' ) ); 
  27.  
  28. // If called via REST API, we need to register later in lifecycle 
  29. add_action( 'restapi_theme_init', array( $this, 'maybe_register_cpt' ) ); 
  30.  
  31. // Add to REST API post type whitelist 
  32. add_filter( 'rest_api_allowed_post_types', array( $this, 'allow_cpt_rest_api_type' ) ); 
  33.  
  34. $this->maybe_register_cpt(); 
  35.  
  36. /** 
  37. * Registers the custom post types and adds action/filter handlers, but 
  38. * only if the site supports it 
  39. */ 
  40. function maybe_register_cpt() { 
  41. // Add an option to enable the CPT 
  42. add_action( 'admin_init', array( $this, 'settings_api_init' ) ); 
  43.  
  44. // Check on theme switch if theme supports CPT and setting is disabled 
  45. add_action( 'after_switch_theme', array( $this, 'activation_post_type_support' ) ); 
  46.  
  47. $setting = get_option( self::OPTION_NAME, '0' ); 
  48.  
  49. // Bail early if Testimonial option is not set and the theme doesn't declare support 
  50. if ( empty( $setting ) && ! $this->site_supports_custom_post_type() ) { 
  51. return; 
  52.  
  53. // Enable Omnisearch for CPT. 
  54. if ( class_exists( 'Jetpack_Omnisearch_Posts' ) ) { 
  55. new Jetpack_Omnisearch_Posts( self::CUSTOM_POST_TYPE ); 
  56.  
  57. // CPT magic 
  58. $this->register_post_types(); 
  59. add_action( sprintf( 'add_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 ); 
  60. add_action( sprintf( 'update_option_%s', self::OPTION_NAME ), array( $this, 'flush_rules_on_enable' ), 10 ); 
  61. add_action( sprintf( 'publish_%s', self::CUSTOM_POST_TYPE ), array( $this, 'flush_rules_on_first_testimonial' ) ); 
  62. add_action( 'after_switch_theme', array( $this, 'flush_rules_on_switch' ) ); 
  63.  
  64. // Admin Customization 
  65. add_filter( 'enter_title_here', array( $this, 'change_default_title' ) ); 
  66. add_filter( sprintf( 'manage_%s_posts_columns', self::CUSTOM_POST_TYPE), array( $this, 'edit_title_column_label' ) ); 
  67. add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) ); 
  68. add_action( 'customize_register', array( $this, 'customize_register' ) ); 
  69.  
  70. // Only add the 'Customize' sub-menu if the theme supports it. 
  71. $num_testimonials = self::count_testimonials(); 
  72. if ( ! empty( $num_testimonials ) && current_theme_supports( self::CUSTOM_POST_TYPE ) ) { 
  73. add_action( 'admin_menu', array( $this, 'add_customize_page' ) ); 
  74.  
  75. if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { 
  76. // Track all the things 
  77. add_action( sprintf( 'add_option_%s', self::OPTION_NAME ), array( $this, 'new_activation_stat_bump' ) ); 
  78. add_action( sprintf( 'update_option_%s', self::OPTION_NAME ), array( $this, 'update_option_stat_bump' ), 11, 2 ); 
  79. add_action( sprintf( 'publish_%s', self::CUSTOM_POST_TYPE), array( $this, 'new_testimonial_stat_bump' ) ); 
  80.  
  81. // Add to Dotcom XML sitemaps 
  82. add_filter( 'wpcom_sitemap_post_types', array( $this, 'add_to_sitemap' ) ); 
  83.  
  84. // Adjust CPT archive and custom taxonomies to obey CPT reading setting 
  85. add_filter( 'pre_get_posts', array( $this, 'query_reading_setting' ), 20 ); 
  86.  
  87. // Register [jetpack_testimonials] always and 
  88. // register [testimonials] if [testimonials] isn't already set 
  89. add_shortcode( 'jetpack_testimonials', array( $this, 'jetpack_testimonial_shortcode' ) ); 
  90.  
  91. if ( ! shortcode_exists( 'testimonials' ) ) { 
  92. add_shortcode( 'testimonials', array( $this, 'jetpack_testimonial_shortcode' ) ); 
  93.  
  94. // If CPT was enabled programatically and no CPT items exist when user switches away, disable 
  95. if ( $setting && $this->site_supports_custom_post_type() ) { 
  96. add_action( 'switch_theme', array( $this, 'deactivation_post_type_support' ) ); 
  97.  
  98. /** 
  99. * Add a checkbox field in 'Settings' > 'Writing' 
  100. * for enabling CPT functionality. 
  101. * 
  102. * @return null 
  103. */ 
  104. function settings_api_init() { 
  105. add_settings_field( 
  106. self::OPTION_NAME,  
  107. '<span class="cpt-options">' . __( 'Testimonials', 'jetpack' ) . '</span>',  
  108. array( $this, 'setting_html' ),  
  109. 'writing',  
  110. 'jetpack_cpt_section' 
  111. ); 
  112.  
  113. register_setting( 
  114. 'writing',  
  115. self::OPTION_NAME,  
  116. 'intval' 
  117. ); 
  118.  
  119. // Check if CPT is enabled first so that intval doesn't get set to NULL on re-registering 
  120. if ( $this->site_supports_custom_post_type() ) { 
  121. register_setting( 
  122. 'writing',  
  123. self::OPTION_READING_SETTING,  
  124. 'intval' 
  125. ); 
  126.  
  127. /** 
  128. * HTML code to display a checkbox true/false option 
  129. * for the CPT setting. 
  130. * 
  131. * @return html 
  132. */ 
  133. function setting_html() { 
  134. if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) : ?> 
  135. <p><?php printf( __( 'Your theme supports Testimonials', 'jetpack' ) ); ?></p> 
  136. <?php else : ?> 
  137. <label for="<?php echo esc_attr( self::OPTION_NAME ); ?>"> 
  138. <input name="<?php echo esc_attr( self::OPTION_NAME ); ?>" id="<?php echo esc_attr( self::OPTION_NAME ); ?>" <?php echo checked( get_option( self::OPTION_NAME, '0' ), true, false ); ?> type="checkbox" value="1" /> 
  139. <?php esc_html_e( 'Enable Testimonials for this site.', 'jetpack' ); ?> 
  140. <a target="_blank" href="http://en.support.wordpress.com/testimonials/"><?php esc_html_e( 'Learn More', 'jetpack' ); ?></a> 
  141. </label> 
  142. <?php endif; 
  143.  
  144. if ( $this->site_supports_custom_post_type() ) : 
  145. printf( '<p><label for="%1$s">%2$s</label></p>',  
  146. esc_attr( self::OPTION_READING_SETTING ),  
  147. sprintf( __( 'Testimonial pages display at most %1$s testimonials', 'jetpack' ),  
  148. sprintf( '<input name="%1$s" id="%1$s" type="number" step="1" min="1" value="%2$s" class="small-text" />',  
  149. esc_attr( self::OPTION_READING_SETTING ),  
  150. esc_attr( get_option( self::OPTION_READING_SETTING, '10' ) ) 
  151. ); 
  152. endif; 
  153.  
  154. /** 
  155. * Should this Custom Post Type be made available? 
  156. */ 
  157. function site_supports_custom_post_type() { 
  158. // If the current theme requests it. 
  159. if ( current_theme_supports( self::CUSTOM_POST_TYPE ) || get_option( self::OPTION_NAME, '0' ) ) { 
  160. return true; 
  161.  
  162. // Otherwise, say no unless something wants to filter us to say yes. 
  163. /** This action is documented in modules/custom-post-types/nova.php */ 
  164. return (bool) apply_filters( 'jetpack_enable_cpt', false, self::CUSTOM_POST_TYPE ); 
  165.  
  166. /** 
  167. * Add to REST API post type whitelist 
  168. */ 
  169. function allow_cpt_rest_api_type( $post_types ) { 
  170. $post_types[] = self::CUSTOM_POST_TYPE; 
  171.  
  172. return $post_types; 
  173.  
  174. /** 
  175. * Bump Testimonial > New Activation stat 
  176. */ 
  177. function new_activation_stat_bump() { 
  178. /** This action is documented in modules/widgets/social-media-icons.php */ 
  179. do_action( 'jetpack_bump_stats_extras', 'testimonials', 'new-activation' ); 
  180.  
  181. /** 
  182. * Bump Testimonial > Option On/Off stats to get total active 
  183. */ 
  184. function update_option_stat_bump( $old, $new ) { 
  185. if ( empty( $old ) && ! empty( $new ) ) { 
  186. /** This action is documented in modules/widgets/social-media-icons.php */ 
  187. do_action( 'jetpack_bump_stats_extras', 'testimonials', 'option-on' ); 
  188.  
  189. if ( ! empty( $old ) && empty( $new ) ) { 
  190. /** This action is documented in modules/widgets/social-media-icons.php */ 
  191. do_action( 'jetpack_bump_stats_extras', 'testimonials', 'option-off' ); 
  192.  
  193. /** 
  194. * Bump Testimonial > Published Testimonials stat when testimonials are published 
  195. */ 
  196. function new_testimonial_stat_bump() { 
  197. /** This action is documented in modules/widgets/social-media-icons.php */ 
  198. do_action ( 'jetpack_bump_stats_extras', 'testimonials', 'published-testimonials' ); 
  199.  
  200. /** 
  201. * Flush permalinks when CPT option is turned on/off 
  202. */ 
  203. function flush_rules_on_enable() { 
  204. flush_rewrite_rules(); 
  205.  
  206. /** 
  207. * Count published testimonials and flush permalinks when first testimonial is published 
  208. */ 
  209. function flush_rules_on_first_testimonial() { 
  210. $testimonials = get_transient( 'jetpack-testimonial-count-cache' ); 
  211.  
  212. if ( false === $testimonials ) { 
  213. flush_rewrite_rules(); 
  214. $testimonials = (int) wp_count_posts( self::CUSTOM_POST_TYPE )->publish; 
  215.  
  216. if ( ! empty( $testimonials ) ) { 
  217. set_transient( 'jetpack-testimonial-count-cache', $testimonials, HOUR_IN_SECONDS * 12 ); 
  218.  
  219. /** 
  220. * Flush permalinks when CPT supported theme is activated 
  221. */ 
  222. function flush_rules_on_switch() { 
  223. if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) { 
  224. flush_rewrite_rules(); 
  225.  
  226. /** 
  227. * On plugin/theme activation, check if current theme supports CPT 
  228. */ 
  229. static function activation_post_type_support() { 
  230. if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) { 
  231. update_option( self::OPTION_NAME, '1' ); 
  232.  
  233. /** 
  234. * On theme switch, check if CPT item exists and disable if not 
  235. */ 
  236. function deactivation_post_type_support() { 
  237. $portfolios = get_posts( array( 
  238. 'fields' => 'ids',  
  239. 'posts_per_page' => 1,  
  240. 'post_type' => self::CUSTOM_POST_TYPE,  
  241. 'suppress_filters' => false 
  242. ) ); 
  243.  
  244. if ( empty( $portfolios ) ) { 
  245. update_option( self::OPTION_NAME, '0' ); 
  246.  
  247. /** 
  248. * Register Post Type 
  249. */ 
  250. function register_post_types() { 
  251. if ( post_type_exists( self::CUSTOM_POST_TYPE ) ) { 
  252. return; 
  253.  
  254. register_post_type( self::CUSTOM_POST_TYPE, array( 
  255. 'description' => __( 'Customer Testimonials', 'jetpack' ),  
  256. 'labels' => array( 
  257. 'name' => esc_html__( 'Testimonials', 'jetpack' ),  
  258. 'singular_name' => esc_html__( 'Testimonial', 'jetpack' ),  
  259. 'menu_name' => esc_html__( 'Testimonials', 'jetpack' ),  
  260. 'all_items' => esc_html__( 'All Testimonials', 'jetpack' ),  
  261. 'add_new' => esc_html__( 'Add New', 'jetpack' ),  
  262. 'add_new_item' => esc_html__( 'Add New Testimonial', 'jetpack' ),  
  263. 'edit_item' => esc_html__( 'Edit Testimonial', 'jetpack' ),  
  264. 'new_item' => esc_html__( 'New Testimonial', 'jetpack' ),  
  265. 'view_item' => esc_html__( 'View Testimonial', 'jetpack' ),  
  266. 'search_items' => esc_html__( 'Search Testimonials', 'jetpack' ),  
  267. 'not_found' => esc_html__( 'No Testimonials found', 'jetpack' ),  
  268. 'not_found_in_trash' => esc_html__( 'No Testimonials found in Trash', 'jetpack' ),  
  269. 'filter_items_list' => esc_html__( 'Filter Testimonials list', 'jetpack' ),  
  270. 'items_list_navigation' => esc_html__( 'Testimonial list navigation', 'jetpack' ),  
  271. 'items_list' => esc_html__( 'Testimonials list', 'jetpack' ),  
  272. ),  
  273. 'supports' => array( 
  274. 'title',  
  275. 'editor',  
  276. 'thumbnail',  
  277. 'page-attributes',  
  278. ),  
  279. 'rewrite' => array( 
  280. 'slug' => 'testimonial',  
  281. 'with_front' => false,  
  282. 'feeds' => false,  
  283. 'pages' => true,  
  284. ),  
  285. 'public' => true,  
  286. 'show_ui' => true,  
  287. 'menu_position' => 20, // below Pages 
  288. 'menu_icon' => 'dashicons-testimonial',  
  289. 'capability_type' => 'page',  
  290. 'map_meta_cap' => true,  
  291. 'has_archive' => true,  
  292. 'query_var' => 'testimonial',  
  293. ) ); 
  294.  
  295. /** 
  296. * Update messages for the Testimonial admin. 
  297. */ 
  298. function updated_messages( $messages ) { 
  299. global $post; 
  300.  
  301. $messages[ self::CUSTOM_POST_TYPE ] = array( 
  302. 0 => '', // Unused. Messages start at index 1. 
  303. 1 => sprintf( __( 'Testimonial updated. <a href="%s">View testimonial</a>', 'jetpack'), esc_url( get_permalink( $post->ID ) ) ),  
  304. 2 => esc_html__( 'Custom field updated.', 'jetpack' ),  
  305. 3 => esc_html__( 'Custom field deleted.', 'jetpack' ),  
  306. 4 => esc_html__( 'Testimonial updated.', 'jetpack' ),  
  307. /** translators: %s: date and time of the revision */ 
  308. 5 => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Testimonial restored to revision from %s', 'jetpack'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,  
  309. 6 => sprintf( __( 'Testimonial published. <a href="%s">View testimonial</a>', 'jetpack' ), esc_url( get_permalink( $post->ID ) ) ),  
  310. 7 => esc_html__( 'Testimonial saved.', 'jetpack' ),  
  311. 8 => sprintf( __( 'Testimonial submitted. <a target="_blank" href="%s">Preview testimonial</a>', 'jetpack'), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),  
  312. 9 => sprintf( __( 'Testimonial scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview testimonial</a>', 'jetpack' ),  
  313. // translators: Publish box date format, see http://php.net/date 
  314. date_i18n( __( 'M j, Y @ G:i', 'jetpack' ), strtotime( $post->post_date ) ), esc_url( get_permalink($post->ID) ) ),  
  315. 10 => sprintf( __( 'Testimonial draft updated. <a target="_blank" href="%s">Preview testimonial</a>', 'jetpack' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),  
  316. ); 
  317.  
  318. return $messages; 
  319.  
  320. /** 
  321. * Change *Enter Title Here* text for the Testimonial. 
  322. */ 
  323. function change_default_title( $title ) { 
  324. $screen = get_current_screen(); 
  325.  
  326. if ( self::CUSTOM_POST_TYPE == $screen->post_type ) 
  327. $title = esc_html__( "Enter the customer's name here", 'jetpack' ); 
  328.  
  329. return $title; 
  330.  
  331. /** 
  332. * Change *Title* column label on all Testimonials page. 
  333. */ 
  334. function edit_title_column_label( $columns ) { 
  335. $columns['title'] = esc_html__( 'Customer Name', 'jetpack' ); 
  336.  
  337. return $columns; 
  338.  
  339. /** 
  340. * Follow CPT reading setting on CPT archive page 
  341. */ 
  342. function query_reading_setting( $query ) { 
  343. if ( ! is_admin() 
  344. && $query->is_main_query() 
  345. && $query->is_post_type_archive( self::CUSTOM_POST_TYPE ) 
  346. ) { 
  347. $query->set( 'posts_per_page', get_option( self::OPTION_READING_SETTING, '10' ) ); 
  348.  
  349. /** 
  350. * Add CPT to Dotcom sitemap 
  351. */ 
  352. function add_to_sitemap( $post_types ) { 
  353. $post_types[] = self::CUSTOM_POST_TYPE; 
  354.  
  355. return $post_types; 
  356.  
  357. function set_testimonial_option() { 
  358. $testimonials = wp_count_posts( self::CUSTOM_POST_TYPE ); 
  359. $published_testimonials = $testimonials->publish; 
  360.  
  361. update_option( self::OPTION_NAME, $published_testimonials ); 
  362.  
  363. function count_testimonials() { 
  364. $testimonials = get_transient( 'jetpack-testimonial-count-cache' ); 
  365.  
  366. if ( false === $testimonials ) { 
  367. $testimonials = (int) wp_count_posts( self::CUSTOM_POST_TYPE )->publish; 
  368.  
  369. if ( ! empty( $testimonials ) ) { 
  370. set_transient( 'jetpack-testimonial-count-cache', $testimonials, 60*60*12 ); 
  371.  
  372. return $testimonials; 
  373.  
  374. /** 
  375. * Adds a submenu link to the Customizer. 
  376. */ 
  377. function add_customize_page() { 
  378. add_submenu_page( 
  379. 'edit.php?post_type=' . self::CUSTOM_POST_TYPE,  
  380. esc_html__( 'Customize Testimonials Archive', 'jetpack' ),  
  381. esc_html__( 'Customize', 'jetpack' ),  
  382. 'edit_theme_options',  
  383. add_query_arg( array( 
  384. 'url' => urlencode( home_url( '/testimonial/' ) ),  
  385. 'autofocus[section]' => 'jetpack_testimonials' 
  386. ), 'customize.php' ) 
  387. ); 
  388.  
  389. /** 
  390. * Adds testimonial section to the Customizer. 
  391. */ 
  392. function customize_register( $wp_customize ) { 
  393. jetpack_testimonial_custom_control_classes(); 
  394.  
  395. $wp_customize->add_section( 'jetpack_testimonials', array( 
  396. 'title' => esc_html__( 'Testimonials', 'jetpack' ),  
  397. 'theme_supports' => self::CUSTOM_POST_TYPE,  
  398. 'priority' => 130,  
  399. ) ); 
  400.  
  401. $wp_customize->add_setting( 'jetpack_testimonials[page-title]', array( 
  402. 'default' => esc_html__( 'Testimonials', 'jetpack' ),  
  403. 'sanitize_callback' => array( 'Jetpack_Testimonial_Title_Control', 'sanitize_content' ),  
  404. 'sanitize_js_callback' => array( 'Jetpack_Testimonial_Title_Control', 'sanitize_content' ),  
  405. ) ); 
  406. $wp_customize->add_control( 'jetpack_testimonials[page-title]', array( 
  407. 'section' => 'jetpack_testimonials',  
  408. 'label' => esc_html__( 'Testimonial Page Title', 'jetpack' ),  
  409. 'type' => 'text',  
  410. ) ); 
  411.  
  412. $wp_customize->add_setting( 'jetpack_testimonials[page-content]', array( 
  413. 'default' => '',  
  414. 'sanitize_callback' => array( 'Jetpack_Testimonial_Textarea_Control', 'sanitize_content' ),  
  415. 'sanitize_js_callback' => array( 'Jetpack_Testimonial_Textarea_Control', 'sanitize_content' ),  
  416. ) ); 
  417. $wp_customize->add_control( new Jetpack_Testimonial_Textarea_Control( $wp_customize, 'jetpack_testimonials[page-content]', array( 
  418. 'section' => 'jetpack_testimonials',  
  419. 'settings' => 'jetpack_testimonials[page-content]',  
  420. 'label' => esc_html__( 'Testimonial Page Content', 'jetpack' ),  
  421. ) ) ); 
  422.  
  423. $wp_customize->add_setting( 'jetpack_testimonials[featured-image]', array( 
  424. 'default' => '',  
  425. 'sanitize_callback' => 'attachment_url_to_postid',  
  426. 'sanitize_js_callback' => 'attachment_url_to_postid',  
  427. 'theme_supports' => 'post-thumbnails',  
  428. ) ); 
  429. $wp_customize->add_control( new WP_Customize_Image_Control( $wp_customize, 'jetpack_testimonials[featured-image]', array( 
  430. 'section' => 'jetpack_testimonials',  
  431. 'label' => esc_html__( 'Testimonial Page Featured Image', 'jetpack' ),  
  432. ) ) ); 
  433.  
  434. // The featured image control doesn't display properly in the Customizer unless we coerce 
  435. // it back into a URL sooner, since that's what WP_Customize_Upload_Control::to_json() expects 
  436. if ( is_admin() ) { 
  437. add_filter( 'theme_mod_jetpack_testimonials', array( $this, 'coerce_testimonial_image_to_url' ) ); 
  438.  
  439. public function coerce_testimonial_image_to_url( $opt ) { 
  440. if ( ! $opt || ! is_array( $opt ) ) { 
  441. return $opt; 
  442. if ( ! isset( $opt['featured-image'] ) || ! is_scalar( $opt['featured-image'] ) ) { 
  443. return $opt; 
  444. $url = wp_get_attachment_url( $opt['featured-image'] ); 
  445. if ( $url ) { 
  446. $opt['featured-image'] = $url; 
  447. return $opt; 
  448.  
  449. /** 
  450. * Our [testimonial] shortcode. 
  451. * Prints Testimonial data styled to look good on *any* theme. 
  452. * 
  453. * @return jetpack_testimonial_shortcode_html 
  454. */ 
  455. static function jetpack_testimonial_shortcode( $atts ) { 
  456. // Default attributes 
  457. $atts = shortcode_atts( array( 
  458. 'display_content' => true,  
  459. 'image' => true,  
  460. 'columns' => 1,  
  461. 'showposts' => -1,  
  462. 'order' => 'asc',  
  463. 'orderby' => 'date',  
  464. ), $atts, 'testimonial' ); 
  465.  
  466. // A little sanitization 
  467. if ( $atts['display_content'] && 'true' != $atts['display_content'] && 'full' != $atts['display_content'] ) { 
  468. $atts['display_content'] = false; 
  469.  
  470. if ( $atts['image'] && 'true' != $atts['image'] ) { 
  471. $atts['image'] = false; 
  472.  
  473. $atts['columns'] = absint( $atts['columns'] ); 
  474.  
  475. $atts['showposts'] = intval( $atts['showposts'] ); 
  476.  
  477. if ( $atts['order'] ) { 
  478. $atts['order'] = urldecode( $atts['order'] ); 
  479. $atts['order'] = strtoupper( $atts['order'] ); 
  480. if ( 'DESC' != $atts['order'] ) { 
  481. $atts['order'] = 'ASC'; 
  482.  
  483. if ( $atts['orderby'] ) { 
  484. $atts['orderby'] = urldecode( $atts['orderby'] ); 
  485. $atts['orderby'] = strtolower( $atts['orderby'] ); 
  486. $allowed_keys = array('author', 'date', 'title', 'rand'); 
  487.  
  488. $parsed = array(); 
  489. foreach ( explode( ', ', $atts['orderby'] ) as $testimonial_index_number => $orderby ) { 
  490. if ( ! in_array( $orderby, $allowed_keys ) ) { 
  491. continue; 
  492. $parsed[] = $orderby; 
  493.  
  494. if ( empty( $parsed ) ) { 
  495. unset($atts['orderby']); 
  496. } else { 
  497. $atts['orderby'] = implode( ' ', $parsed ); 
  498.  
  499. // enqueue shortcode styles when shortcode is used 
  500. wp_enqueue_style( 'jetpack-testimonial-style', plugins_url( 'css/testimonial-shortcode.css', __FILE__ ), array(), '20140326' ); 
  501.  
  502. return self::jetpack_testimonial_shortcode_html( $atts ); 
  503.  
  504. /** 
  505. * The Testimonial shortcode loop. 
  506. * 
  507. * @return html 
  508. */ 
  509. static function jetpack_testimonial_shortcode_html( $atts ) { 
  510. // Default query arguments 
  511. $defaults = array( 
  512. 'order' => $atts['order'],  
  513. 'orderby' => $atts['orderby'],  
  514. 'posts_per_page' => $atts['showposts'],  
  515. ); 
  516.  
  517. $args = wp_parse_args( $atts, $defaults ); 
  518. $args['post_type'] = self::CUSTOM_POST_TYPE; // Force this post type 
  519. $query = new WP_Query( $args ); 
  520.  
  521. $testimonial_index_number = 0; 
  522.  
  523. ob_start(); 
  524.  
  525. // If we have testimonials, create the html 
  526. if ( $query->have_posts() ) { 
  527.  
  528. ?> 
  529. <div class="jetpack-testimonial-shortcode column-<?php echo esc_attr( $atts['columns'] ); ?>"> 
  530. <?php // open .jetpack-testimonial-shortcode 
  531.  
  532. // Construct the loop... 
  533. while ( $query->have_posts() ) { 
  534. $query->the_post(); 
  535. $post_id = get_the_ID(); 
  536. ?> 
  537. <div class="testimonial-entry <?php echo esc_attr( self::get_testimonial_class( $testimonial_index_number, $atts['columns'] ) ); ?>"> 
  538. <?php 
  539. // The content 
  540. if ( false !== $atts['display_content'] ) { 
  541. if ( 'full' === $atts['display_content'] ) { 
  542. ?> 
  543. <div class="testimonial-entry-content"><?php the_content(); ?></div> 
  544. <?php 
  545. } else { 
  546. ?> 
  547. <div class="testimonial-entry-content"><?php the_excerpt(); ?></div> 
  548. <?php 
  549. ?> 
  550. <span class="testimonial-entry-title">― <a href="<?php echo esc_url( get_permalink() ); ?>" title="<?php echo esc_attr( the_title_attribute( ) ); ?>"><?php the_title(); ?></a></span> 
  551. <?php 
  552. // Featured image 
  553. if ( false !== $atts['image'] ) : 
  554. echo self::get_testimonial_thumbnail_link( $post_id ); 
  555. endif; 
  556. ?> 
  557. </div><!-- close .testimonial-entry --> 
  558. <?php 
  559. $testimonial_index_number++; 
  560. } // end of while loop 
  561.  
  562. wp_reset_postdata(); 
  563. ?> 
  564. </div><!-- close .jetpack-testimonial-shortcode --> 
  565. <?php 
  566. } else { ?> 
  567. <p><em><?php _e( 'Your Testimonial Archive currently has no entries. You can start creating them on your dashboard.', 'jetpack' ); ?></p></em> 
  568. <?php 
  569. $html = ob_get_clean(); 
  570.  
  571. // Return the HTML block 
  572. return $html; 
  573.  
  574. /** 
  575. * Individual testimonial class 
  576. * 
  577. * @return string 
  578. */ 
  579. static function get_testimonial_class( $testimonial_index_number, $columns ) { 
  580. $class = array(); 
  581.  
  582. $class[] = 'testimonial-entry-column-'.$columns; 
  583.  
  584. if( $columns > 1) { 
  585. if ( ( $testimonial_index_number % 2 ) == 0 ) { 
  586. $class[] = 'testimonial-entry-mobile-first-item-row'; 
  587. } else { 
  588. $class[] = 'testimonial-entry-mobile-last-item-row'; 
  589.  
  590. // add first and last classes to first and last items in a row 
  591. if ( ( $testimonial_index_number % $columns ) == 0 ) { 
  592. $class[] = 'testimonial-entry-first-item-row'; 
  593. } elseif ( ( $testimonial_index_number % $columns ) == ( $columns - 1 ) ) { 
  594. $class[] = 'testimonial-entry-last-item-row'; 
  595.  
  596.  
  597. /** 
  598. * Filter the class applied to testimonial div in the testimonial 
  599. * 
  600. * @module custom-content-types 
  601. * 
  602. * @since 3.4.0 
  603. * 
  604. * @param string $class class name of the div. 
  605. * @param int $testimonial_index_number iterator count the number of columns up starting from 0. 
  606. * @param int $columns number of columns to display the content in. 
  607. * 
  608. */ 
  609. return apply_filters( 'testimonial-entry-post-class', implode( " ", $class ) , $testimonial_index_number, $columns ); 
  610.  
  611. /** 
  612. * Display the featured image if it's available 
  613. * 
  614. * @return html 
  615. */ 
  616. static function get_testimonial_thumbnail_link( $post_id ) { 
  617. if ( has_post_thumbnail( $post_id ) ) { 
  618. /** 
  619. * Change the thumbnail size for the Testimonial CPT. 
  620. * 
  621. * @module custom-content-types 
  622. * 
  623. * @since 3.4.0 
  624. * 
  625. * @param string|array $var Either a registered size keyword or size array. 
  626. */ 
  627. return '<a class="testimonial-featured-image" href="' . esc_url( get_permalink( $post_id ) ) . '">' . get_the_post_thumbnail( $post_id, apply_filters( 'jetpack_testimonial_thumbnail_size', 'thumbnail' ) ) . '</a>'; 
  628.  
  629. function jetpack_testimonial_custom_control_classes() { 
  630. class Jetpack_Testimonial_Title_Control extends WP_Customize_Control { 
  631. public static function sanitize_content( $value ) { 
  632. if ( '' != $value ) 
  633. $value = trim( convert_chars( wptexturize( $value ) ) ); 
  634.  
  635. return $value; 
  636.  
  637. class Jetpack_Testimonial_Textarea_Control extends WP_Customize_Control { 
  638. public $type = 'textarea'; 
  639.  
  640. public function render_content() { 
  641. ?> 
  642. <label> 
  643. <span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span> 
  644. <textarea rows="5" style="width:100%;" <?php $this->link(); ?>><?php echo esc_textarea( $this->value() ); ?></textarea> 
  645. </label> 
  646. <?php 
  647.  
  648. public static function sanitize_content( $value ) { 
  649. if ( ! empty( $value ) ) 
  650. /** This filter is already documented in core. wp-includes/post-template.php */ 
  651. $value = apply_filters( 'the_content', $value ); 
  652.  
  653. $value = preg_replace( '@<div id="jp-post-flair"([^>]+)?>(.+)?</div>@is', '', $value ); // Strip WPCOM and Jetpack post flair if included in content 
  654.  
  655. return $value; 
  656.  
  657. add_action( 'init', array( 'Jetpack_Testimonial', 'init' ) ); 
  658.  
  659. // Check on plugin activation if theme supports CPT 
  660. register_activation_hook( __FILE__, array( 'Jetpack_Testimonial', 'activation_post_type_support' ) ); 
  661. add_action( 'jetpack_activate_module_custom-content-types', array( 'Jetpack_Testimonial', 'activation_post_type_support' ) ); 
.