Completed
Push — add/jetpack-icon-gutenberg ( b0ee9a...546ac6 )
by
unknown
21:27 queued 10:33
created

Publicize::get_all_connections_for_user()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 11
nc 5
nop 0
dl 0
loc 20
rs 8.8571
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_connections( $service_name, $_blog_id = false, $_user_id = false ) {
124
		$connections           = Jetpack_Options::get_option( 'publicize_connections' );
125
		$connections_to_return = array();
126
		if ( ! empty( $connections ) && is_array( $connections ) ) {
127
			if ( ! empty( $connections[ $service_name ] ) ) {
128
				foreach ( $connections[ $service_name ] as $id => $connection ) {
129
					if ( 0 == $connection['connection_data']['user_id'] || $this->user_id() == $connection['connection_data']['user_id'] ) {
130
						$connections_to_return[ $id ] = $connection;
131
					}
132
				}
133
			}
134
135
			return $connections_to_return;
136
		}
137
138
		return false;
139
	}
140
141
	function get_all_connections_for_user() {
142
		$connections = Jetpack_Options::get_option( 'publicize_connections' );
143
144
		$connections_to_return = array();
145
		if ( ! empty( $connections ) ) {
146
			foreach ( (array) $connections as $service_name => $connections_for_service ) {
147
				foreach ( $connections_for_service as $id => $connection ) {
148
					$user_id = intval( $connection['connection_data']['user_id'] );
149
					// phpcs:ignore WordPress.PHP.YodaConditions.NotYoda
150
					if ( $user_id === 0 || $this->user_id() === $user_id ) {
151
						$connections_to_return[ $service_name ][ $id ] = $connection;
152
					}
153
				}
154
			}
155
156
			return $connections_to_return;
157
		}
158
159
		return false;
160
	}
161
162
	function get_connection_id( $connection ) {
163
		return $connection['connection_data']['id'];
164
	}
165
166
	function get_connection_meta( $connection ) {
167
		$connection['user_id'] = $connection['connection_data']['user_id']; // Allows for shared connections
168
		return $connection;
169
	}
170
171
	function admin_page_load() {
172
		if ( isset( $_GET['action'] ) ) {
173
			if ( isset( $_GET['service'] ) ) {
174
				$service_name = $_GET['service'];
175
			}
176
177
			switch ( $_GET['action'] ) {
178
				case 'error':
179
					add_action( 'pre_admin_screen_sharing', array( $this, 'display_connection_error' ), 9 );
180
					break;
181
182
				case 'request':
183
					check_admin_referer( 'keyring-request', 'kr_nonce' );
184
					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...
185
186
					$verification = Jetpack::generate_secrets( 'publicize' );
187
					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...
188
						$url = Jetpack::admin_url( 'jetpack#/settings' );
189
						wp_die( sprintf( __( "Jetpack is not connected. Please connect Jetpack by visiting <a href='%s'>Settings</a>.", 'jetpack' ), $url ) );
190
191
					}
192
					$stats_options = get_option( 'stats_options' );
193
					$wpcom_blog_id = Jetpack_Options::get_option( 'id' );
194
					$wpcom_blog_id = ! empty( $wpcom_blog_id ) ? $wpcom_blog_id : $stats_options['blog_id'];
195
196
					$user     = wp_get_current_user();
197
					$redirect = $this->api_url( $service_name, urlencode_deep( array(
198
						'action'       => 'request',
199
						'redirect_uri' => add_query_arg( array( 'action' => 'done' ), menu_page_url( 'sharing', false ) ),
200
						'for'          => 'publicize',
201
						// required flag that says this connection is intended for publicize
202
						'siteurl'      => site_url(),
203
						'state'        => $user->ID,
204
						'blog_id'      => $wpcom_blog_id,
205
						'secret_1'     => $verification['secret_1'],
206
						'secret_2'     => $verification['secret_2'],
207
						'eol'          => $verification['exp'],
208
					) ) );
209
					wp_redirect( $redirect );
210
					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...
211
					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...
212
213
				case 'completed':
214
					Jetpack::load_xml_rpc_client();
215
					$xml = new Jetpack_IXR_Client();
216
					$xml->query( 'jetpack.fetchPublicizeConnections' );
217
218
					if ( ! $xml->isError() ) {
219
						$response = $xml->getResponse();
220
						Jetpack_Options::update_option( 'publicize_connections', $response );
221
					}
222
223
					break;
224
225
				case 'delete':
226
					$id = $_GET['id'];
227
228
					check_admin_referer( 'keyring-request', 'kr_nonce' );
229
					check_admin_referer( "keyring-request-$service_name", 'nonce' );
230
231
					$this->disconnect( $service_name, $id );
232
233
					add_action( 'admin_notices', array( $this, 'display_disconnected' ) );
234
					break;
235
			}
236
		}
