Completed
Push — add/simple-payments-admin-warn... ( c4a85f...a05ff9 )
by
unknown
11:46
created

Publicize::options_save_facebook()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 4
nop 0
dl 0
loc 34
rs 9.0648
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_path_options_page', array( $this, 'options_page_path' ) );
17
		add_action( 'wp_ajax_publicize_google_plus_options_page', array( $this, 'options_page_google_plus' ) );
18
19
		add_action( 'wp_ajax_publicize_tumblr_options_save', array( $this, 'options_save_tumblr' ) );
20
		add_action( 'wp_ajax_publicize_facebook_options_save', array( $this, 'options_save_facebook' ) );
21
		add_action( 'wp_ajax_publicize_twitter_options_save', array( $this, 'options_save_twitter' ) );
22
		add_action( 'wp_ajax_publicize_linkedin_options_save', array( $this, 'options_save_linkedin' ) );
23
		add_action( 'wp_ajax_publicize_path_options_save', array( $this, 'options_save_path' ) );
24
		add_action( 'wp_ajax_publicize_google_plus_options_save', array( $this, 'options_save_google_plus' ) );
25
26
		add_action( 'load-settings_page_sharing', array( $this, 'force_user_connection' ) );
27
28
		add_filter( 'publicize_checkbox_default', array( $this, 'publicize_checkbox_default' ), 10, 4 );
29
30
		add_filter( 'jetpack_published_post_flags', array( $this, 'set_post_flags' ), 10, 2 );
31
32
		add_action( 'wp_insert_post', array( $this, 'save_publicized' ), 11, 3 );
33
34
		add_filter( 'jetpack_twitter_cards_site_tag', array( $this, 'enhaced_twitter_cards_site_tag' ) );
35
36
		add_action( 'publicize_save_meta', array( $this, 'save_publicized_twitter_account' ), 10, 4 );
37
		add_action( 'publicize_save_meta', array( $this, 'save_publicized_facebook_account' ), 10, 4 );
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
44
	function force_user_connection() {
45
		global $current_user;
46
		$user_token        = Jetpack_Data::get_access_token( $current_user->ID );
47
		$is_user_connected = $user_token && ! is_wp_error( $user_token );
48
49
		// If the user is already connected via Jetpack, then we're good
50
		if ( $is_user_connected ) {
51
			return;
52
		}
53
54
		// If they're not connected, then remove the Publicize UI and tell them they need to connect first
55
		global $publicize_ui;
56
		remove_action( 'pre_admin_screen_sharing', array( $publicize_ui, 'admin_page' ) );
57
58
		// Do we really need `admin_styles`? With the new admin UI, it's breaking some bits.
59
		// Jetpack::init()->admin_styles();
60
		add_action( 'pre_admin_screen_sharing', array( $this, 'admin_page_warning' ), 1 );
61
	}
62
63
	function admin_page_warning() {
64
		$jetpack   = Jetpack::init();
65
		$blog_name = get_bloginfo( 'blogname' );
66
		if ( empty( $blog_name ) ) {
67
			$blog_name = home_url( '/' );
68
		}
69
70
		?>
71
		<div id="message" class="updated jetpack-message jp-connect">
72
			<div class="jetpack-wrap-container">
73
				<div class="jetpack-text-container">
74
					<p><?php printf(
75
							/* translators: %s is the name of the blog */
76
							esc_html( wptexturize( __( "To use Publicize, you'll need to link your %s account to your WordPress.com account using the link below.", 'jetpack' ) ) ),
77
							'<strong>' . esc_html( $blog_name ) . '</strong>'
78
						); ?></p>
79
					<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>
80
				</div>
81
				<div class="jetpack-install-container">
82
					<p class="submit"><a
83
							href="<?php echo $jetpack->build_connect_url( false, menu_page_url( 'sharing', false ) ); ?>"
84
							class="button-connector"
85
							id="wpcom-connect"><?php esc_html_e( 'Link account with WordPress.com', 'jetpack' ); ?></a>
86
					</p>
87
					<p class="jetpack-install-blurb">
88
						<?php jetpack_render_tos_blurb(); ?>
89
					</p>
90
				</div>
91
			</div>
92
		</div>
93
		<?php
94
	}
95
96
	/**
97
	 * Remove a Publicize connection
98
	 */
