Completed
Push — add/jetpack-plan-support ( c36d23...95100b )
by
unknown
14:33 queued 05:53
created

Jetpack_Testimonial   D

Complexity

Total Complexity 98

Size/Duplication

Total Lines 726
Duplicated Lines 24.1 %

Coupling/Cohesion

Components 2
Dependencies 4

Importance

Changes 0
Metric Value
dl 175
loc 726
rs 4.4444
c 0
b 0
f 0
wmc 98
lcom 2
cbo 4

31 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 9 2
A __construct() 0 12 1
B settings_api_init() 0 24 2
C maybe_register_cpt() 0 72 14
A site_supports_custom_post_type() 10 10 3
A allow_cpt_rest_api_type() 0 5 1
B setting_html() 9 24 3
A new_activation_stat_bump() 0 4 1
B update_option_stat_bump() 0 11 5
A new_testimonial_stat_bump() 0 4 1
A flush_rules_on_enable() 0 3 1
A flush_rules_on_first_testimonial() 12 12 3
A flush_rules_on_switch() 0 5 2
A activation_post_type_support() 0 5 2
A deactivation_post_type_support() 12 12 2
A register_post_types() 48 48 2
A updated_messages() 22 22 2
A change_default_title() 8 8 2
A edit_title_column_label() 0 5 1
A query_reading_setting() 0 8 4
A infinite_scroll_click_posts_per_page() 0 9 4
A add_to_sitemap() 0 5 1
A set_testimonial_option() 0 6 1
A count_testimonials() 0 13 3
A add_customize_page() 0 12 1
A customize_register() 0 48 2
B coerce_testimonial_image_to_url() 0 13 6
C jetpack_testimonial_shortcode() 29 57 12
B jetpack_testimonial_shortcode_html() 11 68 6
B get_testimonial_class() 0 40 6
A get_testimonial_thumbnail_link() 14 14 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Jetpack_Testimonial often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Jetpack_Testimonial, and based on these observations, apply Extract Interface, too.

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