Completed
Push — sync/publicize-ui ( 4f02ff...f38f8e )
by Bernhard
435:27 queued 401:11
created

Publicize   F

Complexity

Total Complexity 133

Size/Duplication

Total Lines 736
Duplicated Lines 5.84 %

Coupling/Cohesion

Components 2
Dependencies 7

Importance

Changes 0
Metric Value
dl 43
loc 736
rs 1.864
c 0
b 0
f 0
wmc 133
lcom 2
cbo 7

45 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 38 1
B get_all_connections_for_user() 0 20 6
A get_connection_id() 0 3 1
A get_connection_meta() 0 4 1
A enhaced_twitter_cards_site_tag() 0 19 5
A save_publicized_twitter_account() 0 9 5
A get_publicized_twitter_account() 0 11 3
B save_publicized_facebook_account() 0 13 7
A add_disconnect_notice() 0 3 1
A force_user_connection() 0 18 3
A admin_page_warning() 0 32 2
A disconnect() 0 3 1
A receive_updated_publicize_connections() 0 5 1
A register_update_publicize_connections_xmlrpc_method() 0 5 1
A get_all_connections() 0 3 1
B get_connections() 0 22 8
A get_connection_unique_id() 0 3 1
A admin_page_load() 0 5 3
B display_connection_error() 0 46 8
A display_disconnected() 0 5 1
A globalization() 0 11 3
A globalize_connection() 10 10 2
A unglobalize_connection() 10 10 2
A connect_url() 0 3 1
A refresh_url() 0 3 1
A disconnect_url() 0 3 1
A get_services() 12 22 4
A get_connection() 0 3 1
A flag_post_for_publicize() 0 25 5
A test_connection() 0 32 3
A save_publicized() 0 21 5
A set_post_flags() 0 20 4
D options_page_facebook() 0 91 17
A options_save_facebook() 0 22 4
B options_page_tumblr() 0 73 9
A get_basehostname() 0 3 1
A options_save_tumblr() 0 8 1
A set_remote_publicize_options() 11 11 2
A options_page_twitter() 0 3 1
A options_page_linkedin() 0 3 1
A options_page_google_plus() 0 3 1
A options_save_twitter() 0 3 1
A options_save_linkedin() 0 3 1
A options_save_google_plus() 0 3 1
A options_save_other() 0 5 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Publicize often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Publicize, and based on these observations, apply Extract Interface, too.

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( 'jetpack_published_post_flags', array( $this, 'set_post_flags' ), 10, 2 );
27
28
		add_action( 'wp_insert_post', array( $this, 'save_publicized' ), 11, 3 );
29
30
		add_filter( 'jetpack_twitter_cards_site_tag', array( $this, 'enhaced_twitter_cards_site_tag' ) );
31
32
		add_action( 'publicize_save_meta', array( $this, 'save_publicized_twitter_account' ), 10, 4 );
33
		add_action( 'publicize_save_meta', array( $this, 'save_publicized_facebook_account' ), 10, 4 );
34
35
		add_action( 'connection_disconnected', array( $this, 'add_disconnect_notice' ) );
36
37
		add_filter( 'jetpack_sharing_twitter_via', array( $this, 'get_publicized_twitter_account' ), 10, 2 );
38
39
		include_once( JETPACK__PLUGIN_DIR . 'modules/publicize/enhanced-open-graph.php' );
40
41
		jetpack_require_lib( 'class.jetpack-keyring-service-helper' );
42
	}
43
44
	function add_disconnect_notice() {
45
		add_action( 'admin_notices', array( $this, 'display_disconnected' ) );
46
	}
47
48
	function force_user_connection() {
49
		global $current_user;
50
		$user_token        = Jetpack_Data::get_access_token( $current_user->ID );
51
		$is_user_connected = $user_token && ! is_wp_error( $user_token );
52
53
		// If the user is already connected via Jetpack, then we're good
54
		if ( $is_user_connected ) {
55
			return;
56
		}
57
58
		// If they're not connected, then remove the Publicize UI and tell them they need to connect first
59
		global $publicize_ui;
60
		remove_action( 'pre_admin_screen_sharing', array( $publicize_ui, 'admin_page' ) );
61
62
		// Do we really need `admin_styles`? With the new admin UI, it's breaking some bits.
63
		// Jetpack::init()->admin_styles();
64
		add_action( 'pre_admin_screen_sharing', array( $this, 'admin_page_warning' ), 1 );
65
	}