237
238
		// Do we really need `admin_styles`? With the new admin UI, it's breaking some bits.
239
		// Errors encountered on WordPress.com's end are passed back as a code
240
		/*
241
		if ( isset( $_GET['action'] ) && 'error' == $_GET['action'] ) {
242
			// Load Jetpack's styles to handle the box
243
			Jetpack::init()->admin_styles();
244
		}
245
		*/
246
	}
247
248
	function display_connection_error() {
249
		$code = false;
250
		if ( isset( $_GET['service'] ) ) {
251
			$service_name = $_GET['service'];
252
			$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 ) );
253
		} else {
254
			if ( isset( $_GET['publicize_error'] ) ) {
255
				$code = strtolower( $_GET['publicize_error'] );
256
				switch ( $code ) {
257
					case '400':
258
						$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' );
259
						break;
260
					case 'secret_mismatch':
261
						$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' );
262
						break;
263
					case 'empty_blog_id':
264
						$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' );
265
						break;
266
					case 'empty_state':
267
						$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() );
268
						break;
269
					default:
270
						$error = __( 'Something which should never happen, happened. Sorry about that. If you try again, maybe it will work.', 'jetpack' );
271
						break;
272
				}
273
			} else {
274
				$error = __( 'There was a problem connecting with Publicize. Please try again in a moment.', 'jetpack' );
275
			}
276
		}
277
		// Using the same formatting/style as Jetpack::admin_notices() error
278
		?>
279
		<div id="message" class="jetpack-message jetpack-err">
280
			<div class="squeezer">
281
				<h2><?php echo wp_kses( $error, array( 'a'      => array( 'href' => true ),
282
				                                       'code'   => true,
283
				                                       'strong' => true,
284
				                                       'br'     => true,
285
				                                       'b'      => true
286
					) ); ?></h2>
287
				<?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...
288
					<p><?php printf( __( 'Error code: %s', 'jetpack' ), esc_html( stripslashes( $code ) ) ); ?></p>
289
				<?php endif; ?>
290
			</div>
291
		</div>
292
		<?php
293
	}
294
295
	function display_disconnected() {
296
		echo "<div class='updated'>\n";
297
		echo '<p>' . esc_html( __( 'That connection has been removed.', 'jetpack' ) ) . "</p>\n";
298
		echo "</div>\n\n";
299
	}
300
301
	function globalization() {
302
		if ( 'on' == $_REQUEST['global'] ) {
303
			$id = $_REQUEST['connection'];
304
305
			if ( ! current_user_can( $this->GLOBAL_CAP ) ) {
306
				return;
307
			}
308
309
			Jetpack::load_xml_rpc_client();
310
			$xml = new Jetpack_IXR_Client();
311
			$xml->query( 'jetpack.globalizePublicizeConnection', $id, 'globalize' );
312
313
			if ( ! $xml->isError() ) {
314
				$response = $xml->getResponse();
315
				Jetpack_Options::update_option( 'publicize_connections', $response );
316
			}
317
		}
318
	}
319
320
	/**
321
	 * Gets a URL to the public-api actions. Works like WP's admin_url
322
	 *
323
	 * @param string $service Shortname of a specific service.
324
	 *
325
	 * @return URL to specific public-api process
326
	 */
327
	// on WordPress.com this is/calls Keyring::admin_url
328
	function api_url( $service = false, $params = array() ) {
329
		/**
330
		 * Filters the API URL used to interact with WordPress.com.
331
		 *
332
		 * @module publicize
333
		 *
334
		 * @since 2.0.0
335
		 *
336
		 * @param string https://public-api.wordpress.com/connect/?jetpack=publicize Default Publicize API URL.
337
		 */
338
		$url = apply_filters( 'publicize_api_url', 'https://public-api.wordpress.com/connect/?jetpack=publicize' );
339
340
		if ( $service ) {
341
			$url = add_query_arg( array( 'service' => $service ), $url );
342
		}
343
344
		if ( count( $params ) ) {
345
			$url = add_query_arg( $params, $url );
346
		}
347
348
		return $url;
349
	}
