Completed
Push — add/jetpack_email_subscribers_... ( fdd93c )
by
unknown
12:39 queued 02:01
created

Jetpack_Subscriptions::setting_invitation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Module Name: Subscriptions
4
 * Module Description: Notify your readers of new posts and comments by 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
class Jetpack_Subscriptions {
29
	public $jetpack = false;
30
31
	public static $hash;
32
33
	/**
34
	 * Singleton
35
	 * @static
36
	 */
37
	static function init() {
38
		static $instance = false;
39
40
		if ( !$instance ) {
41
			$instance = new Jetpack_Subscriptions;
42
		}
43
44
		return $instance;
45
	}
46
47
	function __construct() {
48
		$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...
49
50
		// Don't use COOKIEHASH as it could be shared across installs && is non-unique in multisite.
51
		// @see: https://twitter.com/nacin/status/378246957451333632
52
		self::$hash = md5( get_option( 'siteurl' ) );
53
54
		add_filter( 'jetpack_xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
55
56
		// @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...
57
58
		// Add Configuration Page
59
		add_action( 'admin_init', array( $this, 'configure' ) );
60
61
		// Set up the subscription widget.
62
		add_action( 'widgets_init', array( $this, 'widget_init' ) );
63
64
		// Catch subscription widget submits
65
		if ( isset( $_REQUEST['jetpack_subscriptions_widget'] ) )
66
			add_action( 'template_redirect', array( $this, 'widget_submit' ) );
67
68
		// Set up the comment subscription checkboxes
69
		add_action( 'comment_form', array( $this, 'comment_subscribe_init' ) );
70
71
		// Catch comment posts and check for subscriptions.
72
		add_action( 'comment_post', array( $this, 'comment_subscribe_submit' ), 50, 2 );
73
74
		// Adds post meta checkbox in the post submit metabox
75
		add_action( 'post_submitbox_misc_actions', array( $this, 'subscription_post_page_metabox' ) );
76
77
		add_action( 'transition_post_status', array( $this, 'maybe_send_subscription_email' ), 10, 3 );
78
	}
79
80
	function post_is_public( $the_post ) {
81
		if ( !$post = get_post( $the_post ) ) {
82
			return false;
83
		}
84
85
		if ( 'publish' === $post->post_status && strlen( (string) $post->post_password ) < 1 ) {
86
			/**
87
			 * Filter whether posts can be emailed to subscribers.
88
			 *
89
			 * @module subscriptions
90
			 *
91
			 * @since 2.4.0
92
			 *
93
			 * @param bool true Can the post be emailed to Subscribers. Default to true.
94
			 */
95
			return apply_filters( 'jetpack_is_post_mailable', true );
96
		}
97
	}
98
99
	/**
100
	 * Jetpack_Subscriptions::xmlrpc_methods()
101
	 *
102
	 * Register subscriptions methods with the Jetpack XML-RPC server.
103
	 * @param array $methods
104
	 */
105
	function xmlrpc_methods( $methods ) {
106
		return array_merge(
107
			$methods,
108
			array(
109
				'jetpack.subscriptions.subscribe' => array( $this, 'subscribe' ),
110
			)
111
		);
112
	}
113
114
	/*
115
	 * Disable Subscribe on Single Post
116
	 * Register post meta
117
	 */
118
	function subscription_post_page_metabox() {
119
		if (
120
			/**
121
			 * Filter whether or not to show the per-post subscription option.
122
			 *
123
			 * @module subscriptions
124
			 *
125
			 * @since 3.7.0
126
			 *
127
			 * @param bool true = show checkbox option on all new posts | false = hide the option.
128
			 */
129
			 ! apply_filters( 'jetpack_allow_per_post_subscriptions', false ) )
130
		{
131
			return;
132
		}
133
134
		if ( has_filter( 'jetpack_subscriptions_exclude_these_categories' ) || has_filter( 'jetpack_subscriptions_include_only_these_categories' ) ) {
135
			return;
136
		}
137
138
		global $post;
139
		$disable_subscribe_value = get_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', true );
140
		// Nonce it
141
		wp_nonce_field( 'disable_subscribe', 'disable_subscribe_nonce' );
142
		// only show checkbox if post hasn't been published and is a 'post' post type.
143
		if ( get_post_status( $post->ID ) !== 'publish' && get_post_type( $post->ID ) == 'post' ) : ?>
144
			<div class="misc-pub-section">
145
				<label for="_jetpack_dont_email_post_to_subs"><?php _e( 'Jetpack Subscriptions:', 'jetpack' ); ?></label><br>
146
				<input type="checkbox" name="_jetpack_dont_email_post_to_subs" id="jetpack-per-post-subscribe" value="1" <?php checked( $disable_subscribe_value, 1, true ); ?> />
147
				<?php _e( 'Don&#8217;t send this to subscribers', 'jetpack' ); ?>
148
			</div>
149
		<?php endif;
150
	}
151
152
	/**
153
	 * Checks whether or not the post should be emailed to subscribers
154
	 *
155
	 * It checks for the following things in order:
156
	 * - Usage of filter jetpack_subscriptions_exclude_these_categories
157
	 * - Usage of filter jetpack_subscriptions_include_only_these_categories
158
	 * - Existence of the per-post checkbox option
159
	 *
160
	 * Only one of these can be used at any given time.
161
	 *
162
	 * @param $new_status string - the "new" post status of the transition when saved
163
	 * @param $old_status string - the "old" post status of the transition when saved
164
	 * @param $post obj - The post object
165
	 */
166
	function maybe_send_subscription_email( $new_status, $old_status, $post ) {
167
		// Only do things on publish
168
		if ( 'publish' !== $new_status ) {
169
			return;
170
		}
171
		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
172
			return;
173
		}
174
		$email_subscribers = true;
175
		/**
176
		 * Array of categories that will never trigger subscription emails.
177
		 *
178
		 * Will not send subscription emails from any post from within these categories.
179
		 *
180
		 * @module subscriptions
181
		 *
182
		 * @since 3.7.0
183
		 *
184
		 * @param array $args Array of category slugs or ID's.
185
		 */
186
		$excluded_categories = apply_filters( 'jetpack_subscriptions_exclude_these_categories', array() );
187
		// Never email posts from these categories
188 View Code Duplication
		if ( ! empty( $excluded_categories ) && in_category( $excluded_categories, $post->ID ) ) {
189
			update_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', 1 );
190
			$email_subscribers = false;
191
		}
192
		/**
193
		 * ONLY send subscription emails for these categories
194
		 *
195
		 * Will ONLY send subscription emails to these categories.
196
		 *
197
		 * @module subscriptions
198
		 *
199
		 * @since 3.7.0
200
		 *
201
		 * @param array $args Array of category slugs or ID's.
202
		 */
203
		$only_these_categories = apply_filters( 'jetpack_subscriptions_exclude_all_categories_except', array() );
204
205
		// Only emails posts from these categories
206 View Code Duplication
		if ( ! empty( $only_these_categories ) && ! in_category( $only_these_categories, $post->ID ) ) {
207
			update_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', 1 );
208
			$email_subscribers = false;
209
		}
210
		
211
		// Email the post, depending on the checkbox option
212
		if ( ! empty( $_POST['disable_subscribe_nonce'] ) && wp_verify_nonce( $_POST['disable_subscribe_nonce'], 'disable_subscribe' ) ) {
213
			if ( isset( $_POST['_jetpack_dont_email_post_to_subs'] ) ) {
214
				update_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', $_POST['_jetpack_dont_email_post_to_subs'] );
215
				$email_subscribers = ! (bool) $_POST['_jetpack_dont_email_post_to_subs'];
216
			}
217
		}
218
219
		if ( $email_subscribers ) {
220
			add_action( 'wp_insert_post', array( $this, 'fire_jetpack_email_subscribers_sync' ), 12, 2 );
221
		} else {
222
			// remove action if we are not sending the email.
223
			remove_action( 'wp_insert_post', array( $this, 'fire_jetpack_email_subscribers_sync' ), 12, 2 );
224
		}
225
	}
226
227
	function fire_jetpack_email_subscribers_sync( $post_id, $post ) {
228
		if ( get_post_meta( $post_id, '_jetpack_sent_email_subscribers_action' ) ) {
229
			return;
230
		}
231
		// Don't try to email empty post.
232
		if ( empty( $post->post_content ) ) {
233
			return;
234
		}
235
		if( ! $this->post_is_public( $post ) ) {
236
			return;
237
		}
238
		if( ! $post->post_type == 'post' ) {
239
			return;
240
		}
241
		if ( get_post_meta( $post->ID, '_jetpack_dont_email_post_to_sub' ) ) {
242
			return;
243
		}
244
		do_action( 'jetpack_email_subscribers', $post_id );
245
		update_post_meta( $post_id, '_jetpack_sent_email_subscribers_action', true );
246
	}
247
248
	/**
249
	 * Jetpack_Subscriptions::configure()
250
	 *
251
	 * Jetpack Subscriptions configuration screen.
252
	 */
253
	function configure() {
254
		// Create the section
255
		add_settings_section(
256
			'jetpack_subscriptions',
257
			__( 'Jetpack Subscriptions Settings', 'jetpack' ),
258
			array( $this, 'subscriptions_settings_section' ),
259
			'discussion'
260
		);
261
262
		/** Subscribe to Posts ***************************************************/
263
264
		add_settings_field(
265
			'jetpack_subscriptions_post_subscribe',
266
			__( 'Follow Blog', 'jetpack' ),
267
			array( $this, 'subscription_post_subscribe_setting' ),
268
			'discussion',
269
			'jetpack_subscriptions'
270
		);
271
272
		register_setting(
273
			'discussion',
274
			'stb_enabled'
275
		);
276
277
		/** Subscribe to Comments ******************************************************/
278
279
		add_settings_field(
280
			'jetpack_subscriptions_comment_subscribe',
281
			__( 'Follow Comments', 'jetpack' ),
282
			array( $this, 'subscription_comment_subscribe_setting' ),
283
			'discussion',
284
			'jetpack_subscriptions'
285
		);
286
287
		register_setting(
288
			'discussion',
289
			'stc_enabled'
290
		);
291
292
		/** Subscription Messaging Options ******************************************************/
293
294
		register_setting(
295
			'reading',
296
			'subscription_options',
297
			array( $this, 'validate_settings' )
298
		);
299
300
		add_settings_section(
301
			'email_settings',
302
			__( 'Follower Settings', 'jetpack' ),
303
			array( $this, 'reading_section' ),
304
			'reading'
305
		);
306
307
		add_settings_field(
308
			'invitation',
309
			__( 'Blog follow email text', 'jetpack' ),
310
			array( $this, 'setting_invitation' ),
311
			'reading',
312
			'email_settings'
313
		);
314
315
		add_settings_field(
316
			'comment-follow',
317
			__( 'Comment follow email text', 'jetpack' ),
318
			array( $this, 'setting_comment_follow' ),
319
			'reading',
320
			'email_settings'
321
		);
322
	}
323
324
	/**
325
	 * Discussions setting section blurb
326
	 *
327
	 */
328
	function subscriptions_settings_section() {
329
	?>
330
		<p id="jetpack-subscriptions-settings"><?php _e( 'Change whether your visitors can subscribe to your posts or comments or both.', 'jetpack' ); ?></p>
331
332
	<?php
333
	}
334
335
	/**
336
	 * Post Subscriptions Toggle
337
	 *
338
	 */
339 View Code Duplication
	function subscription_post_subscribe_setting() {
340
341
		$stb_enabled = get_option( 'stb_enabled', 1 ); ?>
342
343
		<p class="description">
344
			<input type="checkbox" name="stb_enabled" id="jetpack-post-subscribe" value="1" <?php checked( $stb_enabled, 1 ); ?> />
345
			<?php _e( "Show a <em>'follow blog'</em> option in the comment form", 'jetpack' ); ?>
346
		</p>
347
	<?php
348
	}
349
350
	/**
351
	 * Comments Subscriptions Toggle
352
	 *
353
	 */
354 View Code Duplication
	function subscription_comment_subscribe_setting() {
355
356
		$stc_enabled = get_option( 'stc_enabled', 1 ); ?>
357
358
		<p class="description">
359
			<input type="checkbox" name="stc_enabled" id="jetpack-comment-subscribe" value="1" <?php checked( $stc_enabled, 1 ); ?> />
360
			<?php _e( "Show a <em>'follow comments'</em> option in the comment form", 'jetpack' ); ?>
361
		</p>
362
363
	<?php
364
	}
365
366
	function validate_settings( $settings ) {
367
		global $allowedposttags;
368
369
		$default = $this->get_default_settings();
370
371
		// Blog Follow
372
		$settings['invitation'] = trim( wp_kses( $settings['invitation'], $allowedposttags ) );
373
		if ( empty( $settings['invitation'] ) )
374
			$settings['invitation'] = $default['invitation'];
375
376
		// Comments Follow (single post)
377
		$settings['comment_follow'] = trim( wp_kses( $settings['comment_follow'], $allowedposttags ) );
378
		if ( empty( $settings['comment_follow'] ) )
379
			$settings['comment_follow'] = $default['comment_follow'];
380
381
		return $settings;
382
	}
383
384
	public function reading_section() {
385
		echo '<p id="follower-settings">';
386
		_e( 'These settings change emails sent from your blog to followers.', 'jetpack' );
387
		echo '</p>';
388
	}
389
390
	public function setting_invitation() {
391
		$settings = $this->get_settings();
392
		echo '<textarea name="subscription_options[invitation]" class="large-text" cols="50" rows="5">' . esc_textarea( $settings['invitation'] ) . '</textarea>';
393
		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>';
394
	}
395
396
	public function setting_comment_follow() {
397
		$settings = $this->get_settings();
398
		echo '<textarea name="subscription_options[comment_follow]" class="large-text" cols="50" rows="5">' . esc_textarea( $settings['comment_follow'] ) . '</textarea>';
399
		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>';
400
	}
401
402
	function get_default_settings() {
403
		return array(
404
			'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' ),
405
			'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' )
406
		);