66
67
	function admin_page_warning() {
68
		$jetpack   = Jetpack::init();
69
		$blog_name = get_bloginfo( 'blogname' );
70
		if ( empty( $blog_name ) ) {
71
			$blog_name = home_url( '/' );
72
		}
73
74
		?>
75
		<div id="message" class="updated jetpack-message jp-connect">
76
			<div class="jetpack-wrap-container">
77
				<div class="jetpack-text-container">
78
					<p><?php printf(
79
							/* translators: %s is the name of the blog */
80
							esc_html( wptexturize( __( "To use Publicize, you'll need to link your %s account to your WordPress.com account using the link below.", 'jetpack' ) ) ),
81
							'<strong>' . esc_html( $blog_name ) . '</strong>'
82
						); ?></p>
83
					<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>
84
				</div>
85
				<div class="jetpack-install-container">
86
					<p class="submit"><a
87
							href="<?php echo $jetpack->build_connect_url( false, menu_page_url( 'sharing', false ) ); ?>"
88
							class="button-connector"
89
							id="wpcom-connect"><?php esc_html_e( 'Link account with WordPress.com', 'jetpack' ); ?></a>
90
					</p>
91
					<p class="jetpack-install-blurb">
92
						<?php jetpack_render_tos_blurb(); ?>
93
					</p>
94
				</div>
95
			</div>
96
		</div>
97
		<?php
98
	}
99
100
	/**
101
	 * Remove a Publicize connection
102
	 */
103
	function disconnect( $service_name, $connection_id, $_blog_id = false, $_user_id = false, $force_delete = false ) {
104
		return Jetpack_Keyring_Service_Helper::disconnect( $service_name, $connection_id, $_blog_id, $_user_id, $force_delete );
105
	}
106
107
	function receive_updated_publicize_connections( $publicize_connections ) {
108
		Jetpack_Options::update_option( 'publicize_connections', $publicize_connections );
109
110
		return true;
111
	}
112
113
	function register_update_publicize_connections_xmlrpc_method( $methods ) {
114
		return array_merge( $methods, array(
115
			'jetpack.updatePublicizeConnections' => array( $this, 'receive_updated_publicize_connections' ),
116
		) );
117
	}
118
119
	function get_all_connections() {
120
		return Jetpack_Options::get_option( 'publicize_connections' );
121
	}
122
123
	function get_connections( $service_name, $_blog_id = false, $_user_id = false ) {
124
		if ( false === $_user_id ) {
125
			$_user_id = $this->user_id();
126
		}
127
128
		$connections           = $this->get_all_connections();
129
		$connections_to_return = array();
130
131
		if ( ! empty( $connections ) && is_array( $connections ) ) {
132
			if ( ! empty( $connections[ $service_name ] ) ) {
133
				foreach ( $connections[ $service_name ] as $id => $connection ) {
134
					if ( 0 == $connection['connection_data']['user_id'] || $_user_id == $connection['connection_data']['user_id'] ) {
135
						$connections_to_return[ $id ] = $connection;
136
					}
137
				}
138
			}
139
140
			return $connections_to_return;
141
		}
142
143
		return false;
144
	}
145
146
	function get_all_connections_for_user() {
147
		$connections = $this->get_all_connections();
148
149
		$connections_to_return = array();
150
		if ( ! empty( $connections ) ) {
151
			foreach ( (array) $connections as $service_name => $connections_for_service ) {
152
				foreach ( $connections_for_service as $id => $connection ) {
153
					$user_id = intval( $connection['connection_data']['user_id'] );
154
					// phpcs:ignore WordPress.PHP.YodaConditions.NotYoda
155
					if ( $user_id === 0 || $this->user_id() === $user_id ) {
156
						$connections_to_return[ $service_name ][ $id ] = $connection;
157
					}
158
				}
159
			}
160
161
			return $connections_to_return;
162
		}
163
164
		return false;
165
	}
166
167
	function get_connection_id( $connection ) {
168
		return $connection['connection_data']['id'];
169
	}
170
171
	function get_connection_unique_id( $connection ) {
172
		return $connection['connection_data']['token_id'];
173
	}
174
175
	function get_connection_meta( $connection ) {
176
		$connection['user_id'] = $connection['connection_data']['user_id']; // Allows for shared connections
177
		return $connection;
178
	}
