Completed
Push — sync/portfolio-cpt ( 6ca18f )
by
unknown
10:09
created

Jetpack_Portfolio::update_option_stat_bump()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 5
nc 4
nop 2
dl 0
loc 9
rs 8.8571
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
		// Enable Omnisearch for Portfolio Items.
49
		if ( class_exists( 'Jetpack_Omnisearch_Posts' ) )
50
			new Jetpack_Omnisearch_Posts( self::CUSTOM_POST_TYPE );
51
52
		// CPT magic
53
		$this->register_post_types();
54
		add_action( sprintf( 'add_option_%s', self::OPTION_NAME ),                     array( $this, 'flush_rules_on_enable' ), 10 );
55
		add_action( sprintf( 'update_option_%s', self::OPTION_NAME ),                  array( $this, 'flush_rules_on_enable' ), 10 );
56
		add_action( sprintf( 'publish_%s', self::CUSTOM_POST_TYPE),                    array( $this, 'flush_rules_on_first_project' ) );
57
		add_action( 'after_switch_theme',                                              array( $this, 'flush_rules_on_switch' ) );
58
59
		// Admin Customization
60
		add_filter( 'post_updated_messages',                                           array( $this, 'updated_messages'   ) );
61
		add_filter( sprintf( 'manage_%s_posts_columns', self::CUSTOM_POST_TYPE),       array( $this, 'edit_admin_columns' ) );
62
		add_filter( sprintf( 'manage_%s_posts_custom_column', self::CUSTOM_POST_TYPE), array( $this, 'image_column'       ), 10, 2 );
63
		add_action( 'customize_register',                                              array( $this, 'customize_register' ) );
64
65
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
66
67
			// Track all the things
68
			add_action( sprintf( 'add_option_%s', self::OPTION_NAME ),                 array( $this, 'new_activation_stat_bump' ) );
69
			add_action( sprintf( 'update_option_%s', self::OPTION_NAME ),              array( $this, 'update_option_stat_bump' ), 11, 2 );
70
			add_action( sprintf( 'publish_%s', self::CUSTOM_POST_TYPE),                array( $this, 'new_project_stat_bump' ) );
71
		}
72
73
		add_image_size( 'jetpack-portfolio-admin-thumb', 50, 50, true );
74
		add_action( 'admin_enqueue_scripts',                                           array( $this, 'enqueue_admin_styles'  ) );
75
76
		// register jetpack_portfolio shortcode and portfolio shortcode (legacy)
77
		add_shortcode( 'portfolio',                                                    array( $this, 'portfolio_shortcode' ) );
78
		add_shortcode( 'jetpack_portfolio',                                            array( $this, 'portfolio_shortcode' ) );
79
80
		// Adjust CPT archive and custom taxonomies to obey CPT reading setting
81
		add_filter( 'infinite_scroll_settings',                                        array( $this, 'infinite_scroll_click_posts_per_page' ) );
82
		add_filter( 'infinite_scroll_results',                                         array( $this, 'infinite_scroll_results' ), 10, 3 );
83
84
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
85
			// Add to Dotcom XML sitemaps
86
			add_filter( 'wpcom_sitemap_post_types',                                    array( $this, 'add_to_sitemap' ) );
87
		} else {
88
			// Add to Jetpack XML sitemap
89
			add_filter( 'jetpack_sitemap_post_types',                                  array( $this, 'add_to_sitemap' ) );
90
		}
91
92
		// Adjust CPT archive and custom taxonomies to obey CPT reading setting
93
		add_filter( 'pre_get_posts',                                                   array( $this, 'query_reading_setting' ) );
94
95
		// If CPT was enabled programatically and no CPT items exist when user switches away, disable
96
		if ( $setting && $this->site_supports_custom_post_type() ) {
97
			add_action( 'switch_theme',                                                array( $this, 'deactivation_post_type_support' ) );
98
		}
99
	}
100
101
	/**
102
	 * Add a checkbox field in 'Settings' > 'Writing'
103
	 * for enabling CPT functionality.
104
	 *
105
	 * @return null
106
	 */
107
	function settings_api_init() {
108
		add_settings_field(
109
			self::OPTION_NAME,
110
			'<span class="cpt-options">' . __( 'Portfolio Projects', 'jetpack' ) . '</span>',
111
			array( $this, 'setting_html' ),
112
			'writing',
113
			'jetpack_cpt_section'
114
		);
115
		register_setting(
116
			'writing',
117
			self::OPTION_NAME,
118
			'intval'
119
		);
120
121
		// Check if CPT is enabled first so that intval doesn't get set to NULL on re-registering
122
		if ( get_option( self::OPTION_NAME, '0' ) || current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
123
			register_setting(
124
				'writing',
125
				self::OPTION_READING_SETTING,
126
				'intval'
127
			);
128
		}
129
	}
