Completed
Push — instant-search-master ( 9522da...404687 )
by
unknown
07:50 queued 10s
created

Publicize_Base::get_service_label()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
nc 7
nop 1
dl 0
loc 17
rs 8.8333
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 = 'publish_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
		add_action( 'jetpack_register_gutenberg_extensions', array( $this, 'register_gutenberg_extension' ) );
123
	}
124
125
/*
126
 * Services: Facebook, Twitter, etc.
127
 */
128
129
	/**
130
	 * Get services for the given blog and user.
131
	 *
132
	 * Can return all available services or just the ones with an active connection.
133
	 *
134
	 * @param string $filter
135
	 *        'all' (default) - Get all services available for connecting
136
	 *        'connected'     - Get all services currently connected
137
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
138
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
139
	 * @return array
140
	 */
141
	abstract function get_services( $filter = 'all', $_blog_id = false, $_user_id = false );
142
143
	function can_connect_service( $service_name ) {
144
		return true;
145
	}
146
147
	/**
148
	 * Does the given user have a connection to the service on the given blog?
149
	 *
150
	 * @param string $service_name 'facebook', 'twitter', etc.
151
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
152
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
153
	 * @return bool
154
	 */
155
	function is_enabled( $service_name, $_blog_id = false, $_user_id = false ) {
156
		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...
157
			$_blog_id = $this->blog_id();
158
159
		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...
160
			$_user_id = $this->user_id();
161
162
		$connections = $this->get_connections( $service_name, $_blog_id, $_user_id );
163
		return ( is_array( $connections ) && count( $connections ) > 0 ? true : false );
164
	}
165
166
	/**
167
	 * Generates a connection URL.
168
	 *
169
	 * This is the URL, which, when visited by the user, starts the authentication
170
	 * process required to forge a connection.
171
	 *
172
	 * @param string $service_name 'facebook', 'twitter', etc.
173
	 * @return string
174
	 */
175
	abstract function connect_url( $service_name );
176
177
	/**
178
	 * Generates a Connection refresh URL.
179
	 *
180
	 * This is the URL, which, when visited by the user, re-authenticates their
181
	 * connection to the service.
182
	 *
183
	 * @param string $service_name 'facebook', 'twitter', etc.
184
	 * @return string
185
	 */
186
	abstract function refresh_url( $service_name );
187
188
	/**
189
	 * Generates a disconnection URL.
190
	 *
191
	 * This is the URL, which, when visited by the user, breaks their connection
192
	 * with the service.
193
	 *
194
	 * @param string $service_name 'facebook', 'twitter', etc.
195
	 * @param string $connection_id Connection ID
196
	 * @return string
197
	 */
198
	abstract function disconnect_url( $service_name, $connection_id );
199
200
	/**
201
	 * Returns a display name for the Service
202
	 *
203
	 * @param string $service_name 'facebook', 'twitter', etc.
204
	 * @return string
205
	 */
206
	public static function get_service_label( $service_name ) {
207
		switch ( $service_name ) {
208
			case 'linkedin':
209
				return 'LinkedIn';
210
				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...
211
			case 'google_drive': // google-drive used to be called google_drive.
212
			case 'google-drive':
213
				return 'Google Drive';
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
			case 'twitter':
216
			case 'facebook':
217
			case 'tumblr':
218
			default:
219
				return ucfirst( $service_name );
220
				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...
221
		}
222
	}
223
224
/*
225
 * Connections: For each Service, there can be multiple connections
226
 * for a given user. For example, one user could be connected to Twitter
227
 * as both @jetpack and as @wordpressdotcom
228
 *
229
 * For historical reasons, Connections are represented as an object
230
 * on WordPress.com and as an array in Jetpack.
231
 */
232
233
	/**
234
	 * Get the active Connections of a Service
235
	 *
236
	 * @param string $service_name 'facebook', 'twitter', etc.
237
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
238
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
239
	 * @return false|object[]|array[] false if no connections exist
240
	 */
241
	abstract function get_connections( $service_name, $_blog_id = false, $_user_id = false );
242
243
	/**
244
	 * Get a single Connection of a Service
245
	 *
246
	 * @param string $service_name 'facebook', 'twitter', etc.
247
	 * @param string $connection_id Connection ID
248
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
249
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
250
	 * @return false|object[]|array[] false if no connections exist
251
	 */
252
	abstract function get_connection( $service_name, $connection_id, $_blog_id = false, $_user_id = false );
253
254
	/**
255
	 * Get the Connection ID.
256
	 *
257
	 * Note that this is different than the Connection's uniqueid.
258
	 *
259
	 * Via a quirk of history, ID is globally unique and unique_id
260
	 * is only unique per site.
261
	 *
262
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
263
	 * @return string
264
	 */
265
	abstract function get_connection_id( $connection );
266
267
	/**
268
	 * Get the Connection unique_id
269
	 *
270
	 * Note that this is different than the Connections ID.
271
	 *
272
	 * Via a quirk of history, ID is globally unique and unique_id
273
	 * is only unique per site.
274
	 *
275
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
276
	 * @return string
277
	 */
278
	abstract function get_connection_unique_id( $connection );
279
280
	/**
281
	 * Get the Connection's Meta data
282
	 *
283
	 * @param object|array Connection
284
	 * @return array Connection Meta
285
	 */
286
	abstract function get_connection_meta( $connection );
