Completed
Push — branch-6.8-built ( ab1229...17d608 )
by Jeremy
15:49 queued 07:50
created

Publicize_Base::test_publicize_conns()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
abstract class Publicize_Base {
4
5
	/**
6
	* Services that are currently connected to the given user
7
	* through publicize.
8
	*/
9
	public $connected_services = array();
10
11
	/**
12
	* Services that are supported by publicize. They don't
13
	* necessarily need to be connected to the current user.
14
	*/
15
	public $services;
16
17
	/**
18
	* key names for post meta
19
	*/
20
	public $ADMIN_PAGE        = 'wpas';
21
	public $POST_MESS         = '_wpas_mess';
22
	public $POST_SKIP         = '_wpas_skip_'; // connection id appended to indicate that a connection should NOT be publicized to
23
	public $POST_DONE         = '_wpas_done_'; // connection id appended to indicate a connection has already been publicized to
24
	public $USER_AUTH         = 'wpas_authorize';
25
	public $USER_OPT          = 'wpas_';
26
	public $PENDING           = '_publicize_pending'; // ready for Publicize to do its thing
27
	public $POST_SERVICE_DONE = '_publicize_done_external'; // array of external ids where we've Publicized
28
29
	/**
30
	* default pieces of the message used in constructing the
31
	* content pushed out to other social networks
32
	*/
33
34
	public $default_prefix  = '';
35
	public $default_message = '%title%';
36
	public $default_suffix  = ' ';
37
38
	/**
39
	 * What WP capability is require to create/delete global connections?
40
	 * All users with this cap can un-globalize all other global connections, and globalize any of their own
41
	 * Globalized connections cannot be unselected by users without this capability when publishing
42
	 */
43
	public $GLOBAL_CAP = 'edit_others_posts';
44
45
	/**
46
	* Sets up the basics of Publicize
47
	*/
48
	function __construct() {
49
		$this->default_message = self::build_sprintf( array(
50
			/**
51
			 * Filter the default Publicize message.
52
			 *
53
			 * @module publicize
54
			 *
55
			 * @since 2.0.0
56
			 *
57
			 * @param string $this->default_message Publicize's default message. Default is the post title.
58
			 */
59
			apply_filters( 'wpas_default_message', $this->default_message ),
60
			'title',
61
			'url',
62
		) );
63
64
		$this->default_prefix = self::build_sprintf( array(
65
			/**
66
			 * Filter the message prepended to the Publicize custom message.
67
			 *
68
			 * @module publicize
69
			 *
70
			 * @since 2.0.0
71
			 *
72
			 * @param string $this->default_prefix String prepended to the Publicize custom message.
73
			 */
74
			apply_filters( 'wpas_default_prefix', $this->default_prefix ),
75
			'url',
76
		) );
77
78
		$this->default_suffix = self::build_sprintf( array(
79
			/**
80
			 * Filter the message appended to the Publicize custom message.
81
			 *
82
			 * @module publicize
83
			 *
84
			 * @since 2.0.0
85
			 *
86
			 * @param string $this->default_suffix String appended to the Publicize custom message.
87
			 */
88
			apply_filters( 'wpas_default_suffix', $this->default_suffix ),
89
			'url',
90
		) );
91
92
		/**
93
		 * Filter the capability to change global Publicize connection options.
94
		 *
95
		 * All users with this cap can un-globalize all other global connections, and globalize any of their own
96
		 * Globalized connections cannot be unselected by users without this capability when publishing.
97
		 *
98
		 * @module publicize
99
		 *
100
		 * @since 2.2.1
101
		 *
102
		 * @param string $this->GLOBAL_CAP default capability in control of global Publicize connection options. Default to edit_others_posts.
103
		 */
104
		$this->GLOBAL_CAP = apply_filters( 'jetpack_publicize_global_connections_cap', $this->GLOBAL_CAP );
105
106
		// stage 1 and 2 of 3-stage Publicize. Flag for Publicize on creation, save meta,
107
		// then check meta and publicize based on that. stage 3 implemented on wpcom
108
		add_action( 'transition_post_status', array( $this, 'flag_post_for_publicize' ), 10, 3 );
109
		add_action( 'save_post', array( &$this, 'save_meta' ), 20, 2 );
110
111
		// Default checkbox state for each Connection
112
		add_filter( 'publicize_checkbox_default', array( $this, 'publicize_checkbox_default' ), 10, 4 );
113
114
		// Alter the "Post Publish" admin notice to mention the Connections we Publicized to.
115
		add_filter( 'post_updated_messages', array( $this, 'update_published_message' ), 20, 1 );
116
117
		// Connection test callback
118
		add_action( 'wp_ajax_test_publicize_conns', array( $this, 'test_publicize_conns' ) );
119
120
		add_action( 'init', array( $this, 'add_post_type_support' ) );
121
		add_action( 'init', array( $this, 'register_post_meta' ), 20 );
122
	}
123
124
/*
125
 * Services: Facebook, Twitter, etc.
126
 */
127
128
	/**
129
	 * Get services for the given blog and user.
130
	 *
131
	 * Can return all available services or just the ones with an active connection.
132
	 *
133
	 * @param string $filter
134
	 *        'all' (default) - Get all services available for connecting
135
	 *        'connected'     - Get all services currently connected
136
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
137
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
138
	 * @return array
139
	 */
140
	abstract function get_services( $filter = 'all', $_blog_id = false, $_user_id = false );
141
142
	/**
143
	 * Does the given user have a connection to the service on the given blog?
144
	 *
145
	 * @param string $service_name 'facebook', 'twitter', etc.
146
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
147
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
148
	 * @return bool
149
	 */
150
	function is_enabled( $service_name, $_blog_id = false, $_user_id = false ) {
151
		if ( !$_blog_id )
0 ignored issues
show
Bug Best Practice introduced by
The expression $_blog_id of type false|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
152
			$_blog_id = $this->blog_id();
153
154
		if ( !$_user_id )
0 ignored issues
show
Bug Best Practice introduced by
The expression $_user_id of type false|integer is loosely compared to false; this is ambiguous if the integer can be zero. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
155
			$_user_id = $this->user_id();
156
157
		$connections = $this->get_connections( $service_name, $_blog_id, $_user_id );
158
		return ( is_array( $connections ) && count( $connections ) > 0 ? true : false );
159
	}
160
161
	/**
162
	 * Generates a connection URL.
163
	 *
164
	 * This is the URL, which, when visited by the user, starts the authentication
165
	 * process required to forge a connection.
166
	 *
167
	 * @param string $service_name 'facebook', 'twitter', etc.
168
	 * @return string
169
	 */
170
	abstract function connect_url( $service_name );
171
172
	/**
173
	 * Generates a Connection refresh URL.
174
	 *
175
	 * This is the URL, which, when visited by the user, re-authenticates their
176
	 * connection to the service.
177
	 *
178
	 * @param string $service_name 'facebook', 'twitter', etc.
179
	 * @return string
180
	 */
181
	abstract function refresh_url( $service_name );
182
183
	/**
184
	 * Generates a disconnection URL.
185
	 *
186
	 * This is the URL, which, when visited by the user, breaks their connection
187
	 * with the service.
188
	 *
189
	 * @param string $service_name 'facebook', 'twitter', etc.
190
	 * @param string $connection_id Connection ID
191
	 * @return string
192
	 */
193
	abstract function disconnect_url( $service_name, $connection_id );
194
195
	/**
196
	 * Returns a display name for the Service
197
	 *
198
	 * @param string $service_name 'facebook', 'twitter', etc.
199
	 * @return string
200
	 */
201
	public static function get_service_label( $service_name ) {
202
		switch ( $service_name ) {
203
			case 'linkedin':
204
				return 'LinkedIn';
205
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
206
			case 'google_plus':
207
				return  'Google+';
208
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
209
			case 'twitter':
210
			case 'facebook':
211
			case 'tumblr':
212
			default:
213
				return ucfirst( $service_name );
214
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
215
		}
216
	}
217
218
/*
219
 * Connections: For each Service, there can be multiple connections
220
 * for a given user. For example, one user could be connected to Twitter
221
 * as both @jetpack and as @wordpressdotcom
222
 *
223
 * For historical reasons, Connections are represented as an object
224
 * on WordPress.com and as an array in Jetpack.
225
 */
226
227
	/**
228
	 * Get the active Connections of a Service
229
	 *
230
	 * @param string $service_name 'facebook', 'twitter', etc.
231
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
232
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
233
	 * @return false|object[]|array[] false if no connections exist
234
	 */
235
	abstract function get_connections( $service_name, $_blog_id = false, $_user_id = false );
236
237
	/**
238
	 * Get a single Connection of a Service
239
	 *
240
	 * @param string $service_name 'facebook', 'twitter', etc.
241
	 * @param string $connection_id Connection ID
242
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
243
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
244
	 * @return false|object[]|array[] false if no connections exist
245
	 */
246
	abstract function get_connection( $service_name, $connection_id, $_blog_id = false, $_user_id = false );
247
248
	/**
249
	 * Get the Connection ID.
250
	 *
251
	 * Note that this is different than the Connection's uniqueid.
252
	 *
253
	 * Via a quirk of history, ID is globally unique and unique_id
254
	 * is only unique per site.
255
	 *
256
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
257
	 * @return string
258
	 */
259
	abstract function get_connection_id( $connection );
260
261
	/**
262
	 * Get the Connection unique_id
263
	 *
264
	 * Note that this is different than the Connections ID.
265
	 *
266
	 * Via a quirk of history, ID is globally unique and unique_id
267
	 * is only unique per site.
268
	 *
269
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
270
	 * @return string
271
	 */
272
	abstract function get_connection_unique_id( $connection );
273
274
	/**
275
	 * Get the Connection's Meta data
276
	 *
277
	 * @param object|array Connection
278
	 * @return array Connection Meta
279
	 */
280
	abstract function get_connection_meta( $connection );
281
282
	/**
283
	 * Disconnect a Connection
284
	 *
285
	 * @param string $service_name 'facebook', 'twitter', etc.
286
	 * @param string $connection_id Connection ID
287
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
288
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
289
	 * @param bool $force_delete Whether to skip permissions checks
290
	 * @return false|void False on failure. Void on success.
291
	 */
292
	abstract function disconnect( $service_name, $connection_id, $_blog_id = false, $_user_id = false, $force_delete = false );
293
294
	/**
295
	 * Globalizes a Connection
296
	 *
297
	 * @param string $connection_id Connection ID
298
	 * @return bool Falsey on failure. Truthy on success.
299
	 */
300
	abstract function globalize_connection( $connection_id );
301
302
	/**
303
	 * Unglobalizes a Connection
304
	 *
305
	 * @param string $connection_id Connection ID
306
	 * @return bool Falsey on failure. Truthy on success.
307
	 */
308
	abstract function unglobalize_connection( $connection_id );
309
310
	/**
311
	 * Returns an external URL to the Connection's profile
312
	 *
313
	 * @param string $service_name 'facebook', 'twitter', etc.
314
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
315
	 * @return false|string False on failure. URL on success.
316
	 */
317
	function get_profile_link( $service_name, $connection ) {
318
		$cmeta = $this->get_connection_meta( $connection );
319
320
		if ( isset( $cmeta['connection_data']['meta']['link'] ) ) {
321
			if ( 'facebook' == $service_name && 0 === strpos( parse_url( $cmeta['connection_data']['meta']['link'], PHP_URL_PATH ), '/app_scoped_user_id/' ) ) {
322
				// App-scoped Facebook user IDs are not usable profile links
323
				return false;
324
			}
325
326
			return $cmeta['connection_data']['meta']['link'];
327 View Code Duplication
		} elseif ( 'facebook' == $service_name && isset( $cmeta['connection_data']['meta']['facebook_page'] ) ) {
328
			return 'https://facebook.com/' . $cmeta['connection_data']['meta']['facebook_page'];
329
		} elseif ( 'tumblr' == $service_name && isset( $cmeta['connection_data']['meta']['tumblr_base_hostname'] ) ) {
330
			 return 'http://' . $cmeta['connection_data']['meta']['tumblr_base_hostname'];
331
		} elseif ( 'twitter' == $service_name ) {
332
			return 'https://twitter.com/' . substr( $cmeta['external_display'], 1 ); // Has a leading '@'
333 View Code Duplication
		} elseif ( 'google_plus' == $service_name && isset( $cmeta['connection_data']['meta']['google_plus_page'] ) ) {
334
			return 'https://plus.google.com/' . $cmeta['connection_data']['meta']['google_plus_page'];
335
		} elseif ( 'google_plus' == $service_name ) {
336
			return 'https://plus.google.com/' . $cmeta['external_id'];
337
		} else if ( 'linkedin' == $service_name ) {
338
			if ( !isset( $cmeta['connection_data']['meta']['profile_url'] ) ) {
339
				return false;
340
			}
341
342
			$profile_url_query = parse_url( $cmeta['connection_data']['meta']['profile_url'], PHP_URL_QUERY );
343
			wp_parse_str( $profile_url_query, $profile_url_query_args );
0 ignored issues
show
Bug introduced by
The variable $profile_url_query_args does not exist. Did you mean $profile_url_query?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
344
			if ( isset( $profile_url_query_args['key'] ) ) {
0 ignored issues
show
Bug introduced by
The variable $profile_url_query_args does not exist. Did you mean $profile_url_query?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
345
				$id = $profile_url_query_args['key'];
346
			} elseif ( isset( $profile_url_query_args['id'] ) ) {
0 ignored issues
show
Bug introduced by
The variable $profile_url_query_args does not exist. Did you mean $profile_url_query?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
347
				$id = $profile_url_query_args['id'];
348
			} else {
349
				return false;
350
			}
351
352
			return esc_url_raw( add_query_arg( 'id', urlencode( $id ), 'http://www.linkedin.com/profile/view' ) );
353
		} else {
354
			return false; // no fallback. we just won't link it
355
		}
356
	}
357
358
	/**
359
	 * Returns a display name for the Connection
360
	 *
361
	 * @param string $service_name 'facebook', 'twitter', etc.
362
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
363
	 * @return string
364
	 */
365
	function get_display_name( $service_name, $connection ) {
366
		$cmeta = $this->get_connection_meta( $connection );
367
368
		if ( isset( $cmeta['connection_data']['meta']['display_name'] ) ) {
369
			return $cmeta['connection_data']['meta']['display_name'];
370 View Code Duplication
		} elseif ( $service_name == 'tumblr' && isset( $cmeta['connection_data']['meta']['tumblr_base_hostname'] ) ) {
371
			 return $cmeta['connection_data']['meta']['tumblr_base_hostname'];
372
		} elseif ( $service_name == 'twitter' ) {
373
			return $cmeta['external_display'];
374
		} else {
375
			$connection_display = $cmeta['external_display'];
376
			if ( empty( $connection_display ) )
377
				$connection_display = $cmeta['external_name'];
378
			return $connection_display;
379
		}
380
	}
381
382
	/**
383
	 * Whether the user needs to select additional options after connecting
384
	 *
385
	 * @param string $service_name 'facebook', 'twitter', etc.
386
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
387
	 * @return bool
388
	 */
389
	function show_options_popup( $service_name, $connection ) {
390
		$cmeta = $this->get_connection_meta( $connection );
391
392
		// always show if no selection has been made for facebook
393
		if ( 'facebook' == $service_name && empty( $cmeta['connection_data']['meta']['facebook_profile'] ) && empty( $cmeta['connection_data']['meta']['facebook_page'] ) )
394
			return true;
395
396
		// always show if no selection has been made for tumblr
397
		if ( 'tumblr' == $service_name && empty ( $cmeta['connection_data']['meta']['tumblr_base_hostname'] ) )
398
			return true;
399
400
		// if we have the specific connection info..
401
		if ( isset( $_GET['id'] ) ) {
402
			if ( $cmeta['connection_data']['id'] == $_GET['id'] )
403
				return true;
404
		} else {
405
			// otherwise, just show if this is the completed step / first load
406
			if ( !empty( $_GET['action'] ) && 'completed' == $_GET['action'] && !empty( $_GET['service'] ) && $service_name == $_GET['service'] && ! in_array( $_GET['service'], array( 'facebook', 'tumblr' ) ) )
407
				return true;
408
		}
409
410
		return false;
411
	}
412
413
	/**
414
	 * Whether the Connection is "valid" wrt Facebook's requirements.
415
	 *
416
	 * Must be connected to a Page (not a Profile).
417
	 * (Also returns true if we're in the middle of the connection process)
418
	 *
419
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
420
	 * @return bool
421
	 */
422
	function is_valid_facebook_connection( $connection ) {
423
		if ( $this->is_connecting_connection( $connection ) ) {
424
			return true;
425
		}
426
		$connection_meta = $this->get_connection_meta( $connection );
427
		$connection_data = $connection_meta['connection_data'];
428
		return isset( $connection_data[ 'meta' ][ 'facebook_page' ] );
429
	}
430
431
	/**
432
	 * Whether the Connection currently being connected
433
	 *
434
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
435
	 * @return bool
436
	 */
437
	function is_connecting_connection( $connection ) {
438
		$connection_meta = $this->get_connection_meta( $connection );
439
		$connection_data = $connection_meta['connection_data'];
440
		return isset( $connection_data[ 'meta' ]['options_responses'] );
441
	}
442
443
	/**
444
	 * AJAX Handler to run connection tests on all Connections
445
	 * @return void
446
	 */
447
	function test_publicize_conns() {
448
		wp_send_json_success( $this->get_publicize_conns_test_results() );
449
	}
450
451
	/**
452
	 * Run connection tests on all Connections
453
	 *
454
	 * @return array {
455
	 *     Array of connection test results.
456
	 *
457
	 *     @type string 'connectionID'          Connection identifier string that is unique for each connection
458
	 *     @type string 'serviceName'           Slug of the connection's service (facebook, twitter, ...)
459
	 *     @type bool   'connectionTestPassed'  Whether the connection test was successful
460
	 *     @type string 'connectionTestMessage' Test success or error message
461
	 *     @type bool   'userCanRefresh'        Whether the user can re-authenticate their connection to the service
462
	 *     @type string 'refreshText'           Message instructing user to re-authenticate their connection to the service
463
	 *     @type string 'refreshURL'            URL, which, when visited by the user, re-authenticates their connection to the service.
464
	 *     @type string 'unique_id'             ID string representing connection
465
	 * }
466
	 */
467
	function get_publicize_conns_test_results() {
468
		$test_results = array();
469
470
		foreach ( (array) $this->get_services( 'connected' ) as $service_name => $connections ) {
471
			foreach ( $connections as $connection ) {
472
473
				$id = $this->get_connection_id( $connection );
474
475
				$connection_test_passed = true;
476
				$connection_test_message = __( 'This connection is working correctly.' , 'jetpack' );
477
				$user_can_refresh = false;
478
				$refresh_text = '';
479
				$refresh_url = '';
480
481
				$connection_test_result = true;
482
				if ( method_exists( $this, 'test_connection' ) ) {
483
					$connection_test_result = $this->test_connection( $service_name, $connection );
484
				}
485
486
				if ( is_wp_error( $connection_test_result ) ) {
487
					$connection_test_passed = false;
488
					$connection_test_message = $connection_test_result->get_error_message();
489
					$error_data = $connection_test_result->get_error_data();
490
491
					$user_can_refresh = $error_data['user_can_refresh'];
492
					$refresh_text = $error_data['refresh_text'];
493
					$refresh_url = $error_data['refresh_url'];
494
				}
495
				// Mark facebook profiles as deprecated
496
				if ( 'facebook' === $service_name ) {
497
					if ( ! $this->is_valid_facebook_connection( $connection ) ) {
498
						$connection_test_passed = false;
499
						$user_can_refresh = false;
500
						$connection_test_message = __( 'Facebook no longer supports Publicize connections to Facebook Profiles, but you can still connect Facebook Pages. Please select a Facebook Page to publish updates to.' );
501
					}
502
				}
503
504
				$unique_id = null;
505 View Code Duplication
				if ( ! empty( $connection->unique_id ) ) {
506
					$unique_id = $connection->unique_id;
507
				} else if ( ! empty( $connection['connection_data']['token_id'] ) ) {
508
					$unique_id = $connection['connection_data']['token_id'];
509
				}
510
511
				$test_results[] = array(
512
					'connectionID'          => $id,
513
					'serviceName'           => $service_name,
514
					'connectionTestPassed'  => $connection_test_passed,
515
					'connectionTestMessage' => esc_attr( $connection_test_message ),
516
					'userCanRefresh'        => $user_can_refresh,
517
					'refreshText'           => esc_attr( $refresh_text ),
518
					'refreshURL'            => $refresh_url,
519
					'unique_id'             => $unique_id,
520
				);
521
			}
522
		}
523
524
		return $test_results;
525
	}
526
527
	/**
528
	 * Run the connection test for the Connection
529
	 *
530
	 * @param string $service_name 'facebook', 'twitter', etc.
531
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
532
	 * @return WP_Error|true WP_Error on failure. True on success
533
	 */
534
	abstract function test_connection( $service_name, $connection );
535
536
	/**
537
	 * Retrieves current list of connections and applies filters.
538
	 *
539
	 * Retrieves current available connections and checks if the connections
540
	 * have already been used to share current post. Finally, the checkbox
541
	 * form UI fields are calculated. This function exposes connection form
542
	 * data directly as array so it can be retrieved for static HTML generation
543
	 * or JSON consumption.
544
	 *
545
	 * @since 6.7.0
546
	 *
547
	 * @param integer $selected_post_id Optional. Post ID to query connection status for.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $selected_post_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
548
	 *
549
	 * @return array {
550
	 *     Array of UI setup data for connection list form.
551
	 *
552
	 *     @type string 'unique_id'     ID string representing connection
553
	 *     @type string 'service_name'  Slug of the connection's service (facebook, twitter, ...)
554
	 *     @type string 'service_label' Service Label (Facebook, Twitter, ...)
555
	 *     @type string 'display_name'  Connection's human-readable Username: "@jetpack"
556
	 *     @type bool   'enabled'       Default value for the connection (e.g., for a checkbox).
557
	 *     @type bool   'done'          Has this connection already been publicized to?
558
	 *     @type bool   'toggleable'    Is the user allowed to change the value for the connection?
559
	 *     @type bool   'global'        Is this connection a global one?
560
	 * }
561
	 */
562
	public function get_filtered_connection_data( $selected_post_id = null ) {
563
		$connection_list = array();
564
565
		$post = get_post( $selected_post_id ); // Defaults to current post if $post_id is null.
566
		// Handle case where there is no current post.
567
		if ( ! empty( $post ) ) {
568
			$post_id = $post->ID;
569
		} else {
570
			$post_id = null;
571
		}
572
573
		$services = $this->get_services( 'connected' );
574
		$all_done = $this->post_is_done_sharing( $post_id );
575
576
		// We don't allow Publicizing to the same external id twice, to prevent spam.
577
		$service_id_done = (array) get_post_meta( $post_id, $this->POST_SERVICE_DONE, true );
578
579
		foreach ( $services as $service_name => $connections ) {
580
			foreach ( $connections as $connection ) {
581
				$connection_meta = $this->get_connection_meta( $connection );
582
				$connection_data = $connection_meta['connection_data'];
583
584
				$unique_id = $this->get_connection_unique_id( $connection );
585
586
587
				// Was this connection (OR, old-format service) already Publicized to?
588
				$done = ! empty( $post ) && (
589
					// New flags
590
					1 == get_post_meta( $post->ID, $this->POST_DONE . $unique_id, true )
591
					||
592
					// old flags
593
					1 == get_post_meta( $post->ID, $this->POST_DONE . $service_name, true )
594
				);
595
596
				/**
597
				 * Filter whether a post should be publicized to a given service.
598
				 *
599
				 * @module publicize
600
				 *
601
				 * @since 2.0.0
602
				 *
603
				 * @param bool true Should the post be publicized to a given service? Default to true.
604
				 * @param int $post_id Post ID.
605
				 * @param string $service_name Service name.
606
				 * @param array $connection_data Array of information about all Publicize details for the site.
607
				 */
608
				if ( ! apply_filters( 'wpas_submit_post?', true, $post_id, $service_name, $connection_data ) ) {
609
					continue;
610
				}
611
612
				// Should we be skipping this one?
613
				$skip = (
614
					(
615
						! empty( $post )
616
						&&
617
						in_array( $post->post_status, array( 'publish', 'draft', 'future' ) )
618
						&&
619
						(
620
							// New flags
621
							get_post_meta( $post->ID, $this->POST_SKIP . $unique_id, true )
622
							||
623
							// Old flags
624
							get_post_meta( $post->ID, $this->POST_SKIP . $service_name )
625
						)
626
					)
627
					||
628
					(
629
						is_array( $connection )
630
						&&
631
						isset( $connection_meta['external_id'] ) && ! empty( $service_id_done[ $service_name ][ $connection_meta['external_id'] ] )
632
					)
633
				);
634
635
				// If this one has already been publicized to, don't let it happen again.
636
				$toggleable = ! $done && ! $all_done;
637
638
				// Determine the state of the checkbox (on/off) and allow filtering.
639
				$enabled = $done || ! $skip;
640
				/**
641
				 * Filter the checkbox state of each Publicize connection appearing in the post editor.
642
				 *
643
				 * @module publicize
644
				 *
645
				 * @since 2.0.1
646
				 *
647
				 * @param bool $enabled Should the Publicize checkbox be enabled for a given service.
648
				 * @param int $post_id Post ID.
649
				 * @param string $service_name Service name.
650
				 * @param array $connection Array of connection details.
651
				 */
652
				$enabled = apply_filters( 'publicize_checkbox_default', $enabled, $post_id, $service_name, $connection );
653
654
				/**
655
				 * If this is a global connection and this user doesn't have enough permissions to modify
656
				 * those connections, don't let them change it.
657
				 */
658
				if ( ! $done && ( 0 == $connection_data['user_id'] && ! current_user_can( $this->GLOBAL_CAP ) ) ) {
659
					$toggleable = false;
660
661
					/**
662
					 * Filters the checkboxes for global connections with non-prilvedged users.
663
					 *
664
					 * @module publicize
665
					 *
666
					 * @since 3.7.0
667
					 *
668
					 * @param bool   $enabled Indicates if this connection should be enabled. Default true.
669
					 * @param int    $post_id ID of the current post
670
					 * @param string $service_name Name of the connection (Facebook, Twitter, etc)
671
					 * @param array  $connection Array of data about the connection.
672
					 */
673
					$enabled = apply_filters( 'publicize_checkbox_global_default', $enabled, $post_id, $service_name, $connection );
674
				}
675
676
				// Force the checkbox to be checked if the post was DONE, regardless of what the filter does.
677
				if ( $done ) {
678
					$enabled = true;
679
				}
680
681
				$connection_list[] = array(
682
					'unique_id'     => $unique_id,
683
					'service_name'  => $service_name,
684
					'service_label' => $this->get_service_label( $service_name ),
685
					'display_name'  => $this->get_display_name( $service_name, $connection ),
686
687
					'enabled'      => $enabled,
688
					'done'         => $done,
689
					'toggleable'   => $toggleable,
690
					'global'       => 0 == $connection_data['user_id'],
691
				);
692
			}
693
		}
694
695
		return $connection_list;
696
	}
697
698
	/**
699
	 * Checks if post has already been shared by Publicize in the past.
700
	 *
701
	 * @since 6.7.0
702
	 *
703
	 * @param integer $post_id Optional. Post ID to query connection status for: will use current post if missing.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $post_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
704
	 *
705
	 * @return bool True if post has already been shared by Publicize, false otherwise.
706
	 */
707
	abstract public function post_is_done_sharing( $post_id = null );
708
709
	/**
710
	 * Retrieves full list of available Publicize connection services.
711
	 *
712
	 * Retrieves current available publicize service connections
713
	 * with associated labels and URLs.
714
	 *
715
	 * @since 6.7.0
716
	 *
717
	 * @return array {
718
	 *     Array of UI service connection data for all services
719
	 *
720
	 *     @type string 'name'  Name of service.
721
	 *     @type string 'label' Display label for service.
722
	 *     @type string 'url'   URL for adding connection to service.
723
	 * }
724
	 */
725
	function get_available_service_data() {
726
		$available_services     = $this->get_services( 'all' );
727
		$available_service_data = array();
728
729
		foreach ( $available_services as $service_name => $service ) {
730
			$available_service_data[] = array(
731
				'name'  => $service_name,
732
				'label' => $this->get_service_label( $service_name ),
733
				'url'   => $this->connect_url( $service_name ),
734
			);
735
		}
736
737
		return $available_service_data;
738
	}
739
740
/*
741
 * Site Data
742
 */
743
744
	function user_id() {
745
		return get_current_user_id();
746
	}
747
748
	function blog_id() {
749
		return get_current_blog_id();
750
	}
751
752
/*
753
 * Posts
754
 */
755
756
	/**
757
	 * Checks old and new status to see if the post should be flagged as
758
	 * ready to Publicize.
759
	 *
760
	 * Attached to the `transition_post_status` filter.
761
	 *
762
	 * @param string $new_status
763
	 * @param string $old_status
764
	 * @param WP_Post $post
765
	 * @return void
766
	 */
767
	abstract function flag_post_for_publicize( $new_status, $old_status, $post );
768
769
	/**
770
	 * Ensures the Post internal post-type supports `publicize`
771
	 *
772
	 * This feature support flag is used by the REST API.
773
	 */
774
	function add_post_type_support() {
775
		add_post_type_support( 'post', 'publicize' );
776
	}
777
778
	/**
779
	 * Can the current user access Publicize Data.
780
	 *
781
	 * @param int $post_id. 0 for general access. Post_ID for specific access.
0 ignored issues
show
Documentation introduced by
There is no parameter named $post_id.. Did you maybe mean $post_id?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
782
	 * @return bool
783
	 */
784
	function current_user_can_access_publicize_data( $post_id = 0 ) {
785
		/**
786
		 * Filter what user capability is required to use the publicize form on the edit post page. Useful if publish post capability has been removed from role.
787
		 *
788
		 * @module publicize
789
		 *
790
		 * @since 4.1.0
791
		 *
792
		 * @param string $capability User capability needed to use publicize
793
		 */
794
		$capability = apply_filters( 'jetpack_publicize_capability', 'publish_posts' );
795
796
		if ( 'publish_posts' === $capability && $post_id ) {
797
			return current_user_can( 'publish_post', $post_id );
798
		}
799
800
		return current_user_can( $capability );
801
	}
802
803
	/**
804
	 * Auth callback for the protected ->POST_MESS post_meta
805
	 *
806
	 * @param bool $allowed
807
	 * @param string $meta_key
808
	 * @param int $object_id Post ID
809
	 * @return bool
810
	 */
811
	function message_meta_auth_callback( $allowed, $meta_key, $object_id ) {
812
		return $this->current_user_can_access_publicize_data( $object_id );
813
	}
814
815
	/**
816
	 * Registers the ->POST_MESS post_meta for use in the REST API.
817
	 *
818
	 * Registers for each post type that with `publicize` feature support.
819
	 */
820
	function register_post_meta() {
821
		$args = array(
822
			'type' => 'string',
823
			'description' => __( 'The message to use instead of the title when sharing to Publicize Services', 'jetpack' ),
824
			'single' => true,
825
			'default' => '',
826
			'show_in_rest' => array(
827
				'name' => 'jetpack_publicize_message'
828
			),
829
			'auth_callback' => array( $this, 'message_meta_auth_callback' ),
830
		);
831
832
		foreach ( get_post_types() as $post_type ) {
833
			if ( ! $this->post_type_is_publicizeable( $post_type ) ) {
834
				continue;
835
			}
836
837
			$args['object_subtype'] = $post_type;
838
839
			register_meta( 'post', $this->POST_MESS, $args );
840
		}
841
	}
842
843
	/**
844
	 * Fires when a post is saved, checks conditions and saves state in postmeta so that it
845
	 * can be picked up later by @see ::publicize_post() on WordPress.com codebase.
846
	 *
847
	 * Attached to the `save_post` action.
848
	 *
849
	 * @param int $post_id
850
	 * @param WP_Post $post
851
	 * @return void
852
	 */
853
	function save_meta( $post_id, $post ) {
854
		$cron_user = null;
855
		$submit_post = true;
856
857
		if ( ! $this->post_type_is_publicizeable( $post->post_type ) )
858
			return;
859
860
		// Don't Publicize during certain contexts:
861
862
		// - import
863
		if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING  ) {
864
			$submit_post = false;
865
		}
866
867
		// - on quick edit, autosave, etc but do fire on p2, quickpress, and instapost ajax
868
		if (
869
			defined( 'DOING_AJAX' )
870
		&&
871
			DOING_AJAX
872
		&&
873
			!did_action( 'p2_ajax' )
874
		&&
875
			!did_action( 'wp_ajax_json_quickpress_post' )
876
		&&
877
			!did_action( 'wp_ajax_instapost_publish' )
878
		&&
879
			!did_action( 'wp_ajax_post_reblog' )
880
		&&
881
			!did_action( 'wp_ajax_press-this-save-post' )
882
		) {
883
			$submit_post = false;
884
		}
885
886
		// - bulk edit
887
		if ( isset( $_GET['bulk_edit'] ) ) {
888
			$submit_post = false;
889
		}
890
891
		// - API/XML-RPC Test Posts
892
		if (
893
			(
894
				defined( 'XMLRPC_REQUEST' )
895
			&&
896
				XMLRPC_REQUEST
897
			||
898
				defined( 'APP_REQUEST' )
899
			&&
900
				APP_REQUEST
901
			)
902
		&&
903
			0 === strpos( $post->post_title, 'Temporary Post Used For Theme Detection' )
904
		) {
905
			$submit_post = false;
906
		}
907
908
		// only work with certain statuses (avoids inherits, auto drafts etc)
909
		if ( !in_array( $post->post_status, array( 'publish', 'draft', 'future' ) ) ) {
910
			$submit_post = false;
911
		}
912
913
		// don't publish password protected posts
914
		if ( '' !== $post->post_password ) {
915
			$submit_post = false;
916
		}
917
918
		// Did this request happen via wp-admin?
919
		$from_web = isset( $_SERVER['REQUEST_METHOD'] )
920
			&&
921
			'post' == strtolower( $_SERVER['REQUEST_METHOD'] )
922
			&&
923
			isset( $_POST[$this->ADMIN_PAGE] );
924
925
		if ( ( $from_web || defined( 'POST_BY_EMAIL' ) ) && isset( $_POST['wpas_title'] ) ) {
926
			if ( empty( $_POST['wpas_title'] ) ) {
927
				delete_post_meta( $post_id, $this->POST_MESS );
928
			} else {
929
				update_post_meta( $post_id, $this->POST_MESS, trim( stripslashes( $_POST['wpas_title'] ) ) );
930
			}
931
		}
932
933
		// change current user to provide context for get_services() if we're running during cron
934
		if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
935
			$cron_user = (int) $GLOBALS['user_ID'];
936
			wp_set_current_user( $post->post_author );
937
		}
