Completed
Push — update/editor-blocks-icon-colo... ( 093ab2...3cfb5e )
by
unknown
08:47
created

Publicize_Base::__construct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 76

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 76
rs 8.5236
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
use Automattic\Jetpack\Redirect;
4
5
abstract class Publicize_Base {
6
7
	/**
8
	* Services that are currently connected to the given user
9
	* through publicize.
10
	*/
11
	public $connected_services = array();
12
13
	/**
14
	* Services that are supported by publicize. They don't
15
	* necessarily need to be connected to the current user.
16
	*/
17
	public $services;
18
19
	/**
20
	* key names for post meta
21
	*/
22
	public $ADMIN_PAGE        = 'wpas';
23
	public $POST_MESS         = '_wpas_mess';
24
	public $POST_SKIP         = '_wpas_skip_'; // connection id appended to indicate that a connection should NOT be publicized to
25
	public $POST_DONE         = '_wpas_done_'; // connection id appended to indicate a connection has already been publicized to
26
	public $USER_AUTH         = 'wpas_authorize';
27
	public $USER_OPT          = 'wpas_';
28
	public $PENDING           = '_publicize_pending'; // ready for Publicize to do its thing
29
	public $POST_SERVICE_DONE = '_publicize_done_external'; // array of external ids where we've Publicized
30
31
	/**
32
	* default pieces of the message used in constructing the
33
	* content pushed out to other social networks
34
	*/
35
36
	public $default_prefix  = '';
37
	public $default_message = '%title%';
38
	public $default_suffix  = ' ';
39
40
	/**
41
	 * What WP capability is require to create/delete global connections?
42
	 * All users with this cap can un-globalize all other global connections, and globalize any of their own
43
	 * Globalized connections cannot be unselected by users without this capability when publishing
44
	 */
45
	public $GLOBAL_CAP = 'publish_posts';
46
47
	/**
48
	* Sets up the basics of Publicize
49
	*/
50
	function __construct() {
51
		$this->default_message = self::build_sprintf( array(
52
			/**
53
			 * Filter the default Publicize message.
54
			 *
55
			 * @module publicize
56
			 *
57
			 * @since 2.0.0
58
			 *
59
			 * @param string $this->default_message Publicize's default message. Default is the post title.
60
			 */
61
			apply_filters( 'wpas_default_message', $this->default_message ),
62
			'title',
63
			'url',
64
		) );
65
66
		$this->default_prefix = self::build_sprintf( array(
67
			/**
68
			 * Filter the message prepended to the Publicize custom message.
69
			 *
70
			 * @module publicize
71
			 *
72
			 * @since 2.0.0
73
			 *
74
			 * @param string $this->default_prefix String prepended to the Publicize custom message.
75
			 */
76
			apply_filters( 'wpas_default_prefix', $this->default_prefix ),
77
			'url',
78
		) );
79
80
		$this->default_suffix = self::build_sprintf( array(
81
			/**
82
			 * Filter the message appended to the Publicize custom message.
83
			 *
84
			 * @module publicize
85
			 *
86
			 * @since 2.0.0
87
			 *
88
			 * @param string $this->default_suffix String appended to the Publicize custom message.
89
			 */
90
			apply_filters( 'wpas_default_suffix', $this->default_suffix ),
91
			'url',
92
		) );
93
94
		/**
95
		 * Filter the capability to change global Publicize connection options.
96
		 *
97
		 * All users with this cap can un-globalize all other global connections, and globalize any of their own
98
		 * Globalized connections cannot be unselected by users without this capability when publishing.
99
		 *
100
		 * @module publicize
101
		 *
102
		 * @since 2.2.1
103
		 *
104
		 * @param string $this->GLOBAL_CAP default capability in control of global Publicize connection options. Default to edit_others_posts.
105
		 */
106
		$this->GLOBAL_CAP = apply_filters( 'jetpack_publicize_global_connections_cap', $this->GLOBAL_CAP );
107
108
		// stage 1 and 2 of 3-stage Publicize. Flag for Publicize on creation, save meta,
109
		// then check meta and publicize based on that. stage 3 implemented on wpcom
110
		add_action( 'transition_post_status', array( $this, 'flag_post_for_publicize' ), 10, 3 );
111
		add_action( 'save_post', array( &$this, 'save_meta' ), 20, 2 );
112
113
		// Default checkbox state for each Connection
114
		add_filter( 'publicize_checkbox_default', array( $this, 'publicize_checkbox_default' ), 10, 4 );
115
116
		// Alter the "Post Publish" admin notice to mention the Connections we Publicized to.
117
		add_filter( 'post_updated_messages', array( $this, 'update_published_message' ), 20, 1 );
118
119
		// Connection test callback
120
		add_action( 'wp_ajax_test_publicize_conns', array( $this, 'test_publicize_conns' ) );
121
122
		add_action( 'init', array( $this, 'add_post_type_support' ) );
123
		add_action( 'init', array( $this, 'register_post_meta' ), 20 );
124
		add_action( 'jetpack_register_gutenberg_extensions', array( $this, 'register_gutenberg_extension' ) );
125
	}
126
127
/*
128
 * Services: Facebook, Twitter, etc.
129
 */
130
131
	/**
132
	 * Get services for the given blog and user.
133
	 *
134
	 * Can return all available services or just the ones with an active connection.
135
	 *
136
	 * @param string $filter
137
	 *        'all' (default) - Get all services available for connecting
138
	 *        'connected'     - Get all services currently connected
139
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
140
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
141
	 * @return array
142
	 */
143
	abstract function get_services( $filter = 'all', $_blog_id = false, $_user_id = false );
144
145
	function can_connect_service( $service_name ) {
146
		return true;
147
	}
148
149
	/**
150
	 * Does the given user have a connection to the service on the given blog?
151
	 *
152
	 * @param string $service_name 'facebook', 'twitter', etc.
153
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
154
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
155
	 * @return bool
156
	 */
157
	function is_enabled( $service_name, $_blog_id = false, $_user_id = false ) {
158
		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...
159
			$_blog_id = $this->blog_id();
160
161
		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...
162
			$_user_id = $this->user_id();
163
164
		$connections = $this->get_connections( $service_name, $_blog_id, $_user_id );
165
		return ( is_array( $connections ) && count( $connections ) > 0 ? true : false );
166
	}
167
168
	/**
169
	 * Generates a connection URL.
170
	 *
171
	 * This is the URL, which, when visited by the user, starts the authentication
172
	 * process required to forge a connection.
173
	 *
174
	 * @param string $service_name 'facebook', 'twitter', etc.
175
	 * @return string
176
	 */
177
	abstract function connect_url( $service_name );
178
179
	/**
180
	 * Generates a Connection refresh URL.
181
	 *
182
	 * This is the URL, which, when visited by the user, re-authenticates their
183
	 * connection to the service.
184
	 *
185
	 * @param string $service_name 'facebook', 'twitter', etc.
186
	 * @return string
187
	 */
188
	abstract function refresh_url( $service_name );
189
190
	/**
191
	 * Generates a disconnection URL.
192
	 *
193
	 * This is the URL, which, when visited by the user, breaks their connection
194
	 * with the service.
195
	 *
196
	 * @param string $service_name 'facebook', 'twitter', etc.
197
	 * @param string $connection_id Connection ID
198
	 * @return string
199
	 */
200
	abstract function disconnect_url( $service_name, $connection_id );
201
202
	/**
203
	 * Returns a display name for the Service
204
	 *
205
	 * @param string $service_name 'facebook', 'twitter', etc.
206
	 * @return string
207
	 */
208
	public static function get_service_label( $service_name ) {
209
		switch ( $service_name ) {
210
			case 'linkedin':
211
				return 'LinkedIn';
212
				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...
213
			case 'google_drive': // google-drive used to be called google_drive.
214
			case 'google-drive':
215
				return 'Google Drive';
216
				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...
217
			case 'twitter':
218
			case 'facebook':
219
			case 'tumblr':
220
			default:
221
				return ucfirst( $service_name );
222
				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...
223
		}
224
	}
225
226
/*
227
 * Connections: For each Service, there can be multiple connections
228
 * for a given user. For example, one user could be connected to Twitter
229
 * as both @jetpack and as @wordpressdotcom
230
 *
231
 * For historical reasons, Connections are represented as an object
232
 * on WordPress.com and as an array in Jetpack.
233
 */
234
235
	/**
236
	 * Get the active Connections of a Service
237
	 *
238
	 * @param string $service_name 'facebook', 'twitter', etc.
239
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
240
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
241
	 * @return false|object[]|array[] false if no connections exist
242
	 */
243
	abstract function get_connections( $service_name, $_blog_id = false, $_user_id = false );
244
245
	/**
246
	 * Get a single Connection of a Service
247
	 *
248
	 * @param string $service_name 'facebook', 'twitter', etc.
249
	 * @param string $connection_id Connection ID
250
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
251
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
252
	 * @return false|object[]|array[] false if no connections exist
253
	 */
254
	abstract function get_connection( $service_name, $connection_id, $_blog_id = false, $_user_id = false );
255
256
	/**
257
	 * Get the Connection ID.
258
	 *
259
	 * Note that this is different than the Connection's uniqueid.
260
	 *
261
	 * Via a quirk of history, ID is globally unique and unique_id
262
	 * is only unique per site.
263
	 *
264
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
265
	 * @return string
266
	 */
267
	abstract function get_connection_id( $connection );
268
269
	/**
270
	 * Get the Connection unique_id
271
	 *
272
	 * Note that this is different than the Connections ID.
273
	 *
274
	 * Via a quirk of history, ID is globally unique and unique_id
275
	 * is only unique per site.
276
	 *
277
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
278
	 * @return string
279
	 */
280
	abstract function get_connection_unique_id( $connection );
281
282
	/**
283
	 * Get the Connection's Meta data
284
	 *
285
	 * @param object|array Connection
286
	 * @return array Connection Meta
287
	 */
288
	abstract function get_connection_meta( $connection );
289
290
	/**
291
	 * Disconnect a Connection
292
	 *
293
	 * @param string $service_name 'facebook', 'twitter', etc.
294
	 * @param string $connection_id Connection ID
295
	 * @param false|int $_blog_id The blog ID. Use false (default) for the current blog
296
	 * @param false|int $_user_id The user ID. Use false (default) for the current user
297
	 * @param bool $force_delete Whether to skip permissions checks
298
	 * @return false|void False on failure. Void on success.
299
	 */
300
	abstract function disconnect( $service_name, $connection_id, $_blog_id = false, $_user_id = false, $force_delete = false );
301
302
	/**
303
	 * Globalizes a Connection
304
	 *
305
	 * @param string $connection_id Connection ID
306
	 * @return bool Falsey on failure. Truthy on success.
307
	 */
308
	abstract function globalize_connection( $connection_id );
309
310
	/**
311
	 * Unglobalizes a Connection
312
	 *
313
	 * @param string $connection_id Connection ID
314
	 * @return bool Falsey on failure. Truthy on success.
315
	 */
316
	abstract function unglobalize_connection( $connection_id );
317
318
	/**
319
	 * Returns an external URL to the Connection's profile
320
	 *
321
	 * @param string $service_name 'facebook', 'twitter', etc.
322
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
323
	 * @return false|string False on failure. URL on success.
324
	 */
325
	function get_profile_link( $service_name, $connection ) {
326
		$cmeta = $this->get_connection_meta( $connection );
327
328
		if ( isset( $cmeta['connection_data']['meta']['link'] ) ) {
329
			if ( 'facebook' == $service_name && 0 === strpos( wp_parse_url( $cmeta['connection_data']['meta']['link'], PHP_URL_PATH ), '/app_scoped_user_id/' ) ) {
0 ignored issues
show
Unused Code introduced by
The call to wp_parse_url() has too many arguments starting with PHP_URL_PATH.

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

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

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

Loading history...
330
				// App-scoped Facebook user IDs are not usable profile links
331
				return false;
332
			}
333
334
			return $cmeta['connection_data']['meta']['link'];
335 View Code Duplication
		} elseif ( 'facebook' == $service_name && isset( $cmeta['connection_data']['meta']['facebook_page'] ) ) {
336
			return 'https://facebook.com/' . $cmeta['connection_data']['meta']['facebook_page'];
337
		} elseif ( 'tumblr' == $service_name && isset( $cmeta['connection_data']['meta']['tumblr_base_hostname'] ) ) {
338
			 return 'https://' . $cmeta['connection_data']['meta']['tumblr_base_hostname'];
339
		} elseif ( 'twitter' == $service_name ) {
340
			return 'https://twitter.com/' . substr( $cmeta['external_display'], 1 ); // Has a leading '@'
341
		} else if ( 'linkedin' == $service_name ) {
342
			if ( !isset( $cmeta['connection_data']['meta']['profile_url'] ) ) {
343
				return false;
344
			}
345
346
			$profile_url_query = wp_parse_url( $cmeta['connection_data']['meta']['profile_url'], PHP_URL_QUERY );
0 ignored issues
show
Unused Code introduced by
The call to wp_parse_url() has too many arguments starting with PHP_URL_QUERY.

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

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

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

Loading history...
347
			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...
348
			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...
349
				$id = $profile_url_query_args['key'];
350
			} 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...
351
				$id = $profile_url_query_args['id'];
352
			} else {
353
				return false;
354
			}
355
356
			return esc_url_raw( add_query_arg( 'id', urlencode( $id ), 'https://www.linkedin.com/profile/view' ) );
357
		} else {
358
			return false; // no fallback. we just won't link it
359
		}