179
180
	function admin_page_load() {
181
		if ( isset( $_GET['action'] ) && 'error' === $_GET['action'] ) {
182
					add_action( 'pre_admin_screen_sharing', array( $this, 'display_connection_error' ), 9 );
183
		}
184
	}
185
186
	function display_connection_error() {
187
		$code = false;
188
		if ( isset( $_GET['service'] ) ) {
189
			$service_name = $_GET['service'];
190
			$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 ) );
191
		} else {
192
			if ( isset( $_GET['publicize_error'] ) ) {
193
				$code = strtolower( $_GET['publicize_error'] );
194
				switch ( $code ) {
195
					case '400':
196
						$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' );
197
						break;
198
					case 'secret_mismatch':
199
						$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' );
200
						break;
201
					case 'empty_blog_id':
202
						$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' );
203
						break;
204
					case 'empty_state':
205
						$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() );
206
						break;
207
					default:
208
						$error = __( 'Something which should never happen, happened. Sorry about that. If you try again, maybe it will work.', 'jetpack' );
209
						break;
210
				}
211
			} else {
212
				$error = __( 'There was a problem connecting with Publicize. Please try again in a moment.', 'jetpack' );
213
			}
214
		}
215
		// Using the same formatting/style as Jetpack::admin_notices() error
216
		?>
217
		<div id="message" class="jetpack-message jetpack-err">
218
			<div class="squeezer">
219
				<h2><?php echo wp_kses( $error, array( 'a'      => array( 'href' => true ),
220
				                                       'code'   => true,
221
				                                       'strong' => true,
222
				                                       'br'     => true,
223
				                                       'b'      => true
224
					) ); ?></h2>
225
				<?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...
226
					<p><?php printf( __( 'Error code: %s', 'jetpack' ), esc_html( stripslashes( $code ) ) ); ?></p>
227
				<?php endif; ?>
228
			</div>
229
		</div>
230
		<?php
231
	}
232
233
	function display_disconnected() {
234
		echo "<div class='updated'>\n";
235
		echo '<p>' . esc_html( __( 'That connection has been removed.', 'jetpack' ) ) . "</p>\n";
236
		echo "</div>\n\n";
237
	}
238
239
	function globalization() {
240
		if ( 'on' == $_REQUEST['global'] ) {
241
			$globalize_connection = $_REQUEST['connection'];
0 ignored issues
show
Unused Code introduced by
$globalize_connection 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...
242
243
			if ( ! current_user_can( $this->GLOBAL_CAP ) ) {
244
				return;
245
			}
246
247
			$this->globalize_connection( $connection_id );
0 ignored issues
show
Bug introduced by
The variable $connection_id does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
248
		}
249
	}
250
251 View Code Duplication
	function globalize_connection( $connection_id ) {
252
		Jetpack::load_xml_rpc_client();
253
		$xml = new Jetpack_IXR_Client();
254
		$xml->query( 'jetpack.globalizePublicizeConnection', $connection_id, 'globalize' );
255
256
		if ( ! $xml->isError() ) {
257
			$response = $xml->getResponse();
258
			$this->receive_updated_publicize_connections( $response );
259
		}
260
	}
261
262 View Code Duplication
	function unglobalize_connection( $connection_id ) {
263
		Jetpack::load_xml_rpc_client();
264
		$xml = new Jetpack_IXR_Client();
265
		$xml->query( 'jetpack.globalizePublicizeConnection', $connection_id, 'unglobalize' );
266
267
		if ( ! $xml->isError() ) {
268
			$response = $xml->getResponse();
269
			$this->receive_updated_publicize_connections( $response );
270
		}
271
	}
272
273
	function connect_url( $service_name, $for = 'publicize' ) {
274
		return Jetpack_Keyring_Service_Helper::connect_url( $service_name, $for );
275
	}
276
277
	function refresh_url( $service_name, $for = 'publicize' ) {
278
		return Jetpack_Keyring_Service_Helper::refresh_url( $service_name, $for );
279
	}
280
281
	function disconnect_url( $service_name, $id ) {
282
		return Jetpack_Keyring_Service_Helper::disconnect_url( $service_name, $id );
283
	}
284
285
	/**
286
	 * Get social networks, either all available or only those that the site is connected to.
287
	 *
288
	 * @since 2.0.0
289
	 * @since 6.6.0 Removed Path. Service closed October 2018.
290
	 *
291
	 * @param string $filter Select the list of services that will be returned. Defaults to 'all', accepts 'connected'.
292
	 *
293
	 * @return array List of social networks.
294
	 */