287
288
	/**
289
	 * Disconnect a Connection
290
	 *
291
	 * @param string $service_name 'facebook', 'twitter', etc.
292
	 * @param string $connection_id Connection ID
293
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
294
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
295
	 * @param bool $force_delete Whether to skip permissions checks
296
	 * @return false|void False on failure. Void on success.
297
	 */
298
	abstract function disconnect( $service_name, $connection_id, $_blog_id = false, $_user_id = false, $force_delete = false );
299
300
	/**
301
	 * Globalizes a Connection
302
	 *
303
	 * @param string $connection_id Connection ID
304
	 * @return bool Falsey on failure. Truthy on success.
305
	 */
306
	abstract function globalize_connection( $connection_id );
307
308
	/**
309
	 * Unglobalizes a Connection
310
	 *
311
	 * @param string $connection_id Connection ID
312
	 * @return bool Falsey on failure. Truthy on success.
313
	 */
314
	abstract function unglobalize_connection( $connection_id );
315
316
	/**
317
	 * Returns an external URL to the Connection's profile
318
	 *
319
	 * @param string $service_name 'facebook', 'twitter', etc.
320
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
321
	 * @return false|string False on failure. URL on success.
322
	 */
323
	function get_profile_link( $service_name, $connection ) {
324
		$cmeta = $this->get_connection_meta( $connection );
325
326
		if ( isset( $cmeta['connection_data']['meta']['link'] ) ) {
327
			if ( 'facebook' == $service_name && 0 === strpos( wp_parse_url( $cmeta['connection_data']['meta']['link'], PHP_URL_PATH ), '/app_scoped_user_id/' ) ) {
328
				// App-scoped Facebook user IDs are not usable profile links
329
				return false;
330
			}
331
332
			return $cmeta['connection_data']['meta']['link'];
333 View Code Duplication
		} elseif ( 'facebook' == $service_name && isset( $cmeta['connection_data']['meta']['facebook_page'] ) ) {
334
			return 'https://facebook.com/' . $cmeta['connection_data']['meta']['facebook_page'];
335
		} elseif ( 'tumblr' == $service_name && isset( $cmeta['connection_data']['meta']['tumblr_base_hostname'] ) ) {
336
			 return 'https://' . $cmeta['connection_data']['meta']['tumblr_base_hostname'];
337
		} elseif ( 'twitter' == $service_name ) {
338
			return 'https://twitter.com/' . substr( $cmeta['external_display'], 1 ); // Has a leading '@'
339
		} else if ( 'linkedin' == $service_name ) {
340
			if ( !isset( $cmeta['connection_data']['meta']['profile_url'] ) ) {
341
				return false;
342
			}
343
344
			$profile_url_query = wp_parse_url( $cmeta['connection_data']['meta']['profile_url'], PHP_URL_QUERY );
345
			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...
346
			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...
347
				$id = $profile_url_query_args['key'];
348
			} 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...
349
				$id = $profile_url_query_args['id'];
350
			} else {
351
				return false;
352
			}
353
354
			return esc_url_raw( add_query_arg( 'id', urlencode( $id ), 'https://www.linkedin.com/profile/view' ) );
355
		} else {
356
			return false; // no fallback. we just won't link it
357
		}
358
	}
359
360
	/**
361
	 * Returns a display name for the Connection
362
	 *
363
	 * @param string $service_name 'facebook', 'twitter', etc.
364
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
365
	 * @return string
366
	 */
367
	function get_display_name( $service_name, $connection ) {
368
		$cmeta = $this->get_connection_meta( $connection );
369
370
		if ( isset( $cmeta['connection_data']['meta']['display_name'] ) ) {
371
			return $cmeta['connection_data']['meta']['display_name'];
372 View Code Duplication
		} elseif ( $service_name == 'tumblr' && isset( $cmeta['connection_data']['meta']['tumblr_base_hostname'] ) ) {
373
			 return $cmeta['connection_data']['meta']['tumblr_base_hostname'];
374
		} elseif ( $service_name == 'twitter' ) {
375
			return $cmeta['external_display'];
376
		} else {
377
			$connection_display = $cmeta['external_display'];
378
			if ( empty( $connection_display ) )
379
				$connection_display = $cmeta['external_name'];
380
			return $connection_display;
381
		}
382
	}
383
384
	/**
385
	 * Whether the user needs to select additional options after connecting
386
	 *
387
	 * @param string $service_name 'facebook', 'twitter', etc.
388
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
389
	 * @return bool
390
	 */
391
	function show_options_popup( $service_name, $connection ) {
392
		$cmeta = $this->get_connection_meta( $connection );
393
394
		// always show if no selection has been made for facebook
395
		if ( 'facebook' == $service_name && empty( $cmeta['connection_data']['meta']['facebook_profile'] ) && empty( $cmeta['connection_data']['meta']['facebook_page'] ) )
396
			return true;
397
398
		// always show if no selection has been made for tumblr
399
		if ( 'tumblr' == $service_name && empty ( $cmeta['connection_data']['meta']['tumblr_base_hostname'] ) )
400
			return true;
401
402
		// if we have the specific connection info..
403
		if ( isset( $_GET['id'] ) ) {
404
			if ( $cmeta['connection_data']['id'] == $_GET['id'] )
405
				return true;
406
		} else {
407
			// otherwise, just show if this is the completed step / first load
408
			if ( !empty( $_GET['action'] ) && 'completed' == $_GET['action'] && !empty( $_GET['service'] ) && $service_name == $_GET['service'] && ! in_array( $_GET['service'], array( 'facebook', 'tumblr' ) ) )
409
				return true;
410
		}
411
412
		return false;
413
	}