938
939
		/**
940
		 * In this phase, we mark connections that we want to SKIP. When Publicize is actually triggered,
941
		 * it will Publicize to everything *except* those marked for skipping.
942
		 */
943
		foreach ( (array) $this->get_services( 'connected' ) as $service_name => $connections ) {
944
			foreach ( $connections as $connection ) {
945
				$connection_data = '';
946
				if ( method_exists( $connection, 'get_meta' ) )
947
					$connection_data = $connection->get_meta( 'connection_data' );
948
				elseif ( ! empty( $connection['connection_data'] ) )
949
					$connection_data = $connection['connection_data'];
950
951
				/** This action is documented in modules/publicize/ui.php */
952
				if ( false == apply_filters( 'wpas_submit_post?', $submit_post, $post_id, $service_name, $connection_data ) ) {
953
					delete_post_meta( $post_id, $this->PENDING );
954
					continue;
955
				}
956
957 View Code Duplication
				if ( !empty( $connection->unique_id ) )
958
					$unique_id = $connection->unique_id;
959
				else if ( !empty( $connection['connection_data']['token_id'] ) )
960
					$unique_id = $connection['connection_data']['token_id'];
961
962
				// This was a wp-admin request, so we need to check the state of checkboxes
963
				if ( $from_web ) {
964
					// delete stray service-based post meta
965
					delete_post_meta( $post_id, $this->POST_SKIP . $service_name );
966
967
					// We *unchecked* this stream from the admin page, or it's set to readonly, or it's a new addition
968
					if ( empty( $_POST[$this->ADMIN_PAGE]['submit'][$unique_id] ) ) {
969
						// Also make sure that the service-specific input isn't there.
970
						// If the user connected to a new service 'in-page' then a hidden field with the service
971
						// name is added, so we just assume they wanted to Publicize to that service.
972
						if ( empty( $_POST[$this->ADMIN_PAGE]['submit'][$service_name] ) ) {
973
							// Nothing seems to be checked, so we're going to mark this one to be skipped
974
							update_post_meta( $post_id, $this->POST_SKIP . $unique_id, 1 );
0 ignored issues
show
Bug introduced by
The variable $unique_id does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
975
							continue;
976
						} else {
977
							// clean up any stray post meta
978
							delete_post_meta( $post_id, $this->POST_SKIP . $unique_id );
979
						}
980
					} else {
981
						// The checkbox for this connection is explicitly checked -- make sure we DON'T skip it
982
						delete_post_meta( $post_id, $this->POST_SKIP . $unique_id );
983
					}
984
				}
985
986
				/**
987
				 * Fires right before the post is processed for Publicize.
988
				 * Users may hook in here and do anything else they need to after meta is written,
989
				 * and before the post is processed for Publicize.
990
				 *
991
				 * @since 2.1.2
992
				 *
993
				 * @param bool $submit_post Should the post be publicized.
994
				 * @param int $post->ID Post ID.
995
				 * @param string $service_name Service name.
996
				 * @param array $connection Array of connection details.
997
				 */
998
				do_action( 'publicize_save_meta', $submit_post, $post_id, $service_name, $connection );
999
			}
1000
		}