350
351 View Code Duplication
	function connect_url( $service_name ) {
352
		return add_query_arg( array(
353
			'action'   => 'request',
354
			'service'  => $service_name,
355
			'kr_nonce' => wp_create_nonce( 'keyring-request' ),
356
			'nonce'    => wp_create_nonce( "keyring-request-$service_name" ),
357
		), menu_page_url( 'sharing', false ) );
358
	}
359
360
	function refresh_url( $service_name ) {
361
		return add_query_arg( array(
362
			'action'   => 'request',
363
			'service'  => $service_name,
364
			'kr_nonce' => wp_create_nonce( 'keyring-request' ),
365
			'refresh'  => 1,
366
			'for'      => 'publicize',
367
			'nonce'    => wp_create_nonce( "keyring-request-$service_name" ),
368
		), admin_url( 'options-general.php?page=sharing' ) );
369
	}
370
371 View Code Duplication
	function disconnect_url( $service_name, $id ) {
372
		return add_query_arg( array(
373
			'action'   => 'delete',
374
			'service'  => $service_name,
375
			'id'       => $id,
376
			'kr_nonce' => wp_create_nonce( 'keyring-request' ),
377
			'nonce'    => wp_create_nonce( "keyring-request-$service_name" ),
378
		), menu_page_url( 'sharing', false ) );
379
	}
380
381
	/**
382
	 * Get social networks, either all available or only those that the site is connected to.
383
	 *
384
	 * @since 2.0
385
	 *
386
	 * @param string $filter Select the list of services that will be returned. Defaults to 'all', accepts 'connected'.
387
	 *
388
	 * @return array List of social networks.
389
	 */
390
	function get_services( $filter = 'all' ) {
391
		$services = array(
392
			'facebook'    => array(),
393
			'twitter'     => array(),
394
			'linkedin'    => array(),
395
			'tumblr'      => array(),
396
			'path'        => array(),
397
			'google_plus' => array(),
398
		);
399
400
		if ( 'all' == $filter ) {
401
			return $services;
402
		} else {
403
			$connected_services = array();
404
			foreach ( $services as $service => $empty ) {
405
				$connections = $this->get_connections( $service );
406
				if ( $connections ) {
407
					$connected_services[ $service ] = $connections;
408
				}
409
			}
410
			return $connected_services;
411
		}
412
	}
413
414
	function get_connection( $service, $id, $_blog_id = false, $_user_id = false ) {
415
		// Stub
416
	}
417
418
	function flag_post_for_publicize( $new_status, $old_status, $post ) {
419
		if ( ! $this->post_type_is_publicizeable( $post->post_type ) ) {
420
			return;
421
		}
422
423
		if ( 'publish' == $new_status && 'publish' != $old_status ) {
424
			/**
425
			 * Determines whether a post being published gets publicized.
426
			 *
427
			 * Side-note: Possibly our most alliterative filter name.
428
			 *
429
			 * @module publicize
430
			 *
431
			 * @since 4.1.0
432
			 *
433
			 * @param bool $should_publicize Should the post be publicized? Default to true.
434
			 * @param WP_POST $post Current Post object.
435
			 */
436
			$should_publicize = apply_filters( 'publicize_should_publicize_published_post', true, $post );
437
438
			if ( $should_publicize ) {
439
				update_post_meta( $post->ID, $this->PENDING, true );
440
			}
441
		}
442
	}