360
	}
361
362
	/**
363
	 * Returns a display name for the Connection
364
	 *
365
	 * @param string $service_name 'facebook', 'twitter', etc.
366
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
367
	 * @return string
368
	 */
369
	function get_display_name( $service_name, $connection ) {
370
		$cmeta = $this->get_connection_meta( $connection );
371
372
		if ( isset( $cmeta['connection_data']['meta']['display_name'] ) ) {
373
			return $cmeta['connection_data']['meta']['display_name'];
374 View Code Duplication
		} elseif ( $service_name == 'tumblr' && isset( $cmeta['connection_data']['meta']['tumblr_base_hostname'] ) ) {
375
			 return $cmeta['connection_data']['meta']['tumblr_base_hostname'];
376
		} elseif ( $service_name == 'twitter' ) {
377
			return $cmeta['external_display'];
378
		} else {
379
			$connection_display = $cmeta['external_display'];
380
			if ( empty( $connection_display ) )
381
				$connection_display = $cmeta['external_name'];
382
			return $connection_display;
383
		}
384
	}
385
386
	/**
387
	 * Whether the user needs to select additional options after connecting
388
	 *
389
	 * @param string $service_name 'facebook', 'twitter', etc.
390
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
391
	 * @return bool
392
	 */
393
	function show_options_popup( $service_name, $connection ) {
394
		$cmeta = $this->get_connection_meta( $connection );
395
396
		// always show if no selection has been made for facebook
397
		if ( 'facebook' == $service_name && empty( $cmeta['connection_data']['meta']['facebook_profile'] ) && empty( $cmeta['connection_data']['meta']['facebook_page'] ) )
398
			return true;
399
400
		// always show if no selection has been made for tumblr
401
		if ( 'tumblr' == $service_name && empty ( $cmeta['connection_data']['meta']['tumblr_base_hostname'] ) )
402
			return true;
403
404
		// if we have the specific connection info..
405
		if ( isset( $_GET['id'] ) ) {
406
			if ( $cmeta['connection_data']['id'] == $_GET['id'] )
407
				return true;
408
		} else {
409
			// otherwise, just show if this is the completed step / first load
410
			if ( !empty( $_GET['action'] ) && 'completed' == $_GET['action'] && !empty( $_GET['service'] ) && $service_name == $_GET['service'] && ! in_array( $_GET['service'], array( 'facebook', 'tumblr' ) ) )
411
				return true;
412
		}
413
414
		return false;
415
	}