130
131
	/**
132
	 * HTML code to display a checkbox true/false option
133
	 * for the Portfolio CPT setting.
134
	 *
135
	 * @return html
136
	 */
137
	function setting_html() {
138 View Code Duplication
		if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) : ?>
139
			<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>
140
		<?php else : ?>
141
			<label for="<?php echo esc_attr( self::OPTION_NAME ); ?>">
142
				<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" />
143
				<?php esc_html_e( 'Enable Portfolio Projects for this site.', 'jetpack' ); ?>
144
				<a target="_blank" href="http://en.support.wordpress.com/portfolios/"><?php esc_html_e( 'Learn More', 'jetpack' ); ?></a>
145
			</label>
146
		<?php endif;
147
		if ( get_option( self::OPTION_NAME, '0' ) || current_theme_supports( self::CUSTOM_POST_TYPE ) ) :
148
			printf( '<p><label for="%1$s">%2$s</label></p>',
149
				esc_attr( self::OPTION_READING_SETTING ),
150
				/* translators: %1$s is replaced with an input field for numbers */
151
				sprintf( __( 'Portfolio pages display at most %1$s projects', 'jetpack' ),
152
					sprintf( '<input name="%1$s" id="%1$s" type="number" step="1" min="1" value="%2$s" class="small-text" />',
153
						esc_attr( self::OPTION_READING_SETTING ),
154
						esc_attr( get_option( self::OPTION_READING_SETTING, '10' ) )
155
					)
156
				)
157
			);
158
		endif;
159
	}
160
161
	/*
162
	 * Bump Portfolio > New Activation stat
163
	 */
164
	function new_activation_stat_bump() {
165
		bump_stats_extras( 'portfolios', 'new-activation' );
166
	}
167
168
	/*
169
	 * Bump Portfolio > Option On/Off stats to get total active
170
	 */
171
	function update_option_stat_bump( $old, $new ) {
172
		if ( empty( $old ) && ! empty( $new ) ) {
173
			bump_stats_extras( 'portfolios', 'option-on' );
174
		}
175
176
		if ( ! empty( $old ) && empty( $new ) ) {
177
			bump_stats_extras( 'portfolios', 'option-off' );
178
		}
179
	}
180
181
	/*
182
	 * Bump Portfolio > Published Projects stat when projects are published
183
	 */
184
	function new_project_stat_bump() {
185
		bump_stats_extras( 'portfolios', 'published-projects' );
186
	}
187
188
	/**
189
	* Should this Custom Post Type be made available?
190
	*/
191 View Code Duplication
	function site_supports_custom_post_type() {
192
		// If the current theme requests it.
193
		if ( current_theme_supports( self::CUSTOM_POST_TYPE ) || get_option( self::OPTION_NAME, '0' ) ) {
194
			return true;
195
		}
196
197
		// Otherwise, say no unless something wants to filter us to say yes.
198
		/** This action is documented in modules/custom-post-types/nova.php */
199
		return (bool) apply_filters( 'jetpack_enable_cpt', false, self::CUSTOM_POST_TYPE );
200
	}
201
202
	/*
203
	 * Flush permalinks when CPT option is turned on/off
204
	 */
205
	function flush_rules_on_enable() {
206
		flush_rewrite_rules();
207
	}
208
209
	/*
210
	 * Count published projects and flush permalinks when first projects is published
211
	 */
212 View Code Duplication
	function flush_rules_on_first_project() {
213
		$projects = get_transient( 'jetpack-portfolio-count-cache' );
214
215
		if ( false === $projects ) {
216
			flush_rewrite_rules();
217
			$projects = (int) wp_count_posts( self::CUSTOM_POST_TYPE )->publish;
218
219
			if ( ! empty( $projects ) ) {
220
				set_transient( 'jetpack-portfolio-count-cache', $projects, HOUR_IN_SECONDS * 12 );
221
			}
222
		}
223
	}
224
225
	/*
226
	 * Flush permalinks when CPT supported theme is activated
227
	 */
228
	function flush_rules_on_switch() {
229
		if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
230
			flush_rewrite_rules();
231
		}
232
	}
233
234
	/**
235
	 * On plugin/theme activation, check if current theme supports CPT
236
	 */
237
	static function activation_post_type_support() {
238
		if ( current_theme_supports( self::CUSTOM_POST_TYPE ) ) {
239
			update_option( self::OPTION_NAME, '1' );
240
		}
241
	}
242
243
	/**
244
	 * On theme switch, check if CPT item exists and disable if not
245
	 */
246 View Code Duplication
	function deactivation_post_type_support() {
247
		$portfolios = get_posts( array(
248
			'fields'           => 'ids',
249
			'posts_per_page'   => 1,
250
			'post_type'        => self::CUSTOM_POST_TYPE,
251
			'suppress_filters' => false
252
		) );
253
254
		if ( empty( $portfolios ) ) {
255
			update_option( self::OPTION_NAME, '0' );
256
		}
257
	}