407
	}
408
409
	function get_settings() {
410
		return wp_parse_args( (array) get_option( 'subscription_options', array() ), $this->get_default_settings() );
411
	}
412
413
	/**
414
	 * Jetpack_Subscriptions::subscribe()
415
	 *
416
	 * Send a synchronous XML-RPC subscribe to blog posts or subscribe to post comments request.
417
	 *
418
	 * @param string $email
419
	 * @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...
420
	 * @param bool   $async    (optional) Should the subscription be performed asynchronously?  Defaults to true.
421
	 *
422
	 * @return true|Jetpack_Error true on success
423
	 *	invalid_email   : not a valid email address
424
	 *	invalid_post_id : not a valid post ID
425
	 *	unknown_post_id : unknown post
426
	 *	not_subscribed  : strange error.  Jetpack servers at WordPress.com could subscribe the email.
427
	 *	disabled        : Site owner has disabled subscriptions.
428
	 *	active          : Already subscribed.
429
	 *	unknown         : strange error.  Jetpack servers at WordPress.com returned something malformed.
430
	 *	unknown_status  : strange error.  Jetpack servers at WordPress.com returned something I didn't understand.
431
	 */
432
	function subscribe( $email, $post_ids = 0, $async = true, $extra_data = array() ) {
433
		if ( !is_email( $email ) ) {
434
			return new Jetpack_Error( 'invalid_email' );
435
		}
436
437
		if ( !$async ) {
438
			Jetpack::load_xml_rpc_client();
439
			$xml = new Jetpack_IXR_ClientMulticall();
440
		}
441
442
		foreach ( (array) $post_ids as $post_id ) {
443
			$post_id = (int) $post_id;
444
			if ( $post_id < 0 ) {
445
				return new Jetpack_Error( 'invalid_post_id' );
446
			} else if ( $post_id && !$post = get_post( $post_id ) ) {
447
				return new Jetpack_Error( 'unknown_post_id' );
448
			}
449
450
			if ( $async ) {
451
				Jetpack::xmlrpc_async_call( 'jetpack.subscribeToSite', $email, $post_id, serialize( $extra_data ) );
452
			} else {
453
				$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...
454
			}
455
		}
456
457
		if ( $async ) {
458
			return;
459
		}
460
461
		// Call
462
		$xml->query();
463
464
		if ( $xml->isError() ) {
465
			return $xml->get_jetpack_error();
466
		}
467
468
		$responses = $xml->getResponse();
469
470
		$r = array();
471
		foreach ( (array) $responses as $response ) {
472
			if ( isset( $response['faultCode'] ) || isset( $response['faultString'] ) ) {
473
				$r[] = $xml->get_jetpack_error( $response['faultCode'], $response['faultString'] );
474
				continue;
475
			}
476
477
			if ( !is_array( $response[0] ) || empty( $response[0]['status'] ) ) {
478
				$r[] = new Jetpack_Error( 'unknown' );
479
				continue;
480
			}
481
482
			switch ( $response[0]['status'] ) {
483
			case 'error' :
484
				$r[] = new Jetpack_Error( 'not_subscribed' );
485
				continue 2;
486
			case 'disabled' :
487
				$r[] = new Jetpack_Error( 'disabled' );
488
				continue 2;
489
			case 'active' :
490
				$r[] = new Jetpack_Error( 'active' );
491
				continue 2;
492
			case 'pending' :
493
				$r[] = true;
494
				continue 2;
495
			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...
496
				$r[] = new Jetpack_Error( 'unknown_status', (string) $response[0]['status'] );
497
				continue 2;
498
			}
499
		}
500
501
		return $r;
502
	}