414
415
	/**
416
	 * Whether the Connection is "valid" wrt Facebook's requirements.
417
	 *
418
	 * Must be connected to a Page (not a Profile).
419
	 * (Also returns true if we're in the middle of the connection process)
420
	 *
421
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
422
	 * @return bool
423
	 */
424
	function is_valid_facebook_connection( $connection ) {
425
		if ( $this->is_connecting_connection( $connection ) ) {
426
			return true;
427
		}
428
		$connection_meta = $this->get_connection_meta( $connection );
429
		$connection_data = $connection_meta['connection_data'];
430
		return isset( $connection_data[ 'meta' ][ 'facebook_page' ] );
431
	}
432
433
	/**
434
	 * LinkedIn needs to be reauthenticated to use v2 of their API.
435
	 * If it's using LinkedIn old API, it's an 'invalid' connection
436
	 *
437
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
438
	 * @return bool
439
	 */
440
	function is_invalid_linkedin_connection( $connection ) {
441
		// LinkedIn API v1 included the profile link in the connection data.
442
		$connection_meta = $this->get_connection_meta( $connection );
443
		return isset( $connection_meta['connection_data']['meta']['profile_url'] );
444
	}
445
446
	/**
447
	 * Whether the Connection currently being connected
448
	 *
449
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
450
	 * @return bool
451
	 */
452
	function is_connecting_connection( $connection ) {
453
		$connection_meta = $this->get_connection_meta( $connection );
454
		$connection_data = $connection_meta['connection_data'];
455
		return isset( $connection_data[ 'meta' ]['options_responses'] );
456
	}
457
458
	/**
459
	 * AJAX Handler to run connection tests on all Connections
460
	 * @return void
461
	 */
462
	function test_publicize_conns() {
463
		wp_send_json_success( $this->get_publicize_conns_test_results() );
464
	}
465
466
	/**
467
	 * Run connection tests on all Connections
468
	 *
469
	 * @return array {
470
	 *     Array of connection test results.
471
	 *
472
	 *     @type string 'connectionID'          Connection identifier string that is unique for each connection
473
	 *     @type string 'serviceName'           Slug of the connection's service (facebook, twitter, ...)
474
	 *     @type bool   'connectionTestPassed'  Whether the connection test was successful
475
	 *     @type string 'connectionTestMessage' Test success or error message
476
	 *     @type bool   'userCanRefresh'        Whether the user can re-authenticate their connection to the service
477
	 *     @type string 'refreshText'           Message instructing user to re-authenticate their connection to the service
478
	 *     @type string 'refreshURL'            URL, which, when visited by the user, re-authenticates their connection to the service.
479
	 *     @type string 'unique_id'             ID string representing connection
480
	 * }
481
	 */
482
	function get_publicize_conns_test_results() {
483
		$test_results = array();
484
485
		foreach ( (array) $this->get_services( 'connected' ) as $service_name => $connections ) {
486
			foreach ( $connections as $connection ) {
487
488
				$id = $this->get_connection_id( $connection );
489
490
				$connection_test_passed = true;
491
				$connection_test_message = __( 'This connection is working correctly.' , 'jetpack' );
492
				$user_can_refresh = false;
493
				$refresh_text = '';
494
				$refresh_url = '';
495
496
				$connection_test_result = true;
497
				if ( method_exists( $this, 'test_connection' ) ) {
498
					$connection_test_result = $this->test_connection( $service_name, $connection );
499
				}
500
501
				if ( is_wp_error( $connection_test_result ) ) {
502
					$connection_test_passed = false;
503
					$connection_test_message = $connection_test_result->get_error_message();
0 ignored issues
show
Bug introduced by
The method get_error_message() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
504
					$error_data = $connection_test_result->get_error_data();
0 ignored issues
show
Bug introduced by
The method get_error_data() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
505
506
					$user_can_refresh = $error_data['user_can_refresh'];
507
					$refresh_text = $error_data['refresh_text'];
508
					$refresh_url = $error_data['refresh_url'];
509
				}
510
				// Mark facebook profiles as deprecated
511
				if ( 'facebook' === $service_name ) {
512
					if ( ! $this->is_valid_facebook_connection( $connection ) ) {
513
						$connection_test_passed = false;
514
						$user_can_refresh = false;
515
						$connection_test_message = __( 'Please select a Facebook Page to publish updates.', 'jetpack' );
516
					}
517
				}
518
519
				// LinkedIn needs reauthentication to be compatible with v2 of their API
520
				if ( 'linkedin' === $service_name && $this->is_invalid_linkedin_connection( $connection ) ) {
521
					$connection_test_passed = 'must_reauth';
522
					$user_can_refresh = false;
523
					$connection_test_message = esc_html__( 'Your LinkedIn connection needs to be reauthenticated to continue working – head to Sharing to take care of it.', 'jetpack' );
524
				}
525
526
				$unique_id = null;
527 View Code Duplication
				if ( ! empty( $connection->unique_id ) ) {
528
					$unique_id = $connection->unique_id;
529
				} else if ( ! empty( $connection['connection_data']['token_id'] ) ) {
530
					$unique_id = $connection['connection_data']['token_id'];
531
				}
532
533
				$test_results[] = array(
534
					'connectionID'          => $id,
535
					'serviceName'           => $service_name,
536
					'connectionTestPassed'  => $connection_test_passed,
537
					'connectionTestMessage' => esc_attr( $connection_test_message ),
538
					'userCanRefresh'        => $user_can_refresh,
539
					'refreshText'           => esc_attr( $refresh_text ),
540
					'refreshURL'            => $refresh_url,
541
					'unique_id'             => $unique_id,
542
				);
543
			}
544
		}
545
546
		return $test_results;
547
	}
