Completed
Push — add/publicize-rest-api-2 ( 7f2ca9...536e08 )
by
unknown
112:29 queued 105:13
created

Publicize_Base::get_service_label()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 6
nop 1
dl 0
loc 16
rs 9.1111
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, 'register_post_meta' ), 20 );
121
122
		// Set up publicize flags right before post is actually published.
123
		add_filter( 'rest_pre_insert_post', array( $this, 'process_publicize_from_rest' ), 10, 2 );
124
	}
125
126
	function message_meta_auth_callback( $allowed, $meta_key, $object_id ) {
127
		return current_user_can( 'edit_post', $object_id );
128
	}
129
130
	function register_post_meta() {
131
		$args = array(
132
			'type' => 'string',
133
			'description' => __( 'The message to use instead of the title when sharing to Publicize Services', 'jetpack' ),
134
			'single' => true,
135
			'default' => '',
136
			'show_in_rest' => array(
137
				'name' => 'jetpack_publicize_message'
138
			),
139
			'auth_callback' => array( $this, 'message_meta_auth_callback' ),
140
		);
141
142
		foreach ( get_post_types() as $post_type ) {
143
			if ( ! $this->post_type_is_publicizeable( $post_type ) ) {
144
				continue;
145
			}
146
147
			$args['object_subtype'] = $post_type;
148
149
			register_meta( 'post', $this->POST_MESS, $args );
150
		}
151
	}
152
153
/*
154
 * Services: Facebook, Twitter, etc.
155
 */
156
157
	/**
158
	 * Get services for the given blog and user.
159
	 *
160
	 * Can return all available services or just the ones with an active connection.
161
	 *
162
	 * @param string $filter
163
	 *        'all' (default) - Get all services available for connecting
164
	 *        'connected'     - Get all services currently connected
165
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
166
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
167
	 * @return array
168
	 */
169
	abstract function get_services( $filter = 'all', $_blog_id = false, $_user_id = false );
170
171
	/**
172
	 * Does the given user have a connection to the service on the given blog?
173
	 *
174
	 * @param string $service_name 'facebook', 'twitter', etc.
175
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
176
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
177
	 * @return bool
178
	 */
179
	function is_enabled( $service_name, $_blog_id = false, $_user_id = false ) {
180
		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...
181
			$_blog_id = $this->blog_id();
182
183
		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...
184
			$_user_id = $this->user_id();
185
186
		$connections = $this->get_connections( $service_name, $_blog_id, $_user_id );
187
		return ( is_array( $connections ) && count( $connections ) > 0 ? true : false );
188
	}
189
190
	/**
191
	 * Generates a connection URL.
192
	 *
193
	 * This is the URL, which, when visited by the user, starts the authentication
194
	 * process required to forge a connection.
195
	 *
196
	 * @param string $service_name 'facebook', 'twitter', etc.
197
	 * @return string
198
	 */
199
	abstract function connect_url( $service_name );
200
201
	/**
202
	 * Generates a Connection refresh URL.
203
	 *
204
	 * This is the URL, which, when visited by the user, re-authenticates their
205
	 * connection to the service.
206
	 *
207
	 * @param string $service_name 'facebook', 'twitter', etc.
208
	 * @return string
209
	 */
210
	abstract function refresh_url( $service_name );
211
212
	/**
213
	 * Generates a disconnection URL.
214
	 *
215
	 * This is the URL, which, when visited by the user, breaks their connection
216
	 * with the service.
217
	 *
218
	 * @param string $service_name 'facebook', 'twitter', etc.
219
	 * @param string $connection_id Connection ID
220
	 * @return string
221
	 */
222
	abstract function disconnect_url( $service_name, $connection_id );
223
224
	/**
225
	 * Returns a display name for the Service
226
	 *
227
	 * @param string $service_name 'facebook', 'twitter', etc.
228
	 * @return string
229
	 */
230
	public static function get_service_label( $service_name ) {
231
		switch ( $service_name ) {
232
			case 'linkedin':
233
				return 'LinkedIn';
234
				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...
235
			case 'google_plus':
236
				return  'Google+';
237
				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...
238
			case 'twitter':
239
			case 'facebook':
240
			case 'tumblr':
241
			default:
242
				return ucfirst( $service_name );
243
				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...
244
		}
245
	}
246
247
/*
248
 * Connections: For each Service, there can be multiple connections
249
 * for a given user. For example, one user could be connected to Twitter
250
 * as both @jetpack and as @wordpressdotcom
251
 *
252
 * For historical reasons, Connections are represented as an object
253
 * on WordPress.com and as an array in Jetpack.
254
 */
255
256
	/**
257
	 * Get the active Connections of a Service
258
	 *
259
	 * @param string $service_name 'facebook', 'twitter', etc.
260
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
261
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
262
	 * @return false|object[]|array[] false if no connections exist
263
	 */
264
	abstract function get_connections( $service_name, $_blog_id = false, $_user_id = false );
265
266
	/**
267
	 * Get a single Connection of a Service
268
	 *
269
	 * @param string $service_name 'facebook', 'twitter', etc.
270
	 * @param string $connection_id Connection ID
271
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
272
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
273
	 * @return false|object[]|array[] false if no connections exist
274
	 */
275
	abstract function get_connection( $service_name, $connection_id, $_blog_id = false, $_user_id = false );
276
277
	/**
278
	 * Get the Connection ID.
279
	 *
280
	 * Note that this is different than the Connection's uniqueid.
281
	 *
282
	 * Via a quirk of history, ID is globally unique and unique_id
283
	 * is only unique per site.
284
	 *
285
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
286
	 * @return string
287
	 */
