Completed
Push — try/publicize-gutenberg-block-... ( 833468 )
by Bernhard
07:09
created

Publicize::done_sharing_post()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
1
<?php
2
3
class Publicize extends Publicize_Base {
4
5
	function __construct() {
6
		parent::__construct();
7
8
		add_filter( 'jetpack_xmlrpc_methods', array( $this, 'register_update_publicize_connections_xmlrpc_method' ) );
9
10
		add_action( 'load-settings_page_sharing', array( $this, 'admin_page_load' ), 9 );
11
12
		add_action( 'wp_ajax_publicize_tumblr_options_page', array( $this, 'options_page_tumblr' ) );
13
		add_action( 'wp_ajax_publicize_facebook_options_page', array( $this, 'options_page_facebook' ) );
14
		add_action( 'wp_ajax_publicize_twitter_options_page', array( $this, 'options_page_twitter' ) );
15
		add_action( 'wp_ajax_publicize_linkedin_options_page', array( $this, 'options_page_linkedin' ) );
16
		add_action( 'wp_ajax_publicize_google_plus_options_page', array( $this, 'options_page_google_plus' ) );
17
18
		add_action( 'wp_ajax_publicize_tumblr_options_save', array( $this, 'options_save_tumblr' ) );
19
		add_action( 'wp_ajax_publicize_facebook_options_save', array( $this, 'options_save_facebook' ) );
20
		add_action( 'wp_ajax_publicize_twitter_options_save', array( $this, 'options_save_twitter' ) );
21
		add_action( 'wp_ajax_publicize_linkedin_options_save', array( $this, 'options_save_linkedin' ) );
22
		add_action( 'wp_ajax_publicize_google_plus_options_save', array( $this, 'options_save_google_plus' ) );
23
24
		add_action( 'load-settings_page_sharing', array( $this, 'force_user_connection' ) );
25
26
		add_filter( 'publicize_checkbox_default', array( $this, 'publicize_checkbox_default' ), 10, 4 );
27
28
		add_filter( 'jetpack_published_post_flags', array( $this, 'set_post_flags' ), 10, 2 );
29
30
		add_action( 'wp_insert_post', array( $this, 'save_publicized' ), 11, 3 );
31
32
		add_filter( 'jetpack_twitter_cards_site_tag', array( $this, 'enhaced_twitter_cards_site_tag' ) );
33
34
		add_action( 'publicize_save_meta', array( $this, 'save_publicized_twitter_account' ), 10, 4 );
35
		add_action( 'publicize_save_meta', array( $this, 'save_publicized_facebook_account' ), 10, 4 );
36
37
		add_action( 'connection_disconnected', array( $this, 'add_disconnect_notice' ) );
38
39
		add_filter( 'jetpack_sharing_twitter_via', array( $this, 'get_publicized_twitter_account' ), 10, 2 );
40
41
		include_once( JETPACK__PLUGIN_DIR . 'modules/publicize/enhanced-open-graph.php' );
42
43
		jetpack_require_lib( 'class.jetpack-keyring-service-helper' );
44
45
		include_once( JETPACK__PLUGIN_DIR . 'modules/publicize/class-jetpack-publicize-gutenberg.php' );
46
47
		// Extend publicize with support for Gutenberg
48
		$async_publicizer = new Jetpack_Publicize_Gutenberg( $this );
0 ignored issues
show
Unused Code introduced by
$async_publicizer is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
49
	}
50
51
	function add_disconnect_notice() {
52
		add_action( 'admin_notices', array( $this, 'display_disconnected' ) );
53
	}
54
55
	function force_user_connection() {
56
		global $current_user;
57
		$user_token        = Jetpack_Data::get_access_token( $current_user->ID );
58
		$is_user_connected = $user_token && ! is_wp_error( $user_token );
59
60
		// If the user is already connected via Jetpack, then we're good
61
		if ( $is_user_connected ) {
62
			return;
63
		}
64
65
		// If they're not connected, then remove the Publicize UI and tell them they need to connect first
66
		global $publicize_ui;
67
		remove_action( 'pre_admin_screen_sharing', array( $publicize_ui, 'admin_page' ) );
68
69
		// Do we really need `admin_styles`? With the new admin UI, it's breaking some bits.
70
		// Jetpack::init()->admin_styles();
71
		add_action( 'pre_admin_screen_sharing', array( $this, 'admin_page_warning' ), 1 );
72
	}
73
74
	function admin_page_warning() {
75
		$jetpack   = Jetpack::init();
76
		$blog_name = get_bloginfo( 'blogname' );
77
		if ( empty( $blog_name ) ) {
78
			$blog_name = home_url( '/' );
79
		}
80
81
		?>
82
		<div id="message" class="updated jetpack-message jp-connect">
83
			<div class="jetpack-wrap-container">
84
				<div class="jetpack-text-container">
85
					<p><?php printf(
86
							/* translators: %s is the name of the blog */
87
							esc_html( wptexturize( __( "To use Publicize, you'll need to link your %s account to your WordPress.com account using the link below.", 'jetpack' ) ) ),
88
							'<strong>' . esc_html( $blog_name ) . '</strong>'
89
						); ?></p>
90
					<p><?php echo esc_html( wptexturize( __( "If you don't have a WordPress.com account yet, you can sign up for free in just a few seconds.", 'jetpack' ) ) ); ?></p>
91
				</div>
92
				<div class="jetpack-install-container">
93
					<p class="submit"><a
94
							href="<?php echo $jetpack->build_connect_url( false, menu_page_url( 'sharing', false ) ); ?>"
95
							class="button-connector"
96
							id="wpcom-connect"><?php esc_html_e( 'Link account with WordPress.com', 'jetpack' ); ?></a>
97
					</p>
98
					<p class="jetpack-install-blurb">
99
						<?php jetpack_render_tos_blurb(); ?>
100
					</p>
101
				</div>
102
			</div>
103
		</div>
104
		<?php
105
	}
106
107
	/**
108
	 * Remove a Publicize connection
109
	 */
110
	function disconnect( $service_name, $connection_id, $_blog_id = false, $_user_id = false, $force_delete = false ) {
111
		return Jetpack_Keyring_Service_Helper::disconnect( $service_name, $connection_id, $_blog_id, $_user_id, $force_delete );
112
	}
113
114
	function receive_updated_publicize_connections( $publicize_connections ) {
115
		Jetpack_Options::update_option( 'publicize_connections', $publicize_connections );
116
117
		return true;
118
	}
119
120
	function register_update_publicize_connections_xmlrpc_method( $methods ) {
121
		return array_merge( $methods, array(
122
			'jetpack.updatePublicizeConnections' => array( $this, 'receive_updated_publicize_connections' ),
123
		) );
124
	}
125
126
	function get_all_connections() {
127
		return Jetpack_Options::get_option( 'publicize_connections' );
128
	}
129
130
	function get_connections( $service_name, $_blog_id = false, $_user_id = false ) {
131
		$connections           = $this->get_all_connections();
132
		$connections_to_return = array();
133
		if ( ! empty( $connections ) && is_array( $connections ) ) {
134
			if ( ! empty( $connections[ $service_name ] ) ) {
135
				foreach ( $connections[ $service_name ] as $id => $connection ) {
136
					if ( 0 == $connection['connection_data']['user_id'] || $this->user_id() == $connection['connection_data']['user_id'] ) {
137
						$connections_to_return[ $id ] = $connection;
138
					}
139
				}
140
			}
141
142
			return $connections_to_return;
143
		}
144
145
		return false;
146
	}
147
148
	function get_all_connections_for_user() {
149
		$connections = $this->get_all_connections();
150
151
		$connections_to_return = array();
152
		if ( ! empty( $connections ) ) {
153
			foreach ( (array) $connections as $service_name => $connections_for_service ) {
154
				foreach ( $connections_for_service as $id => $connection ) {
155
					$user_id = intval( $connection['connection_data']['user_id'] );
156
					// phpcs:ignore WordPress.PHP.YodaConditions.NotYoda
157
					if ( $user_id === 0 || $this->user_id() === $user_id ) {
158
						$connections_to_return[ $service_name ][ $id ] = $connection;
159
					}
160
				}
161
			}
162
163
			return $connections_to_return;
164
		}
165
166
		return false;
167
	}
168
169
	function get_connection_id( $connection ) {
170
		return $connection['connection_data']['id'];
171
	}
172
173
	function get_connection_meta( $connection ) {
174
		$connection['user_id'] = $connection['connection_data']['user_id']; // Allows for shared connections
175
		return $connection;
176
	}
177
178
	function admin_page_load() {
179
		if ( isset( $_GET['action'] ) && 'error' === $_GET['action'] ) {
180
					add_action( 'pre_admin_screen_sharing', array( $this, 'display_connection_error' ), 9 );
181
		}
182
	}
183
184
	function display_connection_error() {
185
		$code = false;
186
		if ( isset( $_GET['service'] ) ) {
187
			$service_name = $_GET['service'];
188
			$error        = sprintf( __( 'There was a problem connecting to %s to create an authorized connection. Please try again in a moment.', 'jetpack' ), Publicize::get_service_label( $service_name ) );
189
		} else {
190
			if ( isset( $_GET['publicize_error'] ) ) {
191
				$code = strtolower( $_GET['publicize_error'] );
192
				switch ( $code ) {
193
					case '400':
194
						$error = __( 'An invalid request was made. This normally means that something intercepted or corrupted the request from your server to the Jetpack Server. Try again and see if it works this time.', 'jetpack' );
195
						break;
196
					case 'secret_mismatch':
197
						$error = __( 'We could not verify that your server is making an authorized request. Please try again, and make sure there is nothing interfering with requests from your server to the Jetpack Server.', 'jetpack' );
198
						break;
199
					case 'empty_blog_id':
200
						$error = __( 'No blog_id was included in your request. Please try disconnecting Jetpack from WordPress.com and then reconnecting it. Once you have done that, try connecting Publicize again.', 'jetpack' );
201
						break;
202
					case 'empty_state':
203
						$error = sprintf( __( 'No user information was included in your request. Please make sure that your user account has connected to Jetpack. Connect your user account by going to the <a href="%s">Jetpack page</a> within wp-admin.', 'jetpack' ), Jetpack::admin_url() );
204
						break;
205
					default:
206
						$error = __( 'Something which should never happen, happened. Sorry about that. If you try again, maybe it will work.', 'jetpack' );
207
						break;
208
				}
209
			} else {
210
				$error = __( 'There was a problem connecting with Publicize. Please try again in a moment.', 'jetpack' );
211
			}
212
		}
213
		// Using the same formatting/style as Jetpack::admin_notices() error
214
		?>
215
		<div id="message" class="jetpack-message jetpack-err">
216
			<div class="squeezer">
217
				<h2><?php echo wp_kses( $error, array( 'a'      => array( 'href' => true ),
218
				                                       'code'   => true,
219
				                                       'strong' => true,
220
				                                       'br'     => true,
221
				                                       'b'      => true
222
					) ); ?></h2>
223
				<?php if ( $code ) : ?>
0 ignored issues
show
Bug Best Practice introduced by
The expression $code of type false|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

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

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
224
					<p><?php printf( __( 'Error code: %s', 'jetpack' ), esc_html( stripslashes( $code ) ) ); ?></p>
225
				<?php endif; ?>
226
			</div>
227
		</div>
228
		<?php
229
	}
230
231
	function display_disconnected() {
232
		echo "<div class='updated'>\n";
233
		echo '<p>' . esc_html( __( 'That connection has been removed.', 'jetpack' ) ) . "</p>\n";
234
		echo "</div>\n\n";
235
	}
236
237
	function globalization() {
238
		if ( 'on' == $_REQUEST['global'] ) {
239
			$id = $_REQUEST['connection'];
240
241
			if ( ! current_user_can( $this->GLOBAL_CAP ) ) {
242
				return;
243
			}
244
245
			Jetpack::load_xml_rpc_client();
246
			$xml = new Jetpack_IXR_Client();
247
			$xml->query( 'jetpack.globalizePublicizeConnection', $id, 'globalize' );
248
249
			if ( ! $xml->isError() ) {
250
				$response = $xml->getResponse();
251
				Jetpack_Options::update_option( 'publicize_connections', $response );
252
			}
253
		}
254
	}
255
256
	function connect_url( $service_name, $for = 'publicize' ) {
257
		return Jetpack_Keyring_Service_Helper::connect_url( $service_name, $for );
258
	}
259
260
	function refresh_url( $service_name, $for = 'publicize' ) {
261
		return Jetpack_Keyring_Service_Helper::refresh_url( $service_name, $for );
262
	}
263
264
	function disconnect_url( $service_name, $id ) {
265
		return Jetpack_Keyring_Service_Helper::disconnect_url( $service_name, $id );
266
	}
267
268
	/**
269
	 * Get social networks, either all available or only those that the site is connected to.
270
	 *
271
	 * @since 2.0.0
272
	 * @since 6.6.0 Removed Path. Service closed October 2018.
273
	 *
274
	 * @param string $filter Select the list of services that will be returned. Defaults to 'all', accepts 'connected'.
275
	 *
276
	 * @return array List of social networks.
277
	 */
278
	function get_services( $filter = 'all' ) {
279
		$services = array(
280
			'facebook'    => array(),
281
			'twitter'     => array(),
282
			'linkedin'    => array(),
283
			'tumblr'      => array(),
284
			'google_plus' => array(),
285
		);
286
287 View Code Duplication
		if ( 'all' == $filter ) {
288
			return $services;
289
		} else {
290
			$connected_services = array();
291
			foreach ( $services as $service => $empty ) {
292
				$connections = $this->get_connections( $service );
293
				if ( $connections ) {
294
					$connected_services[ $service ] = $connections;
295
				}
296
			}
297
			return $connected_services;
298
		}
299
	}
300
301
	/**
302
	 * Retrieves current list of connections and applies filters.
303
	 *
304
	 * Retrieves current available connections and checks if the connections
305
	 * have already been used to share current post. Finally, the checkbox
306
	 * form UI fields are calculated. This function exposes connection form
307
	 * data directly as array so it can be retrieved for static HTML generation
308
	 * or JSON consumption.
309
	 *
310
	 * @since 6.2.0
311
	 *
312
	 * @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...
313
	 *
314
	 * @return array {
315
	 *     Array of UI setup data for connection list form.
316
	 *
317
	 *     @type string 'unique_id'       ID string representing connection
318
	 *     @type bool   'checked'         Default value of checkbox for connection.
319
	 *     @type bool   'disabled'        String of HTML disabled property of checkbox. Empty if not disabled.
320
	 *     @type bool   'active'          True if connection is not skipped by filters and is not already done.
321
	 *     @type bool   'hidden_checkbox' True if the connection should not be shared to by current user.
322
	 *     @type string 'label'           Text description of checkbox.
323
	 *     @type string 'display_name'    Username for sharing account.
324
	 * }
325
	 */
326
	public function get_filtered_connection_data( $selected_post_id = null ) {
327
		$connection_list = array();
328
329
		$post = get_post( $selected_post_id ); // Defaults to current post if $post_id is null.
330
		// Handle case where there is no current post.
331
		if ( ! empty( $post ) ) {
332
			$post_id = $post->ID;
333
		} else {
334
			$post_id = null;
335
		}
336
337
		$services = $this->get_services( 'connected' );
338
		$all_done = $this->done_sharing_post( $post_id );
339
340
		// We don't allow Publicizing to the same external id twice, to prevent spam.
341
		$service_id_done = (array) get_post_meta( $post_id, $this->POST_SERVICE_DONE, true );
342
343
		foreach ( $services as $name => $connections ) {
344
			foreach ( $connections as $connection ) {
345
				$connection_data = '';
346 View Code Duplication
				if ( method_exists( $connection, 'get_meta' ) ) {
347
					$connection_data = $connection->get_meta( 'connection_data' );
348
				} elseif ( ! empty( $connection['connection_data'] ) ) {
349
					$connection_data = $connection['connection_data'];
350
				}
351
352
				/**
353
				 * Filter whether a post should be publicized to a given service.
354
				 *
355
				 * @module publicize
356
				 *
357
				 * @since 2.0.0
358
				 *
359
				 * @param bool true Should the post be publicized to a given service? Default to true.
360
				 * @param int $post_id Post ID.
361
				 * @param string $name Service name.
362
				 * @param array $connection_data Array of information about all Publicize details for the site.
363
				 */
364
				if ( ! apply_filters( 'wpas_submit_post?', true, $post_id, $name, $connection_data ) ) {
365
					continue;
366
				}
367
368 View Code Duplication
				if ( ! empty( $connection->unique_id ) ) {
369
					$unique_id = $connection->unique_id;
370
				} elseif ( ! empty( $connection['connection_data']['token_id'] ) ) {
371
					$unique_id = $connection['connection_data']['token_id'];
372
				}
373
374
				// Should we be skipping this one?
375
				$skip = (
376
					(
377
						! empty( $post )
378
						&&
379
						in_array( $post->post_status, array( 'publish', 'draft', 'future' ) )
380
						&&
381
						get_post_meta( $post_id, $this->POST_SKIP . $unique_id, true )
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...
382
					)
383
					||
384
					(
385
						is_array( $connection )
386
						&&
387
						(
388
							(
389
									isset( $connection['meta']['external_id'] )
390
									&&
391
									! empty( $service_id_done[ $name ][ $connection['meta']['external_id'] ] )
392
							)
393
							||
394
							// Jetpack's connection data looks a little different.
395
							(
396
									isset( $connection['external_id'] )
397
									&&
398
									! empty( $service_id_done[ $name ][ $connection['external_id'] ] )
399
							)
400
						)
401
					)
402
				);
403
404
				// Was this connections (OR, old-format service) already Publicized to.
405
				$done = (
406
							( 1 == get_post_meta( $post_id, $this->POST_DONE . $unique_id, true ) )
407
							||
408
							( 1 == get_post_meta( $post_id, $this->POST_DONE . $name, true ) )
409
				); // New and old style flags.
410
411
				// If this one has already been publicized to, don't let it happen again.
412
				$disabled = false;
413
				if ( $done ) {
414
					$disabled = true;
415
				}
416
417
				/**
418
				 * If this is a global connection and this user doesn't have enough permissions to modify
419
				 * those connections, don't let them change it.
420
				 */
421
				$cmeta           = $this->get_connection_meta( $connection );
422
				$hidden_checkbox = false;
423
				if ( ! $done && ( 0 == $cmeta['connection_data']['user_id'] && ! current_user_can( $this->GLOBAL_CAP ) ) ) {
424
					$disabled = true;
425
					/**
426
					 * Filters the checkboxes for global connections with non-prilvedged users.
427
					 *
428
					 * @module publicize
429
					 *
430
					 * @since 3.7.0
431
					 *
432
					 * @param bool   $checked Indicates if this connection should be enabled. Default true.
433
					 * @param int    $post_id ID of the current post
434
					 * @param string $name Name of the connection (Facebook, Twitter, etc)
435
					 * @param array  $connection Array of data about the connection.
436
					 */
437
					$hidden_checkbox = apply_filters( 'publicize_checkbox_global_default', true, $post_id, $name, $connection );
438
				}
439
440
				// Determine the state of the checkbox (on/off) and allow filtering.
441
				$checked = ( ( 1 != $skip ) || $done );
442
				/**
443
				 * Filter the checkbox state of each Publicize connection appearing in the post editor.
444
				 *
445
				 * @module publicize
446
				 *
447
				 * @since 2.0.1
448
				 *
449
				 * @param bool $checked Should the Publicize checkbox be enabled for a given service.
450
				 * @param int $post_id Post ID.
451
				 * @param string $name Service name.
452
				 * @param array $connection Array of connection details.
453
				 */
454
				$checked = apply_filters( 'publicize_checkbox_default', $checked, $post_id, $name, $connection );
455
456
				// Force the checkbox to be checked if the post was DONE, regardless of what the filter does.
457
				if ( $done ) {
458
					$checked = true;
459
				}
460
461
				// This post has been handled, so disable everything.
462
				if ( $all_done ) {
463
					$disabled = true;
464
				}
465
466
				$label  = sprintf(
467
					_x( '%1$s: %2$s', 'Service: Account connected as', 'jetpack' ),
468
					esc_html( $this->get_service_label( $name ) ),
469
					esc_html( $this->get_display_name( $name, $connection ) )
470
				);
471
				$active = ! $skip || $done;
472
473
				$connection_list[] = array(
474
					'unique_id'       => $unique_id,
475
					'name'            => $name,
476
					'checked'         => $checked,
477
					'disabled'        => $disabled,
478
					'active'          => $active,
479
					'hidden_checkbox' => $hidden_checkbox,
480
					'label'           => esc_html( $label ),
481
					'display_name'    => $this->get_display_name( $name, $connection ),
482
				);
483
			}
484
		}
485
486
		return $connection_list;
487
	}
488
489
	/**
490
	 * Checks if post has already been shared by Publicize in the past.
491
	 *
492
	 * We can set an _all flag to indicate that this post is completely done as
493
	 * far as Publicize is concerned. Jetpack uses this approach. All published posts in Jetpack
494
	 * have Publicize disabled.
495
	 *
496
	 * @since 6.2.0
497
	 *
498
	 * @global Publicize_UI $publicize_ui UI instance that contains the 'in_jetpack' property
499
	 *
500
	 * @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...
501
	 *
502
	 * @return bool True if post has already been shared by Publicize, false otherwise.
503
	 */
504
	public function done_sharing_post( $post_id = null ) {
505
		global $publicize_ui;
506
		$post = get_post( $post_id ); // Defaults to current post if $post_id is null.
507
		if ( is_null( $post ) ) {
508
			return false;
509
		}
510
		return get_post_meta( $post->ID, $this->POST_DONE . 'all', true ) || ( $publicize_ui->in_jetpack && 'publish' == $post->post_status );
511
	}
512
513
	/**
514
	 * Retrieves full list of available Publicize connection services.
515
	 *
516
	 * Retrieves current available publicize service connections
517
	 * with associated labels and URLs.
518
	 *
519
	 * @since 6.2.0
520
	 *
521
	 * @return array {
522
	 *     Array of UI service connection data for all services
523
	 *
524
	 *     @type string 'name'  Name of service.
525
	 *     @type string 'label' Display label for service.
526
	 *     @type string 'url'   URL for adding connection to service.
527
	 * }
528
	 */
529
	function get_available_service_data() {
530
		$available_services     = $this->get_services( 'all' );
531
		$available_service_data = array();
532
533
		foreach ( $available_services as $service_name => $service ) {
534
			$available_service_data[] = array(
535
				'name'  => $service_name,
536
				'label' => $this->get_service_label( $service_name ),
537
				'url'   => $this->connect_url( $service_name ),
538
			);
539
		}
540
541
		return $available_service_data;
542
	}
543
544
	function get_connection( $service, $id, $_blog_id = false, $_user_id = false ) {
545
		// Stub
546
	}
547
548
	function flag_post_for_publicize( $new_status, $old_status, $post ) {
549
		if ( ! $this->post_type_is_publicizeable( $post->post_type ) ) {
550
			return;
551
		}
552
553
		if ( 'publish' == $new_status && 'publish' != $old_status ) {
554
			/**
555
			 * Determines whether a post being published gets publicized.
556
			 *
557
			 * Side-note: Possibly our most alliterative filter name.
558
			 *
559
			 * @module publicize
560
			 *
561
			 * @since 4.1.0
562
			 *
563
			 * @param bool $should_publicize Should the post be publicized? Default to true.
564
			 * @param WP_POST $post Current Post object.
565
			 */
566
			$should_publicize = apply_filters( 'publicize_should_publicize_published_post', true, $post );
567
568
			if ( $should_publicize ) {
569
				update_post_meta( $post->ID, $this->PENDING, true );
570
			}
571
		}
572
	}
573
574
	function test_connection( $service_name, $connection ) {
575
576
		$id = $this->get_connection_id( $connection );
577
578
		Jetpack::load_xml_rpc_client();
579
		$xml = new Jetpack_IXR_Client();
580
		$xml->query( 'jetpack.testPublicizeConnection', $id );
581
582
		// Bail if all is well
583
		if ( ! $xml->isError() ) {
584
			return true;
585
		}
586
587
		$xml_response            = $xml->getResponse();
588
		$connection_test_message = $xml_response['faultString'];
589
590
		// Set up refresh if the user can
591
		$user_can_refresh = current_user_can( $this->GLOBAL_CAP );
592
		if ( $user_can_refresh ) {
593
			$nonce        = wp_create_nonce( "keyring-request-" . $service_name );
0 ignored issues
show
Unused Code introduced by
$nonce is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
594
			$refresh_text = sprintf( _x( 'Refresh connection with %s', 'Refresh connection with {social media service}', 'jetpack' ), $this->get_service_label( $service_name ) );
595
			$refresh_url  = $this->refresh_url( $service_name );
596
		}
597
598
		$error_data = array(
599
			'user_can_refresh' => $user_can_refresh,
600
			'refresh_text'     => $refresh_text,
0 ignored issues
show
Bug introduced by
The variable $refresh_text 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...
601
			'refresh_url'      => $refresh_url
0 ignored issues
show
Bug introduced by
The variable $refresh_url 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...
602
		);
603
604
		return new WP_Error( 'pub_conn_test_failed', $connection_test_message, $error_data );
605
	}
606
607
	/**
608
	 * Save a flag locally to indicate that this post has already been Publicized via the selected
609
	 * connections.
610
	 */
611
	function save_publicized( $post_ID, $post = null, $update = null ) {
612
		if ( is_null( $post ) ) {
613
			return;
614
		}
615
		// Only do this when a post transitions to being published
616
		if ( get_post_meta( $post->ID, $this->PENDING ) && $this->post_type_is_publicizeable( $post->post_type ) ) {
617
			$connected_services = $this->get_all_connections();
618
			if ( ! empty( $connected_services ) ) {
619
				/**
620
				 * Fires when a post is saved that has is marked as pending publicizing
621
				 *
622
				 * @since 4.1.0
623
				 *
624
				 * @param int The post ID
625
				 */
626
				do_action_deprecated( 'jetpack_publicize_post', $post->ID, '4.8.0', 'jetpack_published_post_flags' );
627
			}
628
			delete_post_meta( $post->ID, $this->PENDING );
629
			update_post_meta( $post->ID, $this->POST_DONE . 'all', true );
630
		}
631
	}
632
633
	function set_post_flags( $flags, $post ) {
634
		$flags['publicize_post'] = false;
635
		if ( ! $this->post_type_is_publicizeable( $post->post_type ) ) {
636
			return $flags;
637
		}
638
		/** This filter is already documented in modules/publicize/publicize-jetpack.php */
639
		if ( ! apply_filters( 'publicize_should_publicize_published_post', true, $post ) ) {
640
			return $flags;
641
		}
642
643
		$connected_services = $this->get_all_connections();
644
645
		if ( empty( $connected_services ) ) {
646
			return $flags;
647
		}
648
649
		$flags['publicize_post'] = true;
650
651
		return $flags;
652
	}
653
654
	/**
655
	 * Options Code
656
	 */
657
658
	function options_page_facebook() {
659
		$connected_services = $this->get_all_connections();
660
		$connection         = $connected_services['facebook'][ $_REQUEST['connection'] ];
661
		$options_to_show    = ( ! empty( $connection['connection_data']['meta']['options_responses'] ) ? $connection['connection_data']['meta']['options_responses'] : false );
662
663
		// Nonce check
664
		check_admin_referer( 'options_page_facebook_' . $_REQUEST['connection'] );
665
666
		$pages = ( ! empty( $options_to_show[1]['data'] ) ? $options_to_show[1]['data'] : false );
667
668
		$page_selected   = false;
669
		if ( ! empty( $connection['connection_data']['meta']['facebook_page'] ) ) {
670
			$found = false;
671
			if ( $pages && isset( $pages->data ) && is_array( $pages->data )  ) {
672
				foreach ( $pages->data as $page ) {
673
					if ( $page->id == $connection['connection_data']['meta']['facebook_page'] ) {
674
						$found = true;
675
						break;
676
					}
677
				}
678
			}
679
680
			if ( $found ) {
681
				$page_selected   = $connection['connection_data']['meta']['facebook_page'];
682
			}
683
		}
684
685
		?>
686
687
		<div id="thickbox-content">
688
689
			<?php
690
			ob_start();
691
			Publicize_UI::connected_notice( 'Facebook' );
692
			$update_notice = ob_get_clean();
693
694
			if ( ! empty( $update_notice ) ) {
695
				echo $update_notice;
696
			}
697
			$page_info_message = sprintf(
698
				__( 'Facebook supports Publicize connections to Facebook Pages, but not to Facebook Profiles. <a href="%s">Learn More about Publicize for Facebook</a>', 'jetpack' ),
699
				'https://jetpack.com/support/publicize/facebook'
700
			);
701
702
			if ( $pages ) : ?>
703
				<p><?php _e( 'Publicize to my <strong>Facebook Page</strong>:', 'jetpack' ); ?></p>
704
				<table id="option-fb-fanpage">
705
					<tbody>
706
707
					<?php foreach ( $pages as $i => $page ) : ?>
708
						<?php if ( ! ( $i % 2 ) ) : ?>
709
							<tr>
710
						<?php endif; ?>
711
						<td class="radio"><input type="radio" name="option" data-type="page"
712
						                         id="<?php echo esc_attr( $page['id'] ) ?>"
713
						                         value="<?php echo esc_attr( $page['id'] ) ?>" <?php checked( $page_selected && $page_selected == $page['id'], true ); ?> />
714
						</td>
715
						<td class="thumbnail"><label for="<?php echo esc_attr( $page['id'] ) ?>"><img
716
									src="<?php echo esc_url( str_replace( '_s', '_q', $page['picture']['data']['url'] ) ) ?>"
717
									width="50" height="50"/></label></td>
718
						<td class="details">
719
							<label for="<?php echo esc_attr( $page['id'] ) ?>">
720
								<span class="name"><?php echo esc_html( $page['name'] ) ?></span><br/>
721
								<span class="category"><?php echo esc_html( $page['category'] ) ?></span>
722
							</label>
723
						</td>
724
						<?php if ( ( $i % 2 ) || ( $i == count( $pages ) - 1 ) ): ?>
725
							</tr>
726
						<?php endif; ?>
727
					<?php endforeach; ?>
728
729
					</tbody>
730
				</table>
731
732
				<?php Publicize_UI::global_checkbox( 'facebook', $_REQUEST['connection'] ); ?>
733
				<p style="text-align: center;">
734
					<input type="submit" value="<?php esc_attr_e( 'OK', 'jetpack' ) ?>"
735
					       class="button fb-options save-options" name="save"
736
					       data-connection="<?php echo esc_attr( $_REQUEST['connection'] ); ?>"
737
					       rel="<?php echo wp_create_nonce( 'save_fb_token_' . $_REQUEST['connection'] ) ?>"/>
738
				</p><br/>
739
				<p><?php echo $page_info_message; ?></p>
740
			<?php else: ?>
741
				<div>
742
					<p><?php echo $page_info_message; ?></p>
743
					<p><?php printf( __( '<a class="button" href="%s" target="%s">Create a Facebook page</a> to get started.', 'jetpack' ), 'https://www.facebook.com/pages/creation/', '_blank noopener noreferrer' ); ?></p>
744
				</div>
745
			<?php endif; ?>
746
		</div>
747
		<?php
748
	}
749
750
	function options_save_facebook() {
751
		// Nonce check
752
		check_admin_referer( 'save_fb_token_' . $_REQUEST['connection'] );
753
754
		// Check for a numeric page ID
755
		$page_id = $_POST['selected_id'];
756
		if ( ! ctype_digit( $page_id ) ) {
757
			die( 'Security check' );
758
		}
759
760
		if ( 'page' != $_POST['type'] || ! isset( $_POST['selected_id'] ) ) {
761
			return;
762
		}
763
764
		// Publish to Page
765
		$options = array(
766
			'facebook_page'    => $page_id,
767
			'facebook_profile' => null
768
		);
769
770
		$this->set_remote_publicize_options( $_POST['connection'], $options );
771
	}
772
773
	function options_page_tumblr() {
774
		// Nonce check
775
		check_admin_referer( 'options_page_tumblr_' . $_REQUEST['connection'] );
776
777
		$connected_services = $this->get_all_connections();
778
		$connection         = $connected_services['tumblr'][ $_POST['connection'] ];
779
		$options_to_show    = $connection['connection_data']['meta']['options_responses'];
780
		$request            = $options_to_show[0];
781
782
		$blogs = $request['response']['user']['blogs'];
783
784
		$blog_selected = false;
785
786
		if ( ! empty( $connection['connection_data']['meta']['tumblr_base_hostname'] ) ) {
787
			foreach ( $blogs as $blog ) {
788
				if ( $connection['connection_data']['meta']['tumblr_base_hostname'] == $this->get_basehostname( $blog['url'] ) ) {
789
					$blog_selected = $connection['connection_data']['meta']['tumblr_base_hostname'];
790
					break;
791
				}
792
			}
793
794
		}
795
796
		// Use their Primary blog if they haven't selected one yet
797
		if ( ! $blog_selected ) {
798
			foreach ( $blogs as $blog ) {
799
				if ( $blog['primary'] ) {
800
					$blog_selected = $this->get_basehostname( $blog['url'] );
801
				}
802
			}
803
		} ?>
804
805
		<div id="thickbox-content">
806
807
			<?php
808
			ob_start();
809
			Publicize_UI::connected_notice( 'Tumblr' );
810
			$update_notice = ob_get_clean();
811
812
			if ( ! empty( $update_notice ) ) {
813
				echo $update_notice;
814
			}
815
			?>
816
817
			<p><?php _e( 'Publicize to my <strong>Tumblr blog</strong>:', 'jetpack' ); ?></p>
818
819
			<ul id="option-tumblr-blog">
820
821
				<?php
822
				foreach ( $blogs as $blog ) {
823
					$url = $this->get_basehostname( $blog['url'] ); ?>
824
					<li>
825
						<input type="radio" name="option" data-type="blog" id="<?php echo esc_attr( $url ) ?>"
826
						       value="<?php echo esc_attr( $url ) ?>" <?php checked( $blog_selected == $url, true ); ?> />
827
						<label for="<?php echo esc_attr( $url ) ?>"><span
828
								class="name"><?php echo esc_html( $blog['title'] ) ?></span></label>
829
					</li>
830
				<?php } ?>
831
832
			</ul>
833
834
			<?php Publicize_UI::global_checkbox( 'tumblr', $_REQUEST['connection'] ); ?>
835
836
			<p style="text-align: center;">
837
				<input type="submit" value="<?php esc_attr_e( 'OK', 'jetpack' ) ?>"
838
				       class="button tumblr-options save-options" name="save"
839
				       data-connection="<?php echo esc_attr( $_REQUEST['connection'] ); ?>"
840
				       rel="<?php echo wp_create_nonce( 'save_tumblr_blog_' . $_REQUEST['connection'] ) ?>"/>
841
			</p> <br/>
842
		</div>
843
844
		<?php
845
	}
846
847
	function get_basehostname( $url ) {
848
		return parse_url( $url, PHP_URL_HOST );
849
	}
850
851
	function options_save_tumblr() {
852
		// Nonce check
853
		check_admin_referer( 'save_tumblr_blog_' . $_REQUEST['connection'] );
854
		$options = array( 'tumblr_base_hostname' => $_POST['selected_id'] );
855
856
		$this->set_remote_publicize_options( $_POST['connection'], $options );
857
858
	}
859
860
	function set_remote_publicize_options( $id, $options ) {
861
		Jetpack::load_xml_rpc_client();
862
		$xml = new Jetpack_IXR_Client();
863
		$xml->query( 'jetpack.setPublicizeOptions', $id, $options );
864
865
		if ( ! $xml->isError() ) {
866
			$response = $xml->getResponse();
867
			Jetpack_Options::update_option( 'publicize_connections', $response );
868
			$this->globalization();
869
		}
870
	}
871
872
	function options_page_twitter() {
873
		Publicize_UI::options_page_other( 'twitter' );
874
	}
875
876
	function options_page_linkedin() {
877
		Publicize_UI::options_page_other( 'linkedin' );
878
	}
879
880
	function options_page_google_plus() {
881
		Publicize_UI::options_page_other( 'google_plus' );
882
	}
883
884
	function options_save_twitter() {
885
		$this->options_save_other( 'twitter' );
886
	}
887
888
	function options_save_linkedin() {
889
		$this->options_save_other( 'linkedin' );
890
	}
891
892
	function options_save_google_plus() {
893
		$this->options_save_other( 'google_plus' );
894
	}
895
896
	function options_save_other( $service_name ) {
897
		// Nonce check
898
		check_admin_referer( 'save_' . $service_name . '_token_' . $_REQUEST['connection'] );
899
		$this->globalization();
900
	}
901
902
	/**
903
	 * Already-published posts should not be Publicized by default. This filter sets checked to
904
	 * false if a post has already been published.
905
	 */
906
	function publicize_checkbox_default( $checked, $post_id, $name, $connection ) {
907
		if ( 'publish' == get_post_status( $post_id ) ) {
908
			return false;
909
		}
910
911
		return $checked;
912
	}
913
914
	/**
915
	 * If there's only one shared connection to Twitter set it as twitter:site tag.
916
	 */
917
	function enhaced_twitter_cards_site_tag( $tag ) {
918
		$custom_site_tag = get_option( 'jetpack-twitter-cards-site-tag' );
919
		if ( ! empty( $custom_site_tag ) ) {
920
			return $tag;
921
		}
922
		if ( ! $this->is_enabled( 'twitter' ) ) {
923
			return $tag;
924
		}
925
		$connections = $this->get_connections( 'twitter' );
926
		foreach ( $connections as $connection ) {
0 ignored issues
show
Bug introduced by
The expression $connections of type array|false is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
927
			$connection_meta = $this->get_connection_meta( $connection );
928
			if ( 0 == $connection_meta['connection_data']['user_id'] ) {
929
				// If the connection is shared
930
				return $this->get_display_name( 'twitter', $connection );
931
			}
932
		}
933
934
		return $tag;
935
	}
936
937
	function save_publicized_twitter_account( $submit_post, $post_id, $service_name, $connection ) {
938
		if ( 'twitter' == $service_name && $submit_post ) {
939
			$connection_meta        = $this->get_connection_meta( $connection );
940
			$publicize_twitter_user = get_post_meta( $post_id, '_publicize_twitter_user' );
941
			if ( empty( $publicize_twitter_user ) || 0 != $connection_meta['connection_data']['user_id'] ) {
942
				update_post_meta( $post_id, '_publicize_twitter_user', $this->get_display_name( 'twitter', $connection ) );
943
			}
944
		}
945
	}
946
947
	function get_publicized_twitter_account( $account, $post_id ) {
948
		if ( ! empty( $account ) ) {
949
			return $account;
950
		}
951
		$account = get_post_meta( $post_id, '_publicize_twitter_user', true );
952
		if ( ! empty( $account ) ) {
953
			return $account;
954
		}
955
956
		return '';
957
	}
958
959
	/**
960
	 * Save the Publicized Facebook account when publishing a post
961
	 * Use only Personal accounts, not Facebook Pages
962
	 */
963
	function save_publicized_facebook_account( $submit_post, $post_id, $service_name, $connection ) {
964
		$connection_meta = $this->get_connection_meta( $connection );
965
		if ( 'facebook' == $service_name && isset( $connection_meta['connection_data']['meta']['facebook_profile'] ) && $submit_post ) {
966
			$publicize_facebook_user = get_post_meta( $post_id, '_publicize_facebook_user' );
967
			if ( empty( $publicize_facebook_user ) || 0 != $connection_meta['connection_data']['user_id'] ) {
968
				$profile_link = $this->get_profile_link( 'facebook', $connection );
969
970
				if ( false !== $profile_link ) {
971
					update_post_meta( $post_id, '_publicize_facebook_user', $profile_link );
972
				}
973
			}
974
		}
975
	}
976
}
977