258
259
	/**
260
	 * Register Post Type
261
	 */
262
	function register_post_types() {
263
		if ( post_type_exists( self::CUSTOM_POST_TYPE ) ) {
264
			return;
265
		}
266
267
		register_post_type( self::CUSTOM_POST_TYPE, array(
268
			'description' => __( 'Portfolio Items', 'jetpack' ),
269
			'labels' => array(
270
				'name'                  => esc_html__( 'Projects',                   'jetpack' ),
271
				'singular_name'         => esc_html__( 'Project',                    'jetpack' ),
272
				'menu_name'             => esc_html__( 'Portfolio',                  'jetpack' ),
273
				'all_items'             => esc_html__( 'All Projects',               'jetpack' ),
274
				'add_new'               => esc_html__( 'Add New',                    'jetpack' ),
275
				'add_new_item'          => esc_html__( 'Add New Project',            'jetpack' ),
276
				'edit_item'             => esc_html__( 'Edit Project',               'jetpack' ),
277
				'new_item'              => esc_html__( 'New Project',                'jetpack' ),
278
				'view_item'             => esc_html__( 'View Project',               'jetpack' ),
279
				'search_items'          => esc_html__( 'Search Projects',            'jetpack' ),
280
				'not_found'             => esc_html__( 'No Projects found',          'jetpack' ),
281
				'not_found_in_trash'    => esc_html__( 'No Projects found in Trash', 'jetpack' ),
282
				'filter_items_list'     => esc_html__( 'Filter projects list',       'jetpack' ),
283
				'items_list_navigation' => esc_html__( 'Project list navigation',    'jetpack' ),
284
				'items_list'            => esc_html__( 'Projects list',              'jetpack' ),
285
			),
286
			'supports' => array(
287
				'title',
288
				'editor',
289
				'thumbnail',
290
				'author',
291
				'comments',
292
				'publicize',
293
				'wpcom-markdown',
294
				'revisions',
295
				'excerpt',
296
			),
297
			'rewrite' => array(
298
				'slug'       => 'portfolio',
299
				'with_front' => false,
300
				'feeds'      => true,
301
				'pages'      => true,
302
			),
303
			'public'          => true,
304
			'show_ui'         => true,
305
			'menu_position'   => 20,                    // below Pages
306
			'menu_icon'       => 'dashicons-portfolio', // 3.8+ dashicon option
307
			'capability_type' => 'page',
308
			'map_meta_cap'    => true,
309
			'taxonomies'      => array( self::CUSTOM_TAXONOMY_TYPE, self::CUSTOM_TAXONOMY_TAG ),
310
			'has_archive'     => true,
311
			'query_var'       => 'portfolio',
312
			'show_in_rest'    => true,
313
		) );
314
315
		register_taxonomy( self::CUSTOM_TAXONOMY_TYPE, self::CUSTOM_POST_TYPE, array(
316
			'hierarchical'      => true,
317
			'labels'            => array(
318
				'name'                  => esc_html__( 'Project Types',                 'jetpack' ),
319
				'singular_name'         => esc_html__( 'Project Type',                  'jetpack' ),
320
				'menu_name'             => esc_html__( 'Project Types',                 'jetpack' ),
321
				'all_items'             => esc_html__( 'All Project Types',             'jetpack' ),
322
				'edit_item'             => esc_html__( 'Edit Project Type',             'jetpack' ),
323
				'view_item'             => esc_html__( 'View Project Type',             'jetpack' ),
324
				'update_item'           => esc_html__( 'Update Project Type',           'jetpack' ),
325
				'add_new_item'          => esc_html__( 'Add New Project Type',          'jetpack' ),
326
				'new_item_name'         => esc_html__( 'New Project Type Name',         'jetpack' ),
327
				'parent_item'           => esc_html__( 'Parent Project Type',           'jetpack' ),
328
				'parent_item_colon'     => esc_html__( 'Parent Project Type:',          'jetpack' ),
329
				'search_items'          => esc_html__( 'Search Project Types',          'jetpack' ),
330
				'items_list_navigation' => esc_html__( 'Project type list navigation',  'jetpack' ),
331
				'items_list'            => esc_html__( 'Project type list',             'jetpack' ),
332
			),
333
			'public'            => true,
334
			'show_ui'           => true,
335
			'show_in_nav_menus' => true,
336
			'show_in_rest'      => true,
337
			'show_admin_column' => true,
338
			'query_var'         => true,
339
			'rewrite'           => array( 'slug' => 'project-type' ),
340
		) );
341
342
		register_taxonomy( self::CUSTOM_TAXONOMY_TAG, self::CUSTOM_POST_TYPE, array(
343
			'hierarchical'      => false,
344
			'labels'            => array(
345
				'name'                       => esc_html__( 'Project Tags',                   'jetpack' ),
346
				'singular_name'              => esc_html__( 'Project Tag',                    'jetpack' ),
347
				'menu_name'                  => esc_html__( 'Project Tags',                   'jetpack' ),
348
				'all_items'                  => esc_html__( 'All Project Tags',               'jetpack' ),
349
				'edit_item'                  => esc_html__( 'Edit Project Tag',               'jetpack' ),
350
				'view_item'                  => esc_html__( 'View Project Tag',               'jetpack' ),
351
				'update_item'                => esc_html__( 'Update Project Tag',             'jetpack' ),
352
				'add_new_item'               => esc_html__( 'Add New Project Tag',            'jetpack' ),
353
				'new_item_name'              => esc_html__( 'New Project Tag Name',           'jetpack' ),
354
				'search_items'               => esc_html__( 'Search Project Tags',            'jetpack' ),
355
				'popular_items'              => esc_html__( 'Popular Project Tags',           'jetpack' ),
356
				'separate_items_with_commas' => esc_html__( 'Separate tags with commas',      'jetpack' ),
357
				'add_or_remove_items'        => esc_html__( 'Add or remove tags',             'jetpack' ),
358
				'choose_from_most_used'      => esc_html__( 'Choose from the most used tags', 'jetpack' ),
359
				'not_found'                  => esc_html__( 'No tags found.',                 'jetpack' ),
360
				'items_list_navigation'      => esc_html__( 'Project tag list navigation',    'jetpack' ),
361
				'items_list'                 => esc_html__( 'Project tag list',               'jetpack' ),
362
			),
363
			'public'            => true,
364
			'show_ui'           => true,
365
			'show_in_nav_menus' => true,
366
			'show_in_rest'      => true,
367
			'show_admin_column' => true,
368
			'query_var'         => true,
369
			'rewrite'           => array( 'slug' => 'project-tag' ),
370
		) );
371
	}