288
	abstract function get_connection_id( $connection );
289
290
	/**
291
	 * Get the Connection unique_id
292
	 *
293
	 * Note that this is different than the Connections ID.
294
	 *
295
	 * Via a quirk of history, ID is globally unique and unique_id
296
	 * is only unique per site.
297
	 *
298
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
299
	 * @return string
300
	 */
301
	abstract function get_connection_unique_id( $connection );
302
303
	/**
304
	 * Get the Connection's Meta data
305
	 *
306
	 * @param object|array Connection
307
	 * @return array Connection Meta
308
	 */
309
	abstract function get_connection_meta( $connection );
310
311
	/**
312
	 * Disconnect a Connection
313
	 *
314
	 * @param string $service_name 'facebook', 'twitter', etc.
315
	 * @param string $connection_id Connection ID
316
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
317
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
318
	 * @param bool $force_delete Whether to skip permissions checks
319
	 * @return false|void False on failure. Void on success.
320
	 */
321
	abstract function disconnect( $service_name, $connection_id, $_blog_id = false, $_user_id = false, $force_delete = false );
322
323
	/**
324
	 * Globalizes a Connection
325
	 *
326
	 * @param string $connection_id Connection ID
327
	 * @return bool Falsey on failure. Truthy on success.
328
	 */
329
	abstract function globalize_connection( $connection_id );
330
331
	/**
332
	 * Unglobalizes a Connection
333
	 *
334
	 * @param string $connection_id Connection ID
335
	 * @return bool Falsey on failure. Truthy on success.
336
	 */
337
	abstract function unglobalize_connection( $connection_id );
338
339
	/**
340
	 * Returns an external URL to the Connection's profile
341
	 *
342
	 * @param string $service_name 'facebook', 'twitter', etc.
343
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
344
	 * @return false|string False on failure. URL on success.
345
	 */
346
	function get_profile_link( $service_name, $connection ) {
347
		$cmeta = $this->get_connection_meta( $connection );
348
349
		if ( isset( $cmeta['connection_data']['meta']['link'] ) ) {
350
			if ( 'facebook' == $service_name && 0 === strpos( parse_url( $cmeta['connection_data']['meta']['link'], PHP_URL_PATH ), '/app_scoped_user_id/' ) ) {
351
				// App-scoped Facebook user IDs are not usable profile links
352
				return false;
353
			}
354
355
			return $cmeta['connection_data']['meta']['link'];
356 View Code Duplication
		} elseif ( 'facebook' == $service_name && isset( $cmeta['connection_data']['meta']['facebook_page'] ) ) {
357
			return 'https://facebook.com/' . $cmeta['connection_data']['meta']['facebook_page'];
358
		} elseif ( 'tumblr' == $service_name && isset( $cmeta['connection_data']['meta']['tumblr_base_hostname'] ) ) {
359
			 return 'http://' . $cmeta['connection_data']['meta']['tumblr_base_hostname'];
360
		} elseif ( 'twitter' == $service_name ) {
361
			return 'https://twitter.com/' . substr( $cmeta['external_display'], 1 ); // Has a leading '@'
362 View Code Duplication
		} elseif ( 'google_plus' == $service_name && isset( $cmeta['connection_data']['meta']['google_plus_page'] ) ) {
363
			return 'https://plus.google.com/' . $cmeta['connection_data']['meta']['google_plus_page'];
364
		} elseif ( 'google_plus' == $service_name ) {
365
			return 'https://plus.google.com/' . $cmeta['external_id'];
366
		} else if ( 'linkedin' == $service_name ) {
367
			if ( !isset( $cmeta['connection_data']['meta']['profile_url'] ) ) {
368
				return false;
369
			}
370
371
			$profile_url_query = parse_url( $cmeta['connection_data']['meta']['profile_url'], PHP_URL_QUERY );
372
			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...
373
			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...
374
				$id = $profile_url_query_args['key'];
375
			} 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...
376
				$id = $profile_url_query_args['id'];
377
			} else {
378
				return false;
379
			}
380
381
			return esc_url_raw( add_query_arg( 'id', urlencode( $id ), 'http://www.linkedin.com/profile/view' ) );
382
		} else {
383
			return false; // no fallback. we just won't link it
384
		}
385
	}
386
387
	/**
388
	 * Returns a display name for the Connection
389
	 *
390
	 * @param string $service_name 'facebook', 'twitter', etc.
391
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
392
	 * @return string
393
	 */
394
	function get_display_name( $service_name, $connection ) {
395
		$cmeta = $this->get_connection_meta( $connection );
396
397
		if ( isset( $cmeta['connection_data']['meta']['display_name'] ) ) {
398
			return $cmeta['connection_data']['meta']['display_name'];
399 View Code Duplication
		} elseif ( $service_name == 'tumblr' && isset( $cmeta['connection_data']['meta']['tumblr_base_hostname'] ) ) {
400
			 return $cmeta['connection_data']['meta']['tumblr_base_hostname'];
401
		} elseif ( $service_name == 'twitter' ) {
402
			return $cmeta['external_display'];
403
		} else {
404
			$connection_display = $cmeta['external_display'];
405
			if ( empty( $connection_display ) )
406
				$connection_display = $cmeta['external_name'];
407
			return $connection_display;
408
		}
409
	}