295
	function get_services( $filter = 'all', $_blog_id = false, $_user_id = false ) {
296
		$services = array(
297
			'facebook'    => array(),
298
			'twitter'     => array(),
299
			'linkedin'    => array(),
300
			'tumblr'      => array(),
301
			'google_plus' => array(),
302
		);
303
304 View Code Duplication
		if ( 'all' == $filter ) {
305
			return $services;
306
		} else {
307
			$connected_services = array();
308
			foreach ( $services as $service => $empty ) {
309
				$connections = $this->get_connections( $service, $_blog_id, $_user_id );
310
				if ( $connections ) {
311
					$connected_services[ $service ] = $connections;
312
				}
313
			}
314
			return $connected_services;
315
		}
316
	}
317
318
	function get_connection( $service, $id, $_blog_id = false, $_user_id = false ) {
319
		// Stub
320
	}
321
322
	function flag_post_for_publicize( $new_status, $old_status, $post ) {
323
		if ( ! $this->post_type_is_publicizeable( $post->post_type ) ) {
324
			return;
325
		}
326
327
		if ( 'publish' == $new_status && 'publish' != $old_status ) {
328
			/**
329
			 * Determines whether a post being published gets publicized.
330
			 *
331
			 * Side-note: Possibly our most alliterative filter name.
332
			 *
333
			 * @module publicize
334
			 *
335
			 * @since 4.1.0
336
			 *
337
			 * @param bool $should_publicize Should the post be publicized? Default to true.
338
			 * @param WP_POST $post Current Post object.
339
			 */
340
			$should_publicize = apply_filters( 'publicize_should_publicize_published_post', true, $post );
341
342
			if ( $should_publicize ) {
343
				update_post_meta( $post->ID, $this->PENDING, true );
344
			}
345
		}
346
	}
347
348
	function test_connection( $service_name, $connection ) {
349
350
		$id = $this->get_connection_id( $connection );
351
352
		Jetpack::load_xml_rpc_client();
353
		$xml = new Jetpack_IXR_Client();
354
		$xml->query( 'jetpack.testPublicizeConnection', $id );
355
356
		// Bail if all is well
357
		if ( ! $xml->isError() ) {
358
			return true;
359
		}
360
361
		$xml_response            = $xml->getResponse();
362
		$connection_test_message = $xml_response['faultString'];
363
364
		// Set up refresh if the user can
365
		$user_can_refresh = current_user_can( $this->GLOBAL_CAP );
366
		if ( $user_can_refresh ) {
367
			$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...
368
			$refresh_text = sprintf( _x( 'Refresh connection with %s', 'Refresh connection with {social media service}', 'jetpack' ), $this->get_service_label( $service_name ) );
369
			$refresh_url  = $this->refresh_url( $service_name );
370
		}
371
372
		$error_data = array(
373
			'user_can_refresh' => $user_can_refresh,
374
			'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...
375
			'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...
376
		);
377
378
		return new WP_Error( 'pub_conn_test_failed', $connection_test_message, $error_data );
379
	}
380
381
	/**
382
	 * Save a flag locally to indicate that this post has already been Publicized via the selected
383
	 * connections.
384
	 */
385
	function save_publicized( $post_ID, $post = null, $update = null ) {
386
		if ( is_null( $post ) ) {
387
			return;
388
		}
389
		// Only do this when a post transitions to being published
390
		if ( get_post_meta( $post->ID, $this->PENDING ) && $this->post_type_is_publicizeable( $post->post_type ) ) {
391
			$connected_services = $this->get_all_connections();
392
			if ( ! empty( $connected_services ) ) {
393
				/**
394
				 * Fires when a post is saved that has is marked as pending publicizing
395
				 *
396
				 * @since 4.1.0
397
				 *
398
				 * @param int The post ID
399
				 */
400
				do_action_deprecated( 'jetpack_publicize_post', $post->ID, '4.8.0', 'jetpack_published_post_flags' );
401
			}
402
			delete_post_meta( $post->ID, $this->PENDING );
403
			update_post_meta( $post->ID, $this->POST_DONE . 'all', true );
404
		}
405
	}
