Completed
Pull Request — master (#3703)
by Brandon
101:13 queued 92:37
created

save_metabox()   D

Complexity

Conditions 10
Paths 26

Size

Total Lines 37
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 37
rs 4.8196
cc 10
eloc 19
nc 26
nop 2

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
729
		public static function sanitize_content( $value ) {
730
			if ( '' != $value )
731
				$value = trim( convert_chars( wptexturize( $value ) ) );
732
733
			return $value;
734
		}
735
	}
736
737
	class Jetpack_Testimonial_Textarea_Control extends WP_Customize_Control {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
738
		public $type = 'textarea';
739
740
		public function render_content() {
741
			?>
742
			<label>
743
				<span class="customize-control-title"><?php echo esc_html( $this->label ); ?></span>
744
				<textarea rows="5" style="width:100%;" <?php $this->link(); ?>><?php echo esc_textarea( $this->value() ); ?></textarea>
745
			</label>
746
		<?php
747
		}
748
749
		public static function sanitize_content( $value ) {
750
			if ( ! empty( $value ) )
751
				/** This filter is already documented in core. wp-includes/post-template.php */
752
				$value = apply_filters( 'the_content', $value );
753
754
			$value = preg_replace( '@<div id="jp-post-flair"([^>]+)?>(.+)?</div>@is', '', $value ); // Strip WPCOM and Jetpack post flair if included in content
755
756
			return $value;
757
		}
758
	}