410
411
	/**
412
	 * Whether the user needs to select additional options after connecting
413
	 *
414
	 * @param string $service_name 'facebook', 'twitter', etc.
415
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
416
	 * @return bool
417
	 */
418
	function show_options_popup( $service_name, $connection ) {
419
		$cmeta = $this->get_connection_meta( $connection );
420
421
		// always show if no selection has been made for facebook
422
		if ( 'facebook' == $service_name && empty( $cmeta['connection_data']['meta']['facebook_profile'] ) && empty( $cmeta['connection_data']['meta']['facebook_page'] ) )
423
			return true;
424
425
		// always show if no selection has been made for tumblr
426
		if ( 'tumblr' == $service_name && empty ( $cmeta['connection_data']['meta']['tumblr_base_hostname'] ) )
427
			return true;
428
429
		// if we have the specific connection info..
430
		if ( isset( $_GET['id'] ) ) {
431
			if ( $cmeta['connection_data']['id'] == $_GET['id'] )
432
				return true;
433
		} else {
434
			// otherwise, just show if this is the completed step / first load
435
			if ( !empty( $_GET['action'] ) && 'completed' == $_GET['action'] && !empty( $_GET['service'] ) && $service_name == $_GET['service'] && ! in_array( $_GET['service'], array( 'facebook', 'tumblr' ) ) )
436
				return true;
437
		}
438
439
		return false;
440
	}
441
442
	/**
443
	 * Whether the Connection is "valid" wrt Facebook's requirements.
444
	 *
445
	 * Must be connected to a Page (not a Profile).
446
	 * (Also returns true if we're in the middle of the connection process)
447
	 *
448
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
449
	 * @return bool
450
	 */
451
	function is_valid_facebook_connection( $connection ) {
452
		if ( $this->is_connecting_connection( $connection ) ) {
453
			return true;
454
		}
455
		$connection_meta = $this->get_connection_meta( $connection );
456
		$connection_data = $connection_meta['connection_data'];
457
		return isset( $connection_data[ 'meta' ][ 'facebook_page' ] );
458
	}
459
460
	/**
461
	 * Whether the Connection currently being connected
462
	 *
463
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
464
	 * @return bool
465
	 */
466
	function is_connecting_connection( $connection ) {
467
		$connection_meta = $this->get_connection_meta( $connection );
468
		$connection_data = $connection_meta['connection_data'];
469
		return isset( $connection_data[ 'meta' ]['options_responses'] );
470
	}
471
472
	/**
473
	 * AJAX Handler to run connection tests on all Connections
474
	 * @return void
475
	 */
476
	function test_publicize_conns() {
477
		wp_send_json_success( $this->get_publicize_conns_test_results() );
478
	}
479
480
	/**
481
	 * Run connection tests on all Connections
482
	 *
483
	 * @return array {
484
	 *     Array of connection test results.
485
	 *
486
	 *     @type string 'connectionID'          Connection identifier string that is unique for each connection
487
	 *     @type string 'serviceName'           Slug of the connection's service (facebook, twitter, ...)
488
	 *     @type bool   'connectionTestPassed'  Whether the connection test was successful
489
	 *     @type string 'connectionTestMessage' Test success or error message
490
	 *     @type bool   'userCanRefresh'        Whether the user can re-authenticate their connection to the service
491
	 *     @type string 'refreshText'           Message instructing user to re-authenticate their connection to the service
492
	 *     @type string 'refreshURL'            URL, which, when visited by the user, re-authenticates their connection to the service.
493
	 *     @type string 'unique_id'             ID string representing connection
494
	 * }
495
	 */
496
	function get_publicize_conns_test_results() {
497
		$test_results = array();
498
499
		foreach ( (array) $this->get_services( 'connected' ) as $service_name => $connections ) {
500
			foreach ( $connections as $connection ) {
501
502
				$id = $this->get_connection_id( $connection );
503
504
				$connection_test_passed = true;
505
				$connection_test_message = __( 'This connection is working correctly.' , 'jetpack' );
506
				$user_can_refresh = false;
507
				$refresh_text = '';
508
				$refresh_url = '';
509
510
				$connection_test_result = true;
511
				if ( method_exists( $this, 'test_connection' ) ) {
512
					$connection_test_result = $this->test_connection( $service_name, $connection );
513
				}
514
515
				if ( is_wp_error( $connection_test_result ) ) {
516
					$connection_test_passed = false;
517
					$connection_test_message = $connection_test_result->get_error_message();
518
					$error_data = $connection_test_result->get_error_data();
519
520
					$user_can_refresh = $error_data['user_can_refresh'];
521
					$refresh_text = $error_data['refresh_text'];
522
					$refresh_url = $error_data['refresh_url'];
523
				}
524
				// Mark facebook profiles as deprecated
525
				if ( 'facebook' === $service_name ) {
526
					if ( ! $this->is_valid_facebook_connection( $connection ) ) {
527
						$connection_test_passed = false;
528
						$user_can_refresh = false;
529
						$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.' );
530
					}
531
				}
532
533
				$unique_id = null;
534 View Code Duplication
				if ( ! empty( $connection->unique_id ) ) {
535
					$unique_id = $connection->unique_id;
536
				} else if ( ! empty( $connection['connection_data']['token_id'] ) ) {
537
					$unique_id = $connection['connection_data']['token_id'];
538
				}
539
540
				$test_results[] = array(
541
					'connectionID'          => $id,
542
					'serviceName'           => $service_name,
543
					'connectionTestPassed'  => $connection_test_passed,
544
					'connectionTestMessage' => esc_attr( $connection_test_message ),
545
					'userCanRefresh'        => $user_can_refresh,
546
					'refreshText'           => esc_attr( $refresh_text ),
547
					'refreshURL'            => $refresh_url,
548
					'unique_id'             => $unique_id,
549
				);
550
			}
551
		}
552
553
		return $test_results;
554
	}
