Completed
Push — fix/server-data-array ( c7296b )
by
unknown
09:28
created

subscriptions.php ➔ jetpack_subscriptions_cherry_pick_server_data()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 19
Code Lines 12

Duplication

Lines 3
Ratio 15.79 %

Importance

Changes 0
Metric Value
cc 6
eloc 12
nc 6
nop 0
dl 3
loc 19
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * Module Name: Subscriptions
4
 * Module Description: Allow users to subscribe to your posts and comments and receive notifications via email
5
 * Jumpstart Description: Give visitors two easy subscription options — while commenting, or via a separate email subscription widget you can display.
6
 * Sort Order: 9
7
 * Recommendation Order: 8
8
 * First Introduced: 1.2
9
 * Requires Connection: Yes
10
 * Auto Activate: Yes
11
 * Module Tags: Social
12
 * Feature: Engagement, Jumpstart
13
 * Additional Search Queries: subscriptions, subscription, email, follow, followers, subscribers, signup
14
 */
15
16
add_action( 'jetpack_modules_loaded', 'jetpack_subscriptions_load' );
17
18
function jetpack_subscriptions_load() {
19
	Jetpack::enable_module_configurable( __FILE__ );
20
	Jetpack::module_configuration_load( __FILE__, 'jetpack_subscriptions_configuration_load' );
21
}
22
23
function jetpack_subscriptions_configuration_load() {
24
	wp_safe_redirect( admin_url( 'options-discussion.php#jetpack-subscriptions-settings' ) );
25
	exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function jetpack_subscriptions_configuration_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...
26
}
27
28
/**
29
 * Cherry picks keys from `$_SERVER` array.
30
 *
31
 * @since 6.0.0
32
 *
33
 * @return array An array of server data.
34
 */