416
417
	/**
418
	 * Whether the Connection is "valid" wrt Facebook's requirements.
419
	 *
420
	 * Must be connected to a Page (not a Profile).
421
	 * (Also returns true if we're in the middle of the connection process)
422
	 *
423
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
424
	 * @return bool
425
	 */
426
	function is_valid_facebook_connection( $connection ) {
427
		if ( $this->is_connecting_connection( $connection ) ) {
428
			return true;
429
		}
430
		$connection_meta = $this->get_connection_meta( $connection );
431
		$connection_data = $connection_meta['connection_data'];
432
		return isset( $connection_data[ 'meta' ][ 'facebook_page' ] );
433
	}
434
435
	/**
436
	 * LinkedIn needs to be reauthenticated to use v2 of their API.
437
	 * If it's using LinkedIn old API, it's an 'invalid' connection
438
	 *
439
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
440
	 * @return bool
441
	 */
442
	function is_invalid_linkedin_connection( $connection ) {
443
		// LinkedIn API v1 included the profile link in the connection data.
444
		$connection_meta = $this->get_connection_meta( $connection );
445
		return isset( $connection_meta['connection_data']['meta']['profile_url'] );
446
	}
447
448
	/**
449
	 * Whether the Connection currently being connected
450
	 *
451
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
452
	 * @return bool
453
	 */
454
	function is_connecting_connection( $connection ) {
455
		$connection_meta = $this->get_connection_meta( $connection );
456
		$connection_data = $connection_meta['connection_data'];
457
		return isset( $connection_data[ 'meta' ]['options_responses'] );
458
	}
459
460
	/**
461
	 * AJAX Handler to run connection tests on all Connections
462
	 * @return void
463
	 */
464
	function test_publicize_conns() {
465
		wp_send_json_success( $this->get_publicize_conns_test_results() );
466
	}