372
373
	/**
374
	 * Update messages for the Portfolio admin.
375
	 */
376 View Code Duplication
	function updated_messages( $messages ) {
377
		global $post;
378
379
		$messages[self::CUSTOM_POST_TYPE] = array(
380
			0  => '', // Unused. Messages start at index 1.
381
			1  => sprintf( __( 'Project updated. <a href="%s">View item</a>', 'jetpack'), esc_url( get_permalink( $post->ID ) ) ),
382
			2  => esc_html__( 'Custom field updated.', 'jetpack' ),
383
			3  => esc_html__( 'Custom field deleted.', 'jetpack' ),
384
			4  => esc_html__( 'Project updated.', 'jetpack' ),
385
			/* translators: %s: date and time of the revision */
386
			5  => isset( $_GET['revision'] ) ? sprintf( esc_html__( 'Project restored to revision from %s', 'jetpack'), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
387
			6  => sprintf( __( 'Project published. <a href="%s">View project</a>', 'jetpack' ), esc_url( get_permalink( $post->ID ) ) ),
388
			7  => esc_html__( 'Project saved.', 'jetpack' ),
389
			8  => sprintf( __( 'Project submitted. <a target="_blank" href="%s">Preview project</a>', 'jetpack'), esc_url( add_query_arg( 'preview', 'true', get_permalink( $post->ID ) ) ) ),
390
			9  => sprintf( __( 'Project scheduled for: <strong>%1$s</strong>. <a target="_blank" href="%2$s">Preview project</a>', 'jetpack' ),
391
			// translators: Publish box date format, see http://php.net/date
392
			date_i18n( __( 'M j, Y @ G:i', 'jetpack' ), strtotime( $post->post_date ) ), esc_url( get_permalink( $post->ID ) ) ),
393
			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 ) ) ) ),
394
		);
395
396
		return $messages;
397
	}
398
399
	/**
400
	 * Change ‘Title’ column label
401
	 * Add Featured Image column
402
	 */
403
	function edit_admin_columns( $columns ) {
404
		// change 'Title' to 'Project'
405
		$columns['title'] = __( 'Project', 'jetpack' );
406
		if ( current_theme_supports( 'post-thumbnails' ) ) {
407
			// add featured image before 'Project'
408
			$columns = array_slice( $columns, 0, 1, true ) + array( 'thumbnail' => '' ) + array_slice( $columns, 1, NULL, true );
409
		}
410
411
		return $columns;
412
	}
413
414
	/**
415
	 * Add featured image to column
416
	 */
417
	function image_column( $column, $post_id ) {
418
		global $post;
419
		switch ( $column ) {
420
			case 'thumbnail':
421
				echo get_the_post_thumbnail( $post_id, 'jetpack-portfolio-admin-thumb' );
422
				break;
423
		}
424
	}
425
426
	/**
427
	 * Adjust image column width
428
	 */
