Completed
Push — try/gutenberg-separate-jetpack... ( e8dd3e...f0efb9 )
by Bernhard
39:26 queued 23:22
created

Jetpack_Portfolio::query_reading_setting()   B

Complexity

Conditions 9
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
nc 2
nop 1
dl 0
loc 10
rs 8.0555
c 0
b 0
f 0
1
<?php
2
3
class Jetpack_Portfolio {
4
	const CUSTOM_POST_TYPE       = 'jetpack-portfolio';
5
	const CUSTOM_TAXONOMY_TYPE   = 'jetpack-portfolio-type';
6
	const CUSTOM_TAXONOMY_TAG    = 'jetpack-portfolio-tag';
7
	const OPTION_NAME            = 'jetpack_portfolio';
8
	const OPTION_READING_SETTING = 'jetpack_portfolio_posts_per_page';
9
10
	public $version = '0.1';
11
12
	static function init() {
13
		static $instance = false;
14
15
		if ( ! $instance ) {
16
			$instance = new Jetpack_Portfolio;
17
		}
18
19
		return $instance;
20
	}
21
22
	/**
23
	 * Conditionally hook into WordPress.
24
	 *
25
	 * Setup user option for enabling CPT
26
	 * If user has CPT enabled, show in admin
27
	 */
28
	function __construct() {
29
		// Add an option to enable the CPT
30
		add_action( 'admin_init',                                                      array( $this, 'settings_api_init' ) );
31
32
		// Check on theme switch if theme supports CPT and setting is disabled
33
		add_action( 'after_switch_theme',                                              array( $this, 'activation_post_type_support' ) );
34
35
		// Make sure the post types are loaded for imports
36
		add_action( 'import_start',                                                    array( $this, 'register_post_types' ) );
37
38
		// Add to REST API post type whitelist
39
		add_filter( 'rest_api_allowed_post_types',                                     array( $this, 'allow_portfolio_rest_api_type' ) );
40
41
		$setting = Jetpack_Options::get_option_and_ensure_autoload( self::OPTION_NAME, '0' );
42
43
		// Bail early if Portfolio option is not set and the theme doesn't declare support
44
		if ( empty( $setting ) && ! $this->site_supports_custom_post_type() ) {
45
			return;
46
		}
47
48
		// CPT magic
49
		$this->register_post_types();
50
		add_action( sprintf( 'add_option_%s', self::OPTION_NAME ),                     array( $this, 'flush_rules_on_enable' ), 10 );
51
		add_action( sprintf( 'update_option_%s', self::OPTION_NAME ),                  array( $this, 'flush_rules_on_enable' ), 10 );
52
		add_action( sprintf( 'publish_%s', self::CUSTOM_POST_TYPE),                    array( $this, 'flush_rules_on_first_project' ) );
53
		add_action( 'after_switch_theme',                                              array( $this, 'flush_rules_on_switch' ) );
54
55
		// Admin Customization
56
		add_filter( 'post_updated_messages',                                           array( $this, 'updated_messages'   ) );
57
		add_filter( sprintf( 'manage_%s_posts_columns', self::CUSTOM_POST_TYPE),       array( $this, 'edit_admin_columns' ) );
58
		add_filter( sprintf( 'manage_%s_posts_custom_column', self::CUSTOM_POST_TYPE), array( $this, 'image_column'       ), 10, 2 );
59
		add_action( 'customize_register',                                              array( $this, 'customize_register' ) );
60
61
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
62
63
			// Track all the things
64
			add_action( sprintf( 'add_option_%s', self::OPTION_NAME ),                 array( $this, 'new_activation_stat_bump' ) );
65
			add_action( sprintf( 'update_option_%s', self::OPTION_NAME ),              array( $this, 'update_option_stat_bump' ), 11, 2 );
66
			add_action( sprintf( 'publish_%s', self::CUSTOM_POST_TYPE),                array( $this, 'new_project_stat_bump' ) );
67
		}
68
69
		add_image_size( 'jetpack-portfolio-admin-thumb', 50, 50, true );
70
		add_action( 'admin_enqueue_scripts',                                           array( $this, 'enqueue_admin_styles'  ) );
71
72
		// register jetpack_portfolio shortcode and portfolio shortcode (legacy)
73
		add_shortcode( 'portfolio',                                                    array( $this, 'portfolio_shortcode' ) );
74
		add_shortcode( 'jetpack_portfolio',                                            array( $this, 'portfolio_shortcode' ) );
75
76
		// Adjust CPT archive and custom taxonomies to obey CPT reading setting
77
		add_filter( 'infinite_scroll_settings',                                        array( $this, 'infinite_scroll_click_posts_per_page' ) );
78
		add_filter( 'infinite_scroll_results',                                         array( $this, 'infinite_scroll_results' ), 10, 3 );
79
80
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
81
			// Add to Dotcom XML sitemaps
82
			add_filter( 'wpcom_sitemap_post_types',                                    array( $this, 'add_to_sitemap' ) );
83
		} else {
84
			// Add to Jetpack XML sitemap
85
			add_filter( 'jetpack_sitemap_post_types',                                  array( $this, 'add_to_sitemap' ) );
86
		}