548
549
	/**
550
	 * Run the connection test for the Connection
551
	 *
552
	 * @param string $service_name 'facebook', 'twitter', etc.
553
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
554
	 * @return WP_Error|true WP_Error on failure. True on success
555
	 */
556
	abstract function test_connection( $service_name, $connection );
557
558
	/**
559
	 * Retrieves current list of connections and applies filters.
560
	 *
561
	 * Retrieves current available connections and checks if the connections
562
	 * have already been used to share current post. Finally, the checkbox
563
	 * form UI fields are calculated. This function exposes connection form
564
	 * data directly as array so it can be retrieved for static HTML generation
565
	 * or JSON consumption.
566
	 *
567
	 * @since 6.7.0
568
	 *
569
	 * @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...
570
	 *
571
	 * @return array {
572
	 *     Array of UI setup data for connection list form.
573
	 *
574
	 *     @type string 'unique_id'     ID string representing connection
575
	 *     @type string 'service_name'  Slug of the connection's service (facebook, twitter, ...)
576
	 *     @type string 'service_label' Service Label (Facebook, Twitter, ...)
577
	 *     @type string 'display_name'  Connection's human-readable Username: "@jetpack"
578
	 *     @type bool   'enabled'       Default value for the connection (e.g., for a checkbox).
579
	 *     @type bool   'done'          Has this connection already been publicized to?
580
	 *     @type bool   'toggleable'    Is the user allowed to change the value for the connection?
581
	 *     @type bool   'global'        Is this connection a global one?
582
	 * }
583
	 */
584
	public function get_filtered_connection_data( $selected_post_id = null ) {
585
		$connection_list = array();
586
587
		$post = get_post( $selected_post_id ); // Defaults to current post if $post_id is null.
588
		// Handle case where there is no current post.
589
		if ( ! empty( $post ) ) {
590
			$post_id = $post->ID;
591
		} else {
592
			$post_id = null;
593
		}
594
595
		$services = $this->get_services( 'connected' );
596
		$all_done = $this->post_is_done_sharing( $post_id );
597
598
		// We don't allow Publicizing to the same external id twice, to prevent spam.
599
		$service_id_done = (array) get_post_meta( $post_id, $this->POST_SERVICE_DONE, true );
600
601
		foreach ( $services as $service_name => $connections ) {
602
			foreach ( $connections as $connection ) {
603
				$connection_meta = $this->get_connection_meta( $connection );
604
				$connection_data = $connection_meta['connection_data'];
605
606
				$unique_id = $this->get_connection_unique_id( $connection );
607
608
609
				// Was this connection (OR, old-format service) already Publicized to?
610
				$done = ! empty( $post ) && (
611
					// New flags
612
					1 == get_post_meta( $post->ID, $this->POST_DONE . $unique_id, true )
613
					||
614
					// old flags
615
					1 == get_post_meta( $post->ID, $this->POST_DONE . $service_name, true )
616
				);
617
618
				/**
619
				 * Filter whether a post should be publicized to a given service.
620
				 *
621
				 * @module publicize
622
				 *
623
				 * @since 2.0.0
624
				 *
625
				 * @param bool true Should the post be publicized to a given service? Default to true.
626
				 * @param int $post_id Post ID.
627
				 * @param string $service_name Service name.
628
				 * @param array $connection_data Array of information about all Publicize details for the site.
629
				 */
630
				if ( ! apply_filters( 'wpas_submit_post?', true, $post_id, $service_name, $connection_data ) ) {
631
					continue;
632
				}
633
634
				// Should we be skipping this one?
635
				$skip = (
636
					(
637
						! empty( $post )
638
						&&
639
						in_array( $post->post_status, array( 'publish', 'draft', 'future' ) )
640
						&&
641
						(
642
							// New flags
643
							get_post_meta( $post->ID, $this->POST_SKIP . $unique_id, true )
644
							||
645
							// Old flags
646
							get_post_meta( $post->ID, $this->POST_SKIP . $service_name )
647
						)
648
					)
649
					||
650
					(
651
						is_array( $connection )
652
						&&
653
						isset( $connection_meta['external_id'] ) && ! empty( $service_id_done[ $service_name ][ $connection_meta['external_id'] ] )
654
					)
655
				);
656
657
				// If this one has already been publicized to, don't let it happen again.
658
				$toggleable = ! $done && ! $all_done;
659
660
				// Determine the state of the checkbox (on/off) and allow filtering.
661
				$enabled = $done || ! $skip;
662
				/**
663
				 * Filter the checkbox state of each Publicize connection appearing in the post editor.
664
				 *
665
				 * @module publicize
666
				 *
667
				 * @since 2.0.1
668
				 *
669
				 * @param bool $enabled Should the Publicize checkbox be enabled for a given service.
670
				 * @param int $post_id Post ID.
671
				 * @param string $service_name Service name.
672
				 * @param array $connection Array of connection details.
673
				 */
674
				$enabled = apply_filters( 'publicize_checkbox_default', $enabled, $post_id, $service_name, $connection );
675
676
				/**
677
				 * If this is a global connection and this user doesn't have enough permissions to modify
678
				 * those connections, don't let them change it.
679
				 */
680
				if ( ! $done && ( 0 == $connection_data['user_id'] && ! current_user_can( $this->GLOBAL_CAP ) ) ) {
681
					$toggleable = false;
682
683
					/**
684
					 * Filters the checkboxes for global connections with non-prilvedged users.
685
					 *
686
					 * @module publicize
687
					 *
688
					 * @since 3.7.0
689
					 *
690
					 * @param bool   $enabled Indicates if this connection should be enabled. Default true.
691
					 * @param int    $post_id ID of the current post
692
					 * @param string $service_name Name of the connection (Facebook, Twitter, etc)
693
					 * @param array  $connection Array of data about the connection.
694
					 */
695
					$enabled = apply_filters( 'publicize_checkbox_global_default', $enabled, $post_id, $service_name, $connection );
696
				}
697
698
				// Force the checkbox to be checked if the post was DONE, regardless of what the filter does.
699
				if ( $done ) {
700
					$enabled = true;
701
				}
702
703
				$connection_list[] = array(
704
					'unique_id'     => $unique_id,
705
					'service_name'  => $service_name,
706
					'service_label' => $this->get_service_label( $service_name ),
707
					'display_name'  => $this->get_display_name( $service_name, $connection ),
708
709
					'enabled'      => $enabled,
710
					'done'         => $done,
711
					'toggleable'   => $toggleable,
712
					'global'       => 0 == $connection_data['user_id'],
713
				);
714
			}
715
		}
716
717
		return $connection_list;
718
	}
