PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 6.0.4
Jetpack – WP Security, Backup, Speed, & Growth v6.0.4
16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 All 501 releases
jetpack / modules / subscriptions.php

subscriptions.php in Jetpack – WP Security, Backup, Speed, & Growth 6.0.4, at modules/subscriptions.php

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