443
444
	function test_connection( $service_name, $connection ) {
445
		$connection_test_passed  = true;
446
		$connection_test_message = '';
447
		$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...
448
449
		$id = $this->get_connection_id( $connection );
450
451
		Jetpack::load_xml_rpc_client();
452
		$xml = new Jetpack_IXR_Client();
453
		$xml->query( 'jetpack.testPublicizeConnection', $id );
454
455
		if ( $xml->isError() ) {
456
			$xml_response            = $xml->getResponse();
457
			$connection_test_message = $xml_response['faultString'];
458
			$connection_test_passed  = false;
459
		}
460
461
		// Bail if all is well
462
		if ( $connection_test_passed ) {
463
			return true;
464
		}
465
466
		// Set up refresh if the user can
467
		$user_can_refresh = current_user_can( $this->GLOBAL_CAP );
468
		if ( $user_can_refresh ) {
469
			$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...
470
			$refresh_text = sprintf( _x( 'Refresh connection with %s', 'Refresh connection with {social media service}', 'jetpack' ), $this->get_service_label( $service_name ) );
471
			$refresh_url  = $this->refresh_url( $service_name );
472
		}
473
474
		$error_data = array(
475
			'user_can_refresh' => $user_can_refresh,
476
			'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...
477
			'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...
478
		);
479
480
		return new WP_Error( 'pub_conn_test_failed', $connection_test_message, $error_data );
481
	}
482
483
	/**
484
	 * Save a flag locally to indicate that this post has already been Publicized via the selected
485
	 * connections.
486
	 */
487
	function save_publicized( $post_ID, $post = null, $update = null ) {
488
		if ( is_null( $post ) ) {
489
			return;
490
		}
491
		// Only do this when a post transitions to being published
492
		if ( get_post_meta( $post->ID, $this->PENDING ) && $this->post_type_is_publicizeable( $post->post_type ) ) {
493
			$connected_services = Jetpack_Options::get_option( 'publicize_connections' );
494
			if ( ! empty( $connected_services ) ) {
495
				/**
496
				 * Fires when a post is saved that has is marked as pending publicizing
497
				 *
498
				 * @since 4.1.0
499
				 *
500
				 * @param int The post ID
501
				 */
502
				do_action_deprecated( 'jetpack_publicize_post', $post->ID, '4.8.0', 'jetpack_published_post_flags' );
503
			}
504
			delete_post_meta( $post->ID, $this->PENDING );
505
			update_post_meta( $post->ID, $this->POST_DONE . 'all', true );
506
		}
507
	}
508
509
	function set_post_flags( $flags, $post ) {
510
		$flags['publicize_post'] = false;
511
		if ( ! $this->post_type_is_publicizeable( $post->post_type ) ) {
512
			return $flags;
513
		}
514
		/** This filter is already documented in modules/publicize/publicize-jetpack.php */
515
		if ( ! apply_filters( 'publicize_should_publicize_published_post', true, $post ) ) {
516
			return $flags;
517
		}
518
519
		$connected_services = Jetpack_Options::get_option( 'publicize_connections' );
520
521
		if ( empty( $connected_services ) ) {
522
			return $flags;
523
		}
524
525
		$flags['publicize_post'] = true;
526
527
		return $flags;
528
	}
529
530
	/**
531
	 * Options Code
532
	 */