87
88
		// Adjust CPT archive and custom taxonomies to obey CPT reading setting
89
		add_filter( 'pre_get_posts',                                                   array( $this, 'query_reading_setting' ) );
90
91
		// If CPT was enabled programatically and no CPT items exist when user switches away, disable
92
		if ( $setting && $this->site_supports_custom_post_type() ) {
93
			add_action( 'switch_theme',                                                array( $this, 'deactivation_post_type_support' ) );
94
		}
95
	}
96
97
	/**
98
	 * Add a checkbox field in 'Settings' > 'Writing'
99
	 * for enabling CPT functionality.
100
	 *
101
	 * @return null
102
	 */
103
	function settings_api_init() {
104
		add_settings_field(
105
			self::OPTION_NAME,
106
			'<span class="cpt-options">' . __( 'Portfolio Projects', 'jetpack' ) . '</span>',
107
			array( $this, 'setting_html' ),
108
			'writing',
109
			'jetpack_cpt_section'
110
		);
111
		register_setting(
112
			'writing',
113
			self::OPTION_NAME,
114
			'intval'
115
		);
116
117
		// Check if CPT is enabled first so that intval doesn't get set to NULL on re-registering
118
		if ( get_option( self::OPTION_NAME, '0' ) || current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
119
			register_setting(
120
				'writing',
121
				self::OPTION_READING_SETTING,
122
				'intval'
123
			);
124
		}
125
	}
126
127
	/**
128
	 * HTML code to display a checkbox true/false option
129
	 * for the Portfolio CPT setting.
130
	 *
131
	 * @return html
132
	 */
133
	function setting_html() {
134 View Code Duplication
		if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) : ?>
135
			<p><?php printf( /* translators: %s is the name of a custom post type such as "jetpack-portfolio" */ __( 'Your theme supports <strong>%s</strong>', 'jetpack' ), self::CUSTOM_POST_TYPE ); ?></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 Portfolio Projects for this site.', 'jetpack' ); ?>
140
				<a target="_blank" href="http://en.support.wordpress.com/portfolios/"><?php esc_html_e( 'Learn More', 'jetpack' ); ?></a>
141
			</label>
142
		<?php endif;
143
		if ( get_option( self::OPTION_NAME, '0' ) || current_theme_supports( self::CUSTOM_POST_TYPE ) ) :
144
			printf( '<p><label for="%1$s">%2$s</label></p>',
145
				esc_attr( self::OPTION_READING_SETTING ),
146
				/* translators: %1$s is replaced with an input field for numbers */
147
				sprintf( __( 'Portfolio pages display at most %1$s projects', '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
				)
153
			);
154
		endif;
155
	}
156
157
	/*
158
	 * Bump Portfolio > New Activation stat
159
	 */
160
	function new_activation_stat_bump() {
161
		bump_stats_extras( 'portfolios', 'new-activation' );
162
	}
163
164
	/*
165
	 * Bump Portfolio > Option On/Off stats to get total active
166
	 */
167
	function update_option_stat_bump( $old, $new ) {
168
		if ( empty( $old ) && ! empty( $new ) ) {
169
			bump_stats_extras( 'portfolios', 'option-on' );
170
		}
171
172
		if ( ! empty( $old ) && empty( $new ) ) {
173
			bump_stats_extras( 'portfolios', 'option-off' );
174
		}
175
	}
176
177
	/*
178
	 * Bump Portfolio > Published Projects stat when projects are published
179
	 */
180
	function new_project_stat_bump() {
181
		bump_stats_extras( 'portfolios', 'published-projects' );
182
	}
183
184
	/**
185
	* Should this Custom Post Type be made available?
186
	*/
187 View Code Duplication
	function site_supports_custom_post_type() {
188
		// If the current theme requests it.
189
		if ( current_theme_supports( self::CUSTOM_POST_TYPE ) || get_option( self::OPTION_NAME, '0' ) ) {
190
			return true;
191
		}
192
193
		// Otherwise, say no unless something wants to filter us to say yes.
194
		/** This action is documented in modules/custom-post-types/nova.php */
195
		return (bool) apply_filters( 'jetpack_enable_cpt', false, self::CUSTOM_POST_TYPE );
196
	}
197
198
	/*
199
	 * Flush permalinks when CPT option is turned on/off
200
	 */
201
	function flush_rules_on_enable() {
202
		flush_rewrite_rules();
203
	}
204
205
	/*
206
	 * Count published projects and flush permalinks when first projects is published
207
	 */
208 View Code Duplication
	function flush_rules_on_first_project() {
209
		$projects = get_transient( 'jetpack-portfolio-count-cache' );
210
211
		if ( false === $projects ) {
212
			flush_rewrite_rules();
213
			$projects = (int) wp_count_posts( self::CUSTOM_POST_TYPE )->publish;
214
215
			if ( ! empty( $projects ) ) {
216
				set_transient( 'jetpack-portfolio-count-cache', $projects, HOUR_IN_SECONDS * 12 );
217
			}
218
		}
219
	}
220
221
	/*
222
	 * Flush permalinks when CPT supported theme is activated
223
	 */