503
504
	/**
505
	 * Jetpack_Subscriptions::widget_init()
506
	 *
507
	 * Initialize and register the Jetpack Subscriptions widget.
508
	 */
509
	function widget_init() {
510
		register_widget( 'Jetpack_Subscriptions_Widget' );
511
	}
512
513
	/**
514
	 * Jetpack_Subscriptions::widget_submit()
515
	 *
516
	 * When a user submits their email via the blog subscription widget, check the details and call the subsribe() method.
517
	 */
518
	function widget_submit() {
519
		// Check the nonce.
520
		if ( is_user_logged_in() ) {
521
			check_admin_referer( 'blogsub_subscribe_' . get_current_blog_id() );
522
		}
523
524
		if ( empty( $_REQUEST['email'] ) )
525
			return false;
526
527
		$redirect_fragment = false;
528
		if ( isset( $_REQUEST['redirect_fragment'] ) ) {
529
			$redirect_fragment = preg_replace( '/[^a-z0-9_-]/i', '', $_REQUEST['redirect_fragment'] );
530
		}
531
		if ( !$redirect_fragment ) {
532
			$redirect_fragment = 'subscribe-blog';
533
		}
534
535
		$subscribe = Jetpack_Subscriptions::subscribe(
536
												$_REQUEST['email'],
537
												0,
538
												false,
539
												array(
540
													'source'         => 'widget',
541
													'widget-in-use'  => is_active_widget( false, false, 'blog_subscription', true ) ? 'yes' : 'no',
542
													'comment_status' => '',
543
													'server_data'    => $_SERVER,
544
												)
545
		);
546
547
		if ( is_wp_error( $subscribe ) ) {
548
			$error = $subscribe->get_error_code();
549
		} else {
550
			$error = false;
551
			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...
552
				if ( is_wp_error( $response ) ) {
553
					$error = $response->get_error_code();
554
					break;
555
				}
556
			}
557
		}
