PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 4.9.3
Jetpack – WP Security, Backup, Speed, & Growth v4.9.3
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 4.9.3, at modules/subscriptions.php

1,068 lines 36.9 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 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 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 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 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 /**
683 * Jetpack_Subscriptions::set_cookies()
684 *
685 * Set a cookie to save state on the comment and post subscription checkboxes.
686 *
687 * @param bool $subscribe_to_post Whether the user chose to subscribe to subsequent comments on this post.
688 * @param int $post_id If $subscribe_to_post is true, the post ID they've subscribed to.
689 * @param bool $subscribe_to_blog Whether the user chose to subscribe to all new posts on the blog.
690 */
691 function set_cookies( $subscribe_to_post = false, $post_id = null, $subscribe_to_blog = false ) {
692 $post_id = intval( $post_id );
693
694 /** This filter is already documented in core/wp-includes/comment-functions.php */
695 $cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
696
697 /**
698 * Filter the Jetpack Comment cookie path.
699 *
700 * @module subscriptions
701 *
702 * @since 2.5.0
703 *
704 * @param string COOKIEPATH Cookie path.
705 */
706 $cookie_path = apply_filters( 'jetpack_comment_cookie_path', COOKIEPATH );
707
708 /**
709 * Filter the Jetpack Comment cookie domain.
710 *
711 * @module subscriptions
712 *
713 * @since 2.5.0
714 *
715 * @param string COOKIE_DOMAIN Cookie domain.
716 */
717 $cookie_domain = apply_filters( 'jetpack_comment_cookie_domain', COOKIE_DOMAIN );
718
719 if ( $subscribe_to_post && $post_id >= 0 ) {
720 setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain );
721 } else {
722 setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, '', time() - 3600, $cookie_path, $cookie_domain );
723 }
724
725 if ( $subscribe_to_blog ) {
726 setcookie( 'jetpack_blog_subscribe_' . self::$hash, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain );
727 } else {
728 setcookie( 'jetpack_blog_subscribe_' . self::$hash, '', time() - 3600, $cookie_path, $cookie_domain );
729 }
730 }
731
732 }
733
734 Jetpack_Subscriptions::init();
735
736
737 /***
738 * Blog Subscription Widget
739 */
740
741 class Jetpack_Subscriptions_Widget extends WP_Widget {
742 function __construct() {
743 $widget_ops = array(
744 'classname' => 'jetpack_subscription_widget',
745 'description' => esc_html__( 'Add an email signup form to allow people to subscribe to your blog.', 'jetpack' ),
746 'customize_selective_refresh' => true,
747 );
748
749 parent::__construct(
750 'blog_subscription',
751 /** This filter is documented in modules/widgets/facebook-likebox.php */
752 apply_filters( 'jetpack_widget_name', __( 'Blog Subscriptions', 'jetpack' ) ),
753 $widget_ops
754 );
755
756 if ( is_active_widget( false, false, $this->id_base ) || is_active_widget( false, false, 'monster' ) || is_customize_preview() ) {
757 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_style' ) );
758 }
759 }
760
761 /**
762 * Enqueue the form's CSS.
763 *
764 * @since 4.5.0
765 */
766 function enqueue_style() {
767 wp_register_style( 'jetpack-subscriptions', plugins_url( 'subscriptions/subscriptions.css', __FILE__ ) );
768 wp_enqueue_style( 'jetpack-subscriptions' );
769 }
770
771 function widget( $args, $instance ) {
772 if (
773 ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) &&
774 /** This filter is already documented in modules/contact-form/grunion-contact-form.php */
775 false === apply_filters( 'jetpack_auto_fill_logged_in_user', false )
776 ) {
777 $subscribe_email = '';
778 } else {
779 $current_user = wp_get_current_user();
780 if ( ! empty( $current_user->user_email ) ) {
781 $subscribe_email = esc_attr( $current_user->user_email );
782 } else {
783 $subscribe_email = '';
784 }
785 }
786
787 /** This action is already documented in modules/widgets/gravatar-profile.php */
788 do_action( 'jetpack_stats_extra', 'widget_view', 'jetpack_subscriptions' );
789
790 $source = 'widget';
791 $instance = wp_parse_args( (array) $instance, $this->defaults() );
792 $subscribe_text = isset( $instance['subscribe_text'] ) ? stripslashes( $instance['subscribe_text'] ) : '';
793 $subscribe_placeholder = isset( $instance['subscribe_placeholder'] ) ? stripslashes( $instance['subscribe_placeholder'] ) : '';
794 $subscribe_button = isset( $instance['subscribe_button'] ) ? stripslashes( $instance['subscribe_button'] ) : '';
795 $success_message = isset( $instance['success_message'] ) ? stripslashes( $instance['success_message'] ) : '';
796 $widget_id = esc_attr( !empty( $args['widget_id'] ) ? esc_attr( $args['widget_id'] ) : mt_rand( 450, 550 ) );
797
798 $show_subscribers_total = (bool) $instance['show_subscribers_total'];
799 $subscribers_total = $this->fetch_subscriber_count(); // Only used for the shortcode [total-subscribers]
800
801 // Give the input element a unique ID
802 /**
803 * Filter the subscription form's ID prefix.
804 *
805 * @module subscriptions
806 *
807 * @since 2.7.0
808 *
809 * @param string subscribe-field Subscription form field prefix.
810 * @param int $widget_id Widget ID.
811 */
812 $subscribe_field_id = apply_filters( 'subscribe_field_id', 'subscribe-field', $widget_id );
813
814 // Display the subscription form
815 echo $args['before_widget'];
816
817 // Only show the title if there actually is a title
818 if( ! empty( $instance['title'] ) ) {
819 echo $args['before_title'] . esc_attr( $instance['title'] ) . $args['after_title'] . "\n";
820 }
821
822 $referer = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
823
824 // Display any errors
825 if ( isset( $_GET['subscribe'] ) ) :
826 switch ( $_GET['subscribe'] ) :
827 case 'invalid_email' : ?>
828 <p class="error"><?php esc_html_e( 'The email you entered was invalid. Please check and try again.', 'jetpack' ); ?></p>
829 <?php break;
830 case 'opted_out' : ?>
831 <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' ),
832 'https://subscribe.wordpress.com/',
833 __( 'Manage your email preferences.', 'jetpack' )
834 ); ?>
835 <?php break;
836 case 'already' : ?>
837 <p class="error"><?php esc_html_e( 'You have already subscribed to this site. Please check your inbox.', 'jetpack' ); ?></p>
838 <?php break;
839 case 'success' : ?>
840 <div class="success"><?php echo wpautop( str_replace( '[total-subscribers]', number_format_i18n( $subscribers_total['value'] ), $success_message ) ); ?></div>
841 <?php break;
842 default : ?>
843 <p class="error"><?php esc_html_e( 'There was an error when subscribing. Please try again.', 'jetpack' ); ?></p>
844 <?php break;
845 endswitch;
846 endif;
847
848 // Display a subscribe form
849 if ( isset( $_GET['subscribe'] ) && 'success' == $_GET['subscribe'] ) { ?>
850 <?php
851 } else { ?>
852 <form action="#" method="post" accept-charset="utf-8" id="subscribe-blog-<?php echo $widget_id; ?>">
853 <?php
854 if ( ! isset ( $_GET['subscribe'] ) || 'success' != $_GET['subscribe'] ) {
855 ?><div id="subscribe-text"><?php echo wpautop( str_replace( '[total-subscribers]', number_format_i18n( $subscribers_total['value'] ), $subscribe_text ) ); ?></div><?php
856 }
857
858 if ( $show_subscribers_total && 0 < $subscribers_total['value'] ) {
859 echo wpautop( sprintf( _n( 'Join %s other subscriber', 'Join %s other subscribers', $subscribers_total['value'], 'jetpack' ), number_format_i18n( $subscribers_total['value'] ) ) );
860 }
861 if ( ! isset ( $_GET['subscribe'] ) || 'success' != $_GET['subscribe'] ) { ?>
862 <p id="subscribe-email">
863 <label id="jetpack-subscribe-label" for="<?php echo esc_attr( $subscribe_field_id ) . '-' . esc_attr( $widget_id ); ?>">
864 <?php echo !empty( $subscribe_placeholder ) ? esc_html( $subscribe_placeholder ) : esc_html__( 'Email Address:', 'jetpack' ); ?>
865 </label>
866 <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 ); ?>" />
867 </p>
868
869 <p id="subscribe-submit">
870 <input type="hidden" name="action" value="subscribe" />
871 <input type="hidden" name="source" value="<?php echo esc_url( $referer ); ?>" />
872 <input type="hidden" name="sub-type" value="<?php echo esc_attr( $source ); ?>" />
873 <input type="hidden" name="redirect_fragment" value="<?php echo $widget_id; ?>" />
874 <?php
875 if ( is_user_logged_in() ) {
876 wp_nonce_field( 'blogsub_subscribe_'. get_current_blog_id(), '_wpnonce', false );
877 }
878 ?>
879 <input type="submit" value="<?php echo esc_attr( $subscribe_button ); ?>" name="jetpack_subscriptions_widget" />
880 </p>
881 <?php }?>
882 </form>
883
884 <script>
885 /*
886 Custom functionality for safari and IE
887 */
888 (function( d ) {
889 // In case the placeholder functionality is available we remove labels
890 if ( ( 'placeholder' in d.createElement( 'input' ) ) ) {
891 var label = d.querySelector( 'label[for=subscribe-field-<?php echo $widget_id; ?>]' );
892 label.style.clip = 'rect(1px, 1px, 1px, 1px)';
893 label.style.position = 'absolute';
894 label.style.height = '1px';
895 label.style.width = '1px';
896 label.style.overflow = 'hidden';
897 }
898
899 // Make sure the email value is filled in before allowing submit
900 var form = d.getElementById('subscribe-blog-<?php echo $widget_id; ?>'),
901 input = d.getElementById('<?php echo esc_attr( $subscribe_field_id ) . '-' . esc_attr( $widget_id ); ?>'),
902 handler = function( event ) {
903 if ( '' === input.value ) {
904 input.focus();
905
906 if ( event.preventDefault ){
907 event.preventDefault();
908 }
909
910 return false;
911 }
912 };
913
914 if ( window.addEventListener ) {
915 form.addEventListener( 'submit', handler, false );
916 } else {
917 form.attachEvent( 'onsubmit', handler );
918 }
919 })( document );
920 </script>
921 <?php } ?>
922 <?php
923
924 echo "\n" . $args['after_widget'];
925 }
926
927 function increment_subscriber_count( $current_subs_array = array() ) {
928 $current_subs_array['value']++;
929
930 set_transient( 'wpcom_subscribers_total', $current_subs_array, 3600 ); // try to cache the result for at least 1 hour
931
932 return $current_subs_array;
933 }
934
935 function fetch_subscriber_count() {
936 $subs_count = get_transient( 'wpcom_subscribers_total' );
937
938 if ( FALSE === $subs_count || 'failed' == $subs_count['status'] ) {
939 Jetpack:: load_xml_rpc_client();
940
941 $xml = new Jetpack_IXR_Client( array( 'user_id' => JETPACK_MASTER_USER, ) );
942
943 $xml->query( 'jetpack.fetchSubscriberCount' );
944
945 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
946 $subs_count = array(
947 'status' => 'failed',
948 'code' => $xml->getErrorCode(),
949 'message' => $xml->getErrorMessage(),
950 'value' => ( isset( $subs_count['value'] ) ) ? $subs_count['value'] : 0,
951 );
952 } else {
953 $subs_count = array(
954 'status' => 'success',
955 'value' => $xml->getResponse(),
956 );
957 }
958
959 set_transient( 'wpcom_subscribers_total', $subs_count, 3600 ); // try to cache the result for at least 1 hour
960 }
961
962 return $subs_count;
963 }
964
965 function update( $new_instance, $old_instance ) {
966 $instance = $old_instance;
967
968 $instance['title'] = wp_kses( stripslashes( $new_instance['title'] ), array() );
969 $instance['subscribe_text'] = wp_filter_post_kses( stripslashes( $new_instance['subscribe_text'] ) );
970 $instance['subscribe_placeholder'] = wp_kses( stripslashes( $new_instance['subscribe_placeholder'] ), array() );
971 $instance['subscribe_button'] = wp_kses( stripslashes( $new_instance['subscribe_button'] ), array() );
972 $instance['success_message'] = wp_kses( stripslashes( $new_instance['success_message'] ), array() );
973 $instance['show_subscribers_total'] = isset( $new_instance['show_subscribers_total'] ) && $new_instance['show_subscribers_total'];
974
975 return $instance;
976 }
977
978 public static function defaults() {
979 return array(
980 'title' => esc_html__( 'Subscribe to Blog via Email', 'jetpack' ),
981 'subscribe_text' => esc_html__( 'Enter your email address to subscribe to this blog and receive notifications of new posts by email.', 'jetpack' ),
982 'subscribe_placeholder' => esc_html__( 'Email Address', 'jetpack' ),
983 'subscribe_button' => esc_html__( 'Subscribe', 'jetpack' ),
984 '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' ),
985 'show_subscribers_total' => true,
986 );
987 }
988
989 function form( $instance ) {
990 $instance = wp_parse_args( (array) $instance, $this->defaults() );
991
992 $title = stripslashes( $instance['title'] );
993 $subscribe_text = stripslashes( $instance['subscribe_text'] );
994 $subscribe_placeholder = stripslashes( $instance['subscribe_placeholder'] );
995 $subscribe_button = stripslashes( $instance['subscribe_button'] );
996 $success_message = stripslashes( $instance['success_message']);
997 $show_subscribers_total = checked( $instance['show_subscribers_total'], true, false );
998
999 $subs_fetch = $this->fetch_subscriber_count();
1000
1001 if ( 'failed' == $subs_fetch['status'] ) {
1002 printf( '<div class="error inline"><p>' . __( '%s: %s', 'jetpack' ) . '</p></div>', esc_html( $subs_fetch['code'] ), esc_html( $subs_fetch['message'] ) );
1003 }
1004 $subscribers_total = number_format_i18n( $subs_fetch['value'] );
1005 ?>
1006 <p>
1007 <label for="<?php echo $this->get_field_id( 'title' ); ?>">
1008 <?php _e( 'Widget title:', 'jetpack' ); ?>
1009 <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 ); ?>" />
1010 </label>
1011 </p>
1012 <p>
1013 <label for="<?php echo $this->get_field_id( 'subscribe_text' ); ?>">
1014 <?php _e( 'Optional text to display to your readers:', 'jetpack' ); ?>
1015 <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>
1016 </label>
1017 </p>
1018 <p>
1019 <label for="<?php echo $this->get_field_id( 'subscribe_placeholder' ); ?>">
1020 <?php esc_html_e( 'Subscribe Placeholder:', 'jetpack' ); ?>
1021 <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 ); ?>" />
1022 </label>
1023 </p>
1024 <p>
1025 <label for="<?php echo $this->get_field_id( 'subscribe_button' ); ?>">
1026 <?php _e( 'Subscribe Button:', 'jetpack' ); ?>
1027 <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 ); ?>" />
1028 </label>
1029 </p>
1030 <p>
1031 <label for="<?php echo $this->get_field_id( 'success_message' ); ?>">
1032 <?php _e( 'Success Message Text:', 'jetpack' ); ?>
1033 <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>
1034 </label>
1035 </p>
1036 <p>
1037 <label for="<?php echo $this->get_field_id( 'show_subscribers_total' ); ?>">
1038 <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; ?> />
1039 <?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 ) ); ?>
1040 </label>
1041 </p>
1042 <?php
1043 }
1044 }
1045
1046 add_shortcode( 'jetpack_subscription_form', 'jetpack_do_subscription_form' );
1047 add_shortcode( 'blog_subscription_form', 'jetpack_do_subscription_form' );
1048
1049 function jetpack_do_subscription_form( $instance ) {
1050 if ( empty( $instance ) || ! is_array( $instance ) ) {
1051 $instance = array();
1052 }
1053 $instance['show_subscribers_total'] = empty( $instance['show_subscribers_total'] ) ? false : true;
1054
1055 $instance = shortcode_atts(
1056 Jetpack_Subscriptions_Widget::defaults(),
1057 $instance,
1058 'jetpack_subscription_form'
1059 );
1060 $args = array(
1061 'before_widget' => sprintf( '<div class="%s">', 'jetpack_subscription_widget' ),
1062 );
1063 ob_start();
1064 the_widget( 'Jetpack_Subscriptions_Widget', $instance, $args );
1065 $output = ob_get_clean();
1066 return $output;
1067 }
1068