429
	function enqueue_admin_styles( $hook ) {
430
		$screen = get_current_screen();
431
432
		if ( 'edit.php' == $hook && self::CUSTOM_POST_TYPE == $screen->post_type && current_theme_supports( 'post-thumbnails' ) ) {
433
			wp_add_inline_style( 'wp-admin', '.manage-column.column-thumbnail { width: 50px; } @media screen and (max-width: 360px) { .column-thumbnail{ display:none; } }' );
434
		}
435
	}
436
437
	/**
438
	 * Adds portfolio section to the Customizer.
439
	 */
440
	function customize_register( $wp_customize ) {
441
		$options = get_theme_support( self::CUSTOM_POST_TYPE );
442
443
		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'] ) ) {
444
			return;
445
		}
446
447
		$wp_customize->add_section( 'jetpack_portfolio', array(
448
			'title'                    => esc_html__( 'Portfolio', 'jetpack' ),
449
			'theme_supports'           => self::CUSTOM_POST_TYPE,
450
			'priority'                 => 130,
451
		) );
452
453 View Code Duplication
		if ( isset( $options[0]['title'] ) && true === $options[0]['title'] ) {
454
			$wp_customize->add_setting( 'jetpack_portfolio_title', array(
455
				'default'              => esc_html__( 'Projects', 'jetpack' ),
456
				'type'                 => 'option',
457
				'sanitize_callback'    => 'sanitize_text_field',
458
				'sanitize_js_callback' => 'sanitize_text_field',
459
			) );
460
461
			$wp_customize->add_control( 'jetpack_portfolio_title', array(
462
				'section'              => 'jetpack_portfolio',
463
				'label'                => esc_html__( 'Portfolio Archive Title', 'jetpack' ),
464
				'type'                 => 'text',
465
			) );
466
		}
467
468 View Code Duplication
		if ( isset( $options[0]['content'] ) && true === $options[0]['content'] ) {
469
			$wp_customize->add_setting( 'jetpack_portfolio_content', array(
470
				'default'              => '',
471
				'type'                 => 'option',
472
				'sanitize_callback'    => 'wp_kses_post',
473
				'sanitize_js_callback' => 'wp_kses_post',
474
			) );
475
476
			$wp_customize->add_control( 'jetpack_portfolio_content', array(
477
				'section'              => 'jetpack_portfolio',
478
				'label'                => esc_html__( 'Portfolio Archive Content', 'jetpack' ),
479
				'type'                 => 'textarea',
480
			) );
481
		}
482
483 View Code Duplication
		if ( isset( $options[0]['featured-image'] ) && true === $options[0]['featured-image'] ) {
484
			$wp_customize->add_setting( 'jetpack_portfolio_featured_image', array(
485
				'default'              => '',
486
				'type'                 => 'option',
487
				'sanitize_callback'    => 'attachment_url_to_postid',
488
				'sanitize_js_callback' => 'attachment_url_to_postid',
489
				'theme_supports'       => 'post-thumbnails',
490
			) );
491
492
			$wp_customize->add_control( new WP_Customize_Image_Control( $wp_customize, 'jetpack_portfolio_featured_image', array(
493
				'section'              => 'jetpack_portfolio',
494
				'label'                => esc_html__( 'Portfolio Archive Featured Image', 'jetpack' ),
495
			) ) );
496
		}
497
	}
498
499
	/**
500
	 * Follow CPT reading setting on CPT archive and taxonomy pages
501
	 */
502
	function query_reading_setting( $query ) {
503
		if ( ( ! is_admin() || ( is_admin() && defined( 'DOING_AJAX' ) && DOING_AJAX ) )
504
			&& $query->is_main_query()
505
			&& ( $query->is_post_type_archive( self::CUSTOM_POST_TYPE )
506
				|| $query->is_tax( self::CUSTOM_TAXONOMY_TYPE )
507
				|| $query->is_tax( self::CUSTOM_TAXONOMY_TAG ) )
508
		) {
509
			$query->set( 'posts_per_page', get_option( self::OPTION_READING_SETTING, '10' ) );
510
		}
511
	}
512
513
	/*
514
	 * If Infinite Scroll is set to 'click', use our custom reading setting instead of core's `posts_per_page`.
515
	 */
516
	function infinite_scroll_click_posts_per_page( $settings ) {
517
		global $wp_query;
518
519
		if ( ( ! is_admin() || ( is_admin() && defined( 'DOING_AJAX' ) && DOING_AJAX ) )
520
			&& true === $settings['click_handle']
521
			&& ( $wp_query->is_post_type_archive( self::CUSTOM_POST_TYPE )
522
				|| $wp_query->is_tax( self::CUSTOM_TAXONOMY_TYPE )
523
				|| $wp_query->is_tax( self::CUSTOM_TAXONOMY_TAG ) )
524
		) {
525
			$settings['posts_per_page'] = get_option( self::OPTION_READING_SETTING, $settings['posts_per_page'] );
526
		}
527
528
		return $settings;
529
	}
530
531
	/*
532
	 * Filter the results of infinite scroll to make sure we get `lastbatch` right.
533
	 */