467
468
	/**
469
	 * Run connection tests on all Connections
470
	 *
471
	 * @return array {
472
	 *     Array of connection test results.
473
	 *
474
	 *     @type string 'connectionID'          Connection identifier string that is unique for each connection
475
	 *     @type string 'serviceName'           Slug of the connection's service (facebook, twitter, ...)
476
	 *     @type bool   'connectionTestPassed'  Whether the connection test was successful
477
	 *     @type string 'connectionTestMessage' Test success or error message
478
	 *     @type bool   'userCanRefresh'        Whether the user can re-authenticate their connection to the service
479
	 *     @type string 'refreshText'           Message instructing user to re-authenticate their connection to the service
480
	 *     @type string 'refreshURL'            URL, which, when visited by the user, re-authenticates their connection to the service.
481
	 *     @type string 'unique_id'             ID string representing connection
482
	 * }
483
	 */
484
	function get_publicize_conns_test_results() {
485
		$test_results = array();
486
487
		foreach ( (array) $this->get_services( 'connected' ) as $service_name => $connections ) {
488
			foreach ( $connections as $connection ) {
489
490
				$id = $this->get_connection_id( $connection );
491
492
				$connection_test_passed = true;
493
				$connection_test_message = __( 'This connection is working correctly.' , 'jetpack' );
494
				$user_can_refresh = false;
495
				$refresh_text = '';
496
				$refresh_url = '';
497
498
				$connection_test_result = true;
499
				if ( method_exists( $this, 'test_connection' ) ) {
500
					$connection_test_result = $this->test_connection( $service_name, $connection );
501
				}
502
503
				if ( is_wp_error( $connection_test_result ) ) {
504
					$connection_test_passed = false;
505
					$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...
506
					$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...
507
508
					$user_can_refresh = $error_data['user_can_refresh'];
509
					$refresh_text = $error_data['refresh_text'];
510
					$refresh_url = $error_data['refresh_url'];
511
				}
512
				// Mark facebook profiles as deprecated
513
				if ( 'facebook' === $service_name ) {
514
					if ( ! $this->is_valid_facebook_connection( $connection ) ) {
515
						$connection_test_passed = false;
516
						$user_can_refresh = false;
517
						$connection_test_message = __( 'Please select a Facebook Page to publish updates.', 'jetpack' );
518
					}
519
				}
520
521
				// LinkedIn needs reauthentication to be compatible with v2 of their API
522
				if ( 'linkedin' === $service_name && $this->is_invalid_linkedin_connection( $connection ) ) {
523
					$connection_test_passed = 'must_reauth';
524
					$user_can_refresh = false;
525
					$connection_test_message = esc_html__( 'Your LinkedIn connection needs to be reauthenticated to continue working – head to Sharing to take care of it.', 'jetpack' );
526
				}
527
528
				$unique_id = null;
529 View Code Duplication
				if ( ! empty( $connection->unique_id ) ) {
530
					$unique_id = $connection->unique_id;
531
				} else if ( ! empty( $connection['connection_data']['token_id'] ) ) {
532
					$unique_id = $connection['connection_data']['token_id'];
533
				}
534
535
				$test_results[] = array(
536
					'connectionID'          => $id,
537
					'serviceName'           => $service_name,
538
					'connectionTestPassed'  => $connection_test_passed,
539
					'connectionTestMessage' => esc_attr( $connection_test_message ),
540
					'userCanRefresh'        => $user_can_refresh,
541
					'refreshText'           => esc_attr( $refresh_text ),
542
					'refreshURL'            => $refresh_url,
543
					'unique_id'             => $unique_id,
544
				);
545
			}
546
		}
547
548
		return $test_results;
549
	}
550
551
	/**
552
	 * Run the connection test for the Connection
553
	 *
554
	 * @param string $service_name 'facebook', 'twitter', etc.
555
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
556
	 * @return WP_Error|true WP_Error on failure. True on success
557
	 */
558
	abstract function test_connection( $service_name, $connection );
559
560
	/**
561
	 * Retrieves current list of connections and applies filters.
562
	 *
563
	 * Retrieves current available connections and checks if the connections
564
	 * have already been used to share current post. Finally, the checkbox
565
	 * form UI fields are calculated. This function exposes connection form
566
	 * data directly as array so it can be retrieved for static HTML generation
567
	 * or JSON consumption.
568
	 *
569
	 * @since 6.7.0
570
	 *
571
	 * @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...
572
	 *
573
	 * @return array {
574
	 *     Array of UI setup data for connection list form.
575
	 *
576
	 *     @type string 'unique_id'     ID string representing connection
577
	 *     @type string 'service_name'  Slug of the connection's service (facebook, twitter, ...)
578
	 *     @type string 'service_label' Service Label (Facebook, Twitter, ...)
579
	 *     @type string 'display_name'  Connection's human-readable Username: "@jetpack"
580
	 *     @type bool   'enabled'       Default value for the connection (e.g., for a checkbox).
581
	 *     @type bool   'done'          Has this connection already been publicized to?
582
	 *     @type bool   'toggleable'    Is the user allowed to change the value for the connection?
583
	 *     @type bool   'global'        Is this connection a global one?
584
	 * }
585
	 */