555
556
	/**
557
	 * Run the connection test for the Connection
558
	 *
559
	 * @param string $service_name 'facebook', 'twitter', etc.
560
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
561
	 * @return WP_Error|true WP_Error on failure. True on success
562
	 */
563
	abstract function test_connection( $service_name, $connection );
564
565
	/**
566
	 * Retrieves current list of connections and applies filters.
567
	 *
568
	 * Retrieves current available connections and checks if the connections
569
	 * have already been used to share current post. Finally, the checkbox
570
	 * form UI fields are calculated. This function exposes connection form
571
	 * data directly as array so it can be retrieved for static HTML generation
572
	 * or JSON consumption.
573
	 *
574
	 * @since 6.7.0
575
	 *
576
	 * @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...
577
	 *
578
	 * @return array {
579
	 *     Array of UI setup data for connection list form.
580
	 *
581
	 *     @type string 'unique_id'     ID string representing connection
582
	 *     @type string 'service_name'  Slug of the connection's service (facebook, twitter, ...)
583
	 *     @type string 'service_label' Service Label (Facebook, Twitter, ...)
584
	 *     @type string 'display_name'  Connection's human-readable Username: "@jetpack"
585
	 *     @type bool   'enabled'       Default value for the connection (e.g., for a checkbox).
586
	 *     @type bool   'done'          Has this connection already been publicized to?
587
	 *     @type bool   'toggleable'    Is the user allowed to change the value for the connection?
588
	 *     @type bool   'global'        Is this connection a global one?
589
	 * }
590
	 */
591
	public function get_filtered_connection_data( $selected_post_id = null ) {
592
		$connection_list = array();
593
594
		$post = get_post( $selected_post_id ); // Defaults to current post if $post_id is null.
595
		// Handle case where there is no current post.
596
		if ( ! empty( $post ) ) {
597
			$post_id = $post->ID;
598
		} else {
599
			$post_id = null;
600
		}
601
602
		$services = $this->get_services( 'connected' );
603
		$all_done = $this->post_is_done_sharing( $post_id );
604
605
		// We don't allow Publicizing to the same external id twice, to prevent spam.
606
		$service_id_done = (array) get_post_meta( $post_id, $this->POST_SERVICE_DONE, true );
607
608
		foreach ( $services as $service_name => $connections ) {
609
			foreach ( $connections as $connection ) {
610
				$connection_meta = $this->get_connection_meta( $connection );
611
				$connection_data = $connection_meta['connection_data'];
612
613
				$unique_id = $this->get_connection_unique_id( $connection );
614
615
616
				// Was this connection (OR, old-format service) already Publicized to?
617
				$done = ! empty( $post ) && (
618
					// New flags
619
					1 == get_post_meta( $post->ID, $this->POST_DONE . $unique_id, true )
620
					||
621
					// old flags
622
					1 == get_post_meta( $post->ID, $this->POST_DONE . $service_name, true )
623
				);
624
625
				/**
626
				 * Filter whether a post should be publicized to a given service.
627
				 *
628
				 * @module publicize
629
				 *
630
				 * @since 2.0.0
631
				 *
632
				 * @param bool true Should the post be publicized to a given service? Default to true.
633
				 * @param int $post_id Post ID.
634
				 * @param string $service_name Service name.
635
				 * @param array $connection_data Array of information about all Publicize details for the site.
636
				 */
637
				if ( ! apply_filters( 'wpas_submit_post?', true, $post_id, $service_name, $connection_data ) ) {
638
					continue;
639
				}
640
641
				// Should we be skipping this one?
642
				$skip = (
643
					(
644
						! empty( $post )
645
						&&
646
						in_array( $post->post_status, array( 'publish', 'draft', 'future' ) )
647
						&&
648
						(
649
							// New flags
650
							get_post_meta( $post->ID, $this->POST_SKIP . $unique_id, true )
651
							||
652
							// Old flags
653
							get_post_meta( $post->ID, $this->POST_SKIP . $service_name )
654
						)
655
					)
656
					||
657
					(
658
						is_array( $connection )
659
						&&
660
						isset( $connection_meta['external_id'] ) && ! empty( $service_id_done[ $service_name ][ $connection_meta['external_id'] ] )
661
					)
662
				);
663
664
				// If this one has already been publicized to, don't let it happen again.
665
				$toggleable = ! $done && ! $all_done;
666
667
				// Determine the state of the checkbox (on/off) and allow filtering.
668
				$enabled = $done || ! $skip;
669
				/**
670
				 * Filter the checkbox state of each Publicize connection appearing in the post editor.
671
				 *
672
				 * @module publicize
673
				 *
674
				 * @since 2.0.1
675
				 *
676
				 * @param bool $enabled Should the Publicize checkbox be enabled for a given service.
677
				 * @param int $post_id Post ID.
678
				 * @param string $service_name Service name.
679
				 * @param array $connection Array of connection details.
680
				 */
681
				$enabled = apply_filters( 'publicize_checkbox_default', $enabled, $post_id, $service_name, $connection );
682
683
				/**
684
				 * If this is a global connection and this user doesn't have enough permissions to modify
685
				 * those connections, don't let them change it.
686
				 */
687
				if ( ! $done && ( 0 == $connection_data['user_id'] && ! current_user_can( $this->GLOBAL_CAP ) ) ) {
688
					$toggleable = false;
689
690
					/**
691
					 * Filters the checkboxes for global connections with non-prilvedged users.
692
					 *
693
					 * @module publicize
694
					 *
695
					 * @since 3.7.0
696
					 *
697
					 * @param bool   $enabled Indicates if this connection should be enabled. Default true.
698
					 * @param int    $post_id ID of the current post
699
					 * @param string $service_name Name of the connection (Facebook, Twitter, etc)
700
					 * @param array  $connection Array of data about the connection.
701
					 */
702
					$enabled = apply_filters( 'publicize_checkbox_global_default', $enabled, $post_id, $service_name, $connection );
703
				}
704
705
				// Force the checkbox to be checked if the post was DONE, regardless of what the filter does.
706
				if ( $done ) {
707
					$enabled = true;
708
				}
709
710
				$connection_list[] = array(
711
					'unique_id'     => $unique_id,
712
					'service_name'  => $service_name,
713
					'service_label' => $this->get_service_label( $service_name ),
714
					'display_name'  => $this->get_display_name( $service_name, $connection ),
715
716
					'enabled'      => $enabled,
717
					'done'         => $done,
718
					'toggleable'   => $toggleable,
719
					'global'       => 0 == $connection_data['user_id'],
720
				);
721
			}
722
		}
723
724
		return $connection_list;
725
	}