1001
1002
		if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
1003
			wp_set_current_user( $cron_user );
1004
		}
1005
1006
		// Next up will be ::publicize_post()
1007
	}
1008
1009
	/**
1010
	 * Alters the "Post Published" message to include information about where the post
1011
	 * was Publicized to.
1012
	 *
1013
	 * Attached to the `post_updated_messages` filter
1014
	 *
1015
	 * @param string[] $messages
1016
	 * @return string[]
1017
	 */
1018
	public function update_published_message( $messages ) {
1019
		global $post_type, $post_type_object, $post;
1020
		if ( ! $this->post_type_is_publicizeable( $post_type ) ) {
1021
			return $messages;
1022
		}
1023
		$view_post_link_html = '';
1024
		$viewable = is_post_type_viewable( $post_type_object );
1025
		if ( $viewable ) {
1026
			$view_text = esc_html__( 'View post' ); // intentionally omitted domain
1027
1028
			if ( 'jetpack-portfolio' == $post_type ) {
1029
				$view_text = esc_html__( 'View project', 'jetpack' );
1030
			}
1031
1032
			$view_post_link_html = sprintf( ' <a href="%1$s">%2$s</a>',
1033
				esc_url( get_permalink( $post ) ),
1034
				$view_text
1035
			);
1036
		}
1037
1038
		$services = $this->get_publicizing_services( $post->ID );
1039
		if ( empty( $services ) ) {
1040
			return $messages;
1041
		}
1042
1043
		$labels = array();
1044
		foreach ( $services as $service_name => $display_names ) {
1045
			$labels[] = sprintf(
1046
				/* translators: Service name is %1$s, and account name is %2$s. */
1047
				esc_html__( '%1$s (%2$s)', 'jetpack' ),
1048
				esc_html( $service_name ),
1049
				esc_html( implode( ', ', $display_names ) )
1050
			);
1051
		}
1052
1053
		$messages['post'][6] = sprintf(
1054
			/* translators: %1$s is a comma-separated list of services and accounts. Ex. Facebook (@jetpack), Twitter (@jetpack) */
1055
			esc_html__( 'Post published and sharing on %1$s.', 'jetpack' ),
1056
			implode( ', ', $labels )
1057
		) . $view_post_link_html;
1058
1059
		if ( $post_type == 'post' && class_exists('Jetpack_Subscriptions' ) ) {
1060
			$subscription = Jetpack_Subscriptions::init();
1061
			if ( $subscription->should_email_post_to_subscribers( $post ) ) {
1062
				$messages['post'][6] = sprintf(
1063
					/* translators: %1$s is a comma-separated list of services and accounts. Ex. Facebook (@jetpack), Twitter (@jetpack) */
1064
					esc_html__( 'Post published, sending emails to subscribers and sharing post on %1$s.', 'jetpack' ),
1065
					implode( ', ', $labels )
1066
				) . $view_post_link_html;
1067
			}
1068
		}
1069
1070
		$messages['jetpack-portfolio'][6] = sprintf(
1071
			/* translators: %1$s is a comma-separated list of services and accounts. Ex. Facebook (@jetpack), Twitter (@jetpack) */
1072
			esc_html__( 'Project published and sharing project on %1$s.', 'jetpack' ),
1073
			implode( ', ', $labels )
1074
		) . $view_post_link_html;
1075
1076
		return $messages;
1077
	}