406
407
	function set_post_flags( $flags, $post ) {
408
		$flags['publicize_post'] = false;
409
		if ( ! $this->post_type_is_publicizeable( $post->post_type ) ) {
410
			return $flags;
411
		}
412
		/** This filter is already documented in modules/publicize/publicize-jetpack.php */
413
		if ( ! apply_filters( 'publicize_should_publicize_published_post', true, $post ) ) {
414
			return $flags;
415
		}
416
417
		$connected_services = $this->get_all_connections();
418
419
		if ( empty( $connected_services ) ) {
420
			return $flags;
421
		}
422
423
		$flags['publicize_post'] = true;
424
425
		return $flags;
426
	}
427
428
	/**
429
	 * Options Code
430
	 */
431
432
	function options_page_facebook() {
433
		$connected_services = $this->get_all_connections();
434
		$connection         = $connected_services['facebook'][ $_REQUEST['connection'] ];
435
		$options_to_show    = ( ! empty( $connection['connection_data']['meta']['options_responses'] ) ? $connection['connection_data']['meta']['options_responses'] : false );
436
437
		// Nonce check
438
		check_admin_referer( 'options_page_facebook_' . $_REQUEST['connection'] );
439
440
		$pages = ( ! empty( $options_to_show[1]['data'] ) ? $options_to_show[1]['data'] : false );
441
442
		$page_selected   = false;
443
		if ( ! empty( $connection['connection_data']['meta']['facebook_page'] ) ) {
444
			$found = false;
445
			if ( $pages && isset( $pages->data ) && is_array( $pages->data )  ) {
446
				foreach ( $pages->data as $page ) {
447
					if ( $page->id == $connection['connection_data']['meta']['facebook_page'] ) {
448
						$found = true;
449
						break;
450
					}
451
				}
452
			}
453
454
			if ( $found ) {
455
				$page_selected   = $connection['connection_data']['meta']['facebook_page'];
456
			}
457
		}
458
459
		?>
460
461
		<div id="thickbox-content">
462
463
			<?php
464
			ob_start();
465
			Publicize_UI::connected_notice( 'Facebook' );
466
			$update_notice = ob_get_clean();
467
468
			if ( ! empty( $update_notice ) ) {
469
				echo $update_notice;
470
			}
471
			$page_info_message = sprintf(
472
				__( 'Facebook supports Publicize connections to Facebook Pages, but not to Facebook Profiles. <a href="%s">Learn More about Publicize for Facebook</a>', 'jetpack' ),
473
				'https://jetpack.com/support/publicize/facebook'
474
			);
475
476
			if ( $pages ) : ?>
477
				<p><?php _e( 'Publicize to my <strong>Facebook Page</strong>:', 'jetpack' ); ?></p>
478
				<table id="option-fb-fanpage">
479
					<tbody>
480
481
					<?php foreach ( $pages as $i => $page ) : ?>
482
						<?php if ( ! ( $i % 2 ) ) : ?>
483
							<tr>
484
						<?php endif; ?>
485
						<td class="radio"><input type="radio" name="option" data-type="page"
486
						                         id="<?php echo esc_attr( $page['id'] ) ?>"
487
						                         value="<?php echo esc_attr( $page['id'] ) ?>" <?php checked( $page_selected && $page_selected == $page['id'], true ); ?> />
488
						</td>
489
						<td class="thumbnail"><label for="<?php echo esc_attr( $page['id'] ) ?>"><img
490
									src="<?php echo esc_url( str_replace( '_s', '_q', $page['picture']['data']['url'] ) ) ?>"
491
									width="50" height="50"/></label></td>
492
						<td class="details">
493
							<label for="<?php echo esc_attr( $page['id'] ) ?>">
494
								<span class="name"><?php echo esc_html( $page['name'] ) ?></span><br/>
495
								<span class="category"><?php echo esc_html( $page['category'] ) ?></span>
496
							</label>
497
						</td>
498
						<?php if ( ( $i % 2 ) || ( $i == count( $pages ) - 1 ) ): ?>
499
							</tr>
500
						<?php endif; ?>
501
					<?php endforeach; ?>
502
503
					</tbody>
504
				</table>
505
506
				<?php Publicize_UI::global_checkbox( 'facebook', $_REQUEST['connection'] ); ?>
507
				<p style="text-align: center;">
508
					<input type="submit" value="<?php esc_attr_e( 'OK', 'jetpack' ) ?>"
509
					       class="button fb-options save-options" name="save"
510
					       data-connection="<?php echo esc_attr( $_REQUEST['connection'] ); ?>"
511
					       rel="<?php echo wp_create_nonce( 'save_fb_token_' . $_REQUEST['connection'] ) ?>"/>
512
				</p><br/>
513
				<p><?php echo $page_info_message; ?></p>
514
			<?php else: ?>
515
				<div>
516
					<p><?php echo $page_info_message; ?></p>
517
					<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>
518
				</div>
519
			<?php endif; ?>
520
		</div>
521
		<?php
522
	}