558
559
		switch ( $error ) {
560
			case false:
561
				$result = 'success';
562
				break;
563
			case 'invalid_email':
564
				$result = $error;
565
				break;
566
			case 'active':
567
			case 'blocked_email':
568
				$result = 'opted_out';
569
				break;
570
			case 'pending':
571
				$result = 'already';
572
				break;
573
			default:
574
				$result = 'error';
575
				break;
576
		}
577
578
		$redirect = add_query_arg( 'subscribe', $result );
579
580
		/**
581
		 * Fires on each subscription form submission.
582
		 *
583
		 * @module subscriptions
584
		 *
585
		 * @since 3.7.0
586
		 *
587
		 * @param string $result Result of form submission: success, invalid_email, already, error.
588
		 */
589
		do_action( 'jetpack_subscriptions_form_submission', $result );
590
591
		wp_safe_redirect( "$redirect#$redirect_fragment" );
592
		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...
593
	}
594
595
	/**
596
	 * Jetpack_Subscriptions::comment_subscribe_init()
597
	 *
598
	 * Set up and add the comment subscription checkbox to the comment form.
599
	 */
600
	function comment_subscribe_init() {
601
		global $post;
602
603
		$comments_checked = '';
604
		$blog_checked     = '';
605
606
		// Check for a comment / blog submission and set a cookie to retain the setting and check the boxes.
607
		if ( isset( $_COOKIE[ 'jetpack_comments_subscribe_' . self::$hash ] ) && $_COOKIE[ 'jetpack_comments_subscribe_' . self::$hash ] == $post->ID )
608
			$comments_checked = ' checked="checked"';
609
610
		if ( isset( $_COOKIE[ 'jetpack_blog_subscribe_' . self::$hash ] ) )
611
			$blog_checked = ' checked="checked"';
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
		// Set cookies for this post/comment
681
		$this->set_cookies( isset( $_REQUEST['subscribe_comments'] ), isset( $_REQUEST['subscribe_blog'] ) );
682
683
		if ( !isset( $_REQUEST['subscribe_comments'] ) && !isset( $_REQUEST['subscribe_blog'] ) )
684
			return;
685
686
		$comment  = get_comment( $comment_id );
687
		$post_ids = array();
688
689
		if ( isset( $_REQUEST['subscribe_comments'] ) )
690
			$post_ids[] = $comment->comment_post_ID;
691
692
		if ( isset( $_REQUEST['subscribe_blog'] ) )
693
			$post_ids[] = 0;
694
695
		Jetpack_Subscriptions::subscribe(
696
									$comment->comment_author_email,
697
									$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...
698
									true,
699
									array(
700
										'source'         => 'comment-form',
701
										'widget-in-use'  => is_active_widget( false, false, 'blog_subscription', true ) ? 'yes' : 'no',
702
										'comment_status' => $approved,
703
										'server_data'    => $_SERVER,
704
									)
705
		);
706
	}