726
727
	/**
728
	 * Checks if post has already been shared by Publicize in the past.
729
	 *
730
	 * @since 6.7.0
731
	 *
732
	 * @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...
733
	 *
734
	 * @return bool True if post has already been shared by Publicize, false otherwise.
735
	 */
736
	abstract public function post_is_done_sharing( $post_id = null );
737
738
	/**
739
	 * Retrieves full list of available Publicize connection services.
740
	 *
741
	 * Retrieves current available publicize service connections
742
	 * with associated labels and URLs.
743
	 *
744
	 * @since 6.7.0
745
	 *
746
	 * @return array {
747
	 *     Array of UI service connection data for all services
748
	 *
749
	 *     @type string 'name'  Name of service.
750
	 *     @type string 'label' Display label for service.
751
	 *     @type string 'url'   URL for adding connection to service.
752
	 * }
753
	 */
754
	function get_available_service_data() {
755
		$available_services     = $this->get_services( 'all' );
756
		$available_service_data = array();
757
758
		foreach ( $available_services as $service_name => $service ) {
759
			$available_service_data[] = array(
760
				'name'  => $service_name,
761
				'label' => $this->get_service_label( $service_name ),
762
				'url'   => $this->connect_url( $service_name ),
763
			);
764
		}
765
766
		return $available_service_data;
767
	}
768
769
/*
770
 * Site Data
771
 */
772
773
	function user_id() {
774
		return get_current_user_id();
775
	}
776
777
	function blog_id() {
778
		return get_current_blog_id();
779
	}
780
781
/*
782
 * Posts
783
 */
784
785
	/**
786
	 * Checks old and new status to see if the post should be flagged as
787
	 * ready to Publicize.
788
	 *
789
	 * Attached to the `transition_post_status` filter.
790
	 *
791
	 * @param string $new_status
792
	 * @param string $old_status
793
	 * @param WP_Post $post
794
	 * @return void
795
	 */
796
	abstract function flag_post_for_publicize( $new_status, $old_status, $post );
797
798
	/**
799
	 * Fires when a post is saved, checks conditions and saves state in postmeta so that it
800
	 * can be picked up later by @see ::publicize_post() on WordPress.com codebase.
801
	 *
802
	 * Attached to the `save_post` action.
803
	 *
804
	 * @param int $post_id
805
	 * @param WP_Post $post
806
	 * @return void
807
	 */
808
	function save_meta( $post_id, $post ) {
809
		$cron_user = null;
810
		$submit_post = true;
811
812
		if ( ! $this->post_type_is_publicizeable( $post->post_type ) )
813
			return;
814
815
		// Don't Publicize during certain contexts:
816
817
		// - import
818
		if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING  ) {
819
			$submit_post = false;
820
		}
821
822
		// - on quick edit, autosave, etc but do fire on p2, quickpress, and instapost ajax
823
		if (
824
			defined( 'DOING_AJAX' )
825
		&&
826
			DOING_AJAX
827
		&&
828
			!did_action( 'p2_ajax' )
829
		&&
830
			!did_action( 'wp_ajax_json_quickpress_post' )
831
		&&
832
			!did_action( 'wp_ajax_instapost_publish' )
833
		&&
834
			!did_action( 'wp_ajax_post_reblog' )
835
		&&
836
			!did_action( 'wp_ajax_press-this-save-post' )
837
		) {
838
			$submit_post = false;
839
		}
840
841
		// - bulk edit
842
		if ( isset( $_GET['bulk_edit'] ) ) {
843
			$submit_post = false;
844
		}
845
846
		// - API/XML-RPC Test Posts
847
		if (
848
			(
849
				defined( 'XMLRPC_REQUEST' )
850
			&&
851
				XMLRPC_REQUEST
852
			||
853
				defined( 'APP_REQUEST' )
854
			&&
855
				APP_REQUEST
856
			)
857
		&&
858
			0 === strpos( $post->post_title, 'Temporary Post Used For Theme Detection' )
859
		) {
860
			$submit_post = false;
861
		}