99
	function disconnect( $service_name, $connection_id, $_blog_id = false, $_user_id = false, $force_delete = false ) {
100
		Jetpack::load_xml_rpc_client();
101
		$xml = new Jetpack_IXR_Client();
102
		$xml->query( 'jetpack.deletePublicizeConnection', $connection_id );
103
104
		if ( ! $xml->isError() ) {
105
			Jetpack_Options::update_option( 'publicize_connections', $xml->getResponse() );
106
		} else {
107
			return false;
108
		}
109
	}
110
111
	function receive_updated_publicize_connections( $publicize_connections ) {
112
		Jetpack_Options::update_option( 'publicize_connections', $publicize_connections );
113
114
		return true;
115
	}
116
117
	function register_update_publicize_connections_xmlrpc_method( $methods ) {
118
		return array_merge( $methods, array(
119
			'jetpack.updatePublicizeConnections' => array( $this, 'receive_updated_publicize_connections' ),
120
		) );
121
	}
122
123
	function get_all_connections() {
124
		return Jetpack_Options::get_option( 'publicize_connections' );
125
	}
126
127
	function get_connections( $service_name, $_blog_id = false, $_user_id = false ) {
128
		$connections           = $this->get_all_connections();
129
		$connections_to_return = array();
130
		if ( ! empty( $connections ) && is_array( $connections ) ) {
131
			if ( ! empty( $connections[ $service_name ] ) ) {
132
				foreach ( $connections[ $service_name ] as $id => $connection ) {
133
					if ( 0 == $connection['connection_data']['user_id'] || $this->user_id() == $connection['connection_data']['user_id'] ) {
134
						$connections_to_return[ $id ] = $connection;
135
					}
136
				}
137
			}
138
139
			return $connections_to_return;
140
		}
141
142
		return false;
143
	}
144
145
	function get_all_connections_for_user() {
146
		$connections = $this->get_all_connections();
147
148
		$connections_to_return = array();
149
		if ( ! empty( $connections ) ) {
150
			foreach ( (array) $connections as $service_name => $connections_for_service ) {
151
				foreach ( $connections_for_service as $id => $connection ) {
152
					$user_id = intval( $connection['connection_data']['user_id'] );
153
					// phpcs:ignore WordPress.PHP.YodaConditions.NotYoda
154
					if ( $user_id === 0 || $this->user_id() === $user_id ) {
155
						$connections_to_return[ $service_name ][ $id ] = $connection;
156
					}
157
				}
158
			}
159
160
			return $connections_to_return;
161
		}
162
163
		return false;
164
	}
165
166
	function get_connection_id( $connection ) {
167
		return $connection['connection_data']['id'];
168
	}
169
170
	function get_connection_meta( $connection ) {
171
		$connection['user_id'] = $connection['connection_data']['user_id']; // Allows for shared connections
172
		return $connection;
173
	}