707
708
	/**
709
	 * Jetpack_Subscriptions::set_cookies()
710
	 *
711
	 * Set a cookie to save state on the comment and post subscription checkboxes.
712
	 */
713
	function set_cookies( $comments = true, $posts = true ) {
714
		global $post;
715
716
		/** This filter is already documented in core/wp-includes/comment-functions.php */
717
		$cookie_lifetime = apply_filters( 'comment_cookie_lifetime',       30000000 );
718
		/**
719
		 * Filter the Jetpack Comment cookie path.
720
		 *
721
		 * @module subscriptions
722
		 *
723
		 * @since 2.5.0
724
		 *
725
		 * @param string COOKIEPATH Cookie path.
726
		 */
727
		$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...
728
		/**
729
		 * Filter the Jetpack Comment cookie domain.
730
		 *
731
		 * @module subscriptions
732
		 *
733
		 * @since 2.5.0
734
		 *
735
		 * @param string COOKIE_DOMAIN Cookie domain.
736
		 */
737
		$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...
738
739 View Code Duplication
		if ( $comments )
740
			setcookie( 'jetpack_comments_subscribe_' . self::$hash, $post->ID, time() + $cookie_lifetime, $cookie_path, $cookie_domain );
741
		else
742
			setcookie( 'jetpack_comments_subscribe_' . self::$hash, '', time() - 3600, $cookie_path, $cookie_domain );
743
744 View Code Duplication
		if ( $posts )
745
			setcookie( 'jetpack_blog_subscribe_' . self::$hash, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain );
746
		else
747
			setcookie( 'jetpack_blog_subscribe_' . self::$hash, '', time() - 3600, $cookie_path, $cookie_domain );
748
	}
749
}
750
751
Jetpack_Subscriptions::init();
752
753
754
/***
755
 * Blog Subscription Widget
756
 */
757
758
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...
759 View Code Duplication
	function __construct() {
760
		$widget_ops  = array( 'classname' => 'jetpack_subscription_widget', 'description' => __( 'Add an email signup form to allow people to subscribe to your blog.', 'jetpack' ) );
761
		$control_ops = array( 'width' => 300 );
762
763
		parent::__construct(
764
			'blog_subscription',
765
			/** This filter is documented in modules/widgets/facebook-likebox.php */
766
			apply_filters( 'jetpack_widget_name', __( 'Blog Subscriptions', 'jetpack' ) ),
767
			$widget_ops,
768
			$control_ops
769
		);
770
	}