862
863
		// only work with certain statuses (avoids inherits, auto drafts etc)
864
		if ( !in_array( $post->post_status, array( 'publish', 'draft', 'future' ) ) ) {
865
			$submit_post = false;
866
		}
867
868
		// don't publish password protected posts
869
		if ( '' !== $post->post_password ) {
870
			$submit_post = false;
871
		}
872
873
		// Did this request happen via wp-admin?
874
		$from_web = isset( $_SERVER['REQUEST_METHOD'] )
875
			&&
876
			'post' == strtolower( $_SERVER['REQUEST_METHOD'] )
877
			&&
878
			isset( $_POST[$this->ADMIN_PAGE] );
879
880
		if ( ( $from_web || defined( 'POST_BY_EMAIL' ) ) && isset( $_POST['wpas_title'] ) ) {
881 View Code Duplication
			if ( empty( $_POST['wpas_title'] ) ) {
882
				delete_post_meta( $post_id, $this->POST_MESS );
883
			} else {
884
				update_post_meta( $post_id, $this->POST_MESS, trim( stripslashes( $_POST['wpas_title'] ) ) );
885
			}
886
		}
887
888
		// change current user to provide context for get_services() if we're running during cron
889
		if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
890
			$cron_user = (int) $GLOBALS['user_ID'];
891
			wp_set_current_user( $post->post_author );
892
		}
893
894
		/**
895
		 * In this phase, we mark connections that we want to SKIP. When Publicize is actually triggered,
896
		 * it will Publicize to everything *except* those marked for skipping.
897
		 */
898
		foreach ( (array) $this->get_services( 'connected' ) as $service_name => $connections ) {
899
			foreach ( $connections as $connection ) {
900
				$connection_data = '';
901
				if ( method_exists( $connection, 'get_meta' ) )
902
					$connection_data = $connection->get_meta( 'connection_data' );
903
				elseif ( ! empty( $connection['connection_data'] ) )
904
					$connection_data = $connection['connection_data'];
905
906
				/** This action is documented in modules/publicize/ui.php */
907
				if ( false == apply_filters( 'wpas_submit_post?', $submit_post, $post_id, $service_name, $connection_data ) ) {
908
					delete_post_meta( $post_id, $this->PENDING );
909
					continue;
910
				}
911
912 View Code Duplication
				if ( !empty( $connection->unique_id ) )
913
					$unique_id = $connection->unique_id;
914
				else if ( !empty( $connection['connection_data']['token_id'] ) )
915
					$unique_id = $connection['connection_data']['token_id'];
916
917
				// This was a wp-admin request, so we need to check the state of checkboxes
918
				if ( $from_web ) {
919
					// delete stray service-based post meta
920
					delete_post_meta( $post_id, $this->POST_SKIP . $service_name );
921
922
					// We *unchecked* this stream from the admin page, or it's set to readonly, or it's a new addition
923
					if ( empty( $_POST[$this->ADMIN_PAGE]['submit'][$unique_id] ) ) {
924
						// Also make sure that the service-specific input isn't there.
925
						// If the user connected to a new service 'in-page' then a hidden field with the service
926
						// name is added, so we just assume they wanted to Publicize to that service.
927 View Code Duplication
						if ( empty( $_POST[$this->ADMIN_PAGE]['submit'][$service_name] ) ) {
928
							// Nothing seems to be checked, so we're going to mark this one to be skipped
929
							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...
930
							continue;
931
						} else {
932
							// clean up any stray post meta
933
							delete_post_meta( $post_id, $this->POST_SKIP . $unique_id );
934
						}
935
					} else {
936
						// The checkbox for this connection is explicitly checked -- make sure we DON'T skip it
937
						delete_post_meta( $post_id, $this->POST_SKIP . $unique_id );
938
					}
939
				}
940
941
				/**
942
				 * Fires right before the post is processed for Publicize.
943
				 * Users may hook in here and do anything else they need to after meta is written,
944
				 * and before the post is processed for Publicize.
945
				 *
946
				 * @since 2.1.2
947
				 *
948
				 * @param bool $submit_post Should the post be publicized.
949
				 * @param int $post->ID Post ID.
950
				 * @param string $service_name Service name.
951
				 * @param array $connection Array of connection details.
952
				 */
953
				do_action( 'publicize_save_meta', $submit_post, $post_id, $service_name, $connection );
954
			}
955
		}
956
957
		if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
958
			wp_set_current_user( $cron_user );
959
		}
960
961
		// Next up will be ::publicize_post()
962
	}
963
964
	/**
965
	 * Alters the "Post Published" message to include information about where the post
966
	 * was Publicized to.
967
	 *
968
	 * Attached to the `post_updated_messages` filter
969
	 *
970
	 * @param string[] $messages
971
	 * @return string[]
972
	 */
