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