586
	public function get_filtered_connection_data( $selected_post_id = null ) {
587
		$connection_list = array();
588
589
		$post = get_post( $selected_post_id ); // Defaults to current post if $post_id is null.
590
		// Handle case where there is no current post.
591
		if ( ! empty( $post ) ) {
592
			$post_id = $post->ID;
593
		} else {
594
			$post_id = null;
595
		}
596
597
		$services = $this->get_services( 'connected' );
598
		$all_done = $this->post_is_done_sharing( $post_id );
599
600
		// We don't allow Publicizing to the same external id twice, to prevent spam.
601
		$service_id_done = (array) get_post_meta( $post_id, $this->POST_SERVICE_DONE, true );
602
603
		foreach ( $services as $service_name => $connections ) {
604
			foreach ( $connections as $connection ) {
605
				$connection_meta = $this->get_connection_meta( $connection );
606
				$connection_data = $connection_meta['connection_data'];
607
608
				$unique_id = $this->get_connection_unique_id( $connection );
609
610
611
				// Was this connection (OR, old-format service) already Publicized to?
612
				$done = ! empty( $post ) && (
613
					// New flags
614
					1 == get_post_meta( $post->ID, $this->POST_DONE . $unique_id, true )
615
					||
616
					// old flags
617
					1 == get_post_meta( $post->ID, $this->POST_DONE . $service_name, true )
618
				);
619
620
				/**
621
				 * Filter whether a post should be publicized to a given service.
622
				 *
623
				 * @module publicize
624
				 *
625
				 * @since 2.0.0
626
				 *
627
				 * @param bool true Should the post be publicized to a given service? Default to true.
628
				 * @param int $post_id Post ID.
629
				 * @param string $service_name Service name.
630
				 * @param array $connection_data Array of information about all Publicize details for the site.
631
				 */
632
				if ( ! apply_filters( 'wpas_submit_post?', true, $post_id, $service_name, $connection_data ) ) {
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $post_id.

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

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

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

Loading history...
633
					continue;
634
				}
635
636
				// Should we be skipping this one?
637
				$skip = (
638
					(
639
						! empty( $post )
640
						&&
641
						in_array( $post->post_status, array( 'publish', 'draft', 'future' ) )
642
						&&
643
						(
644
							// New flags
645
							get_post_meta( $post->ID, $this->POST_SKIP . $unique_id, true )
646
							||
647
							// Old flags
648
							get_post_meta( $post->ID, $this->POST_SKIP . $service_name )
649
						)
650
					)
651
					||
652
					(
653
						is_array( $connection )
654
						&&
655
						isset( $connection_meta['external_id'] ) && ! empty( $service_id_done[ $service_name ][ $connection_meta['external_id'] ] )
656
					)
657
				);
658
659
				// If this one has already been publicized to, don't let it happen again.
660
				$toggleable = ! $done && ! $all_done;
661
662
				// Determine the state of the checkbox (on/off) and allow filtering.
663
				$enabled = $done || ! $skip;
664
				/**
665
				 * Filter the checkbox state of each Publicize connection appearing in the post editor.
666
				 *
667
				 * @module publicize
668
				 *
669
				 * @since 2.0.1
670
				 *
671
				 * @param bool $enabled Should the Publicize checkbox be enabled for a given service.
672
				 * @param int $post_id Post ID.
673
				 * @param string $service_name Service name.
674
				 * @param array $connection Array of connection details.
675
				 */
676
				$enabled = apply_filters( 'publicize_checkbox_default', $enabled, $post_id, $service_name, $connection );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $post_id.

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

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

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

Loading history...
677
678
				/**
679
				 * If this is a global connection and this user doesn't have enough permissions to modify
680
				 * those connections, don't let them change it.
681
				 */
682
				if ( ! $done && ( 0 == $connection_data['user_id'] && ! current_user_can( $this->GLOBAL_CAP ) ) ) {
683
					$toggleable = false;
684
685
					/**
686
					 * Filters the checkboxes for global connections with non-prilvedged users.
687
					 *
688
					 * @module publicize
689
					 *
690
					 * @since 3.7.0
691
					 *
692
					 * @param bool   $enabled Indicates if this connection should be enabled. Default true.
693
					 * @param int    $post_id ID of the current post
694
					 * @param string $service_name Name of the connection (Facebook, Twitter, etc)
695
					 * @param array  $connection Array of data about the connection.
696
					 */
697
					$enabled = apply_filters( 'publicize_checkbox_global_default', $enabled, $post_id, $service_name, $connection );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $post_id.

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

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

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

Loading history...
698
				}
699
700
				// Force the checkbox to be checked if the post was DONE, regardless of what the filter does.
701
				if ( $done ) {
702
					$enabled = true;
703
				}
704
705
				$connection_list[] = array(
706
					'unique_id'     => $unique_id,
707
					'service_name'  => $service_name,
708
					'service_label' => $this->get_service_label( $service_name ),
709
					'display_name'  => $this->get_display_name( $service_name, $connection ),
710
711
					'enabled'      => $enabled,
712
					'done'         => $done,
713
					'toggleable'   => $toggleable,
714
					'global'       => 0 == $connection_data['user_id'],
715
				);
716
			}
717
		}
718
719
		return $connection_list;
720
	}
721
722
	/**
723
	 * Checks if post has already been shared by Publicize in the past.
724
	 *
725
	 * @since 6.7.0
726
	 *
727
	 * @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...
728
	 *
729
	 * @return bool True if post has already been shared by Publicize, false otherwise.
730
	 */
731
	abstract public function post_is_done_sharing( $post_id = null );
732
733
	/**
734
	 * Retrieves full list of available Publicize connection services.
735
	 *
736
	 * Retrieves current available publicize service connections
737
	 * with associated labels and URLs.
738
	 *
739
	 * @since 6.7.0
740
	 *
741
	 * @return array {
742
	 *     Array of UI service connection data for all services
743
	 *
744
	 *     @type string 'name'  Name of service.
745
	 *     @type string 'label' Display label for service.
746
	 *     @type string 'url'   URL for adding connection to service.
747
	 * }
748
	 */
749
	function get_available_service_data() {
750
		$available_services     = $this->get_services( 'all' );
751
		$available_service_data = array();
752
753
		foreach ( $available_services as $service_name => $service ) {
754
			$available_service_data[] = array(
755
				'name'  => $service_name,
756
				'label' => $this->get_service_label( $service_name ),
757
				'url'   => $this->connect_url( $service_name ),
758
			);
759
		}
760
761
		return $available_service_data;
762
	}
763
764
/*
765
 * Site Data
766
 */
767
768
	function user_id() {
769
		return get_current_user_id();
770
	}
771
772
	function blog_id() {
773
		return get_current_blog_id();
774
	}
775
776
/*
777
 * Posts
778
 */
779
780
	/**
781
	 * Checks old and new status to see if the post should be flagged as
782
	 * ready to Publicize.
783
	 *
784
	 * Attached to the `transition_post_status` filter.
785
	 *
786
	 * @param string $new_status
787
	 * @param string $old_status
788
	 * @param WP_Post $post
789
	 * @return void
790
	 */