973
	public function update_published_message( $messages ) {
974
		global $post_type, $post_type_object, $post;
975
		if ( ! $this->post_type_is_publicizeable( $post_type ) ) {
976
			return $messages;
977
		}
978
		$view_post_link_html = '';
979
		$viewable = is_post_type_viewable( $post_type_object );
980
		if ( $viewable ) {
981
			$view_text = esc_html__( 'View post' ); // intentionally omitted domain
982
983
			if ( 'jetpack-portfolio' == $post_type ) {
984
				$view_text = esc_html__( 'View project', 'jetpack' );
985
			}
986
987
			$view_post_link_html = sprintf( ' <a href="%1$s">%2$s</a>',
988
				esc_url( get_permalink( $post ) ),
989
				$view_text
990
			);
991
		}
992
993
		$services = $this->get_publicizing_services( $post->ID );
994
		if ( empty( $services ) ) {
995
			return $messages;
996
		}
997
998
		$labels = array();
999
		foreach ( $services as $service_name => $display_names ) {
1000
			$labels[] = sprintf(
1001
				/* translators: Service name is %1$s, and account name is %2$s. */
1002
				esc_html__( '%1$s (%2$s)', 'jetpack' ),
1003
				esc_html( $service_name ),
1004
				esc_html( implode( ', ', $display_names ) )
1005
			);
1006
		}
1007
1008
		$messages['post'][6] = sprintf(
1009
			/* translators: %1$s is a comma-separated list of services and accounts. Ex. Facebook (@jetpack), Twitter (@jetpack) */
1010
			esc_html__( 'Post published and sharing on %1$s.', 'jetpack' ),
1011
			implode( ', ', $labels )
1012
		) . $view_post_link_html;
1013
1014
		if ( $post_type == 'post' && class_exists('Jetpack_Subscriptions' ) ) {
1015
			$subscription = Jetpack_Subscriptions::init();
1016
			if ( $subscription->should_email_post_to_subscribers( $post ) ) {
1017
				$messages['post'][6] = sprintf(
1018
					/* translators: %1$s is a comma-separated list of services and accounts. Ex. Facebook (@jetpack), Twitter (@jetpack) */
1019
					esc_html__( 'Post published, sending emails to subscribers and sharing post on %1$s.', 'jetpack' ),
1020
					implode( ', ', $labels )
1021
				) . $view_post_link_html;
1022
			}
1023
		}
1024
1025
		$messages['jetpack-portfolio'][6] = sprintf(
1026
			/* translators: %1$s is a comma-separated list of services and accounts. Ex. Facebook (@jetpack), Twitter (@jetpack) */
1027
			esc_html__( 'Project published and sharing project on %1$s.', 'jetpack' ),
1028
			implode( ', ', $labels )
1029
		) . $view_post_link_html;
1030
1031
		return $messages;
1032
	}
1033
1034
	/**
1035
	 * Get the Connections the Post was just Publicized to.
1036
	 *
1037
	 * Only reliable just after the Post was published.
1038
	 *
1039
	 * @param int $post_id
1040
	 * @return string[] Array of Service display name => Connection display name
1041
	 */
1042
	function get_publicizing_services( $post_id ) {
1043
		$services = array();
1044
1045
		foreach ( (array) $this->get_services( 'connected' ) as $service_name => $connections ) {
1046
			// services have multiple connections.
1047
			foreach ( $connections as $connection ) {
1048
				$unique_id = '';
1049 View Code Duplication
				if ( ! empty( $connection->unique_id ) )
1050
					$unique_id = $connection->unique_id;
1051
				else if ( ! empty( $connection['connection_data']['token_id'] ) )
1052
					$unique_id = $connection['connection_data']['token_id'];
1053
1054
				// Did we skip this connection?
1055
				if ( get_post_meta( $post_id, $this->POST_SKIP . $unique_id,  true ) ) {
1056
					continue;
1057
				}
1058
				$services[ $this->get_service_label( $service_name ) ][] = $this->get_display_name( $service_name, $connection );
1059
			}
1060
		}
1061
1062
		return $services;
1063
	}
1064
1065
	/**
1066
	 * Is the post Publicize-able?
1067
	 *
1068
	 * Only valid prior to Publicizing a Post.
1069
	 *
1070
	 * @param WP_Post $post
1071
	 * @return bool
1072
	 */
1073
	function post_is_publicizeable( $post ) {
1074
		if ( ! $this->post_type_is_publicizeable( $post->post_type ) )
1075
			return false;
1076
1077
		// This is more a precaution. To only publicize posts that are published. (Mostly relevant for Jetpack sites)
1078
		if ( 'publish' !== $post->post_status ) {
1079
			return false;
1080
		}
1081
1082
		// If it's not flagged as ready, then abort. @see ::flag_post_for_publicize()
1083
		if ( ! get_post_meta( $post->ID, $this->PENDING, true ) )
1084
			return false;
1085
1086
		return true;
1087
	}
1088
1089
	/**
1090
	 * Is a given post type Publicize-able?
1091
	 *
1092
	 * Not every CPT lends itself to Publicize-ation.  Allow CPTs to register by adding their CPT via
1093
	 * the publicize_post_types array filter.
1094
	 *
1095
	 * @param string $post_type The post type to check.
1096
	 * @return bool True if the post type can be Publicized.
1097
	 */
1098
	function post_type_is_publicizeable( $post_type ) {
1099
		if ( 'post' == $post_type )
1100
			return true;
1101
1102
		return post_type_supports( $post_type, 'publicize' );
1103
	}
1104
1105
	/**
1106
	 * Already-published posts should not be Publicized by default. This filter sets checked to
1107
	 * false if a post has already been published.
1108
	 *
1109
	 * Attached to the `publicize_checkbox_default` filter
1110
	 *
1111
	 * @param bool $checked
1112
	 * @param int $post_id
1113
	 * @param string $service_name 'facebook', 'twitter', etc
1114
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
1115
	 * @return bool
1116
	 */
1117
	function publicize_checkbox_default( $checked, $post_id, $service_name, $connection ) {
1118
		if ( 'publish' == get_post_status( $post_id ) ) {
1119
			return false;
1120
		}
1121
1122
		return $checked;
1123
	}