1078
1079
	/**
1080
	 * Get the Connections the Post was just Publicized to.
1081
	 *
1082
	 * Only reliable just after the Post was published.
1083
	 *
1084
	 * @param int $post_id
1085
	 * @return string[] Array of Service display name => Connection display name
1086
	 */
1087
	function get_publicizing_services( $post_id ) {
1088
		$services = array();
1089
1090
		foreach ( (array) $this->get_services( 'connected' ) as $service_name => $connections ) {
1091
			// services have multiple connections.
1092
			foreach ( $connections as $connection ) {
1093
				$unique_id = '';
1094 View Code Duplication
				if ( ! empty( $connection->unique_id ) )
1095
					$unique_id = $connection->unique_id;
1096
				else if ( ! empty( $connection['connection_data']['token_id'] ) )
1097
					$unique_id = $connection['connection_data']['token_id'];
1098
1099
				// Did we skip this connection?
1100
				if ( get_post_meta( $post_id, $this->POST_SKIP . $unique_id,  true ) ) {
1101
					continue;
1102
				}
1103
				$services[ $this->get_service_label( $service_name ) ][] = $this->get_display_name( $service_name, $connection );
1104
			}
1105
		}
1106
1107
		return $services;
1108
	}
1109
1110
	/**
1111
	 * Is the post Publicize-able?
1112
	 *
1113
	 * Only valid prior to Publicizing a Post.
1114
	 *
1115
	 * @param WP_Post $post
1116
	 * @return bool
1117
	 */