719
720
	/**
721
	 * Checks if post has already been shared by Publicize in the past.
722
	 *
723
	 * @since 6.7.0
724
	 *
725
	 * @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...
726
	 *
727
	 * @return bool True if post has already been shared by Publicize, false otherwise.
728
	 */
729
	abstract public function post_is_done_sharing( $post_id = null );
730
731
	/**
732
	 * Retrieves full list of available Publicize connection services.
733
	 *
734
	 * Retrieves current available publicize service connections
735
	 * with associated labels and URLs.
736
	 *
737
	 * @since 6.7.0
738
	 *
739
	 * @return array {
740
	 *     Array of UI service connection data for all services
741
	 *
742
	 *     @type string 'name'  Name of service.
743
	 *     @type string 'label' Display label for service.
744
	 *     @type string 'url'   URL for adding connection to service.
745
	 * }
746
	 */
747
	function get_available_service_data() {
748
		$available_services     = $this->get_services( 'all' );
749
		$available_service_data = array();
750
751
		foreach ( $available_services as $service_name => $service ) {
752
			$available_service_data[] = array(
753
				'name'  => $service_name,
754
				'label' => $this->get_service_label( $service_name ),
755
				'url'   => $this->connect_url( $service_name ),
756
			);
757
		}
758
759
		return $available_service_data;
760
	}
761
762
/*
763
 * Site Data
764
 */
765
766
	function user_id() {
767
		return get_current_user_id();
768
	}
769
770
	function blog_id() {
771
		return get_current_blog_id();
772
	}
773
774
/*
775
 * Posts
776
 */
777
778
	/**
779
	 * Checks old and new status to see if the post should be flagged as
780
	 * ready to Publicize.
781
	 *
782
	 * Attached to the `transition_post_status` filter.
783
	 *
784
	 * @param string $new_status
785
	 * @param string $old_status
786
	 * @param WP_Post $post
787
	 * @return void
788
	 */
789
	abstract function flag_post_for_publicize( $new_status, $old_status, $post );
790
791
	/**
792
	 * Ensures the Post internal post-type supports `publicize`
793
	 *
794
	 * This feature support flag is used by the REST API.
795
	 */
796
	function add_post_type_support() {
797
		add_post_type_support( 'post', 'publicize' );
798
	}
799
800
	/**
801
	 * Register the Publicize Gutenberg extension
802
	 */
803
	function register_gutenberg_extension() {
804
		// TODO: The `gutenberg/available-extensions` endpoint currently doesn't accept a post ID,
805
		// so we cannot pass one to `$this->current_user_can_access_publicize_data()`.
806
807
		if ( $this->current_user_can_access_publicize_data() ) {
808
			Jetpack_Gutenberg::set_extension_available( 'jetpack/publicize' );
809
		} else {
810
			Jetpack_Gutenberg::set_extension_unavailable( 'jetpack/publicize', 'unauthorized' );
811
812
		}
813
	}
814
815
	/**
816
	 * Can the current user access Publicize Data.
817
	 *
818
	 * @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...
819
	 * @return bool
820
	 */
821
	function current_user_can_access_publicize_data( $post_id = 0 ) {
822
		/**
823
		 * 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.
824
		 *
825
		 * @module publicize
826
		 *
827
		 * @since 4.1.0
828
		 *
829
		 * @param string $capability User capability needed to use publicize
830
		 */
831
		$capability = apply_filters( 'jetpack_publicize_capability', 'publish_posts' );
832
833
		if ( 'publish_posts' === $capability && $post_id ) {
834
			return current_user_can( 'publish_post', $post_id );
835
		}
836
837
		return current_user_can( $capability );
838
	}