1124
1125
	/**
1126
	 * Set up Publicize meta fields for publishing post.
1127
	 *
1128
	 * Process 'publicize' REST field to setup Publicize for publishing
1129
	 * post. Sets post meta keys to enable/disable each connection for
1130
	 * the post and sets publicize title meta key if a title message
1131
	 * is provided.
1132
	 *
1133
	 * @since 6.7.0
1134
	 *
1135
	 * @param stdClass        $new_post_obj Updated post object about to be inserted view REST endpoint.
1136
	 * @param WP_REST_Request $request      Request object, possibly containing 'publicize' field {@see add_publicize_rest_fields()}.
1137
	 *
1138
	 * @return WP_Post Returns the original $new_post value unchanged.
1139
	 */
1140
	public function process_publicize_from_rest( $new_post_obj, $request ) {
1141
		if ( property_exists( $new_post_obj, 'ID' ) ) {
1142
			$post = get_post( $new_post_obj->ID );
1143
		} else {
1144
			return $new_post_obj;
1145
		}
1146
1147
		// If 'publicize' field has been set from editor and post is about to be published.
1148
		if ( isset( $request['publicize'] )
1149
				&& ( property_exists( $new_post_obj, 'post_status' ) && ( 'publish' === $new_post_obj->post_status ) )
1150
				&& ( 'publish' !== $post->post_status ) ) {
1151
1152
			$publicize_field = $request['publicize'];
1153
1154 View Code Duplication
			if ( empty( $publicize_field['title'] ) ) {
1155
				delete_post_meta( $post->ID, $this->POST_MESS );
1156
			} else {
1157
				update_post_meta( $post->ID, $this->POST_MESS, trim( stripslashes( $publicize_field['title'] ) ) );
1158
			}
1159
			if ( isset( $publicize_field['connections'] ) ) {
1160
				foreach ( (array) $this->get_services( 'connected' ) as $service_name => $connections ) {
1161
					foreach ( $connections as $connection ) {
1162 View Code Duplication
						if ( ! empty( $connection->unique_id ) ) {
1163
							$unique_id = $connection->unique_id;
1164
						} elseif ( ! empty( $connection['connection_data']['token_id'] ) ) {
1165
							$unique_id = $connection['connection_data']['token_id'];
1166
						}
1167
1168 View Code Duplication
						if ( $this->connection_should_share( $publicize_field['connections'], $unique_id ) ) {
1169
							// Delete skip flag meta key.
1170
							delete_post_meta( $post->ID, $this->POST_SKIP . $unique_id );
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...
1171
						} else {
1172
							// Flag connection to be skipped for this post.
1173
							update_post_meta( $post->ID, $this->POST_SKIP . $unique_id, 1 );
1174
						}
1175
					}
1176
				}
1177
			}
1178
		}
1179
		// Just pass post object through.
1180
		return $new_post_obj;
1181
	}
1182
1183
	/**
1184
	 * Checks if a connection should be shared to.
1185
	 *
1186
	 * Checks $connection_id against $connections_array to see if the connection associated
1187
	 * with $connection_id should be shared to. Will return true if $connection_id is in the
1188
	 * array and 'should_share' property is set to true, and will default to false otherwise.
1189
	 *
1190
	 * @since 6.7.0
1191
	 *
1192
	 * @param array  $connections_array 'connections' from 'publicize' REST field {@see add_publicize_rest_fields()}.
1193
	 * @param string $connection_id     Connection identifier string that is unique for each connection.
1194
	 * @return boolean True if connection should be shared to, false otherwise.
1195
	 */
1196
	private function connection_should_share( $connections_array, $connection_id ) {
1197
		foreach ( $connections_array as $connection ) {
1198
			if ( isset( $connection['unique_id'] )
1199
				&& ( $connection['unique_id'] === $connection_id )
1200
				&& $connection['should_share'] ) {
1201
				return true;
1202
			}
1203
		}
1204
		return false;
1205
	}
1206
1207
/*
1208
 * Util
1209
 */
1210
1211
	/**
1212
	 * Converts a Publicize message template string into a sprintf format string
1213
	 *
1214
	 * @param string[] $args
1215
	 *               0 - The Publicize message template: 'Check out my post: %title% @ %url'
1216
	 *             ... - The template tags 'title', 'url', etc.
1217
	 * @return string
1218
	 */
1219
	protected static function build_sprintf( $args ) {
1220
		$search = array();
1221
		$replace = array();
1222
		foreach ( $args as $k => $arg ) {
1223
			if ( 0 == $k ) {
1224
				$string = $arg;
1225
				continue;
1226
			}
1227
			$search[] = "%$arg%";
1228
			$replace[] = "%$k\$s";
1229
		}
1230
		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...
1231
	}
1232
}
1233
1234
function publicize_calypso_url() {
1235
	$calypso_sharing_url = 'https://wordpress.com/sharing/';
1236
	if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'build_raw_urls' ) ) {
1237
		$site_suffix = Jetpack::build_raw_urls( home_url() );
1238
	} elseif ( class_exists( 'WPCOM_Masterbar' ) && method_exists( 'WPCOM_Masterbar', 'get_calypso_site_slug' ) ) {
1239
		$site_suffix = WPCOM_Masterbar::get_calypso_site_slug( get_current_blog_id() );
1240
	}
1241
1242
	if ( $site_suffix ) {
1243
		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...
1244
	} else {
1245
		return $calypso_sharing_url;
1246
	}
1247
}
1248