523
524
	function options_save_facebook() {
525
		// Nonce check
526
		check_admin_referer( 'save_fb_token_' . $_REQUEST['connection'] );
527
528
		// Check for a numeric page ID
529
		$page_id = $_POST['selected_id'];
530
		if ( ! ctype_digit( $page_id ) ) {
531
			die( 'Security check' );
532
		}
533
534
		if ( 'page' != $_POST['type'] || ! isset( $_POST['selected_id'] ) ) {
535
			return;
536
		}
537
538
		// Publish to Page
539
		$options = array(
540
			'facebook_page'    => $page_id,
541
			'facebook_profile' => null
542
		);
543
544
		$this->set_remote_publicize_options( $_POST['connection'], $options );
545
	}
546
547
	function options_page_tumblr() {
548
		// Nonce check
549
		check_admin_referer( 'options_page_tumblr_' . $_REQUEST['connection'] );
550
551
		$connected_services = $this->get_all_connections();
552
		$connection         = $connected_services['tumblr'][ $_POST['connection'] ];
553
		$options_to_show    = $connection['connection_data']['meta']['options_responses'];
554
		$request            = $options_to_show[0];
555
556
		$blogs = $request['response']['user']['blogs'];
557
558
		$blog_selected = false;
559
560
		if ( ! empty( $connection['connection_data']['meta']['tumblr_base_hostname'] ) ) {
561
			foreach ( $blogs as $blog ) {
562
				if ( $connection['connection_data']['meta']['tumblr_base_hostname'] == $this->get_basehostname( $blog['url'] ) ) {
563
					$blog_selected = $connection['connection_data']['meta']['tumblr_base_hostname'];
564
					break;
565
				}
566
			}
567
568
		}
569
570
		// Use their Primary blog if they haven't selected one yet
571
		if ( ! $blog_selected ) {
572
			foreach ( $blogs as $blog ) {
573
				if ( $blog['primary'] ) {
574
					$blog_selected = $this->get_basehostname( $blog['url'] );
575
				}
576
			}
577
		} ?>
578
579
		<div id="thickbox-content">
580
581
			<?php
582
			ob_start();
583
			Publicize_UI::connected_notice( 'Tumblr' );
584
			$update_notice = ob_get_clean();
585
586
			if ( ! empty( $update_notice ) ) {
587
				echo $update_notice;
588
			}
589
			?>
590
591
			<p><?php _e( 'Publicize to my <strong>Tumblr blog</strong>:', 'jetpack' ); ?></p>
592
593
			<ul id="option-tumblr-blog">
594
595
				<?php
596
				foreach ( $blogs as $blog ) {
597
					$url = $this->get_basehostname( $blog['url'] ); ?>
598
					<li>
599
						<input type="radio" name="option" data-type="blog" id="<?php echo esc_attr( $url ) ?>"
600
						       value="<?php echo esc_attr( $url ) ?>" <?php checked( $blog_selected == $url, true ); ?> />
601
						<label for="<?php echo esc_attr( $url ) ?>"><span
602
								class="name"><?php echo esc_html( $blog['title'] ) ?></span></label>
603
					</li>
604
				<?php } ?>
605
606
			</ul>
607
608
			<?php Publicize_UI::global_checkbox( 'tumblr', $_REQUEST['connection'] ); ?>
609
610
			<p style="text-align: center;">
611
				<input type="submit" value="<?php esc_attr_e( 'OK', 'jetpack' ) ?>"
612
				       class="button tumblr-options save-options" name="save"
613
				       data-connection="<?php echo esc_attr( $_REQUEST['connection'] ); ?>"
614
				       rel="<?php echo wp_create_nonce( 'save_tumblr_blog_' . $_REQUEST['connection'] ) ?>"/>
615
			</p> <br/>
616
		</div>
617
618
		<?php
619
	}
620
621
	function get_basehostname( $url ) {
622
		return parse_url( $url, PHP_URL_HOST );
623
	}