771
772
	function widget( $args, $instance ) {
773
		if (
774
			( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) &&
775
			/** This filter is already documented in modules/contact-form/grunion-contact-form.php */
776
			false === apply_filters( 'jetpack_auto_fill_logged_in_user', false )
777
		) {
778
			$subscribe_email = '';
779
		} else {
780
			global $current_user;
781
			if ( ! empty( $current_user->user_email ) ) {
782
				$subscribe_email = esc_attr( $current_user->user_email );
783
			} else {
784
				$subscribe_email = '';
785
			}
786
		}
787
788
789
790
		$source                 = 'widget';
791
		$instance            	= wp_parse_args( (array) $instance, $this->defaults() );
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $instance. This often makes code more readable.
Loading history...
792
		$subscribe_text      	= isset( $instance['subscribe_text'] )        ? stripslashes( $instance['subscribe_text'] )        : '';
793
		$subscribe_placeholder 	= isset( $instance['subscribe_placeholder'] ) ? stripslashes( $instance['subscribe_placeholder'] ) : '';
794
		$subscribe_button    	= isset( $instance['subscribe_button'] )      ? stripslashes( $instance['subscribe_button'] )      : '';
795
		$success_message    	= isset( $instance['success_message'] )       ? stripslashes( $instance['success_message'] )      : '';
796
		$widget_id              = esc_attr( !empty( $args['widget_id'] )      ? esc_attr( $args['widget_id'] ) : mt_rand( 450, 550 ) );
797
798
		$show_subscribers_total = (bool) $instance['show_subscribers_total'];
799
		$subscribers_total      = $this->fetch_subscriber_count(); // Only used for the shortcode [total-subscribers]
800
801
		// Give the input element a unique ID
802
		/**
803
		 * Filter the subscription form's ID prefix.
804
		 *
805
		 * @module subscriptions
806
		 *
807
		 * @since 2.7.0
808
		 *
809
		 * @param string subscribe-field Subscription form field prefix.
810
		 * @param int $widget_id Widget ID.
811
		 */
812
		$subscribe_field_id = apply_filters( 'subscribe_field_id', 'subscribe-field', $widget_id );
813
814
		// Enqueue the form's CSS
815
		wp_register_style( 'jetpack-subscriptions', plugins_url( 'subscriptions/subscriptions.css', __FILE__ ) );
816
		wp_enqueue_style( 'jetpack-subscriptions' );
817
818
		// Display the subscription form
819
		echo $args['before_widget'];
820
821
		// Only show the title if there actually is a title
822 View Code Duplication
		if( ! empty( $instance['title'] ) ) {
823
			echo $args['before_title'] . esc_attr( $instance['title'] ) . $args['after_title'] . "\n";
824
		}
825
826
		$referer = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
827
828
		// Display any errors
829
		if ( isset( $_GET['subscribe'] ) ) :
830
			switch ( $_GET['subscribe'] ) :
831
				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...
832
					<p class="error"><?php esc_html_e( 'The email you entered was invalid. Please check and try again.', 'jetpack' ); ?></p>
833
				<?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...
834
				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...
835
					<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' ),
836
							'https://subscribe.wordpress.com/',
837
							__( 'Manage your email preferences.', 'jetpack' )
838
						); ?>
839
				<?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...
840
				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...
841
					<p class="error"><?php esc_html_e( 'You have already subscribed to this site. Please check your inbox.', 'jetpack' ); ?></p>
842
				<?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...
843
				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...
844
					<div class="success"><?php echo wpautop( str_replace( '[total-subscribers]', number_format_i18n( $subscribers_total['value'] ), $success_message ) ); ?></div>
845
					<?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...
846
				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...
847
					<p class="error"><?php esc_html_e( 'There was an error when subscribing. Please try again.', 'jetpack' ); ?></p>
848
				<?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...
849
			endswitch;
850
		endif;
851
852
		// Display a subscribe form