534
	function infinite_scroll_results( $results, $query_args, $query ) {
535
		$results['lastbatch'] = $query_args['paged'] >= $query->max_num_pages;
536
		return $results;
537
	}
538
539
	/**
540
	 * Add CPT to Dotcom sitemap
541
	 */
542
	function add_to_sitemap( $post_types ) {
543
		$post_types[] = self::CUSTOM_POST_TYPE;
544
545
		return $post_types;
546
	}
547
548
	/**
549
	 * Add to REST API post type whitelist
550
	 */
551
	function allow_portfolio_rest_api_type( $post_types ) {
552
		$post_types[] = self::CUSTOM_POST_TYPE;
553
554
		return $post_types;
555
	}
556
557
	/**
558
	 * Our [portfolio] shortcode.
559
	 * Prints Portfolio data styled to look good on *any* theme.
560
	 *
561
	 * @return portfolio_shortcode_html
562
	 */
563
	static function portfolio_shortcode( $atts ) {
564
		// Default attributes
565
		$atts = shortcode_atts( array(
566
			'display_types'   => true,
567
			'display_tags'    => true,
568
			'display_content' => true,
569
			'display_author'  => false,
570
			'show_filter'     => false,
571
			'include_type'    => false,
572
			'include_tag'     => false,
573
			'columns'         => 2,
574
			'showposts'       => -1,
575
			'order'           => 'asc',
576
			'orderby'         => 'date',
577
		), $atts, 'portfolio' );
578
579
		// A little sanitization
580
		if ( $atts['display_types'] && 'true' != $atts['display_types'] ) {
581
			$atts['display_types'] = false;
582
		}
583
584
		if ( $atts['display_tags'] && 'true' != $atts['display_tags'] ) {
585
			$atts['display_tags'] = false;
586
		}
587
588
		if ( $atts['display_author'] && 'true' != $atts['display_author'] ) {
589
			$atts['display_author'] = false;
590
		}
591
592 View Code Duplication
		if ( $atts['display_content'] && 'true' != $atts['display_content'] && 'full' != $atts['display_content'] ) {
593
			$atts['display_content'] = false;
594
		}
595
596
		if ( $atts['include_type'] ) {
597
			$atts['include_type'] = explode( ',', str_replace( ' ', '', $atts['include_type'] ) );
598
		}
599
600
		if ( $atts['include_tag'] ) {
601
			$atts['include_tag'] = explode( ',', str_replace( ' ', '', $atts['include_tag'] ) );
602
		}
603
604
		$atts['columns'] = absint( $atts['columns'] );
605
606
		$atts['showposts'] = intval( $atts['showposts'] );
607
608
609 View Code Duplication
		if ( $atts['order'] ) {
610
			$atts['order'] = urldecode( $atts['order'] );
611
			$atts['order'] = strtoupper( $atts['order'] );
612
			if ( 'DESC' != $atts['order'] ) {
613
				$atts['order'] = 'ASC';
614
			}
615
		}
616
617 View Code Duplication
		if ( $atts['orderby'] ) {
618
			$atts['orderby'] = urldecode( $atts['orderby'] );
619
			$atts['orderby'] = strtolower( $atts['orderby'] );
620
			$allowed_keys = array( 'author', 'date', 'title', 'rand' );
621
622
			$parsed = array();
623
			foreach ( explode( ',', $atts['orderby'] ) as $portfolio_index_number => $orderby ) {
624
				if ( ! in_array( $orderby, $allowed_keys ) ) {
625
					continue;
626
				}
627
				$parsed[] = $orderby;
628
			}
629
630
			if ( empty( $parsed ) ) {
631
				unset( $atts['orderby'] );
632
			} else {
633
				$atts['orderby'] = implode( ' ', $parsed );
634
			}
635
		}
636
637
		// enqueue shortcode styles when shortcode is used
638
		wp_enqueue_style( 'jetpack-portfolio-style', plugins_url( 'css/portfolio-shortcode.css', __FILE__ ), array(), '20140326' );
639
640
		return self::portfolio_shortcode_html( $atts );
641
	}
642
643
	/**
644
	 * Query to retrieve entries from the Portfolio post_type.
645
	 *
646
	 * @return object
647
	 */