35
function jetpack_subscriptions_cherry_pick_server_data() {
36
	$data = array();
37
38
	foreach ( $_SERVER as $key => $value ) {
39
		if ( ! is_string( $value ) ) {
40
			continue;
41
		} elseif ( 0 === strpos( $key, 'HTTP_COOKIE' ) ) {
42
			continue;
43
		}
44
45
		if ( 0 === strpos( $key, 'HTTP_' ) ) {
46
			$data[ $key ] = $value; // HTTP headers.
47 View Code Duplication
		} elseif ( in_array( $key, array( 'REMOTE_ADDR', 'REQUEST_URI', 'DOCUMENT_URI' ), true ) ) {
48
			$data[ $key ] = $value;
49
		}
50
	}
51
52
	return $data;
53
}
54
55
class Jetpack_Subscriptions {
56
	public $jetpack = false;
57
58
	public static $hash;
59
60
	/**
61
	 * Singleton
62
	 * @static
63
	 */
64
	static function init() {
65
		static $instance = false;
66
67
		if ( !$instance ) {
68
			$instance = new Jetpack_Subscriptions;
69
		}
70
71
		return $instance;
72
	}
73
74
	function __construct() {
75
		$this->jetpack = Jetpack::init();
0 ignored issues
show
Documentation Bug introduced by
It seems like \Jetpack::init() of type object<Jetpack> is incompatible with the declared type boolean of property $jetpack.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
76
77
		// Don't use COOKIEHASH as it could be shared across installs && is non-unique in multisite.
78
		// @see: https://twitter.com/nacin/status/378246957451333632
79
		self::$hash = md5( get_option( 'siteurl' ) );
80
81
		add_filter( 'jetpack_xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
82
83
		// @todo remove sync from subscriptions and move elsewhere...
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
84
85
		// Add Configuration Page
86
		add_action( 'admin_init', array( $this, 'configure' ) );
87
88
		// Set up the subscription widget.
89
		add_action( 'widgets_init', array( $this, 'widget_init' ) );
90
91
		// Catch subscription widget submits
92
		if ( isset( $_REQUEST['jetpack_subscriptions_widget'] ) )
93
			add_action( 'template_redirect', array( $this, 'widget_submit' ) );
94
95
		// Set up the comment subscription checkboxes
96
		add_action( 'comment_form', array( $this, 'comment_subscribe_init' ) );
97
98
		// Catch comment posts and check for subscriptions.
99
		add_action( 'comment_post', array( $this, 'comment_subscribe_submit' ), 50, 2 );
100
101
		// Adds post meta checkbox in the post submit metabox
102
		add_action( 'post_submitbox_misc_actions', array( $this, 'subscription_post_page_metabox' ) );
103
104
		add_action( 'transition_post_status', array( $this, 'maybe_send_subscription_email' ), 10, 3 );
105
106
		add_filter( 'jetpack_published_post_flags', array( $this, 'set_post_flags' ), 10, 2 );
107
	}
108
109
	/**
110
	 * Jetpack_Subscriptions::xmlrpc_methods()
111
	 *
112
	 * Register subscriptions methods with the Jetpack XML-RPC server.
113
	 * @param array $methods
114
	 */
115
	function xmlrpc_methods( $methods ) {
116
		return array_merge(
117
			$methods,
118
			array(
119
				'jetpack.subscriptions.subscribe' => array( $this, 'subscribe' ),
120
			)
121
		);
122
	}
123
124
	/*
125
	 * Disable Subscribe on Single Post
126
	 * Register post meta
127
	 */
128
	function subscription_post_page_metabox() {
129
		if (
130
			/**
131
			 * Filter whether or not to show the per-post subscription option.
132
			 *
133
			 * @module subscriptions
134
			 *
135
			 * @since 3.7.0
136
			 *
137
			 * @param bool true = show checkbox option on all new posts | false = hide the option.
138
			 */
139
			 ! apply_filters( 'jetpack_allow_per_post_subscriptions', false ) )
140
		{
141
			return;
142
		}
143
144
		if ( has_filter( 'jetpack_subscriptions_exclude_these_categories' ) || has_filter( 'jetpack_subscriptions_include_only_these_categories' ) ) {
145
			return;
146
		}
147
148
		global $post;
149
		$disable_subscribe_value = get_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', true );
150
		// only show checkbox if post hasn't been published and is a 'post' post type.
151
		if ( get_post_status( $post->ID ) !== 'publish' && get_post_type( $post->ID ) == 'post' ) :
152
			// Nonce it
153
			wp_nonce_field( 'disable_subscribe', 'disable_subscribe_nonce' );
154
			?>
155
			<div class="misc-pub-section">
156
				<label for="_jetpack_dont_email_post_to_subs"><?php _e( 'Jetpack Subscriptions:', 'jetpack' ); ?></label><br>
157
				<input type="checkbox" name="_jetpack_dont_email_post_to_subs" id="jetpack-per-post-subscribe" value="1" <?php checked( $disable_subscribe_value, 1, true ); ?> />
158
				<?php _e( 'Don&#8217;t send this to subscribers', 'jetpack' ); ?>
159
			</div>
160
		<?php endif;
161
	}
162
163
	/**
164
	 * Checks whether or not the post should be emailed to subscribers
165
	 *
166
	 * It checks for the following things in order:
167
	 * - Usage of filter jetpack_subscriptions_exclude_these_categories
168
	 * - Usage of filter jetpack_subscriptions_include_only_these_categories
169
	 * - Existence of the per-post checkbox option
170
	 *
171
	 * Only one of these can be used at any given time.
172
	 *
173
	 * @param $new_status string - the "new" post status of the transition when saved
174
	 * @param $old_status string - the "old" post status of the transition when saved
175
	 * @param $post obj - The post object
176
	 */
177
	function maybe_send_subscription_email( $new_status, $old_status, $post ) {
178
179
		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
180
			return;
181
		}
182
183
		// Make sure that the checkbox is preseved
184
		if ( ! empty( $_POST['disable_subscribe_nonce'] ) && wp_verify_nonce( $_POST['disable_subscribe_nonce'], 'disable_subscribe' ) ) {
185
			$set_checkbox = isset( $_POST['_jetpack_dont_email_post_to_subs'] ) ? 1 : 0;
186
			update_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', $set_checkbox );
187
		}
188
	}
189
190
	public function should_email_post_to_subscribers( $post ) {
191
		$should_email = true;
192
		if ( get_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', true ) ) {
193
			return false;
194
		}
195
196
		// Only posts are currently supported
197
		if ( $post->post_type !== 'post' ) {
198
			return false;
199
		}
200
201
		/**
202
		 * Array of categories that will never trigger subscription emails.
203
		 *
204
		 * Will not send subscription emails from any post from within these categories.
205
		 *
206
		 * @module subscriptions
207
		 *
208
		 * @since 3.7.0
209
		 *
210
		 * @param array $args Array of category slugs or ID's.
211
		 */
212
		$excluded_categories = apply_filters( 'jetpack_subscriptions_exclude_these_categories', array() );
213
214
		// Never email posts from these categories
215
		if ( ! empty( $excluded_categories ) && in_category( $excluded_categories, $post->ID ) ) {
216
			$should_email = false;
217
		}
218
219
		/**
220
		 * ONLY send subscription emails for these categories
221
		 *
222
		 * Will ONLY send subscription emails to these categories.
223
		 *
224
		 * @module subscriptions
225
		 *
226
		 * @since 3.7.0
227
		 *
228
		 * @param array $args Array of category slugs or ID's.
229
		 */
230
		$only_these_categories = apply_filters( 'jetpack_subscriptions_exclude_all_categories_except', array() );
231
232
		// Only emails posts from these categories
233
		if ( ! empty( $only_these_categories ) && ! in_category( $only_these_categories, $post->ID ) ) {
234
			$should_email = false;
235
		}
236
237
		return $should_email;
238
	}
239
240
	function set_post_flags( $flags, $post ) {
241
		$flags['send_subscription'] = $this->should_email_post_to_subscribers( $post );
242
		return $flags;
243
	}
244
245
	/**
246
	 * Jetpack_Subscriptions::configure()
247
	 *
248
	 * Jetpack Subscriptions configuration screen.
249
	 */
250
	function configure() {
251
		// Create the section
252
		add_settings_section(
253
			'jetpack_subscriptions',
254
			__( 'Jetpack Subscriptions Settings', 'jetpack' ),
255
			array( $this, 'subscriptions_settings_section' ),
256
			'discussion'
257
		);
258
259
		/** Subscribe to Posts ***************************************************/
260
261
		add_settings_field(
262
			'jetpack_subscriptions_post_subscribe',
263
			__( 'Follow Blog', 'jetpack' ),
264
			array( $this, 'subscription_post_subscribe_setting' ),
265
			'discussion',
266
			'jetpack_subscriptions'
267
		);
268
269
		register_setting(
270
			'discussion',
271
			'stb_enabled'
272
		);
273
274
		/** Subscribe to Comments ******************************************************/
275
276
		add_settings_field(
277
			'jetpack_subscriptions_comment_subscribe',
278
			__( 'Follow Comments', 'jetpack' ),
279
			array( $this, 'subscription_comment_subscribe_setting' ),
280
			'discussion',
281
			'jetpack_subscriptions'
282
		);
283
284
		register_setting(
285
			'discussion',
286
			'stc_enabled'
287
		);
288
289
		/** Subscription Messaging Options ******************************************************/
290
291
		register_setting(
292
			'reading',
293
			'subscription_options',
294
			array( $this, 'validate_settings' )
295
		);
296
297
		add_settings_section(
298
			'email_settings',
299
			__( 'Follower Settings', 'jetpack' ),
300
			array( $this, 'reading_section' ),
301
			'reading'
302
		);
303
304
		add_settings_field(
305
			'invitation',
306
			__( 'Blog follow email text', 'jetpack' ),
307
			array( $this, 'setting_invitation' ),
308
			'reading',
309
			'email_settings'
310
		);
311
312
		add_settings_field(
313
			'comment-follow',
314
			__( 'Comment follow email text', 'jetpack' ),
315
			array( $this, 'setting_comment_follow' ),
316
			'reading',
317
			'email_settings'
318
		);
319
	}
320
321
	/**
322
	 * Discussions setting section blurb
323
	 *
324
	 */
325
	function subscriptions_settings_section() {
326
	?>
327
328
		<p id="jetpack-subscriptions-settings"><?php _e( 'Change whether your visitors can subscribe to your posts or comments or both.', 'jetpack' ); ?></p>
329
330
	<?php
331
	}
332
333
	/**
334
	 * Post Subscriptions Toggle
335
	 *
336
	 */
337 View Code Duplication
	function subscription_post_subscribe_setting() {
338
339
		$stb_enabled = get_option( 'stb_enabled', 1 ); ?>
340
341
		<p class="description">
342
			<input type="checkbox" name="stb_enabled" id="jetpack-post-subscribe" value="1" <?php checked( $stb_enabled, 1 ); ?> />
343
			<?php _e( "Show a <em>'follow blog'</em> option in the comment form", 'jetpack' ); ?>
344
		</p>
345
	<?php
346
	}
347
348
	/**
349
	 * Comments Subscriptions Toggle
350
	 *
351
	 */
352 View Code Duplication
	function subscription_comment_subscribe_setting() {
353
354
		$stc_enabled = get_option( 'stc_enabled', 1 ); ?>
355
356
		<p class="description">
357
			<input type="checkbox" name="stc_enabled" id="jetpack-comment-subscribe" value="1" <?php checked( $stc_enabled, 1 ); ?> />
358
			<?php _e( "Show a <em>'follow comments'</em> option in the comment form", 'jetpack' ); ?>
359
		</p>
360
361
	<?php
362
	}
363
364
	function validate_settings( $settings ) {
365
		global $allowedposttags;
366
367
		$default = $this->get_default_settings();
368
369
		// Blog Follow
370
		$settings['invitation'] = trim( wp_kses( $settings['invitation'], $allowedposttags ) );
371
		if ( empty( $settings['invitation'] ) )
372
			$settings['invitation'] = $default['invitation'];
373
374
		// Comments Follow (single post)
375
		$settings['comment_follow'] = trim( wp_kses( $settings['comment_follow'], $allowedposttags ) );
376
		if ( empty( $settings['comment_follow'] ) )
377
			$settings['comment_follow'] = $default['comment_follow'];
378
379
		return $settings;
380
	}
381
382
	public function reading_section() {
383
		echo '<p id="follower-settings">';
384
		_e( 'These settings change emails sent from your blog to followers.', 'jetpack' );
385
		echo '</p>';
386
	}
387
388
	public function setting_invitation() {
389
		$settings = $this->get_settings();
390
		echo '<textarea name="subscription_options[invitation]" class="large-text" cols="50" rows="5">' . esc_textarea( $settings['invitation'] ) . '</textarea>';
391
		echo '<p><span class="description">'.__( 'Introduction text sent when someone follows your blog. (Site and confirmation details will be automatically added for you.)', 'jetpack' ).'</span></p>';
392
	}
393
394
	public function setting_comment_follow() {
395
		$settings = $this->get_settings();
396
		echo '<textarea name="subscription_options[comment_follow]" class="large-text" cols="50" rows="5">' . esc_textarea( $settings['comment_follow'] ) . '</textarea>';
397
		echo '<p><span class="description">'.__( 'Introduction text sent when someone follows a post on your blog. (Site and confirmation details will be automatically added for you.)', 'jetpack' ).'</span></p>';
398
	}
399
400
	function get_default_settings() {
401
		return array(
402
			'invitation'     => __( "Howdy.\n\nYou recently followed this blog's posts. This means you will receive each new post by email.\n\nTo activate, click confirm below. If you believe this is an error, ignore this message and we'll never bother you again.", 'jetpack' ),
403
			'comment_follow' => __( "Howdy.\n\nYou recently followed one of my posts. This means you will receive an email when new comments are posted.\n\nTo activate, click confirm below. If you believe this is an error, ignore this message and we'll never bother you again.", 'jetpack' )
404
		);
405
	}
406
407
	function get_settings() {
408
		return wp_parse_args( (array) get_option( 'subscription_options', array() ), $this->get_default_settings() );
409
	}
410
411
	/**
412
	 * Jetpack_Subscriptions::subscribe()
413
	 *
414
	 * Send a synchronous XML-RPC subscribe to blog posts or subscribe to post comments request.
415
	 *
416
	 * @param string $email
417
	 * @param array  $post_ids (optional) defaults to 0 for blog posts only: array of post IDs to subscribe to blog's posts
0 ignored issues
show
Documentation introduced by
Should the type for parameter $post_ids not be integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
418
	 * @param bool   $async    (optional) Should the subscription be performed asynchronously?  Defaults to true.
419
	 *
420
	 * @return true|Jetpack_Error true on success
421
	 *	invalid_email   : not a valid email address
422
	 *	invalid_post_id : not a valid post ID
423
	 *	unknown_post_id : unknown post
424
	 *	not_subscribed  : strange error.  Jetpack servers at WordPress.com could subscribe the email.
425
	 *	disabled        : Site owner has disabled subscriptions.
426
	 *	active          : Already subscribed.
427
	 *	unknown         : strange error.  Jetpack servers at WordPress.com returned something malformed.
428
	 *	unknown_status  : strange error.  Jetpack servers at WordPress.com returned something I didn't understand.
429
	 */
430
	function subscribe( $email, $post_ids = 0, $async = true, $extra_data = array() ) {
431
		if ( !is_email( $email ) ) {
432
			return new Jetpack_Error( 'invalid_email' );
433
		}
434
435
		if ( !$async ) {
436
			Jetpack::load_xml_rpc_client();
437
			$xml = new Jetpack_IXR_ClientMulticall();
438
		}
439
440
		foreach ( (array) $post_ids as $post_id ) {
441
			$post_id = (int) $post_id;
442
			if ( $post_id < 0 ) {
443
				return new Jetpack_Error( 'invalid_post_id' );
444
			} else if ( $post_id && !$post = get_post( $post_id ) ) {
445
				return new Jetpack_Error( 'unknown_post_id' );
446
			}
447
448
			if ( $async ) {
449
				Jetpack::xmlrpc_async_call( 'jetpack.subscribeToSite', $email, $post_id, serialize( $extra_data ) );
450
			} else {
451
				$xml->addCall( 'jetpack.subscribeToSite', $email, $post_id, serialize( $extra_data ) );
0 ignored issues
show
Bug introduced by
The variable $xml 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...
452
			}
453
		}
454
455
		if ( $async ) {
456
			return;
457
		}
458
459
		// Call
460
		$xml->query();
461
462
		if ( $xml->isError() ) {
463
			return $xml->get_jetpack_error();
464
		}
465
466
		$responses = $xml->getResponse();
467
468
		$r = array();
469
		foreach ( (array) $responses as $response ) {
470
			if ( isset( $response['faultCode'] ) || isset( $response['faultString'] ) ) {
471
				$r[] = $xml->get_jetpack_error( $response['faultCode'], $response['faultString'] );
472
				continue;
473
			}
474
475
			if ( !is_array( $response[0] ) || empty( $response[0]['status'] ) ) {
476
				$r[] = new Jetpack_Error( 'unknown' );
477
				continue;
478
			}
479
480
			switch ( $response[0]['status'] ) {
481
			case 'error' :
482
				$r[] = new Jetpack_Error( 'not_subscribed' );
483
				continue 2;
484
			case 'disabled' :
485
				$r[] = new Jetpack_Error( 'disabled' );
486
				continue 2;
487
			case 'active' :
488
				$r[] = new Jetpack_Error( 'active' );
489
				continue 2;
490
			case 'pending' :
491
				$r[] = true;
492
				continue 2;
493
			default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
494
				$r[] = new Jetpack_Error( 'unknown_status', (string) $response[0]['status'] );
495
				continue 2;
496
			}
497
		}
498
499
		return $r;
500
	}
501
502
	/**
503
	 * Jetpack_Subscriptions::widget_init()
504
	 *
505
	 * Initialize and register the Jetpack Subscriptions widget.
506
	 */
507
	function widget_init() {
508
		register_widget( 'Jetpack_Subscriptions_Widget' );
509
	}
510
511
	/**
512
	 * Jetpack_Subscriptions::widget_submit()
513
	 *
514
	 * When a user submits their email via the blog subscription widget, check the details and call the subsribe() method.
515
	 */
516
	function widget_submit() {
517
		// Check the nonce.
518
		if ( is_user_logged_in() ) {
519
			check_admin_referer( 'blogsub_subscribe_' . get_current_blog_id() );
520
		}
521
522
		if ( empty( $_REQUEST['email'] ) )
523
			return false;
524
525
		$redirect_fragment = false;
526
		if ( isset( $_REQUEST['redirect_fragment'] ) ) {
527
			$redirect_fragment = preg_replace( '/[^a-z0-9_-]/i', '', $_REQUEST['redirect_fragment'] );
528
		}
529
		if ( !$redirect_fragment ) {
530
			$redirect_fragment = 'subscribe-blog';
531
		}
532
533
		$subscribe = Jetpack_Subscriptions::subscribe(
534
												$_REQUEST['email'],
535
												0,
536
												false,
537
												array(
538
													'source'         => 'widget',
539
													'widget-in-use'  => is_active_widget( false, false, 'blog_subscription', true ) ? 'yes' : 'no',
540
													'comment_status' => '',
541
													'server_data'    => jetpack_subscriptions_cherry_pick_server_data(),
542
												)
543
		);
544
545
		if ( is_wp_error( $subscribe ) ) {
546
			$error = $subscribe->get_error_code();
547
		} else {
548
			$error = false;
549
			foreach ( $subscribe as $response ) {
0 ignored issues
show
Bug introduced by
The expression $subscribe of type object<Jetpack_Error>|null|array 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...
550
				if ( is_wp_error( $response ) ) {
551
					$error = $response->get_error_code();
552
					break;
553
				}
554
			}
555
		}
556
557
		switch ( $error ) {
558
			case false:
559
				$result = 'success';
560
				break;
561
			case 'invalid_email':
562
				$result = $error;
563
				break;
564
			case 'blocked_email':
565
				$result = 'opted_out';
566
				break;
567
			case 'active':
568
			case 'pending':
569
				$result = 'already';
570
				break;
571
			default:
572
				$result = 'error';
573
				break;
574
		}
575
576
		$redirect = add_query_arg( 'subscribe', $result );
577
578
		/**
579
		 * Fires on each subscription form submission.
580
		 *
581
		 * @module subscriptions
582
		 *
583
		 * @since 3.7.0
584
		 *
585
		 * @param string $result Result of form submission: success, invalid_email, already, error.
586
		 */
587
		do_action( 'jetpack_subscriptions_form_submission', $result );
588
589
		wp_safe_redirect( "$redirect#$redirect_fragment" );
590
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method widget_submit() 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...
591
	}
592
593
	/**
594
	 * Jetpack_Subscriptions::comment_subscribe_init()
595
	 *
596
	 * Set up and add the comment subscription checkbox to the comment form.
597
	 */
598
	function comment_subscribe_init() {
599
		global $post;
600
601
		$comments_checked = '';
602
		$blog_checked     = '';
603
604
		// Check for a comment / blog submission and set a cookie to retain the setting and check the boxes.
605
		if ( isset( $_COOKIE[ 'jetpack_comments_subscribe_' . self::$hash . '_' . $post->ID ] ) ) {
606
			$comments_checked = ' checked="checked"';
607
		}
608
609
		if ( isset( $_COOKIE[ 'jetpack_blog_subscribe_' . self::$hash ] ) ) {
610
			$blog_checked = ' checked="checked"';
611
		}
612
613
		// Some themes call this function, don't show the checkbox again
614
		remove_action( 'comment_form', 'subscription_comment_form' );
615
616
		// Check if Mark Jaquith's Subscribe to Comments plugin is active - if so, suppress Jetpack checkbox
617
618
		$str = '';
619
620
		if ( FALSE === has_filter( 'comment_form', 'show_subscription_checkbox' ) && 1 == get_option( 'stc_enabled', 1 ) && empty( $post->post_password ) && 'post' == get_post_type() ) {
621
			// Subscribe to comments checkbox
622
			$str .= '<p class="comment-subscription-form"><input type="checkbox" name="subscribe_comments" id="subscribe_comments" value="subscribe" style="width: auto; -moz-appearance: checkbox; -webkit-appearance: checkbox;"' . $comments_checked . ' /> ';
623
			$comment_sub_text = __( 'Notify me of follow-up comments by email.', 'jetpack' );
624
			$str .=	'<label class="subscribe-label" id="subscribe-label" for="subscribe_comments">' . esc_html(
625
				/**
626
				 * Filter the Subscribe to comments text appearing below the comment form.
627
				 *
628
				 * @module subscriptions
629
				 *
630
				 * @since 3.4.0
631
				 *
632
				 * @param string $comment_sub_text Subscribe to comments text.
633
				 */
634
				apply_filters( 'jetpack_subscribe_comment_label', $comment_sub_text )
635
			) . '</label>';
636
			$str .= '</p>';
637
		}
638
639
		if ( 1 == get_option( 'stb_enabled', 1 ) ) {
640
			// Subscribe to blog checkbox
641
			$str .= '<p class="comment-subscription-form"><input type="checkbox" name="subscribe_blog" id="subscribe_blog" value="subscribe" style="width: auto; -moz-appearance: checkbox; -webkit-appearance: checkbox;"' . $blog_checked . ' /> ';
642
			$blog_sub_text = __( 'Notify me of new posts by email.', 'jetpack' );
643
			$str .=	'<label class="subscribe-label" id="subscribe-blog-label" for="subscribe_blog">' . esc_html(
644
				/**
645
				 * Filter the Subscribe to blog text appearing below the comment form.
646
				 *
647
				 * @module subscriptions
648
				 *
649
				 * @since 3.4.0
650
				 *
651
				 * @param string $comment_sub_text Subscribe to blog text.
652
				 */
653
				apply_filters( 'jetpack_subscribe_blog_label', $blog_sub_text )
654
			) . '</label>';
655
			$str .= '</p>';
656
		}
657
658
		/**
659
		 * Filter the output of the subscription options appearing below the comment form.
660
		 *
661
		 * @module subscriptions
662
		 *
663
		 * @since 1.2.0
664
		 *
665
		 * @param string $str Comment Subscription form HTML output.
666
		 */
667
		echo apply_filters( 'jetpack_comment_subscription_form', $str );
668
	}
669
670
	/**
671
	 * Jetpack_Subscriptions::comment_subscribe_init()
672
	 *
673
	 * When a user checks the comment subscribe box and submits a comment, subscribe them to the comment thread.
674
	 */
675
	function comment_subscribe_submit( $comment_id, $approved ) {
676
		if ( 'spam' === $approved ) {
677
			return;
678
		}
679
680
		$comment = get_comment( $comment_id );
681
682
		// Set cookies for this post/comment
683
		$this->set_cookies( isset( $_REQUEST['subscribe_comments'] ), $comment->comment_post_ID, isset( $_REQUEST['subscribe_blog'] ) );
684
685
		if ( !isset( $_REQUEST['subscribe_comments'] ) && !isset( $_REQUEST['subscribe_blog'] ) )
686
			return;
687
688
		$post_ids = array();
689
690
		if ( isset( $_REQUEST['subscribe_comments'] ) )
691
			$post_ids[] = $comment->comment_post_ID;
692
693
		if ( isset( $_REQUEST['subscribe_blog'] ) )
694
			$post_ids[] = 0;
695
696
		$result = Jetpack_Subscriptions::subscribe(
697
									$comment->comment_author_email,
698
									$post_ids,
0 ignored issues
show
Documentation introduced by
$post_ids is of type array, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
699
									true,
700
									array(
701
										'source'         => 'comment-form',
702
										'widget-in-use'  => is_active_widget( false, false, 'blog_subscription', true ) ? 'yes' : 'no',
703
										'comment_status' => $approved,
704
										'server_data'    => jetpack_subscriptions_cherry_pick_server_data(),
705
									)
706
		);
707
708
		/**
709
		 * Fires on each comment subscription form submission.
710
		 *
711
		 * @module subscriptions
712
		 *
713
		 * @since 5.5.0
714
		 *
715
		 * @param NULL|WP_Error $result Result of form submission: NULL on success, WP_Error otherwise.
716
		 * @param Array $post_ids An array of post IDs that the user subscribed to, 0 means blog subscription.
717
		 */
718
		do_action( 'jetpack_subscriptions_comment_form_submission', $result, $post_ids );
719
	}
720
721
	/**
722
	 * Jetpack_Subscriptions::set_cookies()
723
	 *
724
	 * Set a cookie to save state on the comment and post subscription checkboxes.
725
	 *
726
	 * @param bool $subscribe_to_post Whether the user chose to subscribe to subsequent comments on this post.
727
	 * @param int $post_id If $subscribe_to_post is true, the post ID they've subscribed to.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $post_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
728
	 * @param bool $subscribe_to_blog Whether the user chose to subscribe to all new posts on the blog.
729
	 */
730
	function set_cookies( $subscribe_to_post = false, $post_id = null, $subscribe_to_blog = false ) {
731
		$post_id = intval( $post_id );
732
733
		/** This filter is already documented in core/wp-includes/comment-functions.php */
734
		$cookie_lifetime = apply_filters( 'comment_cookie_lifetime',       30000000 );
735
736
		/**
737
		 * Filter the Jetpack Comment cookie path.
738
		 *
739
		 * @module subscriptions
740
		 *
741
		 * @since 2.5.0
742
		 *
743
		 * @param string COOKIEPATH Cookie path.
744
		 */
745
		$cookie_path     = apply_filters( 'jetpack_comment_cookie_path',   COOKIEPATH );
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 5 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...
746
747
		/**
748
		 * Filter the Jetpack Comment cookie domain.
749
		 *
750
		 * @module subscriptions
751
		 *
752
		 * @since 2.5.0
753
		 *
754
		 * @param string COOKIE_DOMAIN Cookie domain.
755
		 */
756
		$cookie_domain   = apply_filters( 'jetpack_comment_cookie_domain', COOKIE_DOMAIN );
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...
757
758
		if ( $subscribe_to_post && $post_id >= 0 ) {
759
			setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain );
760
		} else {
761
			setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, '', time() - 3600, $cookie_path, $cookie_domain );
762
		}