1118
	function post_is_publicizeable( $post ) {
1119
		if ( ! $this->post_type_is_publicizeable( $post->post_type ) )
1120
			return false;
1121
1122
		// This is more a precaution. To only publicize posts that are published. (Mostly relevant for Jetpack sites)
1123
		if ( 'publish' !== $post->post_status ) {
1124
			return false;
1125
		}
1126
1127
		// If it's not flagged as ready, then abort. @see ::flag_post_for_publicize()
1128
		if ( ! get_post_meta( $post->ID, $this->PENDING, true ) )
1129
			return false;
1130
1131
		return true;
1132
	}
1133
1134
	/**
1135
	 * Is a given post type Publicize-able?
1136
	 *
1137
	 * Not every CPT lends itself to Publicize-ation.  Allow CPTs to register by adding their CPT via
1138
	 * the publicize_post_types array filter.
1139
	 *
1140
	 * @param string $post_type The post type to check.
1141
	 * @return bool True if the post type can be Publicized.
1142
	 */
1143
	function post_type_is_publicizeable( $post_type ) {
1144
		if ( 'post' == $post_type )
1145
			return true;
1146
1147
		return post_type_supports( $post_type, 'publicize' );
1148
	}
1149
1150
	/**
1151
	 * Already-published posts should not be Publicized by default. This filter sets checked to
1152
	 * false if a post has already been published.
1153
	 *
1154
	 * Attached to the `publicize_checkbox_default` filter
1155
	 *
1156
	 * @param bool $checked
1157
	 * @param int $post_id
1158
	 * @param string $service_name 'facebook', 'twitter', etc
1159
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
1160
	 * @return bool
1161
	 */