174
175
	function admin_page_load() {
176
		if ( isset( $_GET['action'] ) ) {
177
			if ( isset( $_GET['service'] ) ) {
178
				$service_name = $_GET['service'];
179
			}
180
181
			switch ( $_GET['action'] ) {
182
				case 'error':
183
					add_action( 'pre_admin_screen_sharing', array( $this, 'display_connection_error' ), 9 );
184
					break;
185
186
				case 'request':
187
					check_admin_referer( 'keyring-request', 'kr_nonce' );
188
					check_admin_referer( "keyring-request-$service_name", 'nonce' );
0 ignored issues
show
Bug introduced by
The variable $service_name 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...
189
190
					$verification = Jetpack::generate_secrets( 'publicize' );
191
					if ( ! $verification ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $verification of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
192
						$url = Jetpack::admin_url( 'jetpack#/settings' );
193
						wp_die( sprintf( __( "Jetpack is not connected. Please connect Jetpack by visiting <a href='%s'>Settings</a>.", 'jetpack' ), $url ) );
194
195
					}
196
					$stats_options = get_option( 'stats_options' );
197
					$wpcom_blog_id = Jetpack_Options::get_option( 'id' );
198
					$wpcom_blog_id = ! empty( $wpcom_blog_id ) ? $wpcom_blog_id : $stats_options['blog_id'];
199
200
					$user     = wp_get_current_user();
201
					$redirect = $this->api_url( $service_name, urlencode_deep( array(
202
						'action'       => 'request',
203
						'redirect_uri' => add_query_arg( array( 'action' => 'done' ), menu_page_url( 'sharing', false ) ),
204
						'for'          => 'publicize',
205
						// required flag that says this connection is intended for publicize
206
						'siteurl'      => site_url(),
207
						'state'        => $user->ID,
208
						'blog_id'      => $wpcom_blog_id,
209
						'secret_1'     => $verification['secret_1'],
210
						'secret_2'     => $verification['secret_2'],
211
						'eol'          => $verification['exp'],
212
					) ) );
213
					wp_redirect( $redirect );
214
					exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method admin_page_load() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
215
					break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
216
217
				case 'completed':
218
					Jetpack::load_xml_rpc_client();
219
					$xml = new Jetpack_IXR_Client();
220
					$xml->query( 'jetpack.fetchPublicizeConnections' );
221
222
					if ( ! $xml->isError() ) {
223
						$response = $xml->getResponse();
224
						Jetpack_Options::update_option( 'publicize_connections', $response );
225
					}
226
227
					break;
228
229
				case 'delete':
230
					$id = $_GET['id'];
231
232
					check_admin_referer( 'keyring-request', 'kr_nonce' );
233
					check_admin_referer( "keyring-request-$service_name", 'nonce' );
234
235
					$this->disconnect( $service_name, $id );
236
237
					add_action( 'admin_notices', array( $this, 'display_disconnected' ) );
238
					break;
239
			}
240
		}
241
242
		// Do we really need `admin_styles`? With the new admin UI, it's breaking some bits.
243
		// Errors encountered on WordPress.com's end are passed back as a code
244
		/*
245
		if ( isset( $_GET['action'] ) && 'error' == $_GET['action'] ) {
246
			// Load Jetpack's styles to handle the box
247
			Jetpack::init()->admin_styles();
248
		}
249
		*/
250
	}
251
252
	function display_connection_error() {
253
		$code = false;
254
		if ( isset( $_GET['service'] ) ) {
255
			$service_name = $_GET['service'];
256
			$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 ) );
257
		} else {
258
			if ( isset( $_GET['publicize_error'] ) ) {
259
				$code = strtolower( $_GET['publicize_error'] );
260
				switch ( $code ) {
261
					case '400':
262
						$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' );
263
						break;
264
					case 'secret_mismatch':
265
						$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' );
266
						break;
267
					case 'empty_blog_id':
268
						$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' );
269
						break;
270
					case 'empty_state':
271
						$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() );
272
						break;
273
					default:
274
						$error = __( 'Something which should never happen, happened. Sorry about that. If you try again, maybe it will work.', 'jetpack' );
275
						break;
276
				}
277
			} else {
278
				$error = __( 'There was a problem connecting with Publicize. Please try again in a moment.', 'jetpack' );
279
			}
280
		}
281
		// Using the same formatting/style as Jetpack::admin_notices() error
282
		?>
283
		<div id="message" class="jetpack-message jetpack-err">
284
			<div class="squeezer">
285
				<h2><?php echo wp_kses( $error, array( 'a'      => array( 'href' => true ),
286
				                                       'code'   => true,
287
				                                       'strong' => true,
288
				                                       'br'     => true,
289
				                                       'b'      => true
290
					) ); ?></h2>
291
				<?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...
292
					<p><?php printf( __( 'Error code: %s', 'jetpack' ), esc_html( stripslashes( $code ) ) ); ?></p>
293
				<?php endif; ?>
294
			</div>
295
		</div>
296
		<?php
297
	}
298
299
	function display_disconnected() {
300
		echo "<div class='updated'>\n";
301
		echo '<p>' . esc_html( __( 'That connection has been removed.', 'jetpack' ) ) . "</p>\n";
302
		echo "</div>\n\n";
303
	}
304
305
	function globalization() {
306
		if ( 'on' == $_REQUEST['global'] ) {
307
			$id = $_REQUEST['connection'];
308
309
			if ( ! current_user_can( $this->GLOBAL_CAP ) ) {
310
				return;
311
			}
312
313
			Jetpack::load_xml_rpc_client();
314
			$xml = new Jetpack_IXR_Client();
315
			$xml->query( 'jetpack.globalizePublicizeConnection', $id, 'globalize' );
316
317
			if ( ! $xml->isError() ) {
318
				$response = $xml->getResponse();
319
				Jetpack_Options::update_option( 'publicize_connections', $response );
320
			}
321
		}
322
	}
