Completed
Push — master-stable ( e17c57...c646dd )
by
unknown
276:05 queued 265:38
created

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