648
	static function portfolio_query( $atts ) {
649
		// Default query arguments
650
		$default = array(
651
			'order'          => $atts['order'],
652
			'orderby'        => $atts['orderby'],
653
			'posts_per_page' => $atts['showposts'],
654
		);
655
656
		$args = wp_parse_args( $atts, $default );
657
		$args['post_type'] = self::CUSTOM_POST_TYPE; // Force this post type
658
659 View Code Duplication
		if ( false != $atts['include_type'] || false != $atts['include_tag'] ) {
660
			$args['tax_query'] = array();
661
		}
662
663
		// If 'include_type' has been set use it on the main query
664 View Code Duplication
		if ( false != $atts['include_type'] ) {
665
			array_push( $args['tax_query'], array(
666
				'taxonomy' => self::CUSTOM_TAXONOMY_TYPE,
667
				'field'    => 'slug',
668
				'terms'    => $atts['include_type'],
669
			) );
670
		}
671
672
		// If 'include_tag' has been set use it on the main query
673 View Code Duplication
		if ( false != $atts['include_tag'] ) {
674
			array_push( $args['tax_query'], array(
675
				'taxonomy' => self::CUSTOM_TAXONOMY_TAG,
676
				'field'    => 'slug',
677
				'terms'    => $atts['include_tag'],
678
			) );
679
		}
680
681 View Code Duplication
		if ( false != $atts['include_type'] && false != $atts['include_tag'] ) {
682
			$args['tax_query']['relation'] = 'AND';
683
		}
684
685
		// Run the query and return
686
		$query = new WP_Query( $args );
687
		return $query;
688
	}
689
690
	/**
691
	 * The Portfolio shortcode loop.
692
	 *
693
	 * @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...
694
	 * @return html
695
	 */
696
	static function portfolio_shortcode_html( $atts ) {
697
698
		$query = self::portfolio_query( $atts );
699
		$portfolio_index_number = 0;
700
701
		ob_start();
702
703
		// If we have posts, create the html
704
		// with hportfolio markup
705
		if ( $query->have_posts() ) {
706
707
			// Render styles
708
			//self::themecolor_styles();
709
710
		?>
711
			<div class="jetpack-portfolio-shortcode column-<?php echo esc_attr( $atts['columns'] ); ?>">
712
			<?php  // open .jetpack-portfolio
713
714
			// Construct the loop...
715
			while ( $query->have_posts() ) {
716
				$query->the_post();
717
				$post_id = get_the_ID();
718
				?>
719
				<div class="portfolio-entry <?php echo esc_attr( self::get_project_class( $portfolio_index_number, $atts['columns'] ) ); ?>">
720
					<header class="portfolio-entry-header">
721
					<?php
722
					// Featured image
723
					echo self::get_portfolio_thumbnail_link( $post_id );
724
					?>
725
726
					<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>
727
728
						<div class="portfolio-entry-meta">
729
						<?php
730
						if ( false != $atts['display_types'] ) {
731
							echo self::get_project_type( $post_id );
732
						}
733
734
						if ( false != $atts['display_tags'] ) {
735
							echo self::get_project_tags( $post_id );
736
						}
737
738
						if ( false != $atts['display_author'] ) {
739
							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...
740
						}
741
						?>
742
						</div>
743
744
					</header>
745
746
				<?php
747
				// The content
748
				if ( false !== $atts['display_content'] ) {
749
					add_filter( 'wordads_inpost_disable', '__return_true', 20 );
750
					if ( 'full' === $atts['display_content'] ) {
751
					?>
752
						<div class="portfolio-entry-content"><?php the_content(); ?></div>
753
					<?php
754
					} else {
755
					?>
756
						<div class="portfolio-entry-content"><?php the_excerpt(); ?></div>
757
					<?php
758
					}
759
					remove_filter( 'wordads_inpost_disable', '__return_true', 20 );
760
				}
761
				?>
762
				</div><!-- close .portfolio-entry -->
763
				<?php $portfolio_index_number++;
764
			} // end of while loop
765
766
			wp_reset_postdata();
767
			?>
768
			</div><!-- close .jetpack-portfolio -->
769
		<?php
770
		} else { ?>
771
			<p><em><?php _e( 'Your Portfolio Archive currently has no entries. You can start creating them on your dashboard.', 'jetpack' ); ?></p></em>
772
		<?php
773
		}
774
		$html = ob_get_clean();
775
776
		// If there is a [portfolio] within a [portfolio], remove the shortcode
777
		if ( has_shortcode( $html, 'portfolio' ) ){
778
			remove_shortcode( 'portfolio' );
779
		}
780
781
		// Return the HTML block
782
		return $html;
783
	}
784
785
	/**
786
	 * Individual project class
787
	 *
788
	 * @return string
789
	 */