323
324
	/**
325
	 * Gets a URL to the public-api actions. Works like WP's admin_url
326
	 *
327
	 * @param string $service Shortname of a specific service.
328
	 *
329
	 * @return URL to specific public-api process
330
	 */
331
	// on WordPress.com this is/calls Keyring::admin_url
332
	function api_url( $service = false, $params = array() ) {
333
		/**
334
		 * Filters the API URL used to interact with WordPress.com.
335
		 *
336
		 * @module publicize
337
		 *
338
		 * @since 2.0.0
339
		 *
340
		 * @param string https://public-api.wordpress.com/connect/?jetpack=publicize Default Publicize API URL.
341
		 */
342
		$url = apply_filters( 'publicize_api_url', 'https://public-api.wordpress.com/connect/?jetpack=publicize' );
343
344
		if ( $service ) {
345
			$url = add_query_arg( array( 'service' => $service ), $url );
346
		}
347
348
		if ( count( $params ) ) {
349
			$url = add_query_arg( $params, $url );
350
		}
351
352
		return $url;
353
	}
354
355 View Code Duplication
	function connect_url( $service_name ) {
356
		return add_query_arg( array(
357
			'action'   => 'request',
358
			'service'  => $service_name,
359
			'kr_nonce' => wp_create_nonce( 'keyring-request' ),
360
			'nonce'    => wp_create_nonce( "keyring-request-$service_name" ),
361
		), menu_page_url( 'sharing', false ) );
362
	}
363
364
	function refresh_url( $service_name ) {
365
		return add_query_arg( array(
366
			'action'   => 'request',
367
			'service'  => $service_name,
368
			'kr_nonce' => wp_create_nonce( 'keyring-request' ),
369
			'refresh'  => 1,
370
			'for'      => 'publicize',
371
			'nonce'    => wp_create_nonce( "keyring-request-$service_name" ),
372
		), admin_url( 'options-general.php?page=sharing' ) );
373
	}
374
375 View Code Duplication
	function disconnect_url( $service_name, $id ) {
376
		return add_query_arg( array(
377
			'action'   => 'delete',
378
			'service'  => $service_name,
379
			'id'       => $id,
380
			'kr_nonce' => wp_create_nonce( 'keyring-request' ),
381
			'nonce'    => wp_create_nonce( "keyring-request-$service_name" ),
382
		), menu_page_url( 'sharing', false ) );
383
	}
384
385
	/**
386
	 * Get social networks, either all available or only those that the site is connected to.
387
	 *
388
	 * @since 2.0
389
	 *
390
	 * @param string $filter Select the list of services that will be returned. Defaults to 'all', accepts 'connected'.
391
	 *
392
	 * @return array List of social networks.
393
	 */
394
	function get_services( $filter = 'all' ) {
395
		$services = array(
396
			'facebook'    => array(),
397
			'twitter'     => array(),
398
			'linkedin'    => array(),
399
			'tumblr'      => array(),
400
			'path'        => array(),
401
			'google_plus' => array(),
402
		);
403
404
		if ( 'all' == $filter ) {
405
			return $services;
406
		} else {
407
			$connected_services = array();
408
			foreach ( $services as $service => $empty ) {
409
				$connections = $this->get_connections( $service );
410
				if ( $connections ) {
411
					$connected_services[ $service ] = $connections;
412
				}
413
			}
414
			return $connected_services;
415
		}
416
	}
417
418
	function get_connection( $service, $id, $_blog_id = false, $_user_id = false ) {
419
		// Stub
420
	}
421
422
	function flag_post_for_publicize( $new_status, $old_status, $post ) {
423
		if ( ! $this->post_type_is_publicizeable( $post->post_type ) ) {
424
			return;
425
		}
426
427
		if ( 'publish' == $new_status && 'publish' != $old_status ) {
428
			/**
429
			 * Determines whether a post being published gets publicized.
430
			 *
431
			 * Side-note: Possibly our most alliterative filter name.
432
			 *
433
			 * @module publicize
434
			 *
435
			 * @since 4.1.0
436
			 *
437
			 * @param bool $should_publicize Should the post be publicized? Default to true.
438
			 * @param WP_POST $post Current Post object.
439
			 */
440
			$should_publicize = apply_filters( 'publicize_should_publicize_published_post', true, $post );
441
442
			if ( $should_publicize ) {
443
				update_post_meta( $post->ID, $this->PENDING, true );
444
			}
445
		}
446
	}
