Completed
Push — fix/subscription-add-event ( 70d8c0...c904c5 )
by
unknown
140:50 queued 133:45
created

maybe_trigger_send_email_action()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 1
dl 0
loc 8
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
	static $send_email = false;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $send_email.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

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