839
840
	/**
841
	 * Auth callback for the protected ->POST_MESS post_meta
842
	 *
843
	 * @param bool $allowed
844
	 * @param string $meta_key
845
	 * @param int $object_id Post ID
846
	 * @return bool
847
	 */
848
	function message_meta_auth_callback( $allowed, $meta_key, $object_id ) {
849
		return $this->current_user_can_access_publicize_data( $object_id );
850
	}
851
852
	/**
853
	 * Registers the ->POST_MESS post_meta for use in the REST API.
854
	 *
855
	 * Registers for each post type that with `publicize` feature support.
856
	 */
857
	function register_post_meta() {
858
		$args = array(
859
			'type' => 'string',
860
			'description' => __( 'The message to use instead of the title when sharing to Publicize Services', 'jetpack' ),
861
			'single' => true,
862
			'default' => '',
863
			'show_in_rest' => array(
864
				'name' => 'jetpack_publicize_message'
865
			),
866
			'auth_callback' => array( $this, 'message_meta_auth_callback' ),
867
		);
868
869
		foreach ( get_post_types() as $post_type ) {
870
			if ( ! $this->post_type_is_publicizeable( $post_type ) ) {
871
				continue;
872
			}
873
874
			$args['object_subtype'] = $post_type;
875
876
			register_meta( 'post', $this->POST_MESS, $args );
877
		}
878
	}
879
880
	/**
881
	 * Fires when a post is saved, checks conditions and saves state in postmeta so that it
882
	 * can be picked up later by @see ::publicize_post() on WordPress.com codebase.
883
	 *
884
	 * Attached to the `save_post` action.
885
	 *
886
	 * @param int $post_id
887
	 * @param WP_Post $post
888
	 * @return void
889
	 */
890
	function save_meta( $post_id, $post ) {
891
		$cron_user = null;
892
		$submit_post = true;
893
894
		if ( ! $this->post_type_is_publicizeable( $post->post_type ) )
895
			return;
896
897
		// Don't Publicize during certain contexts:
898
899
		// - import
900
		if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING  ) {
901
			$submit_post = false;
902
		}
903
904
		// - on quick edit, autosave, etc but do fire on p2, quickpress, and instapost ajax
905
		if (
906
			defined( 'DOING_AJAX' )
907
		&&
908
			DOING_AJAX
909
		&&
910
			!did_action( 'p2_ajax' )
911
		&&
912
			!did_action( 'wp_ajax_json_quickpress_post' )
913
		&&
914
			!did_action( 'wp_ajax_instapost_publish' )
915
		&&
916
			!did_action( 'wp_ajax_post_reblog' )
917
		&&
918
			!did_action( 'wp_ajax_press-this-save-post' )
919
		) {
920
			$submit_post = false;
921
		}
922
923
		// - bulk edit
924
		if ( isset( $_GET['bulk_edit'] ) ) {
925
			$submit_post = false;
926
		}
927
928
		// - API/XML-RPC Test Posts
929
		if (
930
			(
931
				defined( 'XMLRPC_REQUEST' )
932
			&&
933
				XMLRPC_REQUEST
934
			||
935
				defined( 'APP_REQUEST' )
936
			&&
937
				APP_REQUEST
938
			)
939
		&&
940
			0 === strpos( $post->post_title, 'Temporary Post Used For Theme Detection' )
941
		) {
942
			$submit_post = false;
943
		}
944
945
		// only work with certain statuses (avoids inherits, auto drafts etc)
946
		if ( !in_array( $post->post_status, array( 'publish', 'draft', 'future' ) ) ) {
947
			$submit_post = false;
948
		}
949
950
		// don't publish password protected posts
951
		if ( '' !== $post->post_password ) {
952
			$submit_post = false;
953
		}
954
955
		// Did this request happen via wp-admin?
956
		$from_web = isset( $_SERVER['REQUEST_METHOD'] )
957
			&&
958
			'post' == strtolower( $_SERVER['REQUEST_METHOD'] )
959
			&&
960
			isset( $_POST[$this->ADMIN_PAGE] );
961
962
		if ( ( $from_web || defined( 'POST_BY_EMAIL' ) ) && isset( $_POST['wpas_title'] ) ) {
963
			if ( empty( $_POST['wpas_title'] ) ) {
964
				delete_post_meta( $post_id, $this->POST_MESS );
965
			} else {
966
				update_post_meta( $post_id, $this->POST_MESS, trim( stripslashes( $_POST['wpas_title'] ) ) );
967
			}
968
		}
969
970
		// change current user to provide context for get_services() if we're running during cron
971
		if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
972
			$cron_user = (int) $GLOBALS['user_ID'];
973
			wp_set_current_user( $post->post_author );
974
		}
975
976
		/**
977
		 * In this phase, we mark connections that we want to SKIP. When Publicize is actually triggered,
978
		 * it will Publicize to everything *except* those marked for skipping.
979
		 */