447
448
	function test_connection( $service_name, $connection ) {
449
		$connection_test_passed  = true;
450
		$connection_test_message = '';
451
		$user_can_refresh        = false;
0 ignored issues
show
Unused Code introduced by
$user_can_refresh 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...
452
453
		$id = $this->get_connection_id( $connection );
454
455
		Jetpack::load_xml_rpc_client();
456
		$xml = new Jetpack_IXR_Client();
457
		$xml->query( 'jetpack.testPublicizeConnection', $id );
458
459
		if ( $xml->isError() ) {
460
			$xml_response            = $xml->getResponse();
461
			$connection_test_message = $xml_response['faultString'];
462
			$connection_test_passed  = false;
463
		}
464
465
		// Bail if all is well
466
		if ( $connection_test_passed ) {
467
			return true;
468
		}
469
470
		// Set up refresh if the user can
471
		$user_can_refresh = current_user_can( $this->GLOBAL_CAP );
472
		if ( $user_can_refresh ) {
473
			$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...
474
			$refresh_text = sprintf( _x( 'Refresh connection with %s', 'Refresh connection with {social media service}', 'jetpack' ), $this->get_service_label( $service_name ) );
475
			$refresh_url  = $this->refresh_url( $service_name );
476
		}
477
478
		$error_data = array(
479
			'user_can_refresh' => $user_can_refresh,
480
			'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...
481
			'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...
482
		);
483
484
		return new WP_Error( 'pub_conn_test_failed', $connection_test_message, $error_data );
485
	}
486
487
	/**
488
	 * Save a flag locally to indicate that this post has already been Publicized via the selected
489
	 * connections.
490
	 */
491
	function save_publicized( $post_ID, $post = null, $update = null ) {
492
		if ( is_null( $post ) ) {
493
			return;
494
		}
495
		// Only do this when a post transitions to being published
496
		if ( get_post_meta( $post->ID, $this->PENDING ) && $this->post_type_is_publicizeable( $post->post_type ) ) {
497
			$connected_services = $this->get_all_connections();
498
			if ( ! empty( $connected_services ) ) {
499
				/**
500
				 * Fires when a post is saved that has is marked as pending publicizing
501
				 *
502
				 * @since 4.1.0
503
				 *
504
				 * @param int The post ID
505
				 */
506
				do_action_deprecated( 'jetpack_publicize_post', $post->ID, '4.8.0', 'jetpack_published_post_flags' );
507
			}
508
			delete_post_meta( $post->ID, $this->PENDING );
509
			update_post_meta( $post->ID, $this->POST_DONE . 'all', true );
510
		}
511
	}
512
513
	function set_post_flags( $flags, $post ) {
514
		$flags['publicize_post'] = false;
515
		if ( ! $this->post_type_is_publicizeable( $post->post_type ) ) {
516
			return $flags;
517
		}
518
		/** This filter is already documented in modules/publicize/publicize-jetpack.php */
519
		if ( ! apply_filters( 'publicize_should_publicize_published_post', true, $post ) ) {
520
			return $flags;
521
		}
522
523
		$connected_services = $this->get_all_connections();
524
525
		if ( empty( $connected_services ) ) {
526
			return $flags;
527
		}
528
529
		$flags['publicize_post'] = true;
530
531
		return $flags;
532
	}
533
534
	/**
535
	 * Options Code
536
	 */