791
	abstract function flag_post_for_publicize( $new_status, $old_status, $post );
792
793
	/**
794
	 * Ensures the Post internal post-type supports `publicize`
795
	 *
796
	 * This feature support flag is used by the REST API.
797
	 */
798
	function add_post_type_support() {
799
		add_post_type_support( 'post', 'publicize' );
800
	}
801
802
	/**
803
	 * Register the Publicize Gutenberg extension
804
	 */
805
	function register_gutenberg_extension() {
806
		// TODO: The `gutenberg/available-extensions` endpoint currently doesn't accept a post ID,
807
		// so we cannot pass one to `$this->current_user_can_access_publicize_data()`.
808
809
		if ( $this->current_user_can_access_publicize_data() ) {
810
			Jetpack_Gutenberg::set_extension_available( 'jetpack/publicize' );
811
		} else {
812
			Jetpack_Gutenberg::set_extension_unavailable( 'jetpack/publicize', 'unauthorized' );
813
814
		}
815
	}
816
817
	/**
818
	 * Can the current user access Publicize Data.
819
	 *
820
	 * @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...
821
	 * @return bool
822
	 */
823
	function current_user_can_access_publicize_data( $post_id = 0 ) {
824
		/**
825
		 * 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.
826
		 *
827
		 * @module publicize
828
		 *
829
		 * @since 4.1.0
830
		 *
831
		 * @param string $capability User capability needed to use publicize
832
		 */
833
		$capability = apply_filters( 'jetpack_publicize_capability', 'publish_posts' );
834
835
		if ( 'publish_posts' === $capability && $post_id ) {
836
			return current_user_can( 'publish_post', $post_id );
837
		}
838
839
		return current_user_can( $capability );
840
	}
841
842
	/**
843
	 * Auth callback for the protected ->POST_MESS post_meta
844
	 *
845
	 * @param bool $allowed
846
	 * @param string $meta_key
847
	 * @param int $object_id Post ID
848
	 * @return bool
849
	 */
850
	function message_meta_auth_callback( $allowed, $meta_key, $object_id ) {
851
		return $this->current_user_can_access_publicize_data( $object_id );
852
	}
853
854
	/**
855
	 * Registers the ->POST_MESS post_meta for use in the REST API.
856
	 *
857
	 * Registers for each post type that with `publicize` feature support.
858
	 */
859
	function register_post_meta() {
860
		$args = array(
861
			'type' => 'string',
862
			'description' => __( 'The message to use instead of the title when sharing to Publicize Services', 'jetpack' ),
863
			'single' => true,
864
			'default' => '',
865
			'show_in_rest' => array(
866
				'name' => 'jetpack_publicize_message'
867
			),
868
			'auth_callback' => array( $this, 'message_meta_auth_callback' ),
869
		);
870
871
		foreach ( get_post_types() as $post_type ) {
872
			if ( ! $this->post_type_is_publicizeable( $post_type ) ) {
873
				continue;
874
			}
875
876
			$args['object_subtype'] = $post_type;
877
878
			register_meta( 'post', $this->POST_MESS, $args );
879
		}
880
	}
881
882
	/**
883
	 * Fires when a post is saved, checks conditions and saves state in postmeta so that it
884
	 * can be picked up later by @see ::publicize_post() on WordPress.com codebase.
885
	 *
886
	 * Attached to the `save_post` action.
887
	 *
888
	 * @param int $post_id
889
	 * @param WP_Post $post
890
	 * @return void
891
	 */
892
	function save_meta( $post_id, $post ) {
893
		$cron_user = null;
894
		$submit_post = true;
895
896
		if ( ! $this->post_type_is_publicizeable( $post->post_type ) )
897
			return;
898
899
		// Don't Publicize during certain contexts:
900
901
		// - import
902
		if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING  ) {
903
			$submit_post = false;
904
		}
905
906
		// - on quick edit, autosave, etc but do fire on p2, quickpress, and instapost ajax
907
		if (
908
			defined( 'DOING_AJAX' )
909
		&&
910
			DOING_AJAX
911
		&&
912
			!did_action( 'p2_ajax' )
913
		&&
914
			!did_action( 'wp_ajax_json_quickpress_post' )
915
		&&
916
			!did_action( 'wp_ajax_instapost_publish' )
917
		&&
918
			!did_action( 'wp_ajax_post_reblog' )
919
		&&
920
			!did_action( 'wp_ajax_press-this-save-post' )
921
		) {
922
			$submit_post = false;
923
		}
924
925
		// - bulk edit
926
		if ( isset( $_GET['bulk_edit'] ) ) {
927
			$submit_post = false;
928
		}
929
930
		// - API/XML-RPC Test Posts
931
		if (
932
			(
933
				defined( 'XMLRPC_REQUEST' )
934
			&&
935
				XMLRPC_REQUEST
936
			||
937
				defined( 'APP_REQUEST' )
938
			&&
939
				APP_REQUEST
940
			)
941
		&&
942
			0 === strpos( $post->post_title, 'Temporary Post Used For Theme Detection' )
943
		) {
944
			$submit_post = false;
945
		}
946
947
		// only work with certain statuses (avoids inherits, auto drafts etc)
948
		if ( !in_array( $post->post_status, array( 'publish', 'draft', 'future' ) ) ) {
949
			$submit_post = false;
950
		}
951
952
		// don't publish password protected posts
953
		if ( '' !== $post->post_password ) {
954
			$submit_post = false;
955
		}
956
957
		// Did this request happen via wp-admin?
958
		$from_web = isset( $_SERVER['REQUEST_METHOD'] )
959
			&&
960
			'post' == strtolower( $_SERVER['REQUEST_METHOD'] )
961
			&&
962
			isset( $_POST[$this->ADMIN_PAGE] );