790
	static function get_project_class( $portfolio_index_number, $columns ) {
791
		$project_types = wp_get_object_terms( get_the_ID(), self::CUSTOM_TAXONOMY_TYPE, array( 'fields' => 'slugs' ) );
792
		$class = array();
793
794
		$class[] = 'portfolio-entry-column-'.$columns;
795
		// add a type- class for each project type
796
		foreach ( $project_types as $project_type ) {
797
			$class[] = 'type-' . esc_html( $project_type );
798
		}
799
		if( $columns > 1) {
800
			if ( ( $portfolio_index_number % 2 ) == 0 ) {
801
				$class[] = 'portfolio-entry-mobile-first-item-row';
802
			} else {
803
				$class[] = 'portfolio-entry-mobile-last-item-row';
804
			}
805
		}
806
807
		// add first and last classes to first and last items in a row
808
		if ( ( $portfolio_index_number % $columns ) == 0 ) {
809
			$class[] = 'portfolio-entry-first-item-row';
810
		} elseif ( ( $portfolio_index_number % $columns ) == ( $columns - 1 ) ) {
811
			$class[] = 'portfolio-entry-last-item-row';
812
		}
813
814
815
		/**
816
		 * Filter the class applied to project div in the portfolio
817
		 *
818
		 * @module custom-content-types
819
		 *
820
		 * @since 3.1.0
821
		 *
822
		 * @param string $class class name of the div.
823
		 * @param int $portfolio_index_number iterator count the number of columns up starting from 0.
824
		 * @param int $columns number of columns to display the content in.
825
		 *
826
		 */
827
		return apply_filters( 'portfolio-project-post-class', implode( " ", $class ) , $portfolio_index_number, $columns );
828
	}
829
830
	/**
831
	 * Displays the project type that a project belongs to.
832
	 *
833
	 * @return html
834
	 */
835 View Code Duplication
	static function get_project_type( $post_id ) {
836
		$project_types = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TYPE );
837
838
		// If no types, return empty string
839
		if ( empty( $project_types ) || is_wp_error( $project_types ) ) {
840
			return;
841
		}
842
843
		$html = '<div class="project-types"><span>' . __( 'Types', 'jetpack' ) . ':</span>';
844
		$types = array();
845
		// Loop thorugh all the types
846
		foreach ( $project_types as $project_type ) {
847
			$project_type_link = get_term_link( $project_type, self::CUSTOM_TAXONOMY_TYPE );
848
849
			if ( is_wp_error( $project_type_link ) ) {
850
				return $project_type_link;
851
			}
852
853
			$types[] = '<a href="' . esc_url( $project_type_link ) . '" rel="tag">' . esc_html( $project_type->name ) . '</a>';
854
		}
855
		$html .= ' '.implode( ', ', $types );
856
		$html .= '</div>';
857
858
		return $html;
859
	}
860
861
	/**
862
	 * Displays the project tags that a project belongs to.
863
	 *
864
	 * @return html
865
	 */
866 View Code Duplication
	static function get_project_tags( $post_id ) {
867
		$project_tags = get_the_terms( $post_id, self::CUSTOM_TAXONOMY_TAG );
868
869
		// If no tags, return empty string
870
		if ( empty( $project_tags ) || is_wp_error( $project_tags ) ) {
871
			return false;
872
		}
873
874
		$html = '<div class="project-tags"><span>' . __( 'Tags', 'jetpack' ) . ':</span>';
875
		$tags = array();
876
		// Loop thorugh all the tags
877
		foreach ( $project_tags as $project_tag ) {
878
			$project_tag_link = get_term_link( $project_tag, self::CUSTOM_TAXONOMY_TYPE );
879
880
			if ( is_wp_error( $project_tag_link ) ) {
881
				return $project_tag_link;
882
			}
883
884
			$tags[] = '<a href="' . esc_url( $project_tag_link ) . '" rel="tag">' . esc_html( $project_tag->name ) . '</a>';
885
		}
886
		$html .= ' '. implode( ', ', $tags );
887
		$html .= '</div>';
888
889
		return $html;
890
	}
891
892
	/**
893
	 * Displays the author of the current portfolio project.
894
	 *
895
	 * @return html
896
	 */
897
	static function get_project_author() {
898
		$html = '<div class="project-author">';
899
		/* translators: %1$s is link to author posts, %2$s is author display name */
900
		$html .= sprintf( __( '<span>Author:</span> <a href="%1$s">%2$s</a>', 'jetpack' ),
901
			esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
902
			esc_html( get_the_author() )
903
		);
904
		$html .= '</div>';
905
906
		return $html;
907
	}
908
909
	/**
910
	 * Display the featured image if it's available
911
	 *
912
	 * @return html
913
	 */
914 View Code Duplication
	static function get_portfolio_thumbnail_link( $post_id ) {
915
		if ( has_post_thumbnail( $post_id ) ) {
916
			/**
917
			 * Change the Portfolio thumbnail size.
918
			 *
919
			 * @module custom-content-types
920
			 *
921
			 * @since 3.4.0
922
			 *
923
			 * @param string|array $var Either a registered size keyword or size array.
924
			 */
925
			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>';
926
		}
927
	}
928
}
929
930
add_action( 'init', array( 'Jetpack_Portfolio', 'init' ) );
931
932
// Check on plugin activation if theme supports CPT
933
register_activation_hook( __FILE__,                         array( 'Jetpack_Portfolio', 'activation_post_type_support' ) );
934
add_action( 'jetpack_activate_module_custom-content-types', array( 'Jetpack_Portfolio', 'activation_post_type_support' ) );
935