537
538
	function options_page_facebook() {
539
		$connected_services = $this->get_all_connections();
540
		$connection         = $connected_services['facebook'][ $_REQUEST['connection'] ];
541
		$options_to_show    = ( ! empty( $connection['connection_data']['meta']['options_responses'] ) ? $connection['connection_data']['meta']['options_responses'] : false );
542
543
		// Nonce check
544
		check_admin_referer( 'options_page_facebook_' . $_REQUEST['connection'] );
545
546
		$pages = ( ! empty( $options_to_show[1]['data'] ) ? $options_to_show[1]['data'] : false );
547
548
		$page_selected   = false;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 3 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
549
		if ( ! empty( $connection['connection_data']['meta']['facebook_page'] ) ) {
550
			$found = false;
551
			if ( $pages && is_array( $pages->data ) ) {
552
				foreach ( $pages->data as $page ) {
553
					if ( $page->id == $connection['connection_data']['meta']['facebook_page'] ) {
554
						$found = true;
555
						break;
556
					}
557
				}
558
			}
559
560
			if ( $found ) {
561
				$page_selected   = $connection['connection_data']['meta']['facebook_page'];
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 3 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
562
			}
563
		}
564
565
		?>
566
567
		<div id="thickbox-content">
568
569
			<?php
570
			ob_start();
571
			Publicize_UI::connected_notice( 'Facebook' );
572
			$update_notice = ob_get_clean();
573
574
			if ( ! empty( $update_notice ) ) {
575
				echo $update_notice;
576
			}
577
			$page_info_message = sprintf(
578
				__( 'Facebook supports Publicize connections to Facebook Pages, but not to Facebook Profiles. <a href="%s">Learn More about Publicize for Facebook</a>', 'jetpack' ),
579
				'https://jetpack.com/support/publicize/facebook'
580
			);
581
582
			if ( $pages ) : ?>
583
				<p><?php _e( 'Publicize to my <strong>Facebook Page</strong>:', 'jetpack' ); ?></p>
584
				<table id="option-fb-fanpage">
585
					<tbody>
586
587
					<?php foreach ( $pages as $i => $page ) : ?>
588
						<?php if ( ! ( $i % 2 ) ) : ?>
589
							<tr>
590
						<?php endif; ?>
591
						<td class="radio"><input type="radio" name="option" data-type="page"
592
						                         id="<?php echo esc_attr( $page['id'] ) ?>"
593
						                         value="<?php echo esc_attr( $page['id'] ) ?>" <?php checked( $page_selected && $page_selected == $page['id'], true ); ?> />
594
						</td>
595
						<td class="thumbnail"><label for="<?php echo esc_attr( $page['id'] ) ?>"><img
596
									src="<?php echo esc_url( str_replace( '_s', '_q', $page['picture']['data']['url'] ) ) ?>"
597
									width="50" height="50"/></label></td>
598
						<td class="details">
599
							<label for="<?php echo esc_attr( $page['id'] ) ?>">
600
								<span class="name"><?php echo esc_html( $page['name'] ) ?></span><br/>
601
								<span class="category"><?php echo esc_html( $page['category'] ) ?></span>
602
							</label>
603
						</td>
604
						<?php if ( ( $i % 2 ) || ( $i == count( $pages ) - 1 ) ): ?>
605
							</tr>
606
						<?php endif; ?>
607
					<?php endforeach; ?>
608
609
					</tbody>
610
				</table>
611
612
				<?php Publicize_UI::global_checkbox( 'facebook', $_REQUEST['connection'] ); ?>
613
				<p style="text-align: center;">
614
					<input type="submit" value="<?php esc_attr_e( 'OK', 'jetpack' ) ?>"
615
					       class="button fb-options save-options" name="save"
616
					       data-connection="<?php echo esc_attr( $_REQUEST['connection'] ); ?>"
617
					       rel="<?php echo wp_create_nonce( 'save_fb_token_' . $_REQUEST['connection'] ) ?>"/>
618
				</p><br/>
619
				<p><?php echo $page_info_message; ?></p>
620
			<?php else: ?>
621
				<div>
622
					<p><?php echo $page_info_message; ?></p>
623
					<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>
624
				</div>
625
			<?php endif; ?>
626
		</div>
627
		<?php
628
	}
629
630
	function options_save_facebook() {
631
		// Nonce check
632
		check_admin_referer( 'save_fb_token_' . $_REQUEST['connection'] );
633
634
		$id = $_POST['connection'];
635
636
		// Check for a numeric page ID
637
		$page_id = $_POST['selected_id'];
638
		if ( ! ctype_digit( $page_id ) ) {
639
			die( 'Security check' );
0 ignored issues
show
Coding Style Compatibility introduced by
The method options_save_facebook() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
640
		}
641
642
		if ( 'page' != $_POST['type'] || ! isset( $_POST['selected_id'] ) ) {
643
			return;
644
		}
645
646
		// Publish to Page
647
		$options = array(
648
			'facebook_page'    => $page_id,
649
			'facebook_profile' => null
650
		);
651
652
653
		Jetpack::load_xml_rpc_client();
654
		$xml = new Jetpack_IXR_Client();
655
		$xml->query( 'jetpack.setPublicizeOptions', $id, $options );
656
657
		if ( ! $xml->isError() ) {
658
			$response = $xml->getResponse();
659
			Jetpack_Options::update_option( 'publicize_connections', $response );
660
		}
661
662
		$this->globalization();
663
	}