759
}
760
761
add_action( 'init', array( 'Jetpack_Testimonial', 'init' ) );
762
763
// Check on plugin activation if theme supports CPT
764
register_activation_hook( __FILE__,                         array( 'Jetpack_Testimonial', 'activation_post_type_support' ) );
765
add_action( 'jetpack_activate_module_custom-content-types', array( 'Jetpack_Testimonial', 'activation_post_type_support' ) );
766
767
768
class Jetpack_Testimonial_Details_Metabox {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
769
770
	public function __construct() {
771
772
		if ( is_admin() ) {
773
			add_action( 'load-post.php',     array( $this, 'init_metabox' ) );
774
			add_action( 'load-post-new.php', array( $this, 'init_metabox' ) );
775
		}
776
777
	}
778
779
	public function init_metabox() {
780
781
		add_action( 'add_meta_boxes',        array( $this, 'add_metabox' )         );
782
		add_action( 'save_post',             array( $this, 'save_metabox' ), 10, 2 );
783
784
	}
785
786
	public function add_metabox() {
787
788
		add_meta_box(
789
			'',
790
			__( 'Testimonial Details', 'jetpack' ),
791
			array( $this, 'render_details_metabox' ),
792
			'jetpack-testimonial',
793
			'advanced',
794
			'default'
795
		);
796
797
	}
798
799
	public function render_details_metabox( $post ) {
800
801
		// Add nonce for security and authentication.
802
		wp_nonce_field( 'nonce_action', 'nonce' );
803
804
		// Retrieve an existing value from the database.
805
		$jetpack_testimonial_gravatar_email_address = get_post_meta( $post->ID, 'jetpack_testimonial_gravatar_email_address', true );
806
		$jetpack_testimonial_byline = get_post_meta( $post->ID, 'jetpack_testimonial_byline', true );
807
		$jetpack_testimonial_url = get_post_meta( $post->ID, 'jetpack_testimonial_url', true );
808
809
		// Set default values.
810
		if( empty( $jetpack_testimonial_gravatar_email_address ) ) $jetpack_testimonial_gravatar_email_address = '';
811
		if( empty( $jetpack_testimonial_byline ) ) $jetpack_testimonial_byline = '';
812
		if( empty( $jetpack_testimonial_url ) ) $jetpack_testimonial_url = '';
813
814
		// Form fields.
815
		echo '<table class="form-table">';
816
817
		echo '	<tr>';
818
		echo '		<th><label for="jetpack_testimonial_gravatar_email_address" class="jetpack_testimonial_gravatar_email_address_label">' . __( 'Gravatar Email Address', 'jetpack' ) . '</label></th>';
819
		echo '		<td>';
820
		echo '			<input type="email" id="jetpack_testimonial_gravatar_email_address" name="jetpack_testimonial_gravatar_email_address" class="jetpack_testimonial_gravatar_email_address_field" placeholder="' . esc_attr__( '[email protected]', 'jetpack' ) . '" value="' . esc_attr__( $jetpack_testimonial_gravatar_email_address ) . '">';
821
		echo '			<p class="description">' . __( 'Enter in an e-mail address, to use a <a href="https://gravatar.com" target="_blank">Gravatar</a>, instead of using the "Featured Image".', 'jetpack' ) . '</p>';
822
		echo '		</td>';
823
		echo '	</tr>';
824
825
		echo '	<tr>';
826
		echo '		<th><label for="jetpack_testimonial_byline" class="jetpack_testimonial_byline_label">' . __( 'Byline', 'jetpack' ) . '</label></th>';
827
		echo '		<td>';
828
		echo '			<input type="text" id="jetpack_testimonial_byline" name="jetpack_testimonial_byline" class="jetpack_testimonial_byline_field" placeholder="' . esc_attr__( 'Job Title of Company', 'jetpack' ) . '" value="' . esc_attr__( $jetpack_testimonial_byline ) . '">';
829
		echo '			<p class="description">' . __( 'Enter a byline for the customer giving this testimonial (for example: "CEO of Company").', 'jetpack' ) . '</p>';
830
		echo '		</td>';
831
		echo '	</tr>';
832
833
		echo '	<tr>';
834
		echo '		<th><label for="jetpack_testimonial_url" class="jetpack_testimonial_url_label">' . __( 'URL', 'jetpack' ) . '</label></th>';
835
		echo '		<td>';
836
		echo '			<input type="url" id="jetpack_testimonial_url" name="jetpack_testimonial_url" class="jetpack_testimonial_url_field" placeholder="' . esc_attr__( 'https://example.com', 'jetpack' ) . '" value="' . esc_attr__( $jetpack_testimonial_url ) . '">';
837
		echo '			<p class="description">' . __( 'Enter a URL that applies to this customer (for example: https://jetpack.com/).', 'jetpack' ) . '</p>';
838
		echo '		</td>';
839
		echo '	</tr>';
840
841
		echo '</table>';
842
843
	}
844
845
	public function save_metabox( $post_id, $post ) {
0 ignored issues
show
Unused Code introduced by
The parameter $post is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
846
847
		// Add nonce for security and authentication.
848
		$nonce_name   = isset( $_POST['nonce'] ) ? $_POST['nonce'] : '';
849
		$nonce_action = 'nonce_action';
850
851
		// Check if a nonce is set.
852
		if ( ! isset( $nonce_name ) )
853
			return;
854
855
		// Check if a nonce is valid.
856
		if ( ! wp_verify_nonce( $nonce_name, $nonce_action ) )
857
			return;
858
859
		// Check if the user has permissions to save data.
860
		if ( ! current_user_can( 'edit_post', $post_id ) )
861
			return;
862
863
		// Check if it's not an autosave.
864
		if ( wp_is_post_autosave( $post_id ) )
865
			return;
866
867
		// Check if it's not a revision.
868
		if ( wp_is_post_revision( $post_id ) )
869
			return;
870
871
		// Sanitize user input.
872
		$new_jetpack_testimonial_gravatar_email_address = isset( $_POST[ 'jetpack_testimonial_gravatar_email_address' ] ) ? sanitize_email( $_POST[ 'jetpack_testimonial_gravatar_email_address' ] ) : '';
873
		$new_jetpack_testimonial_byline = isset( $_POST[ 'jetpack_testimonial_byline' ] ) ? sanitize_text_field( $_POST[ 'jetpack_testimonial_byline' ] ) : '';
874
		$new_jetpack_testimonial_url = isset( $_POST[ 'jetpack_testimonial_url' ] ) ? esc_url( $_POST[ 'jetpack_testimonial_url' ] ) : '';
875
876
		// Update the meta field in the database.
877
		update_post_meta( $post_id, 'jetpack_testimonial_gravatar_email_address', $new_jetpack_testimonial_gravatar_email_address );
878
		update_post_meta( $post_id, 'jetpack_testimonial_byline', $new_jetpack_testimonial_byline );
879
		update_post_meta( $post_id, 'jetpack_testimonial_url', $new_jetpack_testimonial_url );
880
881
	}
882
883
}
884
885
new Jetpack_Testimonial_Details_Metabox;
886