963
964
		if ( ( $from_web || defined( 'POST_BY_EMAIL' ) ) && isset( $_POST['wpas_title'] ) ) {
965
			if ( empty( $_POST['wpas_title'] ) ) {
966
				delete_post_meta( $post_id, $this->POST_MESS );
967
			} else {
968
				update_post_meta( $post_id, $this->POST_MESS, trim( stripslashes( $_POST['wpas_title'] ) ) );
969
			}
970
		}
971
972
		// change current user to provide context for get_services() if we're running during cron
973
		if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
974
			$cron_user = (int) $GLOBALS['user_ID'];
975
			wp_set_current_user( $post->post_author );
976
		}
977
978
		/**
979
		 * In this phase, we mark connections that we want to SKIP. When Publicize is actually triggered,
980
		 * it will Publicize to everything *except* those marked for skipping.
981
		 */
982
		foreach ( (array) $this->get_services( 'connected' ) as $service_name => $connections ) {
983
			foreach ( $connections as $connection ) {
984
				$connection_data = '';
985
				if ( method_exists( $connection, 'get_meta' ) )
986
					$connection_data = $connection->get_meta( 'connection_data' );
987
				elseif ( ! empty( $connection['connection_data'] ) )
988
					$connection_data = $connection['connection_data'];
989
990
				/** This action is documented in modules/publicize/ui.php */
991
				if ( false == apply_filters( 'wpas_submit_post?', $submit_post, $post_id, $service_name, $connection_data ) ) {
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $post_id.

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

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

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

Loading history...
992
					delete_post_meta( $post_id, $this->PENDING );
993
					continue;
994
				}
995
996 View Code Duplication
				if ( !empty( $connection->unique_id ) )
997
					$unique_id = $connection->unique_id;
998
				else if ( !empty( $connection['connection_data']['token_id'] ) )
999
					$unique_id = $connection['connection_data']['token_id'];
1000
1001
				// This was a wp-admin request, so we need to check the state of checkboxes
1002
				if ( $from_web ) {
1003
					// delete stray service-based post meta
1004
					delete_post_meta( $post_id, $this->POST_SKIP . $service_name );
1005
1006
					// We *unchecked* this stream from the admin page, or it's set to readonly, or it's a new addition
1007
					if ( empty( $_POST[$this->ADMIN_PAGE]['submit'][$unique_id] ) ) {
1008
						// Also make sure that the service-specific input isn't there.
1009
						// If the user connected to a new service 'in-page' then a hidden field with the service
1010
						// name is added, so we just assume they wanted to Publicize to that service.
1011
						if ( empty( $_POST[$this->ADMIN_PAGE]['submit'][$service_name] ) ) {
1012
							// Nothing seems to be checked, so we're going to mark this one to be skipped
1013
							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...
1014
							continue;
1015
						} else {
1016
							// clean up any stray post meta
1017
							delete_post_meta( $post_id, $this->POST_SKIP . $unique_id );
1018
						}
1019
					} else {
1020
						// The checkbox for this connection is explicitly checked -- make sure we DON'T skip it
1021
						delete_post_meta( $post_id, $this->POST_SKIP . $unique_id );
1022
					}
1023
				}
1024
1025
				/**
1026
				 * Fires right before the post is processed for Publicize.
1027
				 * Users may hook in here and do anything else they need to after meta is written,
1028
				 * and before the post is processed for Publicize.
1029
				 *
1030
				 * @since 2.1.2
1031
				 *
1032
				 * @param bool $submit_post Should the post be publicized.
1033
				 * @param int $post->ID Post ID.
1034
				 * @param string $service_name Service name.
1035
				 * @param array $connection Array of connection details.
1036
				 */
1037
				do_action( 'publicize_save_meta', $submit_post, $post_id, $service_name, $connection );
1038
			}
1039
		}
1040
1041
		if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
1042
			wp_set_current_user( $cron_user );
1043
		}
1044
1045
		// Next up will be ::publicize_post()
1046
	}
1047
1048
	/**
1049
	 * Alters the "Post Published" message to include information about where the post
1050
	 * was Publicized to.
1051
	 *
1052
	 * Attached to the `post_updated_messages` filter
1053
	 *
1054
	 * @param string[] $messages
1055
	 * @return string[]
1056
	 */
1057
	public function update_published_message( $messages ) {
1058
		global $post_type, $post_type_object, $post;
1059
		if ( ! $this->post_type_is_publicizeable( $post_type ) ) {
1060
			return $messages;
1061
		}
1062
1063
		// Bail early if the post is private.
1064
		if ( 'publish' !== $post->post_status ) {
1065
			return $messages;
1066
		}
1067
1068
		$view_post_link_html = '';
1069
		$viewable = is_post_type_viewable( $post_type_object );
1070
		if ( $viewable ) {
1071
			$view_text = esc_html__( 'View post' ); // intentionally omitted domain
1072
1073
			if ( 'jetpack-portfolio' == $post_type ) {
1074
				$view_text = esc_html__( 'View project', 'jetpack' );
1075
			}
1076
1077
			$view_post_link_html = sprintf( ' <a href="%1$s">%2$s</a>',
1078
				esc_url( get_permalink( $post ) ),
1079
				$view_text
1080
			);
1081
		}
1082
1083
		$services = $this->get_publicizing_services( $post->ID );
1084
		if ( empty( $services ) ) {
1085
			return $messages;
1086
		}
1087
1088
		$labels = array();
1089
		foreach ( $services as $service_name => $display_names ) {
1090
			$labels[] = sprintf(
1091
				/* translators: Service name is %1$s, and account name is %2$s. */
1092
				esc_html__( '%1$s (%2$s)', 'jetpack' ),
1093
				esc_html( $service_name ),
1094
				esc_html( implode( ', ', $display_names ) )
1095
			);
1096
		}
1097
1098
		$messages['post'][6] = sprintf(
1099
			/* translators: %1$s is a comma-separated list of services and accounts. Ex. Facebook (@jetpack), Twitter (@jetpack) */
1100
			esc_html__( 'Post published and sharing on %1$s.', 'jetpack' ),
1101
			implode( ', ', $labels )
1102
		) . $view_post_link_html;
1103
1104
		if ( $post_type == 'post' && class_exists('Jetpack_Subscriptions' ) ) {
1105
			$subscription = Jetpack_Subscriptions::init();
1106
			if ( $subscription->should_email_post_to_subscribers( $post ) ) {
1107
				$messages['post'][6] = sprintf(
1108
					/* translators: %1$s is a comma-separated list of services and accounts. Ex. Facebook (@jetpack), Twitter (@jetpack) */
1109
					esc_html__( 'Post published, sending emails to subscribers and sharing post on %1$s.', 'jetpack' ),
1110
					implode( ', ', $labels )
1111
				) . $view_post_link_html;
1112
			}
1113
		}
1114
1115
		$messages['jetpack-portfolio'][6] = sprintf(
1116
			/* translators: %1$s is a comma-separated list of services and accounts. Ex. Facebook (@jetpack), Twitter (@jetpack) */
1117
			esc_html__( 'Project published and sharing project on %1$s.', 'jetpack' ),
1118
			implode( ', ', $labels )
1119
		) . $view_post_link_html;
1120
1121
		return $messages;
1122
	}