664
665
	function options_page_tumblr() {
666
		// Nonce check
667
		check_admin_referer( 'options_page_tumblr_' . $_REQUEST['connection'] );
668
669
		$connected_services = $this->get_all_connections();
670
		$connection         = $connected_services['tumblr'][ $_POST['connection'] ];
671
		$options_to_show    = $connection['connection_data']['meta']['options_responses'];
672
		$request            = $options_to_show[0];
673
674
		$blogs = $request['response']['user']['blogs'];
675
676
		$blog_selected = false;
677
678
		if ( ! empty( $connection['connection_data']['meta']['tumblr_base_hostname'] ) ) {
679
			foreach ( $blogs as $blog ) {
680
				if ( $connection['connection_data']['meta']['tumblr_base_hostname'] == $this->get_basehostname( $blog['url'] ) ) {
681
					$blog_selected = $connection['connection_data']['meta']['tumblr_base_hostname'];
682
					break;
683
				}
684
			}
685
686
		}
687
688
		// Use their Primary blog if they haven't selected one yet
689
		if ( ! $blog_selected ) {
690
			foreach ( $blogs as $blog ) {
691
				if ( $blog['primary'] ) {
692
					$blog_selected = $this->get_basehostname( $blog['url'] );
693
				}
694
			}
695
		} ?>
696
697
		<div id="thickbox-content">
698
699
			<?php
700
			ob_start();
701
			Publicize_UI::connected_notice( 'Tumblr' );
702
			$update_notice = ob_get_clean();
703
704
			if ( ! empty( $update_notice ) ) {
705
				echo $update_notice;
706
			}
707
			?>
708
709
			<p><?php _e( 'Publicize to my <strong>Tumblr blog</strong>:', 'jetpack' ); ?></p>
710
711
			<ul id="option-tumblr-blog">
712
713
				<?php
714
				foreach ( $blogs as $blog ) {
715
					$url = $this->get_basehostname( $blog['url'] ); ?>
716
					<li>
717
						<input type="radio" name="option" data-type="blog" id="<?php echo esc_attr( $url ) ?>"
718
						       value="<?php echo esc_attr( $url ) ?>" <?php checked( $blog_selected == $url, true ); ?> />
719
						<label for="<?php echo esc_attr( $url ) ?>"><span
720
								class="name"><?php echo esc_html( $blog['title'] ) ?></span></label>
721
					</li>
722
				<?php } ?>
723
724
			</ul>
725
726
			<?php Publicize_UI::global_checkbox( 'tumblr', $_REQUEST['connection'] ); ?>
727
728
			<p style="text-align: center;">
729
				<input type="submit" value="<?php esc_attr_e( 'OK', 'jetpack' ) ?>"
730
				       class="button tumblr-options save-options" name="save"
731
				       data-connection="<?php echo esc_attr( $_REQUEST['connection'] ); ?>"
732
				       rel="<?php echo wp_create_nonce( 'save_tumblr_blog_' . $_REQUEST['connection'] ) ?>"/>
733
			</p> <br/>
734
		</div>
735
736
		<?php
737
	}
738
739
	function get_basehostname( $url ) {
740
		return parse_url( $url, PHP_URL_HOST );
741
	}
742
743
	function options_save_tumblr() {
744
		// Nonce check
745
		check_admin_referer( 'save_tumblr_blog_' . $_REQUEST['connection'] );
746
747
		$id = $_POST['connection'];
748
749
		$options = array( 'tumblr_base_hostname' => $_POST['selected_id'] );
750
751
		Jetpack::load_xml_rpc_client();
752
		$xml = new Jetpack_IXR_Client();
753
		$xml->query( 'jetpack.setPublicizeOptions', $id, $options );
754
755
		if ( ! $xml->isError() ) {
756
			$response = $xml->getResponse();
757
			Jetpack_Options::update_option( 'publicize_connections', $response );
758
		}
759
760
		$this->globalization();
761
	}