1162
	function publicize_checkbox_default( $checked, $post_id, $service_name, $connection ) {
1163
		if ( 'publish' == get_post_status( $post_id ) ) {
1164
			return false;
1165
		}
1166
1167
		return $checked;
1168
	}
1169
1170
/*
1171
 * Util
1172
 */
1173
1174
	/**
1175
	 * Converts a Publicize message template string into a sprintf format string
1176
	 *
1177
	 * @param string[] $args
1178
	 *               0 - The Publicize message template: 'Check out my post: %title% @ %url'
1179
	 *             ... - The template tags 'title', 'url', etc.
1180
	 * @return string
1181
	 */
1182
	protected static function build_sprintf( $args ) {
1183
		$search = array();
1184
		$replace = array();
1185
		foreach ( $args as $k => $arg ) {
1186
			if ( 0 == $k ) {
1187
				$string = $arg;
1188
				continue;
1189
			}
1190
			$search[] = "%$arg%";
1191
			$replace[] = "%$k\$s";
1192
		}
1193
		return str_replace( $search, $replace, $string );
0 ignored issues
show
Bug introduced by
The variable $string does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1194
	}
1195
}
1196
1197
function publicize_calypso_url() {
1198
	$calypso_sharing_url = 'https://wordpress.com/sharing/';
1199
	if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'build_raw_urls' ) ) {
1200
		$site_suffix = Jetpack::build_raw_urls( home_url() );
1201
	} elseif ( class_exists( 'WPCOM_Masterbar' ) && method_exists( 'WPCOM_Masterbar', 'get_calypso_site_slug' ) ) {
1202
		$site_suffix = WPCOM_Masterbar::get_calypso_site_slug( get_current_blog_id() );
1203
	}
1204
1205
	if ( $site_suffix ) {
1206
		return $calypso_sharing_url . $site_suffix;
0 ignored issues
show
Bug introduced by
The variable $site_suffix does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1207
	} else {
1208
		return $calypso_sharing_url;
1209
	}
1210
}
1211