1123
1124
	/**
1125
	 * Get the Connections the Post was just Publicized to.
1126
	 *
1127
	 * Only reliable just after the Post was published.
1128
	 *
1129
	 * @param int $post_id
1130
	 * @return string[] Array of Service display name => Connection display name
1131
	 */
1132
	function get_publicizing_services( $post_id ) {
1133
		$services = array();
1134
1135
		foreach ( (array) $this->get_services( 'connected' ) as $service_name => $connections ) {
1136
			// services have multiple connections.
1137
			foreach ( $connections as $connection ) {
1138
				$unique_id = '';
1139 View Code Duplication
				if ( ! empty( $connection->unique_id ) )
1140
					$unique_id = $connection->unique_id;
1141
				else if ( ! empty( $connection['connection_data']['token_id'] ) )
1142
					$unique_id = $connection['connection_data']['token_id'];
1143
1144
				// Did we skip this connection?
1145
				if ( get_post_meta( $post_id, $this->POST_SKIP . $unique_id,  true ) ) {
1146
					continue;
1147
				}
1148
				$services[ $this->get_service_label( $service_name ) ][] = $this->get_display_name( $service_name, $connection );
1149
			}
1150
		}
1151
1152
		return $services;
1153
	}
1154
1155
	/**
1156
	 * Is the post Publicize-able?
1157
	 *
1158
	 * Only valid prior to Publicizing a Post.
1159
	 *
1160
	 * @param WP_Post $post
1161
	 * @return bool
1162
	 */
1163
	function post_is_publicizeable( $post ) {
1164
		if ( ! $this->post_type_is_publicizeable( $post->post_type ) )
1165
			return false;
1166
1167
		// This is more a precaution. To only publicize posts that are published. (Mostly relevant for Jetpack sites)
1168
		if ( 'publish' !== $post->post_status ) {
1169
			return false;
1170
		}
1171
1172
		// If it's not flagged as ready, then abort. @see ::flag_post_for_publicize()
1173
		if ( ! get_post_meta( $post->ID, $this->PENDING, true ) )
1174
			return false;
1175
1176
		return true;
1177
	}
1178
1179
	/**
1180
	 * Is a given post type Publicize-able?
1181
	 *
1182
	 * Not every CPT lends itself to Publicize-ation.  Allow CPTs to register by adding their CPT via
1183
	 * the publicize_post_types array filter.
1184
	 *
1185
	 * @param string $post_type The post type to check.
1186
	 * @return bool True if the post type can be Publicized.
1187
	 */
1188
	function post_type_is_publicizeable( $post_type ) {
1189
		if ( 'post' == $post_type )
1190
			return true;
1191
1192
		return post_type_supports( $post_type, 'publicize' );
1193
	}
1194
1195
	/**
1196
	 * Already-published posts should not be Publicized by default. This filter sets checked to
1197
	 * false if a post has already been published.
1198
	 *
1199
	 * Attached to the `publicize_checkbox_default` filter
1200
	 *
1201
	 * @param bool $checked
1202
	 * @param int $post_id
1203
	 * @param string $service_name 'facebook', 'twitter', etc
1204
	 * @param object|array The Connection object (WordPress.com) or array (Jetpack)
1205
	 * @return bool
1206
	 */
1207
	function publicize_checkbox_default( $checked, $post_id, $service_name, $connection ) {
1208
		if ( 'publish' == get_post_status( $post_id ) ) {
1209
			return false;
1210
		}
1211
1212
		return $checked;
1213
	}
1214
1215
/*
1216
 * Util
1217
 */
1218
1219
	/**
1220
	 * Converts a Publicize message template string into a sprintf format string
1221
	 *
1222
	 * @param string[] $args
1223
	 *               0 - The Publicize message template: 'Check out my post: %title% @ %url'
1224
	 *             ... - The template tags 'title', 'url', etc.
1225
	 * @return string
1226
	 */
1227
	protected static function build_sprintf( $args ) {
1228
		$search = array();
1229
		$replace = array();
1230
		foreach ( $args as $k => $arg ) {
1231
			if ( 0 == $k ) {
1232
				$string = $arg;
1233
				continue;
1234
			}
1235
			$search[] = "%$arg%";
1236
			$replace[] = "%$k\$s";
1237
		}
1238
		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...
1239
	}
1240
}
1241
1242
function publicize_calypso_url() {
1243
	if ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'build_raw_urls' ) ) {
1244
		$site_suffix = Jetpack::build_raw_urls( home_url() );
1245
	} elseif ( class_exists( 'WPCOM_Masterbar' ) && method_exists( 'WPCOM_Masterbar', 'get_calypso_site_slug' ) ) {
1246
		$site_suffix = WPCOM_Masterbar::get_calypso_site_slug( get_current_blog_id() );
1247
	}
1248
1249
	if ( $site_suffix ) {
1250
		return Redirect::get_url( 'calypso-marketing-connections', array( 'site' => $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...
1251
	} else {
1252
		return Redirect::get_url( 'calypso-marketing-connections-base' );
1253
	}
1254
}
1255