980
		foreach ( (array) $this->get_services( 'connected' ) as $service_name => $connections ) {
981
			foreach ( $connections as $connection ) {
982
				$connection_data = '';
983
				if ( method_exists( $connection, 'get_meta' ) )
984
					$connection_data = $connection->get_meta( 'connection_data' );
985
				elseif ( ! empty( $connection['connection_data'] ) )
986
					$connection_data = $connection['connection_data'];
987
988
				/** This action is documented in modules/publicize/ui.php */
989
				if ( false == apply_filters( 'wpas_submit_post?', $submit_post, $post_id, $service_name, $connection_data ) ) {
990
					delete_post_meta( $post_id, $this->PENDING );
991
					continue;
992
				}
993
994 View Code Duplication
				if ( !empty( $connection->unique_id ) )
995
					$unique_id = $connection->unique_id;
996
				else if ( !empty( $connection['connection_data']['token_id'] ) )
997
					$unique_id = $connection['connection_data']['token_id'];
998
999
				// This was a wp-admin request, so we need to check the state of checkboxes
1000
				if ( $from_web ) {
1001
					// delete stray service-based post meta
1002
					delete_post_meta( $post_id, $this->POST_SKIP . $service_name );
1003
1004
					// We *unchecked* this stream from the admin page, or it's set to readonly, or it's a new addition
1005
					if ( empty( $_POST[$this->ADMIN_PAGE]['submit'][$unique_id] ) ) {
1006
						// Also make sure that the service-specific input isn't there.
1007
						// If the user connected to a new service 'in-page' then a hidden field with the service
1008
						// name is added, so we just assume they wanted to Publicize to that service.
1009
						if ( empty( $_POST[$this->ADMIN_PAGE]['submit'][$service_name] ) ) {
1010
							// Nothing seems to be checked, so we're going to mark this one to be skipped
1011
							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...
1012
							continue;
1013
						} else {
1014
							// clean up any stray post meta
1015
							delete_post_meta( $post_id, $this->POST_SKIP . $unique_id );
1016
						}
1017
					} else {
1018
						// The checkbox for this connection is explicitly checked -- make sure we DON'T skip it
1019
						delete_post_meta( $post_id, $this->POST_SKIP . $unique_id );
1020
					}
1021
				}
1022
1023
				/**
1024
				 * Fires right before the post is processed for Publicize.
1025
				 * Users may hook in here and do anything else they need to after meta is written,
1026
				 * and before the post is processed for Publicize.
1027
				 *
1028
				 * @since 2.1.2
1029
				 *
1030
				 * @param bool $submit_post Should the post be publicized.
1031
				 * @param int $post->ID Post ID.
1032
				 * @param string $service_name Service name.
1033
				 * @param array $connection Array of connection details.
1034
				 */
1035
				do_action( 'publicize_save_meta', $submit_post, $post_id, $service_name, $connection );
1036
			}
1037
		}
1038
1039
		if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
1040
			wp_set_current_user( $cron_user );
1041
		}
1042
1043
		// Next up will be ::publicize_post()
1044
	}
1045
1046
	/**
1047
	 * Alters the "Post Published" message to include information about where the post
1048
	 * was Publicized to.
1049
	 *
1050
	 * Attached to the `post_updated_messages` filter
1051
	 *
1052
	 * @param string[] $messages
1053
	 * @return string[]
1054
	 */
1055
	public function update_published_message( $messages ) {
1056
		global $post_type, $post_type_object, $post;
1057
		if ( ! $this->post_type_is_publicizeable( $post_type ) ) {
1058
			return $messages;
1059
		}
1060
1061
		// Bail early if the post is private.
1062
		if ( 'publish' !== $post->post_status ) {
1063
			return $messages;
1064
		}
1065
1066
		$view_post_link_html = '';
1067
		$viewable = is_post_type_viewable( $post_type_object );
1068
		if ( $viewable ) {
1069
			$view_text = esc_html__( 'View post' ); // intentionally omitted domain
1070
1071
			if ( 'jetpack-portfolio' == $post_type ) {
1072
				$view_text = esc_html__( 'View project', 'jetpack' );
1073
			}
1074
1075
			$view_post_link_html = sprintf( ' <a href="%1$s">%2$s</a>',
1076
				esc_url( get_permalink( $post ) ),
1077
				$view_text
1078
			);
1079
		}
1080
1081
		$services = $this->get_publicizing_services( $post->ID );
1082
		if ( empty( $services ) ) {
1083
			return $messages;
1084
		}
1085
1086
		$labels = array();
1087
		foreach ( $services as $service_name => $display_names ) {
1088
			$labels[] = sprintf(
1089
				/* translators: Service name is %1$s, and account name is %2$s. */
1090
				esc_html__( '%1$s (%2$s)', 'jetpack' ),
1091
				esc_html( $service_name ),
1092
				esc_html( implode( ', ', $display_names ) )
1093
			);
1094
		}
1095
1096
		$messages['post'][6] = sprintf(
1097
			/* translators: %1$s is a comma-separated list of services and accounts. Ex. Facebook (@jetpack), Twitter (@jetpack) */
1098
			esc_html__( 'Post published and sharing on %1$s.', 'jetpack' ),
1099
			implode( ', ', $labels )
1100
		) . $view_post_link_html;
1101
1102
		if ( $post_type == 'post' && class_exists('Jetpack_Subscriptions' ) ) {
1103
			$subscription = Jetpack_Subscriptions::init();
1104
			if ( $subscription->should_email_post_to_subscribers( $post ) ) {
1105
				$messages['post'][6] = sprintf(
1106
					/* translators: %1$s is a comma-separated list of services and accounts. Ex. Facebook (@jetpack), Twitter (@jetpack) */
1107
					esc_html__( 'Post published, sending emails to subscribers and sharing post on %1$s.', 'jetpack' ),
1108
					implode( ', ', $labels )
1109
				) . $view_post_link_html;
1110
			}
1111
		}
1112
1113
		$messages['jetpack-portfolio'][6] = sprintf(
1114
			/* translators: %1$s is a comma-separated list of services and accounts. Ex. Facebook (@jetpack), Twitter (@jetpack) */
1115
			esc_html__( 'Project published and sharing project on %1$s.', 'jetpack' ),
1116
			implode( ', ', $labels )
1117
		) . $view_post_link_html;
1118
1119
		return $messages;
1120
	}