762
763
	function options_page_twitter() {
764
		Publicize_UI::options_page_other( 'twitter' );
765
	}
766
767
	function options_page_linkedin() {
768
		Publicize_UI::options_page_other( 'linkedin' );
769
	}
770
771
	function options_page_path() {
772
		Publicize_UI::options_page_other( 'path' );
773
	}
774
775
	function options_page_google_plus() {
776
		Publicize_UI::options_page_other( 'google_plus' );
777
	}
778
779
	function options_save_twitter() {
780
		$this->options_save_other( 'twitter' );
781
	}
782
783
	function options_save_linkedin() {
784
		$this->options_save_other( 'linkedin' );
785
	}
786
787
	function options_save_path() {
788
		$this->options_save_other( 'path' );
789
	}
790
791
	function options_save_google_plus() {
792
		$this->options_save_other( 'google_plus' );
793
	}
794
795
	function options_save_other( $service_name ) {
796
		// Nonce check
797
		check_admin_referer( 'save_' . $service_name . '_token_' . $_REQUEST['connection'] );
798
		$this->globalization();
799
	}
800
801
	/**
802
	 * Already-published posts should not be Publicized by default. This filter sets checked to
803
	 * false if a post has already been published.
804
	 */
805
	function publicize_checkbox_default( $checked, $post_id, $name, $connection ) {
806
		if ( 'publish' == get_post_status( $post_id ) ) {
807
			return false;
808
		}
809
810
		return $checked;
811
	}
812
813
	/**
814
	 * If there's only one shared connection to Twitter set it as twitter:site tag.
815
	 */
816
	function enhaced_twitter_cards_site_tag( $tag ) {
817
		$custom_site_tag = get_option( 'jetpack-twitter-cards-site-tag' );
818
		if ( ! empty( $custom_site_tag ) ) {
819
			return $tag;
820
		}
821
		if ( ! $this->is_enabled( 'twitter' ) ) {
822
			return $tag;
823
		}
824
		$connections = $this->get_connections( 'twitter' );
825
		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...
826
			$connection_meta = $this->get_connection_meta( $connection );
827
			if ( 0 == $connection_meta['connection_data']['user_id'] ) {
828
				// If the connection is shared
829
				return $this->get_display_name( 'twitter', $connection );
830
			}
831
		}
832
833
		return $tag;
834
	}
835
836
	function save_publicized_twitter_account( $submit_post, $post_id, $service_name, $connection ) {
837
		if ( 'twitter' == $service_name && $submit_post ) {
838
			$connection_meta        = $this->get_connection_meta( $connection );
839
			$publicize_twitter_user = get_post_meta( $post_id, '_publicize_twitter_user' );
840
			if ( empty( $publicize_twitter_user ) || 0 != $connection_meta['connection_data']['user_id'] ) {
841
				update_post_meta( $post_id, '_publicize_twitter_user', $this->get_display_name( 'twitter', $connection ) );
842
			}
843
		}
844
	}
845
846
	function get_publicized_twitter_account( $account, $post_id ) {
847
		if ( ! empty( $account ) ) {
848
			return $account;
849
		}
850
		$account = get_post_meta( $post_id, '_publicize_twitter_user', true );
851
		if ( ! empty( $account ) ) {
852
			return $account;
853
		}
854
855
		return '';
856
	}
857
858
	/**
859
	 * Save the Publicized Facebook account when publishing a post
860
	 * Use only Personal accounts, not Facebook Pages
861
	 */
862
	function save_publicized_facebook_account( $submit_post, $post_id, $service_name, $connection ) {
863
		$connection_meta = $this->get_connection_meta( $connection );
864
		if ( 'facebook' == $service_name && isset( $connection_meta['connection_data']['meta']['facebook_profile'] ) && $submit_post ) {
865
			$publicize_facebook_user = get_post_meta( $post_id, '_publicize_facebook_user' );
866
			if ( empty( $publicize_facebook_user ) || 0 != $connection_meta['connection_data']['user_id'] ) {
867
				$profile_link = $this->get_profile_link( 'facebook', $connection );
868
869
				if ( false !== $profile_link ) {
870
					update_post_meta( $post_id, '_publicize_facebook_user', $profile_link );
871
				}
872
			}
873
		}
874
	}
875
}
876