224
	function flush_rules_on_switch() {
225
		if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
226
			flush_rewrite_rules();
227
		}
228
	}
229
230
	/**
231
	 * On plugin/theme activation, check if current theme supports CPT
232
	 */
233
	static function activation_post_type_support() {
234
		if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
235
			update_option( self::OPTION_NAME, '1' );
236
		}
237
	}
238
239
	/**
240
	 * On theme switch, check if CPT item exists and disable if not
241
	 */
242 View Code Duplication
	function deactivation_post_type_support() {
243
		$portfolios = get_posts( array(
244
			'fields'           => 'ids',
245
			'posts_per_page'   => 1,
246
			'post_type'        => self::CUSTOM_POST_TYPE,
247
			'suppress_filters' => false
248
		) );
249
250
		if ( empty( $portfolios ) ) {
251
			update_option( self::OPTION_NAME, '0' );
252
		}
253
	}
254
255
	/**
256
	 * Register Post Type
257
	 */
258
	function register_post_types() {
259
		if ( post_type_exists( self::CUSTOM_POST_TYPE ) ) {
260
			return;
261
		}
262
263
		register_post_type( self::CUSTOM_POST_TYPE, array(
264
			'description' => __( 'Portfolio Items', 'jetpack' ),
265
			'labels' => array(
266
				'name'                  => esc_html__( 'Projects',                   'jetpack' ),
267
				'singular_name'         => esc_html__( 'Project',                    'jetpack' ),
268
				'menu_name'             => esc_html__( 'Portfolio',                  'jetpack' ),
269
				'all_items'             => esc_html__( 'All Projects',               'jetpack' ),
270
				'add_new'               => esc_html__( 'Add New',                    'jetpack' ),
271
				'add_new_item'          => esc_html__( 'Add New Project',            'jetpack' ),
272
				'edit_item'             => esc_html__( 'Edit Project',               'jetpack' ),
273
				'new_item'              => esc_html__( 'New Project',                'jetpack' ),
274
				'view_item'             => esc_html__( 'View Project',               'jetpack' ),
275
				'search_items'          => esc_html__( 'Search Projects',            'jetpack' ),
276
				'not_found'             => esc_html__( 'No Projects found',          'jetpack' ),
277
				'not_found_in_trash'    => esc_html__( 'No Projects found in Trash', 'jetpack' ),
278
				'filter_items_list'     => esc_html__( 'Filter projects list',       'jetpack' ),
279
				'items_list_navigation' => esc_html__( 'Project list navigation',    'jetpack' ),
280
				'items_list'            => esc_html__( 'Projects list',              'jetpack' ),
281
			),
282
			'supports' => array(
283
				'title',
284
				'editor',
285
				'thumbnail',
286
				'author',
287
				'comments',
288
				'publicize',
289
				'wpcom-markdown',
290
				'revisions',
291
				'excerpt',
292
			),
293
			'rewrite' => array(
294
				'slug'       => 'portfolio',
295
				'with_front' => false,
296
				'feeds'      => true,
297
				'pages'      => true,
298
			),
299
			'public'          => true,
300
			'show_ui'         => true,
301
			'menu_position'   => 20,                    // below Pages
302
			'menu_icon'       => 'dashicons-portfolio', // 3.8+ dashicon option
303
			'capability_type' => 'page',
304
			'map_meta_cap'    => true,
305
			'taxonomies'      => array( self::CUSTOM_TAXONOMY_TYPE, self::CUSTOM_TAXONOMY_TAG ),
306
			'has_archive'     => true,
307
			'query_var'       => 'portfolio',
308
			'show_in_rest'    => true,
309
		) );
310
311
		register_taxonomy( self::CUSTOM_TAXONOMY_TYPE, self::CUSTOM_POST_TYPE, array(
312
			'hierarchical'      => true,
313
			'labels'            => array(
314
				'name'                  => esc_html__( 'Project Types',                 'jetpack' ),
315
				'singular_name'         => esc_html__( 'Project Type',                  'jetpack' ),
316
				'menu_name'             => esc_html__( 'Project Types',                 'jetpack' ),
317
				'all_items'             => esc_html__( 'All Project Types',             'jetpack' ),
318
				'edit_item'             => esc_html__( 'Edit Project Type',             'jetpack' ),
319
				'view_item'             => esc_html__( 'View Project Type',             'jetpack' ),
320
				'update_item'           => esc_html__( 'Update Project Type',           'jetpack' ),
321
				'add_new_item'          => esc_html__( 'Add New Project Type',          'jetpack' ),
322
				'new_item_name'         => esc_html__( 'New Project Type Name',         'jetpack' ),
323
				'parent_item'           => esc_html__( 'Parent Project Type',           'jetpack' ),
324
				'parent_item_colon'     => esc_html__( 'Parent Project Type:',          'jetpack' ),
325
				'search_items'          => esc_html__( 'Search Project Types',          'jetpack' ),
326
				'items_list_navigation' => esc_html__( 'Project type list navigation',  'jetpack' ),
327
				'items_list'            => esc_html__( 'Project type list',             'jetpack' ),
328
			),
329
			'public'            => true,
330
			'show_ui'           => true,
331
			'show_in_nav_menus' => true,
332
			'show_in_rest'      => true,
333
			'show_admin_column' => true,
334
			'query_var'         => true,
335
			'rewrite'           => array( 'slug' => 'project-type' ),
336
		) );
337
338
		register_taxonomy( self::CUSTOM_TAXONOMY_TAG, self::CUSTOM_POST_TYPE, array(
339
			'hierarchical'      => false,
340
			'labels'            => array(
341
				'name'                       => esc_html__( 'Project Tags',                   'jetpack' ),
342
				'singular_name'              => esc_html__( 'Project Tag',                    'jetpack' ),
343
				'menu_name'                  => esc_html__( 'Project Tags',                   'jetpack' ),
344
				'all_items'                  => esc_html__( 'All Project Tags',               'jetpack' ),
345
				'edit_item'                  => esc_html__( 'Edit Project Tag',               'jetpack' ),
346
				'view_item'                  => esc_html__( 'View Project Tag',               'jetpack' ),
347
				'update_item'                => esc_html__( 'Update Project Tag',             'jetpack' ),
348
				'add_new_item'               => esc_html__( 'Add New Project Tag',            'jetpack' ),
349
				'new_item_name'              => esc_html__( 'New Project Tag Name',           'jetpack' ),
350
				'search_items'               => esc_html__( 'Search Project Tags',            'jetpack' ),
351
				'popular_items'              => esc_html__( 'Popular Project Tags',           'jetpack' ),
352
				'separate_items_with_commas' => esc_html__( 'Separate tags with commas',      'jetpack' ),
353
				'add_or_remove_items'        => esc_html__( 'Add or remove tags',             'jetpack' ),
354
				'choose_from_most_used'      => esc_html__( 'Choose from the most used tags', 'jetpack' ),
355
				'not_found'                  => esc_html__( 'No tags found.',                 'jetpack' ),
356
				'items_list_navigation'      => esc_html__( 'Project tag list navigation',    'jetpack' ),
357
				'items_list'                 => esc_html__( 'Project tag list',               'jetpack' ),
358
			),
359
			'public'            => true,
360
			'show_ui'           => true,
361
			'show_in_nav_menus' => true,
362
			'show_in_rest'      => true,
363
			'show_admin_column' => true,
364
			'query_var'         => true,
365
			'rewrite'           => array( 'slug' => 'project-tag' ),
366
		) );
367
	}