1121
1122
	/**
1123
	 * Get the Connections the Post was just Publicized to.
1124
	 *
1125
	 * Only reliable just after the Post was published.
1126
	 *
1127
	 * @param int $post_id
1128
	 * @return string[] Array of Service display name => Connection display name
1129
	 */
1130
	function get_publicizing_services( $post_id ) {
1131
		$services = array();
1132
1133
		foreach ( (array) $this->get_services( 'connected' ) as $service_name => $connections ) {
1134
			// services have multiple connections.
1135
			foreach ( $connections as $connection ) {
1136
				$unique_id = '';
1137 View Code Duplication
				if ( ! empty( $connection->unique_id ) )
1138
					$unique_id = $connection->unique_id;
1139
				else if ( ! empty( $connection['connection_data']['token_id'] ) )
1140
					$unique_id = $connection['connection_data']['token_id'];
1141
1142
				// Did we skip this connection?
1143
				if ( get_post_meta( $post_id, $this->POST_SKIP . $unique_id,  true ) ) {
1144
					continue;
1145
				}
1146
				$services[ $this->get_service_label( $service_name ) ][] = $this->get_display_name( $service_name, $connection );
1147
			}
1148
		}
1149
1150
		return $services;
1151
	}
1152
1153
	/**
1154
	 * Is the post Publicize-able?
1155
	 *
1156
	 * Only valid prior to Publicizing a Post.
1157
	 *
1158
	 * @param WP_Post $post
1159
	 * @return bool
1160
	 */
1161
	function post_is_publicizeable( $post ) {
1162
		if ( ! $this->post_type_is_publicizeable( $post->post_type ) )
1163
			return false;
1164
1165
		// This is more a precaution. To only publicize posts that are published. (Mostly relevant for Jetpack sites)
1166
		if ( 'publish' !== $post->post_status ) {
1167
			return false;
1168
		}
1169
1170
		// If it's not flagged as ready, then abort. @see ::flag_post_for_publicize()
1171
		if ( ! get_post_meta( $post->ID, $this->PENDING, true ) )
1172
			return false;
1173
1174
		return true;
1175
	}
1176
1177
	/**
1178
	 * Is a given post type Publicize-able?
1179
	 *
1180
	 * Not every CPT lends itself to Publicize-ation.  Allow CPTs to register by adding their CPT via
1181
	 * the publicize_post_types array filter.
1182
	 *
1183
	 * @param string $post_type The post type to check.
1184
	 * @return bool True if the post type can be Publicized.
1185
	 */
1186
	function post_type_is_publicizeable( $post_type ) {
1187
		if ( 'post' == $post_type )
1188
			return true;
1189
1190
		return post_type_supports( $post_type, 'publicize' );
1191
	}
1192
1193
	/**
1194
	 * Already-published posts should not be Publicized by default. This filter sets checked to
1195
	 * false if a post has already been published.
1196
	 *
1197
	 * Attached to the `publicize_checkbox_default` filter
1198
	 *
1199
	 * @param bool $checked
1200
	 * @param int $post_id
1201
	 * @param string $service_name 'facebook', 'twitter', etc
1202
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
1203
	 * @return bool
1204
	 */
1205
	function publicize_checkbox_default( $checked, $post_id, $service_name, $connection ) {
1206
		if ( 'publish' == get_post_status( $post_id ) ) {
1207
			return false;
1208
		}
1209
1210
		return $checked;
1211
	}
1212
1213
/*
1214
 * Util
1215
 */
1216
1217
	/**
1218
	 * Converts a Publicize message template string into a sprintf format string
1219
	 *
1220
	 * @param string[] $args
1221
	 *               0 - The Publicize message template: 'Check out my post: %title% @ %url'
1222
	 *             ... - The template tags 'title', 'url', etc.
1223
	 * @return string
1224
	 */
1225
	protected static function build_sprintf( $args ) {
1226
		$search = array();
1227
		$replace = array();
1228
		foreach ( $args as $k => $arg ) {
1229
			if ( 0 == $k ) {
1230
				$string = $arg;
1231
				continue;
1232
			}
1233
			$search[] = "%$arg%";
1234
			$replace[] = "%$k\$s";
1235
		}
1236
		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...
1237
	}
1238
}
1239
1240
function publicize_calypso_url() {
1241
	$calypso_sharing_url = 'https://wordpress.com/marketing/connections/';
1242
	if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'build_raw_urls' ) ) {
1243
		$site_suffix = Jetpack::build_raw_urls( home_url() );
1244
	} elseif ( class_exists( 'WPCOM_Masterbar' ) && method_exists( 'WPCOM_Masterbar', 'get_calypso_site_slug' ) ) {
1245
		$site_suffix = WPCOM_Masterbar::get_calypso_site_slug( get_current_blog_id() );
1246
	}
1247
1248
	if ( $site_suffix ) {
1249
		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...
1250
	} else {
1251
		return $calypso_sharing_url;
1252
	}
1253
}
1254