853
		if ( isset( $_GET['subscribe'] ) && 'success' == $_GET['subscribe'] ) { ?>
854
			<?php
855
		} else { ?>
856
			<form action="#" method="post" accept-charset="utf-8" id="subscribe-blog-<?php echo $widget_id; ?>">
857
				<?php
858
				if ( ! isset ( $_GET['subscribe'] ) || 'success' != $_GET['subscribe'] ) {
859
					?><div id="subscribe-text"><?php echo wpautop( str_replace( '[total-subscribers]', number_format_i18n( $subscribers_total['value'] ), $subscribe_text ) ); ?></div><?php
860
				}
861
862
				if ( $show_subscribers_total && 0 < $subscribers_total['value'] ) {
863
					echo wpautop( sprintf( _n( 'Join %s other subscriber', 'Join %s other subscribers', $subscribers_total['value'], 'jetpack' ), number_format_i18n( $subscribers_total['value'] ) ) );
864
				}
865
				if ( ! isset ( $_GET['subscribe'] ) || 'success' != $_GET['subscribe'] ) { ?>
866
					<p id="subscribe-email">
867
						<label id="jetpack-subscribe-label" for="<?php echo esc_attr( $subscribe_field_id ) . '-' . esc_attr( $widget_id ); ?>">
868
							<?php echo !empty( $subscribe_placeholder ) ? esc_html( $subscribe_placeholder ) : esc_html__( 'Email Address:', 'jetpack' ); ?>
869
						</label>
870
						<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 ); ?>" />
871
					</p>
872
873
					<p id="subscribe-submit">
874
						<input type="hidden" name="action" value="subscribe" />
875
						<input type="hidden" name="source" value="<?php echo esc_url( $referer ); ?>" />
876
						<input type="hidden" name="sub-type" value="<?php echo esc_attr( $source ); ?>" />
877
						<input type="hidden" name="redirect_fragment" value="<?php echo $widget_id; ?>" />
878
						<?php
879
							if ( is_user_logged_in() ) {
880
								wp_nonce_field( 'blogsub_subscribe_'. get_current_blog_id(), '_wpnonce', false );
881
							}
882
						?>
883
						<input type="submit" value="<?php echo esc_attr( $subscribe_button ); ?>" name="jetpack_subscriptions_widget" />
884
					</p>
885
				<?php }?>
886
			</form>
887
888
			<script>
889
			/*
890
			Custom functionality for safari and IE
891
			 */
892
			(function( d ) {
893
				// In case the placeholder functionality is available we remove labels
894
				if ( ( 'placeholder' in d.createElement( 'input' ) ) ) {
895
					var label = d.querySelector( 'label[for=subscribe-field-<?php echo $widget_id; ?>]' );
896
						label.style.clip 	 = 'rect(1px, 1px, 1px, 1px)';
897
						label.style.position = 'absolute';
898
						label.style.height   = '1px';
899
						label.style.width    = '1px';
900
						label.style.overflow = 'hidden';
901
				}
902
903
				// Make sure the email value is filled in before allowing submit
904
				var form = d.getElementById('subscribe-blog-<?php echo $widget_id; ?>'),
905
					input = d.getElementById('<?php echo esc_attr( $subscribe_field_id ) . '-' . esc_attr( $widget_id ); ?>'),
906
					handler = function( event ) {
907
						if ( '' === input.value ) {
908
							input.focus();
909
910
							if ( event.preventDefault ){
911
								event.preventDefault();
912
							}
913
914
							return false;
915
						}
916
					};
917
918
				if ( window.addEventListener ) {
919
					form.addEventListener( 'submit', handler, false );
920
				} else {
921
					form.attachEvent( 'onsubmit', handler );
922
				}
923
			})( document );
924
			</script>
925
		<?php } ?>
926
		<?php
927
928
		echo "\n" . $args['after_widget'];
929
	}
930
931
	function increment_subscriber_count( $current_subs_array = array() ) {
932
		$current_subs_array['value']++;
933
934
		set_transient( 'wpcom_subscribers_total', $current_subs_array, 3600 ); // try to cache the result for at least 1 hour
935
936
		return $current_subs_array;
937
	}
938
939
	function fetch_subscriber_count() {
940
		$subs_count = get_transient( 'wpcom_subscribers_total' );
941
942
		if ( FALSE === $subs_count || 'failed' == $subs_count['status'] ) {
943
			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...
944
945
			$xml = new Jetpack_IXR_Client( array( 'user_id' => JETPACK_MASTER_USER, ) );
946
947
			$xml->query( 'jetpack.fetchSubscriberCount' );
948
949
			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
950
				$subs_count = array(
951
					'status'  => 'failed',
952
					'code'    => $xml->getErrorCode(),
953
					'message' => $xml->getErrorMessage(),
954
					'value'	  => ( isset( $subs_count['value'] ) ) ? $subs_count['value'] : 0,
955
				);
956
			} else {
957
				$subs_count = array(
958
					'status' => 'success',
959
					'value'  => $xml->getResponse(),
960
				);
961
			}
962
963
			set_transient( 'wpcom_subscribers_total', $subs_count, 3600 ); // try to cache the result for at least 1 hour
964
		}
965
966
		return $subs_count;
967
	}
968
969
	function update( $new_instance, $old_instance ) {
970
		$instance = $old_instance;
971
972
		$instance['title']					= wp_kses( stripslashes( $new_instance['title'] ), array() );
973
		$instance['subscribe_text']			= wp_filter_post_kses( stripslashes( $new_instance['subscribe_text'] ) );
974
		$instance['subscribe_placeholder']	= wp_kses( stripslashes( $new_instance['subscribe_placeholder'] ), array() );
975
		$instance['subscribe_button']		= wp_kses( stripslashes( $new_instance['subscribe_button'] ), array() );
976
		$instance['success_message']		= wp_kses( stripslashes( $new_instance['success_message'] ), array() );
977
		$instance['show_subscribers_total']	= isset( $new_instance['show_subscribers_total'] ) && $new_instance['show_subscribers_total'];
978
979
		return $instance;
980
	}