368
369
	/**
370
	 * Update messages for the Portfolio admin.
371
	 */
372 View Code Duplication
	function updated_messages( $messages ) {
373
		global $post;
374
375
		$messages[self::CUSTOM_POST_TYPE] = array(
376
			0  => '', // Unused. Messages start at index 1.
377
			1  => sprintf( __( 'Project updated. <a href="%s">View item</a>', 'jetpack'), esc_url( get_permalink( $post->ID ) ) ),
378
			2  => esc_html__( 'Custom field updated.', 'jetpack' ),
379
			3  => esc_html__( 'Custom field deleted.', 'jetpack' ),
380
			4  => esc_html__( 'Project updated.', 'jetpack' ),
381
			/* translators: %s: date and time of the revision */
382
			5  => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Project restored to revision from %s', 'jetpack'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
383
			6  => sprintf( __( 'Project published. <a href="%s">View project</a>', 'jetpack' ), esc_url( get_permalink( $post->ID ) ) ),
384
			7  => esc_html__( 'Project saved.', 'jetpack' ),
385
			8  => sprintf( __( 'Project submitted. <a target="_blank" href="%s">Preview project</a>', 'jetpack'), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
386
			9  => sprintf( __( 'Project scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview project</a>', 'jetpack' ),
387
			// translators: Publish box date format, see http://php.net/date
388
			date_i18n( __( 'M j, Y @ G:i', 'jetpack' ), strtotime( $post->post_date ) ), esc_url( get_permalink( $post->ID ) ) ),
389
			10 => sprintf( __( 'Project item draft updated. <a target="_blank" href="%s">Preview project</a>', 'jetpack' ), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
390
		);
391
392
		return $messages;
393
	}
394
395
	/**
396
	 * Change ‘Title’ column label
397
	 * Add Featured Image column
398
	 */
399
	function edit_admin_columns( $columns ) {
400
		// change 'Title' to 'Project'
401
		$columns['title'] = __( 'Project', 'jetpack' );
402
		if ( current_theme_supports( 'post-thumbnails' ) ) {
403
			// add featured image before 'Project'
404
			$columns = array_slice( $columns, 0, 1, true ) + array( 'thumbnail' => '' ) + array_slice( $columns, 1, NULL, true );
405
		}
406
407
		return $columns;
408
	}
409
410
	/**
411
	 * Add featured image to column
412
	 */
413
	function image_column( $column, $post_id ) {
414
		global $post;
415
		switch ( $column ) {
416
			case 'thumbnail':
417
				echo get_the_post_thumbnail( $post_id, 'jetpack-portfolio-admin-thumb' );
418
				break;
419
		}
420
	}
421
422
	/**
423
	 * Adjust image column width
424
	 */
425
	function enqueue_admin_styles( $hook ) {
426
		$screen = get_current_screen();
427
428
		if ( 'edit.php' == $hook && self::CUSTOM_POST_TYPE == $screen->post_type && current_theme_supports( 'post-thumbnails' ) ) {
429
			wp_add_inline_style( 'wp-admin', '.manage-column.column-thumbnail { width: 50px; } @media screen and (max-width: 360px) { .column-thumbnail{ display:none; } }' );
430
		}
431
	}
432
433
	/**
434
	 * Adds portfolio section to the Customizer.
435
	 */
436
	function customize_register( $wp_customize ) {
437
		$options = get_theme_support( self::CUSTOM_POST_TYPE );
438
439
		if ( ( ! isset( $options[0]['title'] ) || true !== $options[0]['title'] ) && ( ! isset( $options[0]['content'] ) || true !== $options[0]['content'] ) && ( ! isset( $options[0]['featured-image'] ) || true !== $options[0]['featured-image'] ) ) {
440
			return;
441
		}
442
443
		$wp_customize->add_section( 'jetpack_portfolio', array(
444
			'title'                    => esc_html__( 'Portfolio', 'jetpack' ),
445
			'theme_supports'           => self::CUSTOM_POST_TYPE,
446
			'priority'                 => 130,
447
		) );
448
449 View Code Duplication
		if ( isset( $options[0]['title'] ) && true === $options[0]['title'] ) {
450
			$wp_customize->add_setting( 'jetpack_portfolio_title', array(
451
				'default'              => esc_html__( 'Projects', 'jetpack' ),
452
				'type'                 => 'option',
453
				'sanitize_callback'    => 'sanitize_text_field',
454
				'sanitize_js_callback' => 'sanitize_text_field',
455
			) );
456
457
			$wp_customize->add_control( 'jetpack_portfolio_title', array(
458
				'section'              => 'jetpack_portfolio',
459
				'label'                => esc_html__( 'Portfolio Archive Title', 'jetpack' ),
460
				'type'                 => 'text',
461
			) );
462
		}
463
464 View Code Duplication
		if ( isset( $options[0]['content'] ) && true === $options[0]['content'] ) {
465
			$wp_customize->add_setting( 'jetpack_portfolio_content', array(
466
				'default'              => '',
467
				'type'                 => 'option',
468
				'sanitize_callback'    => 'wp_kses_post',
469
				'sanitize_js_callback' => 'wp_kses_post',
470
			) );
471
472
			$wp_customize->add_control( 'jetpack_portfolio_content', array(
473
				'section'              => 'jetpack_portfolio',
474
				'label'                => esc_html__( 'Portfolio Archive Content', 'jetpack' ),
475
				'type'                 => 'textarea',
476
			) );
477
		}
478
479 View Code Duplication
		if ( isset( $options[0]['featured-image'] ) && true === $options[0]['featured-image'] ) {
480
			$wp_customize->add_setting( 'jetpack_portfolio_featured_image', array(
481
				'default'              => '',
482
				'type'                 => 'option',
483
				'sanitize_callback'    => 'attachment_url_to_postid',
484
				'sanitize_js_callback' => 'attachment_url_to_postid',
485
				'theme_supports'       => 'post-thumbnails',
486
			) );
487
488
			$wp_customize->add_control( new WP_Customize_Image_Control( $wp_customize, 'jetpack_portfolio_featured_image', array(
489
				'section'              => 'jetpack_portfolio',
490
				'label'                => esc_html__( 'Portfolio Archive Featured Image', 'jetpack' ),
491
			) ) );
492
		}
493
	}
494
495
	/**
496
	 * Follow CPT reading setting on CPT archive and taxonomy pages
497
	 */
498
	function query_reading_setting( $query ) {
499
		if ( ( ! is_admin() || ( is_admin() && defined( 'DOING_AJAX' ) && DOING_AJAX ) )
500
			&& $query->is_main_query()
501
			&& ( $query->is_post_type_archive( self::CUSTOM_POST_TYPE )
502
				|| $query->is_tax( self::CUSTOM_TAXONOMY_TYPE )
503
				|| $query->is_tax( self::CUSTOM_TAXONOMY_TAG ) )
504
		) {
505
			$query->set( 'posts_per_page', get_option( self::OPTION_READING_SETTING, '10' ) );
506
		}
507
	}
508
509
	/*
510
	 * If Infinite Scroll is set to 'click', use our custom reading setting instead of core's `posts_per_page`.
511
	 */
512
	function infinite_scroll_click_posts_per_page( $settings ) {
513
		global $wp_query;
514
515
		if ( ( ! is_admin() || ( is_admin() && defined( 'DOING_AJAX' ) && DOING_AJAX ) )
516
			&& true === $settings['click_handle']
517
			&& ( $wp_query->is_post_type_archive( self::CUSTOM_POST_TYPE )
518
				|| $wp_query->is_tax( self::CUSTOM_TAXONOMY_TYPE )
519
				|| $wp_query->is_tax( self::CUSTOM_TAXONOMY_TAG ) )
520
		) {
521
			$settings['posts_per_page'] = get_option( self::OPTION_READING_SETTING, $settings['posts_per_page'] );
522
		}
523
524
		return $settings;
525
	}
526
527
	/*
528
	 * Filter the results of infinite scroll to make sure we get `lastbatch` right.
529
	 */
530
	function infinite_scroll_results( $results, $query_args, $query ) {
531
		$results['lastbatch'] = $query_args['paged'] >= $query->max_num_pages;
532
		return $results;
533
	}
534
535
	/**
536
	 * Add CPT to Dotcom sitemap
537
	 */
538
	function add_to_sitemap( $post_types ) {
539
		$post_types[] = self::CUSTOM_POST_TYPE;
540
541
		return $post_types;
542
	}
543
544
	/**
545
	 * Add to REST API post type whitelist
546
	 */
547
	function allow_portfolio_rest_api_type( $post_types ) {
548
		$post_types[] = self::CUSTOM_POST_TYPE;
549
550
		return $post_types;
551
	}
552
553
	/**
554
	 * Our [portfolio] shortcode.
555
	 * Prints Portfolio data styled to look good on *any* theme.
556
	 *
557
	 * @return portfolio_shortcode_html
558
	 */
559
	static function portfolio_shortcode( $atts ) {
560
		// Default attributes
561
		$atts = shortcode_atts( array(
562
			'display_types'   => true,
563
			'display_tags'    => true,
564
			'display_content' => true,
565
			'display_author'  => false,
566
			'show_filter'     => false,
567
			'include_type'    => false,
568
			'include_tag'     => false,
569
			'columns'         => 2,
570
			'showposts'       => -1,
571
			'order'           => 'asc',
572
			'orderby'         => 'date',
573
		), $atts, 'portfolio' );
574
575
		// A little sanitization
576
		if ( $atts['display_types'] && 'true' != $atts['display_types'] ) {
577
			$atts['display_types'] = false;
578
		}
579
580
		if ( $atts['display_tags'] && 'true' != $atts['display_tags'] ) {
581
			$atts['display_tags'] = false;
582
		}
583
584
		if ( $atts['display_author'] && 'true' != $atts['display_author'] ) {
585
			$atts['display_author'] = false;
586
		}
587
588 View Code Duplication
		if ( $atts['display_content'] && 'true' != $atts['display_content'] && 'full' != $atts['display_content'] ) {
589
			$atts['display_content'] = false;
590
		}
591
592
		if ( $atts['include_type'] ) {
593
			$atts['include_type'] = explode( ',', str_replace( ' ', '', $atts['include_type'] ) );
594
		}
595
596
		if ( $atts['include_tag'] ) {
597
			$atts['include_tag'] = explode( ',', str_replace( ' ', '', $atts['include_tag'] ) );
598
		}
599
600
		$atts['columns'] = absint( $atts['columns'] );
601
602
		$atts['showposts'] = intval( $atts['showposts'] );
603
604
605 View Code Duplication
		if ( $atts['order'] ) {
606
			$atts['order'] = urldecode( $atts['order'] );
607
			$atts['order'] = strtoupper( $atts['order'] );
608
			if ( 'DESC' != $atts['order'] ) {
609
				$atts['order'] = 'ASC';
610
			}
611
		}
612
613 View Code Duplication
		if ( $atts['orderby'] ) {
614
			$atts['orderby'] = urldecode( $atts['orderby'] );
615
			$atts['orderby'] = strtolower( $atts['orderby'] );
616
			$allowed_keys = array( 'author', 'date', 'title', 'rand' );
617
618
			$parsed = array();
619
			foreach ( explode( ',', $atts['orderby'] ) as $portfolio_index_number => $orderby ) {
620
				if ( ! in_array( $orderby, $allowed_keys ) ) {
621
					continue;
622
				}
623
				$parsed[] = $orderby;
624
			}
625
626
			if ( empty( $parsed ) ) {
627
				unset( $atts['orderby'] );
628
			} else {
629
				$atts['orderby'] = implode( ' ', $parsed );
630
			}
631
		}
632
633
		// enqueue shortcode styles when shortcode is used
634
		wp_enqueue_style( 'jetpack-portfolio-style', plugins_url( 'css/portfolio-shortcode.css', __FILE__ ), array(), '20140326' );
635
636
		return self::portfolio_shortcode_html( $atts );
637
	}
638
639
	/**
640
	 * Query to retrieve entries from the Portfolio post_type.
641
	 *
642
	 * @return object
643
	 */
644
	static function portfolio_query( $atts ) {
645
		// Default query arguments
646
		$default = array(
647
			'order'          => $atts['order'],
648
			'orderby'        => $atts['orderby'],
649
			'posts_per_page' => $atts['showposts'],
650
		);
651
652
		$args = wp_parse_args( $atts, $default );
653
		$args['post_type'] = self::CUSTOM_POST_TYPE; // Force this post type
654
655 View Code Duplication
		if ( false != $atts['include_type'] || false != $atts['include_tag'] ) {
656
			$args['tax_query'] = array();
657
		}
658
659
		// If 'include_type' has been set use it on the main query
660 View Code Duplication
		if ( false != $atts['include_type'] ) {
661
			array_push( $args['tax_query'], array(
662
				'taxonomy' => self::CUSTOM_TAXONOMY_TYPE,
663
				'field'    => 'slug',
664
				'terms'    => $atts['include_type'],
665
			) );
666
		}
667
668
		// If 'include_tag' has been set use it on the main query
669 View Code Duplication
		if ( false != $atts['include_tag'] ) {
670
			array_push( $args['tax_query'], array(
671
				'taxonomy' => self::CUSTOM_TAXONOMY_TAG,
672
				'field'    => 'slug',
673
				'terms'    => $atts['include_tag'],
674
			) );
675
		}
676
677 View Code Duplication
		if ( false != $atts['include_type'] && false != $atts['include_tag'] ) {
678
			$args['tax_query']['relation'] = 'AND';
679
		}
680
681
		// Run the query and return
682
		$query = new WP_Query( $args );
683
		return $query;
684
	}
685
686
	/**
687
	 * The Portfolio shortcode loop.
688
	 *
689
	 * @todo add theme color styles
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
690
	 * @return html
691
	 */
692
	static function portfolio_shortcode_html( $atts ) {
693
694
		$query = self::portfolio_query( $atts );
695
		$portfolio_index_number = 0;
696
697
		ob_start();
698
699
		// If we have posts, create the html
700
		// with hportfolio markup
701
		if ( $query->have_posts() ) {
702
703
			// Render styles
704
			//self::themecolor_styles();
705
706
		?>
707
			<div class="jetpack-portfolio-shortcode column-<?php echo esc_attr( $atts['columns'] ); ?>">
708
			<?php  // open .jetpack-portfolio
709
710
			// Construct the loop...
711
			while ( $query->have_posts() ) {
712
				$query->the_post();
713
				$post_id = get_the_ID();
714
				?>
715
				<div class="portfolio-entry <?php echo esc_attr( self::get_project_class( $portfolio_index_number, $atts['columns'] ) ); ?>">
716
					<header class="portfolio-entry-header">
717
					<?php
718
					// Featured image
719
					echo self::get_portfolio_thumbnail_link( $post_id );
720
					?>
721
722
					<h2 class="portfolio-entry-title"><a href="<?php echo esc_url( get_permalink() ); ?>" title="<?php echo esc_attr( the_title_attribute( ) ); ?>"><?php the_title(); ?></a></h2>
723
724
						<div class="portfolio-entry-meta">
725
						<?php
726
						if ( false != $atts['display_types'] ) {
727
							echo self::get_project_type( $post_id );
728
						}
729
730
						if ( false != $atts['display_tags'] ) {
731
							echo self::get_project_tags( $post_id );
732
						}
733
734
						if ( false != $atts['display_author'] ) {
735
							echo self::get_project_author( $post_id );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Portfolio::get_project_author() has too many arguments starting with $post_id.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
736
						}
737
						?>
738
						</div>
739
740
					</header>
741
742
				<?php
743
				// The content
744
				if ( false !== $atts['display_content'] ) {
745
					add_filter( 'wordads_inpost_disable', '__return_true', 20 );
746
					if ( 'full' === $atts['display_content'] ) {
747
					?>
748
						<div class="portfolio-entry-content"><?php the_content(); ?></div>
749
					<?php
750
					} else {
751
					?>
752
						<div class="portfolio-entry-content"><?php the_excerpt(); ?></div>
753
					<?php
754
					}
755
					remove_filter( 'wordads_inpost_disable', '__return_true', 20 );
756
				}
757
				?>
758
				</div><!-- close .portfolio-entry -->
759
				<?php $portfolio_index_number++;
760
			} // end of while loop
761
762
			wp_reset_postdata();
763
			?>
764
			</div><!-- close .jetpack-portfolio -->
765
		<?php
766
		} else { ?>
767
			<p><em><?php _e( 'Your Portfolio Archive currently has no entries. You can start creating them on your dashboard.', 'jetpack' ); ?></p></em>
768
		<?php
769
		}
770
		$html = ob_get_clean();
771
772
		// If there is a [portfolio] within a [portfolio], remove the shortcode
773
		if ( has_shortcode( $html, 'portfolio' ) ){
774
			remove_shortcode( 'portfolio' );
775
		}
776
777
		// Return the HTML block
778
		return $html;
779
	}
780
781
	/**
782
	 * Individual project class
783
	 *
784
	 * @return string
785
	 */
786
	static function get_project_class( $portfolio_index_number, $columns ) {
787
		$project_types = wp_get_object_terms( get_the_ID(), self::CUSTOM_TAXONOMY_TYPE, array( 'fields' => 'slugs' ) );
788
		$class = array();
789
790
		$class[] = 'portfolio-entry-column-'.$columns;
791
		// add a type- class for each project type
792
		foreach ( $project_types as $project_type ) {
793
			$class[] = 'type-' . esc_html( $project_type );
794
		}
795
		if( $columns > 1) {
796
			if ( ( $portfolio_index_number % 2 ) == 0 ) {
797
				$class[] = 'portfolio-entry-mobile-first-item-row';
798
			} else {
799
				$class[] = 'portfolio-entry-mobile-last-item-row';
800
			}
801
		}
802
803
		// add first and last classes to first and last items in a row
804
		if ( ( $portfolio_index_number % $columns ) == 0 ) {
805
			$class[] = 'portfolio-entry-first-item-row';
806
		} elseif ( ( $portfolio_index_number % $columns ) == ( $columns - 1 ) ) {
807
			$class[] = 'portfolio-entry-last-item-row';
808
		}
809
810
811
		/**
812
		 * Filter the class applied to project div in the portfolio
813
		 *
814
		 * @module custom-content-types
815
		 *
816
		 * @since 3.1.0
817
		 *
818
		 * @param string $class class name of the div.
819
		 * @param int $portfolio_index_number iterator count the number of columns up starting from 0.
820
		 * @param int $columns number of columns to display the content in.
821
		 *
822
		 */
823
		return apply_filters( 'portfolio-project-post-class', implode( " ", $class ) , $portfolio_index_number, $columns );
824
	}
825
826
	/**
827
	 * Displays the project type that a project belongs to.
828
	 *
829
	 * @return html
830
	 */
831 View Code Duplication
	static function get_project_type( $post_id ) {
832
		$project_types = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TYPE );
833
834
		// If no types, return empty string
835
		if ( empty( $project_types ) || is_wp_error( $project_types ) ) {
836
			return;
837
		}
838
839
		$html = '<div class="project-types"><span>' . __( 'Types', 'jetpack' ) . ':</span>';
840
		$types = array();
841
		// Loop thorugh all the types
842
		foreach ( $project_types as $project_type ) {
843
			$project_type_link = get_term_link( $project_type, self::CUSTOM_TAXONOMY_TYPE );
844
845
			if ( is_wp_error( $project_type_link ) ) {
846
				return $project_type_link;
847
			}
848
849
			$types[] = '<a href="' . esc_url( $project_type_link ) . '" rel="tag">' . esc_html( $project_type->name ) . '</a>';
850
		}
851
		$html .= ' '.implode( ', ', $types );
852
		$html .= '</div>';
853
854
		return $html;
855
	}
856
857
	/**
858
	 * Displays the project tags that a project belongs to.
859
	 *
860
	 * @return html
861
	 */
862 View Code Duplication
	static function get_project_tags( $post_id ) {
863
		$project_tags = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TAG );
864
865
		// If no tags, return empty string
866
		if ( empty( $project_tags ) || is_wp_error( $project_tags ) ) {
867
			return false;
868
		}
869
870
		$html = '<div class="project-tags"><span>' . __( 'Tags', 'jetpack' ) . ':</span>';
871
		$tags = array();
872
		// Loop thorugh all the tags
873
		foreach ( $project_tags as $project_tag ) {
874
			$project_tag_link = get_term_link( $project_tag, self::CUSTOM_TAXONOMY_TYPE );
875
876
			if ( is_wp_error( $project_tag_link ) ) {
877
				return $project_tag_link;
878
			}
879
880
			$tags[] = '<a href="' . esc_url( $project_tag_link ) . '" rel="tag">' . esc_html( $project_tag->name ) . '</a>';
881
		}
882
		$html .= ' '. implode( ', ', $tags );
883
		$html .= '</div>';
884
885
		return $html;
886
	}