533
534
	function options_page_facebook() {
535
		$connected_services = Jetpack_Options::get_option( 'publicize_connections' );
536
		$connection         = $connected_services['facebook'][ $_REQUEST['connection'] ];
537
		$options_to_show    = ( ! empty( $connection['connection_data']['meta']['options_responses'] ) ? $connection['connection_data']['meta']['options_responses'] : false );
538
539
		// Nonce check
540
		check_admin_referer( 'options_page_facebook_' . $_REQUEST['connection'] );
541
542
		$me    = ( ! empty( $options_to_show[0] ) ? $options_to_show[0] : false );
543
		$pages = ( ! empty( $options_to_show[1]['data'] ) ? $options_to_show[1]['data'] : false );
544
545
		$profile_checked = true;
546
		$page_selected   = false;
547
548
		if ( ! empty( $connection['connection_data']['meta']['facebook_page'] ) ) {
549
			$found = false;
550
			if ( $pages && is_array( $pages->data ) ) {
551
				foreach ( $pages->data as $page ) {
552
					if ( $page->id == $connection['connection_data']['meta']['facebook_page'] ) {
553
						$found = true;
554
						break;
555
					}
556
				}
557
			}
558
559
			if ( $found ) {
560
				$profile_checked = false;
561
				$page_selected   = $connection['connection_data']['meta']['facebook_page'];
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
			?>
578
579
			<?php if ( ! empty( $me['name'] ) ) : ?>
580
				<p><?php _e( 'Publicize to my <strong>Facebook Wall</strong>:', 'jetpack' ); ?></p>
581
				<table id="option-profile">
582
					<tbody>
583
					<tr>
584
						<td class="radio"><input type="radio" name="option" data-type="profile"
585
						                         id="<?php echo esc_attr( $me['id'] ) ?>"
586
						                         value="" <?php checked( $profile_checked, true ); ?> /></td>
587
						<td class="thumbnail"><label for="<?php echo esc_attr( $me['id'] ) ?>"><img
588
									src="<?php echo esc_url( $me['picture']['data']['url'] ) ?>" width="50"
589
									height="50"/></label></td>
590
						<td class="details"><label
591
								for="<?php echo esc_attr( $me['id'] ) ?>"><?php echo esc_html( $me['name'] ) ?></label>
592
						</td>
593
					</tr>
594
					</tbody>
595
				</table>
596
			<?php endif; ?>
597
598
			<?php if ( $pages ) : ?>
599
600
				<p><?php _e( 'Publicize to my <strong>Facebook Page</strong>:', 'jetpack' ); ?></p>
601
				<table id="option-fb-fanpage">
602
					<tbody>
603
604
					<?php foreach ( $pages as $i => $page ) : ?>
605
						<?php if ( ! ( $i % 2 ) ) : ?>
606
							<tr>
607
						<?php endif; ?>
608
						<td class="radio"><input type="radio" name="option" data-type="page"
609
						                         id="<?php echo esc_attr( $page['id'] ) ?>"
610
						                         value="<?php echo esc_attr( $page['id'] ) ?>" <?php checked( $page_selected && $page_selected == $page['id'], true ); ?> />
611
						</td>
612
						<td class="thumbnail"><label for="<?php echo esc_attr( $page['id'] ) ?>"><img
613
									src="<?php echo esc_url( str_replace( '_s', '_q', $page['picture']['data']['url'] ) ) ?>"
614
									width="50" height="50"/></label></td>
615
						<td class="details">
616
							<label for="<?php echo esc_attr( $page['id'] ) ?>">
617
								<span class="name"><?php echo esc_html( $page['name'] ) ?></span><br/>
618
								<span class="category"><?php echo esc_html( $page['category'] ) ?></span>
619
							</label>
620
						</td>
621
						<?php if ( ( $i % 2 ) || ( $i == count( $pages ) - 1 ) ): ?>
622
							</tr>
623
						<?php endif; ?>
624
					<?php endforeach; ?>
625
626
					</tbody>
627
				</table>
628
629
			<?php endif; ?>
630
631
			<?php Publicize_UI::global_checkbox( 'facebook', $_REQUEST['connection'] ); ?>
632
633
			<p style="text-align: center;">
634
				<input type="submit" value="<?php esc_attr_e( 'OK', 'jetpack' ) ?>"
635
				       class="button fb-options save-options" name="save"
636
				       data-connection="<?php echo esc_attr( $_REQUEST['connection'] ); ?>"
637
				       rel="<?php echo wp_create_nonce( 'save_fb_token_' . $_REQUEST['connection'] ) ?>"/>
638
			</p><br/>
639
		</div>
640
641
		<?php
642
	}
643
644
	function options_save_facebook() {
645
		// Nonce check
646
		check_admin_referer( 'save_fb_token_' . $_REQUEST['connection'] );
647
648
		$id = $_POST['connection'];
649
650
		// Check for a numeric page ID
651
		$page_id = $_POST['selected_id'];
652
		if ( ! ctype_digit( $page_id ) ) {
653
			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...
654
		}
655
656
		if ( isset( $_POST['selected_id'] ) && 'profile' == $_POST['type'] ) {
657
			// Publish to User Wall/Profile
658
			$options = array(
659
				'facebook_page'    => null,
660
				'facebook_profile' => true
661
			);
662
663
		} else {
664
			if ( 'page' != $_POST['type'] || ! isset( $_POST['selected_id'] ) ) {
665
				return;
666
			}
667
668
			// Publish to Page
669
			$options = array(
670
				'facebook_page'    => $page_id,
671
				'facebook_profile' => null
672
			);
673
		}
674
675
		Jetpack::load_xml_rpc_client();
676
		$xml = new Jetpack_IXR_Client();
677
		$xml->query( 'jetpack.setPublicizeOptions', $id, $options );
678
679
		if ( ! $xml->isError() ) {
680
			$response = $xml->getResponse();
681
			Jetpack_Options::update_option( 'publicize_connections', $response );
682
		}
683
684
		$this->globalization();
685
	}
686
687
	function options_page_tumblr() {
688
		// Nonce check
689
		check_admin_referer( 'options_page_tumblr_' . $_REQUEST['connection'] );
690
691
		$connected_services = Jetpack_Options::get_option( 'publicize_connections' );
692
		$connection         = $connected_services['tumblr'][ $_POST['connection'] ];
693
		$options_to_show    = $connection['connection_data']['meta']['options_responses'];
694
		$request            = $options_to_show[0];
695
696
		$blogs = $request['response']['user']['blogs'];
697
698
		$blog_selected = false;
699
700
		if ( ! empty( $connection['connection_data']['meta']['tumblr_base_hostname'] ) ) {
701
			foreach ( $blogs as $blog ) {
702
				if ( $connection['connection_data']['meta']['tumblr_base_hostname'] == $this->get_basehostname( $blog['url'] ) ) {
703
					$blog_selected = $connection['connection_data']['meta']['tumblr_base_hostname'];
704
					break;
705
				}
706
			}
707
708
		}
709
710
		// Use their Primary blog if they haven't selected one yet
711
		if ( ! $blog_selected ) {
712
			foreach ( $blogs as $blog ) {
713
				if ( $blog['primary'] ) {
714
					$blog_selected = $this->get_basehostname( $blog['url'] );
715
				}
716
			}
717
		} ?>
718
719
		<div id="thickbox-content">
720
721
			<?php
722
			ob_start();
723
			Publicize_UI::connected_notice( 'Tumblr' );
724
			$update_notice = ob_get_clean();
725
726
			if ( ! empty( $update_notice ) ) {
727
				echo $update_notice;
728
			}
729
			?>
730
731
			<p><?php _e( 'Publicize to my <strong>Tumblr blog</strong>:', 'jetpack' ); ?></p>
732
733
			<ul id="option-tumblr-blog">
734
735
				<?php
736
				foreach ( $blogs as $blog ) {
737
					$url = $this->get_basehostname( $blog['url'] ); ?>
738
					<li>
739
						<input type="radio" name="option" data-type="blog" id="<?php echo esc_attr( $url ) ?>"
740
						       value="<?php echo esc_attr( $url ) ?>" <?php checked( $blog_selected == $url, true ); ?> />
741
						<label for="<?php echo esc_attr( $url ) ?>"><span
742
								class="name"><?php echo esc_html( $blog['title'] ) ?></span></label>
743
					</li>
744
				<?php } ?>
745
746
			</ul>
747
748
			<?php Publicize_UI::global_checkbox( 'tumblr', $_REQUEST['connection'] ); ?>
749
750
			<p style="text-align: center;">
751
				<input type="submit" value="<?php esc_attr_e( 'OK', 'jetpack' ) ?>"
752
				       class="button tumblr-options save-options" name="save"
753
				       data-connection="<?php echo esc_attr( $_REQUEST['connection'] ); ?>"
754
				       rel="<?php echo wp_create_nonce( 'save_tumblr_blog_' . $_REQUEST['connection'] ) ?>"/>
755
			</p> <br/>
756
		</div>
757
758
		<?php
759
	}
760
761
	function get_basehostname( $url ) {
762
		return parse_url( $url, PHP_URL_HOST );
763
	}
764
765
	function options_save_tumblr() {
766
		// Nonce check
767
		check_admin_referer( 'save_tumblr_blog_' . $_REQUEST['connection'] );
768
769
		$id = $_POST['connection'];
770
771
		$options = array( 'tumblr_base_hostname' => $_POST['selected_id'] );
772
773
		Jetpack::load_xml_rpc_client();
774
		$xml = new Jetpack_IXR_Client();
775
		$xml->query( 'jetpack.setPublicizeOptions', $id, $options );
776
777
		if ( ! $xml->isError() ) {
778
			$response = $xml->getResponse();
779
			Jetpack_Options::update_option( 'publicize_connections', $response );
780
		}
781
782
		$this->globalization();
783
	}
784
785
	function options_page_twitter() {
786
		Publicize_UI::options_page_other( 'twitter' );
787
	}
788
789
	function options_page_linkedin() {
790
		Publicize_UI::options_page_other( 'linkedin' );
791
	}
792
793
	function options_page_path() {
794
		Publicize_UI::options_page_other( 'path' );
795
	}
796
797
	function options_page_google_plus() {
798
		Publicize_UI::options_page_other( 'google_plus' );
799
	}
800
801
	function options_save_twitter() {
802
		$this->options_save_other( 'twitter' );
803
	}
804
805
	function options_save_linkedin() {
806
		$this->options_save_other( 'linkedin' );
807
	}
808
809
	function options_save_path() {
810
		$this->options_save_other( 'path' );
811
	}
812
813
	function options_save_google_plus() {
814
		$this->options_save_other( 'google_plus' );
815
	}
816
817
	function options_save_other( $service_name ) {
818
		// Nonce check
819
		check_admin_referer( 'save_' . $service_name . '_token_' . $_REQUEST['connection'] );
820
		$this->globalization();
821
	}
822
823
	/**
824
	 * Already-published posts should not be Publicized by default. This filter sets checked to
825
	 * false if a post has already been published.
826
	 */
827
	function publicize_checkbox_default( $checked, $post_id, $name, $connection ) {
828
		if ( 'publish' == get_post_status( $post_id ) ) {
829
			return false;
830
		}
831
832
		return $checked;
833
	}
834
835
	/**
836
	 * If there's only one shared connection to Twitter set it as twitter:site tag.
837
	 */
838
	function enhaced_twitter_cards_site_tag( $tag ) {
839
		$custom_site_tag = get_option( 'jetpack-twitter-cards-site-tag' );
840
		if ( ! empty( $custom_site_tag ) ) {
841
			return $tag;
842
		}
843
		if ( ! $this->is_enabled( 'twitter' ) ) {
844
			return $tag;
845
		}
846
		$connections = $this->get_connections( 'twitter' );
847
		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...
848
			$connection_meta = $this->get_connection_meta( $connection );
849
			if ( 0 == $connection_meta['connection_data']['user_id'] ) {
850
				// If the connection is shared
851
				return $this->get_display_name( 'twitter', $connection );
852
			}
853
		}
854
855
		return $tag;
856
	}
857
858
	function save_publicized_twitter_account( $submit_post, $post_id, $service_name, $connection ) {
859
		if ( 'twitter' == $service_name && $submit_post ) {
860
			$connection_meta        = $this->get_connection_meta( $connection );
861
			$publicize_twitter_user = get_post_meta( $post_id, '_publicize_twitter_user' );
862
			if ( empty( $publicize_twitter_user ) || 0 != $connection_meta['connection_data']['user_id'] ) {
863
				update_post_meta( $post_id, '_publicize_twitter_user', $this->get_display_name( 'twitter', $connection ) );
864
			}
865
		}
866
	}
867
868
	function get_publicized_twitter_account( $account, $post_id ) {
869
		if ( ! empty( $account ) ) {
870
			return $account;
871
		}
872
		$account = get_post_meta( $post_id, '_publicize_twitter_user', true );
873
		if ( ! empty( $account ) ) {
874
			return $account;
875
		}
876
877
		return '';
878
	}
879
880
	/**
881
	 * Save the Publicized Facebook account when publishing a post
882
	 * Use only Personal accounts, not Facebook Pages
883
	 */
884
	function save_publicized_facebook_account( $submit_post, $post_id, $service_name, $connection ) {
885
		$connection_meta = $this->get_connection_meta( $connection );
886
		if ( 'facebook' == $service_name && isset( $connection_meta['connection_data']['meta']['facebook_profile'] ) && $submit_post ) {
887
			$publicize_facebook_user = get_post_meta( $post_id, '_publicize_facebook_user' );
888
			if ( empty( $publicize_facebook_user ) || 0 != $connection_meta['connection_data']['user_id'] ) {
889
				$profile_link = $this->get_profile_link( 'facebook', $connection );
890
891
				if ( false !== $profile_link ) {
892
					update_post_meta( $post_id, '_publicize_facebook_user', $profile_link );
893
				}
894
			}
895
		}
896
	}
897
}
898