763
764
		if ( $subscribe_to_blog ) {
765
			setcookie( 'jetpack_blog_subscribe_' . self::$hash, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain );
766
		} else {
767
			setcookie( 'jetpack_blog_subscribe_' . self::$hash, '', time() - 3600, $cookie_path, $cookie_domain );
768
		}
769
	}
770
771
}
772
773
Jetpack_Subscriptions::init();
774
775
776
/***
777
 * Blog Subscription Widget
778
 */
779
780
class Jetpack_Subscriptions_Widget extends WP_Widget {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
781 View Code Duplication
	function __construct() {
782
		$widget_ops  = array(
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 2 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...
783
			'classname' => 'jetpack_subscription_widget',
784
			'description' => esc_html__( 'Add an email signup form to allow people to subscribe to your blog.', 'jetpack' ),
785
			'customize_selective_refresh' => true,
786
		);
787
788
		parent::__construct(
789
			'blog_subscription',
790
			/** This filter is documented in modules/widgets/facebook-likebox.php */
791
			apply_filters( 'jetpack_widget_name', __( 'Blog Subscriptions', 'jetpack' ) ),
792
			$widget_ops
793
		);
794
795
		if ( is_active_widget( false, false, $this->id_base ) || is_active_widget( false, false, 'monster' ) || is_customize_preview() ) {
796
			add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_style' ) );
797
		}
798
	}
799
800
	/**
801
	 * Enqueue the form's CSS.
802
	 *
803
	 * @since 4.5.0
804
	 */
805
	function enqueue_style() {
806
		wp_register_style( 'jetpack-subscriptions', plugins_url( 'subscriptions/subscriptions.css', __FILE__ ) );
807
		wp_enqueue_style( 'jetpack-subscriptions' );
808
	}
809
810
	function widget( $args, $instance ) {
811
		if (
812
			( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) &&
813
			/** This filter is already documented in modules/contact-form/grunion-contact-form.php */
814
			false === apply_filters( 'jetpack_auto_fill_logged_in_user', false )
815
		) {
816
			$subscribe_email = '';
817
		} else {
818
			$current_user = wp_get_current_user();
819
			if ( ! empty( $current_user->user_email ) ) {
820
				$subscribe_email = esc_attr( $current_user->user_email );
821
			} else {
822
				$subscribe_email = '';
823
			}
824
		}
825
826
		/** This action is already documented in modules/widgets/gravatar-profile.php */
827
		do_action( 'jetpack_stats_extra', 'widget_view', 'jetpack_subscriptions' );
828
829
		$source                 = 'widget';
830
		$instance            	= wp_parse_args( (array) $instance, $this->defaults() );
831
		$subscribe_text      	= isset( $instance['subscribe_text'] )        ? stripslashes( $instance['subscribe_text'] )        : '';
832
		$subscribe_placeholder 	= isset( $instance['subscribe_placeholder'] ) ? stripslashes( $instance['subscribe_placeholder'] ) : '';
833
		$subscribe_button    	= isset( $instance['subscribe_button'] )      ? stripslashes( $instance['subscribe_button'] )      : '';
834
		$success_message    	= isset( $instance['success_message'] )       ? stripslashes( $instance['success_message'] )      : '';
835
		$widget_id              = esc_attr( !empty( $args['widget_id'] )      ? esc_attr( $args['widget_id'] ) : mt_rand( 450, 550 ) );
836
837
		$show_subscribers_total = (bool) $instance['show_subscribers_total'];
838
		$subscribers_total      = $this->fetch_subscriber_count(); // Only used for the shortcode [total-subscribers]
839
840
		// Give the input element a unique ID
841
		/**
842
		 * Filter the subscription form's ID prefix.
843
		 *
844
		 * @module subscriptions
845
		 *
846
		 * @since 2.7.0
847
		 *
848
		 * @param string subscribe-field Subscription form field prefix.
849
		 * @param int $widget_id Widget ID.
850
		 */
851
		$subscribe_field_id = apply_filters( 'subscribe_field_id', 'subscribe-field', $widget_id );
852
853
		// Display the subscription form
854
		echo $args['before_widget'];
855
856
		// Only show the title if there actually is a title
857 View Code Duplication
		if( ! empty( $instance['title'] ) ) {
858
			echo $args['before_title'] . esc_attr( $instance['title'] ) . $args['after_title'] . "\n";
859
		}
860
861
		$referer = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
862
863
		// Display any errors
864
		if ( isset( $_GET['subscribe'] ) ) :
865
			switch ( $_GET['subscribe'] ) :
866
				case 'invalid_email' : ?>
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
867
					<p class="error"><?php esc_html_e( 'The email you entered was invalid. Please check and try again.', 'jetpack' ); ?></p>
868
				<?php break;
0 ignored issues
show
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
869
				case 'opted_out' : ?>
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
870
					<p class="error"><?php printf( __( 'The email address has opted out of subscription emails. <br /> You can manage your preferences at <a href="%1$s" title="%2$s" target="_blank">subscribe.wordpress.com</a>', 'jetpack' ),
871
							'https://subscribe.wordpress.com/',
872
							__( 'Manage your email preferences.', 'jetpack' )
873
						); ?>
874
				<?php break;
0 ignored issues
show
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
875
				case 'already' : ?>
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
876
					<p class="error"><?php esc_html_e( 'You have already subscribed to this site. Please check your inbox.', 'jetpack' ); ?></p>
877
				<?php break;
0 ignored issues
show
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
878
				case 'success' : ?>
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
879
					<div class="success"><?php echo wpautop( str_replace( '[total-subscribers]', number_format_i18n( $subscribers_total['value'] ), $success_message ) ); ?></div>
880
					<?php break;
0 ignored issues
show
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
881
				default : ?>
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
The default body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a default statement must start on the line immediately following the statement.

switch ($expr) {
    default:
        doSomething(); //right
        break;
}


switch ($expr) {
    default:

        doSomething(); //wrong
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
882
					<p class="error"><?php esc_html_e( 'There was an error when subscribing. Please try again.', 'jetpack' ); ?></p>
883
				<?php break;
0 ignored issues
show
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
884
			endswitch;
885
		endif;
886
887
		// Display a subscribe form
888
		if ( isset( $_GET['subscribe'] ) && 'success' == $_GET['subscribe'] ) { ?>
889
			<?php
890
		} else { ?>
891
			<form action="#" method="post" accept-charset="utf-8" id="subscribe-blog-<?php echo $widget_id; ?>">
892
				<?php
893
				if ( ! isset ( $_GET['subscribe'] ) || 'success' != $_GET['subscribe'] ) {
894
					?><div id="subscribe-text"><?php echo wpautop( str_replace( '[total-subscribers]', number_format_i18n( $subscribers_total['value'] ), $subscribe_text ) ); ?></div><?php
895
				}
896
897
				if ( $show_subscribers_total && 0 < $subscribers_total['value'] ) {
898
					echo wpautop( sprintf( _n( 'Join %s other subscriber', 'Join %s other subscribers', $subscribers_total['value'], 'jetpack' ), number_format_i18n( $subscribers_total['value'] ) ) );
899
				}
900
				if ( ! isset ( $_GET['subscribe'] ) || 'success' != $_GET['subscribe'] ) { ?>
901
					<p id="subscribe-email">
902
						<label id="jetpack-subscribe-label" for="<?php echo esc_attr( $subscribe_field_id ) . '-' . esc_attr( $widget_id ); ?>">
903
							<?php echo !empty( $subscribe_placeholder ) ? esc_html( $subscribe_placeholder ) : esc_html__( 'Email Address:', 'jetpack' ); ?>
904
						</label>
905
						<input type="email" name="email" required="required" class="required" value="<?php echo esc_attr( $subscribe_email ); ?>" id="<?php echo esc_attr( $subscribe_field_id ) . '-' . esc_attr( $widget_id ); ?>" placeholder="<?php echo esc_attr( $subscribe_placeholder ); ?>" />
906
					</p>
907
908
					<p id="subscribe-submit">
909
						<input type="hidden" name="action" value="subscribe" />
910
						<input type="hidden" name="source" value="<?php echo esc_url( $referer ); ?>" />
911
						<input type="hidden" name="sub-type" value="<?php echo esc_attr( $source ); ?>" />
912
						<input type="hidden" name="redirect_fragment" value="<?php echo $widget_id; ?>" />
913
						<?php
914
							if ( is_user_logged_in() ) {
915
								wp_nonce_field( 'blogsub_subscribe_'. get_current_blog_id(), '_wpnonce', false );
916
							}
917
						?>
918
						<input type="submit" value="<?php echo esc_attr( $subscribe_button ); ?>" name="jetpack_subscriptions_widget" />
919
					</p>
920
				<?php }?>
921
			</form>
922
923
			<script>
924
			/*
925
			Custom functionality for safari and IE
926
			 */
927
			(function( d ) {
928
				// In case the placeholder functionality is available we remove labels
929
				if ( ( 'placeholder' in d.createElement( 'input' ) ) ) {
930
					var label = d.querySelector( 'label[for=subscribe-field-<?php echo $widget_id; ?>]' );
931
						label.style.clip 	 = 'rect(1px, 1px, 1px, 1px)';
932
						label.style.position = 'absolute';
933
						label.style.height   = '1px';
934
						label.style.width    = '1px';
935
						label.style.overflow = 'hidden';
936
				}
937
938
				// Make sure the email value is filled in before allowing submit
939
				var form = d.getElementById('subscribe-blog-<?php echo $widget_id; ?>'),
940
					input = d.getElementById('<?php echo esc_attr( $subscribe_field_id ) . '-' . esc_attr( $widget_id ); ?>'),
941
					handler = function( event ) {
942
						if ( '' === input.value ) {
943
							input.focus();
944
945
							if ( event.preventDefault ){
946
								event.preventDefault();
947
							}
948
949
							return false;
950
						}
951
					};
952
953
				if ( window.addEventListener ) {
954
					form.addEventListener( 'submit', handler, false );
955
				} else {
956
					form.attachEvent( 'onsubmit', handler );
957
				}
958
			})( document );
959
			</script>
960
		<?php } ?>
961
		<?php
962
963
		echo "\n" . $args['after_widget'];
964
	}
965
966
	function increment_subscriber_count( $current_subs_array = array() ) {
967
		$current_subs_array['value']++;
968
969
		set_transient( 'wpcom_subscribers_total', $current_subs_array, 3600 ); // try to cache the result for at least 1 hour
970
971
		return $current_subs_array;
972
	}
973
974
	function fetch_subscriber_count() {
975
		$subs_count = get_transient( 'wpcom_subscribers_total' );
976
977
		if ( FALSE === $subs_count || 'failed' == $subs_count['status'] ) {
978
			Jetpack:: load_xml_rpc_client();
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces after double colon; 1 found

This check looks for references to static members where there are spaces between the name of the type and the actual member.

An example:

Certificate:: TRIPLEDES_CBC

will actually work, but is not very readable.

Loading history...
979
980
			$xml = new Jetpack_IXR_Client( array( 'user_id' => JETPACK_MASTER_USER, ) );
981
982
			$xml->query( 'jetpack.fetchSubscriberCount' );
983
984
			if ( $xml->isError() ) { // if we get an error from .com, set the status to failed so that we will try again next time the data is requested
985
				$subs_count = array(
986
					'status'  => 'failed',
987
					'code'    => $xml->getErrorCode(),
988
					'message' => $xml->getErrorMessage(),
989
					'value'	  => ( isset( $subs_count['value'] ) ) ? $subs_count['value'] : 0,
990
				);
991
			} else {
992
				$subs_count = array(
993
					'status' => 'success',
994
					'value'  => $xml->getResponse(),
995
				);
996
			}
997
998
			set_transient( 'wpcom_subscribers_total', $subs_count, 3600 ); // try to cache the result for at least 1 hour
999
		}
1000
1001
		return $subs_count;
1002
	}
1003
1004
	function update( $new_instance, $old_instance ) {
1005
		$instance = $old_instance;
1006
1007
		$instance['title']					= wp_kses( stripslashes( $new_instance['title'] ), array() );
1008
		$instance['subscribe_text']			= wp_filter_post_kses( stripslashes( $new_instance['subscribe_text'] ) );
1009
		$instance['subscribe_placeholder']	= wp_kses( stripslashes( $new_instance['subscribe_placeholder'] ), array() );
1010
		$instance['subscribe_button']		= wp_kses( stripslashes( $new_instance['subscribe_button'] ), array() );
1011
		$instance['success_message']		= wp_kses( stripslashes( $new_instance['success_message'] ), array() );
1012
		$instance['show_subscribers_total']	= isset( $new_instance['show_subscribers_total'] ) && $new_instance['show_subscribers_total'];
1013
1014
		return $instance;
1015
	}
1016
1017
	public static function defaults() {
1018
		return array(
1019
			'title'               	 => esc_html__( 'Subscribe to Blog via Email', 'jetpack' ),
1020
			'subscribe_text'      	 => esc_html__( 'Enter your email address to subscribe to this blog and receive notifications of new posts by email.', 'jetpack' ),
1021
			'subscribe_placeholder'	 => esc_html__( 'Email Address', 'jetpack' ),
1022
			'subscribe_button'    	 => esc_html__( 'Subscribe', 'jetpack' ),
1023
			'success_message'    	 => esc_html__( "Success! An email was just sent to confirm your subscription. Please find the email now and click 'Confirm Follow' to start subscribing.", 'jetpack' ),
1024
			'show_subscribers_total' => true,
1025
		);
1026
	}
1027
1028
	function form( $instance ) {
1029
		$instance = wp_parse_args( (array) $instance, $this->defaults() );
1030
1031
		$title               	= stripslashes( $instance['title'] );
1032
		$subscribe_text      	= stripslashes( $instance['subscribe_text'] );
1033
		$subscribe_placeholder 	= stripslashes( $instance['subscribe_placeholder'] );
1034
		$subscribe_button    	= stripslashes( $instance['subscribe_button'] );
1035
		$success_message		= stripslashes( $instance['success_message']);
1036
		$show_subscribers_total = checked( $instance['show_subscribers_total'], true, false );
1037
1038
		$subs_fetch = $this->fetch_subscriber_count();
1039
1040
		if ( 'failed' == $subs_fetch['status'] ) {
1041
			printf( '<div class="error inline"><p>' . __( '%s: %s', 'jetpack' ) . '</p></div>', esc_html( $subs_fetch['code'] ), esc_html( $subs_fetch['message'] ) );
1042
		}
1043
		$subscribers_total = number_format_i18n( $subs_fetch['value'] );
1044
?>
1045
<p>
1046
	<label for="<?php echo $this->get_field_id( 'title' ); ?>">
1047
		<?php _e( 'Widget title:', 'jetpack' ); ?>
1048
		<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
1049
	</label>
1050
</p>
1051
<p>
1052
	<label for="<?php echo $this->get_field_id( 'subscribe_text' ); ?>">
1053
		<?php _e( 'Optional text to display to your readers:', 'jetpack' ); ?>
1054
		<textarea class="widefat" id="<?php echo $this->get_field_id( 'subscribe_text' ); ?>" name="<?php echo $this->get_field_name( 'subscribe_text' ); ?>" rows="3"><?php echo esc_html( $subscribe_text ); ?></textarea>
1055
	</label>
1056
</p>
1057
<p>
1058
	<label for="<?php echo $this->get_field_id( 'subscribe_placeholder' ); ?>">
1059
		<?php esc_html_e( 'Subscribe Placeholder:', 'jetpack' ); ?>
1060
		<input class="widefat" id="<?php echo $this->get_field_id( 'subscribe_placeholder' ); ?>" name="<?php echo $this->get_field_name( 'subscribe_placeholder' ); ?>" type="text" value="<?php echo esc_attr( $subscribe_placeholder ); ?>" />
1061
	</label>
1062
</p>
1063
<p>
1064
	<label for="<?php echo $this->get_field_id( 'subscribe_button' ); ?>">
1065
		<?php _e( 'Subscribe Button:', 'jetpack' ); ?>
1066
		<input class="widefat" id="<?php echo $this->get_field_id( 'subscribe_button' ); ?>" name="<?php echo $this->get_field_name( 'subscribe_button' ); ?>" type="text" value="<?php echo esc_attr( $subscribe_button ); ?>" />
1067
	</label>
1068
</p>
1069
<p>
1070
	<label for="<?php echo $this->get_field_id( 'success_message' ); ?>">
1071
		<?php _e( 'Success Message Text:', 'jetpack' ); ?>
1072
		<textarea class="widefat" id="<?php echo $this->get_field_id( 'success_message' ); ?>" name="<?php echo $this->get_field_name( 'success_message' ); ?>" rows="5"><?php echo esc_html( $success_message ); ?></textarea>
1073
	</label>
1074
</p>
1075
<p>
1076
	<label for="<?php echo $this->get_field_id( 'show_subscribers_total' ); ?>">
1077
		<input type="checkbox" id="<?php echo $this->get_field_id( 'show_subscribers_total' ); ?>" name="<?php echo $this->get_field_name( 'show_subscribers_total' ); ?>" value="1"<?php echo $show_subscribers_total; ?> />
1078
		<?php echo esc_html( sprintf( _n( 'Show total number of subscribers? (%s subscriber)', 'Show total number of subscribers? (%s subscribers)', $subscribers_total, 'jetpack' ), $subscribers_total ) ); ?>
1079
	</label>
1080
</p>
1081
<?php
1082
	}
1083
}
1084
1085
add_shortcode( 'jetpack_subscription_form', 'jetpack_do_subscription_form' );
1086
add_shortcode( 'blog_subscription_form', 'jetpack_do_subscription_form' );
1087
1088
function jetpack_do_subscription_form( $instance ) {
1089
	if ( empty( $instance ) || ! is_array( $instance ) ) {
1090
		$instance = array();
1091
	}
1092
	$instance['show_subscribers_total'] = empty( $instance['show_subscribers_total'] ) ? false : true;
1093
1094
	$instance = shortcode_atts(
1095
		Jetpack_Subscriptions_Widget::defaults(),
1096
		$instance,
1097
		'jetpack_subscription_form'
1098
	);
1099
	$args = array(
1100
		'before_widget' => sprintf( '<div class="%s">', 'jetpack_subscription_widget' ),
1101
	);
1102
	ob_start();
1103
	the_widget( 'Jetpack_Subscriptions_Widget', $instance, $args );
1104
	$output = ob_get_clean();
1105
	return $output;
1106
}
1107