Completed
Push — fix/typo-pro-promo ( e5832e...fc698e )
by
unknown
29:20
created

modules/subscriptions.php (6 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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