624
625
	function options_save_tumblr() {
626
		// Nonce check
627
		check_admin_referer( 'save_tumblr_blog_' . $_REQUEST['connection'] );
628
		$options = array( 'tumblr_base_hostname' => $_POST['selected_id'] );
629
630
		$this->set_remote_publicize_options( $_POST['connection'], $options );
631
632
	}
633
634 View Code Duplication
	function set_remote_publicize_options( $id, $options ) {
635
		Jetpack::load_xml_rpc_client();
636
		$xml = new Jetpack_IXR_Client();
637
		$xml->query( 'jetpack.setPublicizeOptions', $id, $options );
638
639
		if ( ! $xml->isError() ) {
640
			$response = $xml->getResponse();
641
			Jetpack_Options::update_option( 'publicize_connections', $response );
642
			$this->globalization();
643
		}
644
	}
645
646
	function options_page_twitter() {
647
		Publicize_UI::options_page_other( 'twitter' );
648
	}
649
650
	function options_page_linkedin() {
651
		Publicize_UI::options_page_other( 'linkedin' );
652
	}
653
654
	function options_page_google_plus() {
655
		Publicize_UI::options_page_other( 'google_plus' );
656
	}
657
658
	function options_save_twitter() {
659
		$this->options_save_other( 'twitter' );
660
	}
661
662
	function options_save_linkedin() {
663
		$this->options_save_other( 'linkedin' );
664
	}
665
666
	function options_save_google_plus() {
667
		$this->options_save_other( 'google_plus' );
668
	}
669
670
	function options_save_other( $service_name ) {
671
		// Nonce check
672
		check_admin_referer( 'save_' . $service_name . '_token_' . $_REQUEST['connection'] );
673
		$this->globalization();
674
	}
675
676
	/**
677
	 * If there's only one shared connection to Twitter set it as twitter:site tag.
678
	 */
679
	function enhaced_twitter_cards_site_tag( $tag ) {
680
		$custom_site_tag = get_option( 'jetpack-twitter-cards-site-tag' );
681
		if ( ! empty( $custom_site_tag ) ) {
682
			return $tag;
683
		}
684
		if ( ! $this->is_enabled( 'twitter' ) ) {
685
			return $tag;
686
		}
687
		$connections = $this->get_connections( 'twitter' );
688
		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...
689
			$connection_meta = $this->get_connection_meta( $connection );
690
			if ( 0 == $connection_meta['connection_data']['user_id'] ) {
691
				// If the connection is shared
692
				return $this->get_display_name( 'twitter', $connection );
693
			}
694
		}
695
696
		return $tag;
697
	}
698
699
	function save_publicized_twitter_account( $submit_post, $post_id, $service_name, $connection ) {
700
		if ( 'twitter' == $service_name && $submit_post ) {
701
			$connection_meta        = $this->get_connection_meta( $connection );
702
			$publicize_twitter_user = get_post_meta( $post_id, '_publicize_twitter_user' );
703
			if ( empty( $publicize_twitter_user ) || 0 != $connection_meta['connection_data']['user_id'] ) {
704
				update_post_meta( $post_id, '_publicize_twitter_user', $this->get_display_name( 'twitter', $connection ) );
705
			}
706
		}
707
	}
708
709
	function get_publicized_twitter_account( $account, $post_id ) {
710
		if ( ! empty( $account ) ) {
711
			return $account;
712
		}
713
		$account = get_post_meta( $post_id, '_publicize_twitter_user', true );
714
		if ( ! empty( $account ) ) {
715
			return $account;
716
		}
717
718
		return '';
719
	}
720
721
	/**
722
	 * Save the Publicized Facebook account when publishing a post
723
	 * Use only Personal accounts, not Facebook Pages
724
	 */
725
	function save_publicized_facebook_account( $submit_post, $post_id, $service_name, $connection ) {
726
		$connection_meta = $this->get_connection_meta( $connection );
727
		if ( 'facebook' == $service_name && isset( $connection_meta['connection_data']['meta']['facebook_profile'] ) && $submit_post ) {
728
			$publicize_facebook_user = get_post_meta( $post_id, '_publicize_facebook_user' );
729
			if ( empty( $publicize_facebook_user ) || 0 != $connection_meta['connection_data']['user_id'] ) {
730
				$profile_link = $this->get_profile_link( 'facebook', $connection );
731
732
				if ( false !== $profile_link ) {
733
					update_post_meta( $post_id, '_publicize_facebook_user', $profile_link );
734
				}
735
			}
736
		}
737
	}
738
}
739