981
982
	public static function defaults() {
983
		return array(
984
			'title'               	 => esc_html__( 'Subscribe to Blog via Email', 'jetpack' ),
985
			'subscribe_text'      	 => esc_html__( 'Enter your email address to subscribe to this blog and receive notifications of new posts by email.', 'jetpack' ),
986
			'subscribe_placeholder'	 => esc_html__( 'Email Address', 'jetpack' ),
987
			'subscribe_button'    	 => esc_html__( 'Subscribe', 'jetpack' ),
988
			'success_message'    	 => esc_html__( 'Success! An email was just sent to confirm your subscription. Please find the email now and click activate to start subscribing.', 'jetpack' ),
989
			'show_subscribers_total' => true,
990
		);
991
	}
992
993
	function form( $instance ) {
994
		$instance = wp_parse_args( (array) $instance, $this->defaults() );
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $instance. This often makes code more readable.
Loading history...
995
996
		$title               	= stripslashes( $instance['title'] );
997
		$subscribe_text      	= stripslashes( $instance['subscribe_text'] );
998
		$subscribe_placeholder 	= stripslashes( $instance['subscribe_placeholder'] );
999
		$subscribe_button    	= stripslashes( $instance['subscribe_button'] );
1000
		$success_message		= stripslashes( $instance['success_message']);
1001
		$show_subscribers_total = checked( $instance['show_subscribers_total'], true, false );
1002
1003
		$subs_fetch = $this->fetch_subscriber_count();
1004
1005
		if ( 'failed' == $subs_fetch['status'] ) {
1006
			printf( '<div class="error inline"><p>' . __( '%s: %s', 'jetpack' ) . '</p></div>', esc_html( $subs_fetch['code'] ), esc_html( $subs_fetch['message'] ) );
1007
		}
1008
		$subscribers_total = number_format_i18n( $subs_fetch['value'] );
1009
?>
1010
<p>
1011
	<label for="<?php echo $this->get_field_id( 'title' ); ?>">
1012
		<?php _e( 'Widget title:', 'jetpack' ); ?>
1013
		<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 ); ?>" />
1014
	</label>
1015
</p>
1016
<p>
1017
	<label for="<?php echo $this->get_field_id( 'subscribe_text' ); ?>">
1018
		<?php _e( 'Optional text to display to your readers:', 'jetpack' ); ?>
1019
		<textarea style="width: 95%" id="<?php echo $this->get_field_id( 'subscribe_text' ); ?>" name="<?php echo $this->get_field_name( 'subscribe_text' ); ?>" type="text"><?php echo esc_html( $subscribe_text ); ?></textarea>
1020
	</label>
1021
</p>
1022
<p>
1023
	<label for="<?php echo $this->get_field_id( 'subscribe_placeholder' ); ?>">
1024
		<?php esc_html_e( 'Subscribe Placeholder:', 'jetpack' ); ?>
1025
		<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 ); ?>" />
1026
	</label>
1027
</p>
1028
<p>
1029
	<label for="<?php echo $this->get_field_id( 'subscribe_button' ); ?>">
1030
		<?php _e( 'Subscribe Button:', 'jetpack' ); ?>
1031
		<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 ); ?>" />
1032
	</label>
1033
</p>
1034
<p>
1035
	<label for="<?php echo $this->get_field_id( 'success_message' ); ?>">
1036
		<?php _e( 'Success Message Text:', 'jetpack' ); ?>
1037
		<textarea style="width: 95%" id="<?php echo $this->get_field_id( 'success_message' ); ?>" name="<?php echo $this->get_field_name( 'success_message' ); ?>" type="text"><?php echo esc_html( $success_message ); ?></textarea>
1038
	</label>
1039
</p>
1040
<p>
1041
	<label for="<?php echo $this->get_field_id( 'show_subscribers_total' ); ?>">
1042
		<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; ?> />
1043
		<?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 ) ); ?>
1044
	</label>
1045
</p>
1046
<?php
1047
	}
1048
}
1049
1050
add_shortcode( 'jetpack_subscription_form', 'jetpack_do_subscription_form' );
1051
1052
function jetpack_do_subscription_form( $instance ) {
1053
	$instance['show_subscribers_total'] = empty( $instance['show_subscribers_total'] ) ? false : true;
1054
	$instance = shortcode_atts( Jetpack_Subscriptions_Widget::defaults(), $instance, 'jetpack_subscription_form' );
0 ignored issues
show
Coding Style introduced by
Consider using a different name than the parameter $instance. This often makes code more readable.
Loading history...
1055
	$args = array(
1056
		'before_widget' => sprintf( '<div class="%s">', 'jetpack_subscription_widget' ),
1057
	);
1058
	ob_start();
1059
	the_widget( 'Jetpack_Subscriptions_Widget', $instance, $args );
1060
	$output = ob_get_clean();
1061
	return $output;
1062
}
1063