887
888
	/**
889
	 * Displays the author of the current portfolio project.
890
	 *
891
	 * @return html
892
	 */
893
	static function get_project_author() {
894
		$html = '<div class="project-author">';
895
		/* translators: %1$s is link to author posts, %2$s is author display name */
896
		$html .= sprintf( __( '<span>Author:</span> <a href="%1$s">%2$s</a>', 'jetpack' ),
897
			esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
898
			esc_html( get_the_author() )
899
		);
900
		$html .= '</div>';
901
902
		return $html;
903
	}
904
905
	/**
906
	 * Display the featured image if it's available
907
	 *
908
	 * @return html
909
	 */
910 View Code Duplication
	static function get_portfolio_thumbnail_link( $post_id ) {
911
		if ( has_post_thumbnail( $post_id ) ) {
912
			/**
913
			 * Change the Portfolio thumbnail size.
914
			 *
915
			 * @module custom-content-types
916
			 *
917
			 * @since 3.4.0
918
			 *
919
			 * @param string|array $var Either a registered size keyword or size array.
920
			 */
921
			return '<a class="portfolio-featured-image" href="' . esc_url( get_permalink( $post_id ) ) . '">' . get_the_post_thumbnail( $post_id, apply_filters( 'jetpack_portfolio_thumbnail_size', 'large' ) ) . '</a>';
922
		}
923
	}
924
}
925
926
add_action( 'init', array( 'Jetpack_Portfolio', 'init' ) );
927
928
// Check on plugin activation if theme supports CPT
929
register_activation_hook( __FILE__,                         array( 'Jetpack_Portfolio', 'activation_post_type_support' ) );
930
add_action( 'jetpack_activate_module_custom-content-types', array( 'Jetpack_Portfolio', 'activation_post_type_support' ) );
931