PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 6.2
Jetpack – WP Security, Backup, Speed, & Growth v6.2
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
← All changes | modules/subscriptions.php +542 -468 14.2.26.2 View file →
@@ -1,40 +1,31 @@
1 -<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName)
1 +<?php
2 2 /**
3 - * Module Name: Newsletter
4 - * Module Description: Let visitors subscribe to new posts and comments via email
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.
5 6 * Sort Order: 9
6 7 * Recommendation Order: 8
7 8 * First Introduced: 1.2
8 9 * Requires Connection: Yes
9 - * Requires User Connection: Yes
10 - * Auto Activate: No
10 + * Auto Activate: Yes
11 11 * Module Tags: Social
12 - * Feature: Engagement
13 - * Additional Search Queries: subscriptions, subscription, email, follow, followers, subscribers, signup, newsletter, creator
12 + * Feature: Engagement, Jumpstart
13 + * Additional Search Queries: subscriptions, subscription, email, follow, followers, subscribers, signup
14 14 */
15 15
16 -// phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
17 -
18 -use Automattic\Jetpack\Admin_UI\Admin_Menu;
19 -use Automattic\Jetpack\Connection\Manager as Connection_Manager;
20 -use Automattic\Jetpack\Connection\XMLRPC_Async_Call;
21 -use Automattic\Jetpack\Redirect;
22 -use Automattic\Jetpack\Status;
23 -use Automattic\Jetpack\Status\Host;
24 -
25 16 add_action( 'jetpack_modules_loaded', 'jetpack_subscriptions_load' );
26 17
27 -// Loads the User Content Link Redirection feature.
28 -require_once __DIR__ . '/subscriptions/jetpack-user-content-link-redirection.php';
29 -
30 -/**
31 - * Loads the Subscriptions module.
32 - */
33 18 function jetpack_subscriptions_load() {
34 19 Jetpack::enable_module_configurable( __FILE__ );
20 + Jetpack::module_configuration_load( __FILE__, 'jetpack_subscriptions_configuration_load' );
35 21 }
36 22
23 +function jetpack_subscriptions_configuration_load() {
24 + wp_safe_redirect( admin_url( 'options-discussion.php#jetpack-subscriptions-settings' ) );
25 + exit;
26 +}
27 +
37 28 /**
38 29 * Cherry picks keys from `$_SERVER` array.
39 30 *
40 31 * @since 6.0.0
@@ -44,13 +35,13 @@
44 35 function jetpack_subscriptions_cherry_pick_server_data() {
45 36 $data = array();
46 37
47 38 foreach ( $_SERVER as $key => $value ) {
48 - if ( ! is_string( $value ) || str_starts_with( $key, 'HTTP_COOKIE' ) ) {
39 + if ( ! is_string( $value ) || 0 === strpos( $key, 'HTTP_COOKIE' ) ) {
49 40 continue;
50 41 }
51 42
52 - if ( str_starts_with( $key, 'HTTP_' ) || in_array( $key, array( 'REMOTE_ADDR', 'REQUEST_URI', 'DOCUMENT_URI' ), true ) ) {
43 + if ( 0 === strpos( $key, 'HTTP_' ) || in_array( $key, array( 'REMOTE_ADDR', 'REQUEST_URI', 'DOCUMENT_URI' ), true ) ) {
53 44 $data[ $key ] = $value;
54 45 }
55 46 }
56 47
@@ -56,49 +47,32 @@
56 47
57 48 return $data;
58 49 }
59 50
60 -/**
61 - * Main class file for the Subscriptions module.
62 - */
63 51 class Jetpack_Subscriptions {
64 - /**
65 - * Whether Jetpack has been instantiated or not.
66 - *
67 - * @var bool
68 - */
69 52 public $jetpack = false;
70 53
71 - /**
72 - * Hash of the siteurl option.
73 - *
74 - * @var string
75 - */
76 54 public static $hash;
77 55
78 56 /**
79 57 * Singleton
80 - *
81 58 * @static
82 59 */
83 - public static function init() {
60 + static function init() {
84 61 static $instance = false;
85 62
86 - if ( ! $instance ) {
87 - $instance = new Jetpack_Subscriptions();
63 + if ( !$instance ) {
64 + $instance = new Jetpack_Subscriptions;
88 65 }
89 66
90 67 return $instance;
91 68 }
92 69
93 - /**
94 - * Jetpack_Subscriptions constructor.
95 - */
96 - public function __construct() {
70 + function __construct() {
97 71 $this->jetpack = Jetpack::init();
98 72
99 73 // Don't use COOKIEHASH as it could be shared across installs && is non-unique in multisite.
100 - // @see: https://twitter.com/nacin/status/378246957451333632 .
74 + // @see: https://twitter.com/nacin/status/378246957451333632
101 75 self::$hash = md5( get_option( 'siteurl' ) );
102 76
103 77 add_filter( 'jetpack_xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
104 78
@@ -103,23 +77,25 @@
103 77 add_filter( 'jetpack_xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
104 78
105 79 // @todo remove sync from subscriptions and move elsewhere...
106 80
107 - // Add Configuration Page.
81 + // Add Configuration Page
108 82 add_action( 'admin_init', array( $this, 'configure' ) );
109 83
110 - // Catch subscription widget submits.
111 - if ( isset( $_REQUEST['jetpack_subscriptions_widget'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce checked in widget_submit() for logged in users.
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'] ) )
112 89 add_action( 'template_redirect', array( $this, 'widget_submit' ) );
113 - }
114 90
115 - // Set up the comment subscription checkboxes.
116 - add_filter( 'comment_form_submit_field', array( $this, 'comment_subscribe_init' ), 10, 2 );
91 + // Set up the comment subscription checkboxes
92 + add_action( 'comment_form', array( $this, 'comment_subscribe_init' ) );
117 93
118 94 // Catch comment posts and check for subscriptions.
119 95 add_action( 'comment_post', array( $this, 'comment_subscribe_submit' ), 50, 2 );
120 96
121 - // Adds post meta checkbox in the post submit metabox.
97 + // Adds post meta checkbox in the post submit metabox
122 98 add_action( 'post_submitbox_misc_actions', array( $this, 'subscription_post_page_metabox' ) );
123 99
124 100 add_action( 'transition_post_status', array( $this, 'maybe_send_subscription_email' ), 10, 3 );
125 101
@@ -125,29 +101,8 @@
125 101
126 102 add_filter( 'jetpack_published_post_flags', array( $this, 'set_post_flags' ), 10, 2 );
127 103
128 104 add_filter( 'post_updated_messages', array( $this, 'update_published_message' ), 18, 1 );
129 -
130 - // Set "social_notifications_subscribe" option during the first-time activation.
131 - add_action( 'jetpack_activate_module_subscriptions', array( $this, 'set_social_notifications_subscribe' ) );
132 -
133 - // Hide subscription messaging in Publish panel for posts that were published in the past
134 - add_action( 'init', array( $this, 'register_post_meta' ), 20 );
135 - add_action( 'transition_post_status', array( $this, 'maybe_set_first_published_status' ), 10, 3 );
136 -
137 - // Add Subscribers menu to Jetpack navigation.
138 - add_action( 'jetpack_admin_menu', array( $this, 'add_subscribers_menu' ) );
139 -
140 - // Customize the configuration URL to lead to the Subscriptions settings.
141 - add_filter(
142 - 'jetpack_module_configuration_url_subscriptions',
143 - function () {
144 - return Jetpack::admin_url( array( 'page' => 'jetpack#/newsletter' ) );
145 - }
146 - );
147 -
148 - // Track categories created through the category editor page
149 - add_action( 'wp_ajax_add-tag', array( $this, 'track_newsletter_category_creation' ), 1 );
150 105 }
151 106
152 107 /**
153 108 * Jetpack_Subscriptions::xmlrpc_methods()
@@ -152,12 +107,11 @@
152 107 /**
153 108 * Jetpack_Subscriptions::xmlrpc_methods()
154 109 *
155 110 * Register subscriptions methods with the Jetpack XML-RPC server.
156 - *
157 - * @param array $methods Methods being registered.
111 + * @param array $methods
158 112 */
159 - public function xmlrpc_methods( $methods ) {
113 + function xmlrpc_methods( $methods ) {
160 114 return array_merge(
161 115 $methods,
162 116 array(
163 117 'jetpack.subscriptions.subscribe' => array( $this, 'subscribe' ),
@@ -164,13 +118,13 @@
164 118 )
165 119 );
166 120 }
167 121
168 - /**
122 + /*
169 123 * Disable Subscribe on Single Post
170 124 * Register post meta
171 125 */
172 - public function subscription_post_page_metabox() {
126 + function subscription_post_page_metabox() {
173 127 if (
174 128 /**
175 129 * Filter whether or not to show the per-post subscription option.
176 130 *
@@ -179,9 +133,10 @@
179 133 * @since 3.7.0
180 134 *
181 135 * @param bool true = show checkbox option on all new posts | false = hide the option.
182 136 */
183 - ! apply_filters( 'jetpack_allow_per_post_subscriptions', false ) ) {
137 + ! apply_filters( 'jetpack_allow_per_post_subscriptions', false ) )
138 + {
184 139 return;
185 140 }
186 141
187 142 if ( has_filter( 'jetpack_subscriptions_exclude_these_categories' ) || has_filter( 'jetpack_subscriptions_include_only_these_categories' ) ) {
@@ -190,19 +145,18 @@
190 145
191 146 global $post;
192 147 $disable_subscribe_value = get_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', true );
193 148 // only show checkbox if post hasn't been published and is a 'post' post type.
194 - if ( get_post_status( $post->ID ) !== 'publish' && get_post_type( $post->ID ) === 'post' ) :
195 - // Nonce it.
149 + if ( get_post_status( $post->ID ) !== 'publish' && get_post_type( $post->ID ) == 'post' ) :
150 + // Nonce it
196 151 wp_nonce_field( 'disable_subscribe', 'disable_subscribe_nonce' );
197 152 ?>
198 153 <div class="misc-pub-section">
199 - <label for="_jetpack_dont_email_post_to_subs"><?php esc_html_e( 'Jetpack Subscriptions:', 'jetpack' ); ?></label><br>
154 + <label for="_jetpack_dont_email_post_to_subs"><?php _e( 'Jetpack Subscriptions:', 'jetpack' ); ?></label><br>
200 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 ); ?> />
201 - <?php esc_html_e( 'Don&#8217;t send this to subscribers', 'jetpack' ); ?>
156 + <?php _e( 'Don&#8217;t send this to subscribers', 'jetpack' ); ?>
202 157 </div>
203 - <?php
204 - endif;
158 + <?php endif;
205 159 }
206 160
207 161 /**
208 162 * Checks whether or not the post should be emailed to subscribers
@@ -213,54 +167,43 @@
213 167 * - Existence of the per-post checkbox option
214 168 *
215 169 * Only one of these can be used at any given time.
216 170 *
217 - * @param string $new_status Tthe "new" post status of the transition when saved.
218 - * @param string $old_status The "old" post status of the transition when saved.
219 - * @param object $post obj The post object.
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
220 174 */
221 - public function maybe_send_subscription_email( $new_status, $old_status, $post ) {
175 + function maybe_send_subscription_email( $new_status, $old_status, $post ) {
222 176
223 177 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
224 178 return;
225 179 }
226 180
227 - // Make sure that the checkbox is preseved.
228 - if ( ! empty( $_POST['disable_subscribe_nonce'] ) && wp_verify_nonce( $_POST['disable_subscribe_nonce'], 'disable_subscribe' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- WP Core doesn't unslash or sanitize nonces either.
181 + // Make sure that the checkbox is preseved
182 + if ( ! empty( $_POST['disable_subscribe_nonce'] ) && wp_verify_nonce( $_POST['disable_subscribe_nonce'], 'disable_subscribe' ) ) {
229 183 $set_checkbox = isset( $_POST['_jetpack_dont_email_post_to_subs'] ) ? 1 : 0;
230 184 update_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', $set_checkbox );
231 185 }
232 186 }
233 187
234 - /**
235 - * Message used when publishing a post.
236 - *
237 - * @param array $messages Message array for a post.
238 - */
239 - public function update_published_message( $messages ) {
188 + function update_published_message( $messages ) {
240 189 global $post;
241 190 if ( ! $this->should_email_post_to_subscribers( $post ) ) {
242 191 return $messages;
243 192 }
244 193
245 - $view_post_link_html = sprintf(
246 - ' <a href="%1$s">%2$s</a>',
194 + $view_post_link_html = sprintf( ' <a href="%1$s">%2$s</a>',
247 195 esc_url( get_permalink( $post ) ),
248 - __( 'View post', 'jetpack' )
196 + __( 'View post' ) // intentinally omitted domain
249 197 );
250 198
251 199 $messages['post'][6] = sprintf(
252 200 /* translators: Message shown after a post is published */
253 201 esc_html__( 'Post published and sending emails to subscribers.', 'jetpack' )
254 - ) . $view_post_link_html;
202 + ) . $view_post_link_html;
255 203 return $messages;
256 204 }
257 205
258 - /**
259 - * Determine if a post should notifiy subscribers via email.
260 - *
261 - * @param object $post The post.
262 - */
263 206 public function should_email_post_to_subscribers( $post ) {
264 207 $should_email = true;
265 208 if ( get_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', true ) ) {
266 209 return false;
@@ -265,18 +208,13 @@
265 208 if ( get_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', true ) ) {
266 209 return false;
267 210 }
268 211
269 - // Only posts are currently supported.
270 - if ( 'post' !== $post->post_type ) {
212 + // Only posts are currently supported
213 + if ( $post->post_type !== 'post' ) {
271 214 return false;
272 215 }
273 216
274 - // Private posts are not sent to subscribers.
275 - if ( 'private' === $post->post_status ) {
276 - return false;
277 - }
278 -
279 217 /**
280 218 * Array of categories that will never trigger subscription emails.
281 219 *
282 220 * Will not send subscription emails from any post from within these categories.
@@ -288,9 +226,9 @@
288 226 * @param array $args Array of category slugs or ID's.
289 227 */
290 228 $excluded_categories = apply_filters( 'jetpack_subscriptions_exclude_these_categories', array() );
291 229
292 - // Never email posts from these categories.
230 + // Never email posts from these categories
293 231 if ( ! empty( $excluded_categories ) && in_category( $excluded_categories, $post->ID ) ) {
294 232 $should_email = false;
295 233 }
296 234
@@ -306,9 +244,9 @@
306 244 * @param array $args Array of category slugs or ID's.
307 245 */
308 246 $only_these_categories = apply_filters( 'jetpack_subscriptions_exclude_all_categories_except', array() );
309 247
310 - // Only emails posts from these categories.
248 + // Only emails posts from these categories
311 249 if ( ! empty( $only_these_categories ) && ! in_category( $only_these_categories, $post->ID ) ) {
312 250 $should_email = false;
313 251 }
314 252
@@ -314,15 +252,9 @@
314 252
315 253 return $should_email;
316 254 }
317 255
318 - /**
319 - * Retrieve which flags should be added to a particular post.
320 - *
321 - * @param array $flags Flags to be added.
322 - * @param object $post A post object.
323 - */
324 - public function set_post_flags( $flags, $post ) {
256 + function set_post_flags( $flags, $post ) {
325 257 $flags['send_subscription'] = $this->should_email_post_to_subscribers( $post );
326 258 return $flags;
327 259 }
328 260
@@ -330,10 +262,10 @@
330 262 * Jetpack_Subscriptions::configure()
331 263 *
332 264 * Jetpack Subscriptions configuration screen.
333 265 */
334 - public function configure() {
335 - // Create the section.
266 + function configure() {
267 + // Create the section
336 268 add_settings_section(
337 269 'jetpack_subscriptions',
338 270 __( 'Jetpack Subscriptions Settings', 'jetpack' ),
339 271 array( $this, 'subscriptions_settings_section' ),
@@ -339,9 +271,9 @@
339 271 array( $this, 'subscriptions_settings_section' ),
340 272 'discussion'
341 273 );
342 274
343 - /** Subscribe to Posts */
275 + /** Subscribe to Posts ***************************************************/
344 276
345 277 add_settings_field(
346 278 'jetpack_subscriptions_post_subscribe',
347 279 __( 'Follow Blog', 'jetpack' ),
@@ -354,9 +286,9 @@
354 286 'discussion',
355 287 'stb_enabled'
356 288 );
357 289
358 - /** Subscribe to Comments */
290 + /** Subscribe to Comments ******************************************************/
359 291
360 292 add_settings_field(
361 293 'jetpack_subscriptions_comment_subscribe',
362 294 __( 'Follow Comments', 'jetpack' ),
@@ -369,180 +301,155 @@
369 301 'discussion',
370 302 'stc_enabled'
371 303 );
372 304
373 - /** Email me whenever: Someone subscribes to my blog */
374 - /* @since 8.1 */
305 + /** Subscription Messaging Options ******************************************************/
375 306
307 + register_setting(
308 + 'reading',
309 + 'subscription_options',
310 + array( $this, 'validate_settings' )
311 + );
312 +
376 313 add_settings_section(
377 - 'notifications_section',
378 - __( 'Someone subscribes to my blog', 'jetpack' ),
379 - array( $this, 'social_notifications_subscribe_section' ),
380 - 'discussion'
314 + 'email_settings',
315 + __( 'Follower Settings', 'jetpack' ),
316 + array( $this, 'reading_section' ),
317 + 'reading'
381 318 );
382 319
383 320 add_settings_field(
384 - 'jetpack_subscriptions_social_notifications_subscribe',
385 - __( 'Email me whenever', 'jetpack' ),
386 - array( $this, 'social_notifications_subscribe_field' ),
387 - 'discussion',
388 - 'notifications_section'
321 + 'invitation',
322 + __( 'Blog follow email text', 'jetpack' ),
323 + array( $this, 'setting_invitation' ),
324 + 'reading',
325 + 'email_settings'
389 326 );
390 327
391 - register_setting(
392 - 'discussion',
393 - 'social_notifications_subscribe',
394 - array( $this, 'social_notifications_subscribe_validate' )
328 + add_settings_field(
329 + 'comment-follow',
330 + __( 'Comment follow email text', 'jetpack' ),
331 + array( $this, 'setting_comment_follow' ),
332 + 'reading',
333 + 'email_settings'
395 334 );
396 335 }
397 336
398 337 /**
399 - * Discussions setting section blurb.
338 + * Discussions setting section blurb
339 + *
400 340 */
401 - public function subscriptions_settings_section() {
402 - ?>
403 - <p id="jetpack-subscriptions-settings"><?php esc_html_e( 'Change whether your visitors can subscribe to your posts or comments or both.', 'jetpack' ); ?></p>
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>
404 344
405 - <?php
345 + <?php
406 346 }
407 347
408 348 /**
409 - * Post Subscriptions Toggle.
349 + * Post Subscriptions Toggle
350 + *
410 351 */
411 - public function subscription_post_subscribe_setting() {
352 + function subscription_post_subscribe_setting() {
412 353
413 - $stb_enabled = get_option( 'stb_enabled', 1 );
414 - ?>
354 + $stb_enabled = get_option( 'stb_enabled', 1 ); ?>
415 355
416 356 <p class="description">
417 357 <input type="checkbox" name="stb_enabled" id="jetpack-post-subscribe" value="1" <?php checked( $stb_enabled, 1 ); ?> />
418 - <?php
419 - echo wp_kses(
420 - __(
421 - "Show a <em>'follow blog'</em> option in the comment form",
422 - 'jetpack'
423 - ),
424 - array( 'em' => array() )
425 - );
426 - ?>
358 + <?php _e( "Show a <em>'follow blog'</em> option in the comment form", 'jetpack' ); ?>
427 359 </p>
428 - <?php
360 + <?php
429 361 }
430 362
431 363 /**
432 - * Comments Subscriptions Toggle.
364 + * Comments Subscriptions Toggle
365 + *
433 366 */
434 - public function subscription_comment_subscribe_setting() {
367 + function subscription_comment_subscribe_setting() {
435 368
436 - $stc_enabled = get_option( 'stc_enabled', 1 );
437 - ?>
369 + $stc_enabled = get_option( 'stc_enabled', 1 ); ?>
438 370
439 371 <p class="description">
440 372 <input type="checkbox" name="stc_enabled" id="jetpack-comment-subscribe" value="1" <?php checked( $stc_enabled, 1 ); ?> />
441 - <?php
442 - echo wp_kses(
443 - __(
444 - "Show a <em>'follow comments'</em> option in the comment form",
445 - 'jetpack'
446 - ),
447 - array( 'em' => array() )
448 - );
449 - ?>
373 + <?php _e( "Show a <em>'follow comments'</em> option in the comment form", 'jetpack' ); ?>
450 374 </p>
451 375
452 - <?php
376 + <?php
453 377 }
454 378
455 - /**
456 - * Someone subscribes to my blog section
457 - *
458 - * @since 8.1
459 - */
460 - public function social_notifications_subscribe_section() {
461 - // Atypical usage here. We emit jquery to move subscribe notification checkbox to be with the rest of the email notification settings.
462 - ?>
463 - <script type="text/javascript">
464 - jQuery( function( $ ) {
465 - var table = $( '#social_notifications_subscribe' ).parents( 'table:first' ),
466 - header = table.prevAll( 'h2:first' ),
467 - newParent = $( '#moderation_notify' ).parent( 'label' ).parent();
379 + function validate_settings( $settings ) {
380 + global $allowedposttags;
468 381
469 - if ( ! table.length || ! header.length || ! newParent.length ) {
470 - return;
471 - }
382 + $default = $this->get_default_settings();
472 383
473 - newParent.append( '<br/>' ).append( table.end().parent( 'label' ).siblings().andSelf() );
474 - header.remove();
475 - table.remove();
476 - } );
477 - </script>
478 - <?php
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;
479 395 }
480 396
481 - /**
482 - * Someone subscribes to my blog Toggle
483 - *
484 - * @since 8.1
485 - */
486 - public function social_notifications_subscribe_field() {
487 - $checked = (int) ( 'on' === get_option( 'social_notifications_subscribe', 'on' ) );
488 - ?>
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 + }
489 402
490 - <label>
491 - <input type="checkbox" name="social_notifications_subscribe" id="social_notifications_subscribe" value="1" <?php checked( $checked ); ?> />
492 - <?php
493 - /* translators: this is a label for a setting that starts with "Email me whenever" */
494 - esc_html_e( 'Someone subscribes to my blog', 'jetpack' );
495 - ?>
496 - </label>
497 - <?php
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>';
498 407 }
499 408
500 - /**
501 - * Validate "Someone subscribes to my blog" option
502 - *
503 - * @since 8.1
504 - *
505 - * @param String $input the input string to be validated.
506 - * @return string on|off
507 - */
508 - public function social_notifications_subscribe_validate( $input ) {
509 - // If it's not set (was unchecked during form submission) or was set to off (during option update), return 'off'.
510 - if ( ! $input || 'off' === $input ) {
511 - return 'off';
512 - }
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 + }
513 414
514 - // Otherwise we return 'on'.
515 - return 'on';
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 + );
516 420 }
517 421
422 + function get_settings() {
423 + return wp_parse_args( (array) get_option( 'subscription_options', array() ), $this->get_default_settings() );
424 + }
425 +
518 426 /**
519 427 * Jetpack_Subscriptions::subscribe()
520 428 *
521 429 * Send a synchronous XML-RPC subscribe to blog posts or subscribe to post comments request.
522 430 *
523 - * @param string $email being subscribed.
524 - * @param array $post_ids (optional) defaults to 0 for blog posts only: array of post IDs to subscribe to blog's posts.
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
525 433 * @param bool $async (optional) Should the subscription be performed asynchronously? Defaults to true.
526 - * @param array $extra_data Additional data passed to the `jetpack.subscribeToSite` call.
527 434 *
528 - * @return true|WP_Error true on success
529 - * invalid_email : not a valid email address
530 - * invalid_post_id : not a valid post ID
531 - * unknown_post_id : unknown post
532 - * not_subscribed : strange error. Jetpack servers at WordPress.com could subscribe the email.
533 - * disabled : Site owner has disabled subscriptions.
534 - * active : Already subscribed.
535 - * pending : Tried to subscribe before but the confirmation link is never clicked. No confirmation email is sent.
536 - * unknown : strange error. Jetpack servers at WordPress.com returned something malformed.
537 - * unknown_status : strange error. Jetpack servers at WordPress.com returned something I didn't understand.
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.
538 444 */
539 - public function subscribe( $email, $post_ids = 0, $async = true, $extra_data = array() ) {
540 - if ( ! is_email( $email ) ) {
541 - return new WP_Error( 'invalid_email' );
445 + function subscribe( $email, $post_ids = 0, $async = true, $extra_data = array() ) {
446 + if ( !is_email( $email ) ) {
447 + return new Jetpack_Error( 'invalid_email' );
542 448 }
543 449
544 - if ( ! $async ) {
450 + if ( !$async ) {
451 + Jetpack::load_xml_rpc_client();
545 452 $xml = new Jetpack_IXR_ClientMulticall();
546 453 }
547 454
548 455 foreach ( (array) $post_ids as $post_id ) {
@@ -547,17 +454,17 @@
547 454
548 455 foreach ( (array) $post_ids as $post_id ) {
549 456 $post_id = (int) $post_id;
550 457 if ( $post_id < 0 ) {
551 - return new WP_Error( 'invalid_post_id' );
552 - } elseif ( $post_id && ! get_post( $post_id ) ) {
553 - return new WP_Error( 'unknown_post_id' );
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' );
554 461 }
555 462
556 463 if ( $async ) {
557 - XMLRPC_Async_Call::add_call( 'jetpack.subscribeToSite', 0, $email, $post_id, serialize( $extra_data ) ); //phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
464 + Jetpack::xmlrpc_async_call( 'jetpack.subscribeToSite', $email, $post_id, serialize( $extra_data ) );
558 465 } else {
559 - $xml->addCall( 'jetpack.subscribeToSite', $email, $post_id, serialize( $extra_data ) ); //phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
466 + $xml->addCall( 'jetpack.subscribeToSite', $email, $post_id, serialize( $extra_data ) );
560 467 }
561 468 }
562 469
563 470 if ( $async ) {
@@ -563,9 +470,9 @@
563 470 if ( $async ) {
564 471 return;
565 472 }
566 473
567 - // Call.
474 + // Call
568 475 $xml->query();
569 476
570 477 if ( $xml->isError() ) {
571 478 return $xml->get_jetpack_error();
@@ -579,32 +486,29 @@
579 486 $r[] = $xml->get_jetpack_error( $response['faultCode'], $response['faultString'] );
580 487 continue;
581 488 }
582 489
583 - if ( ! is_array( $response[0] ) || empty( $response[0]['status'] ) ) {
584 - $r[] = new WP_Error( 'unknown' );
490 + if ( !is_array( $response[0] ) || empty( $response[0]['status'] ) ) {
491 + $r[] = new Jetpack_Error( 'unknown' );
585 492 continue;
586 493 }
587 494
588 495 switch ( $response[0]['status'] ) {
589 - case 'error':
590 - $r[] = new WP_Error( 'not_subscribed' );
591 - continue 2;
592 - case 'disabled':
593 - $r[] = new WP_Error( 'disabled' );
594 - continue 2;
595 - case 'active':
596 - $r[] = new WP_Error( 'active' );
597 - continue 2;
598 - case 'confirming':
599 - $r[] = true;
600 - continue 2;
601 - case 'pending':
602 - $r[] = new WP_Error( 'pending' );
603 - continue 2;
604 - default:
605 - $r[] = new WP_Error( 'unknown_status', (string) $response[0]['status'] );
606 - continue 2;
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;
607 511 }
608 512 }
609 513
610 514 return $r;
@@ -610,40 +514,48 @@
610 514 return $r;
611 515 }
612 516
613 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 + /**
614 527 * Jetpack_Subscriptions::widget_submit()
615 528 *
616 529 * When a user submits their email via the blog subscription widget, check the details and call the subsribe() method.
617 530 */
618 - public function widget_submit() {
531 + function widget_submit() {
619 532 // Check the nonce.
620 - if ( ! wp_verify_nonce( isset( $_REQUEST['_wpnonce'] ) ? sanitize_key( $_REQUEST['_wpnonce'] ) : '', 'blogsub_subscribe_' . \Jetpack_Options::get_option( 'id' ) ) ) {
621 - return false;
533 + if ( is_user_logged_in() ) {
534 + check_admin_referer( 'blogsub_subscribe_' . get_current_blog_id() );
622 535 }
623 536
624 - if ( empty( $_REQUEST['email'] ) || ! is_string( $_REQUEST['email'] ) ) {
537 + if ( empty( $_REQUEST['email'] ) )
625 538 return false;
626 - }
627 539
628 540 $redirect_fragment = false;
629 541 if ( isset( $_REQUEST['redirect_fragment'] ) ) {
630 - $redirect_fragment = preg_replace( '/[^a-z0-9_-]/i', '', $_REQUEST['redirect_fragment'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- This is manually unslashing and sanitizing.
542 + $redirect_fragment = preg_replace( '/[^a-z0-9_-]/i', '', $_REQUEST['redirect_fragment'] );
631 543 }
632 - if ( ! $redirect_fragment || ! is_string( $redirect_fragment ) ) {
544 + if ( !$redirect_fragment ) {
633 545 $redirect_fragment = 'subscribe-blog';
634 546 }
635 547
636 - $subscribe = self::subscribe(
637 - isset( $_REQUEST['email'] ) ? wp_unslash( $_REQUEST['email'] ) : null, // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Validated inside self::subscribe().
638 - 0,
639 - false,
640 - array(
641 - 'source' => 'widget',
642 - 'widget-in-use' => is_active_widget( false, false, 'blog_subscription', true ) ? 'yes' : 'no',
643 - 'comment_status' => '',
644 - 'server_data' => jetpack_subscriptions_cherry_pick_server_data(),
645 - )
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 + )
646 558 );
647 559
648 560 if ( is_wp_error( $subscribe ) ) {
649 561 $error = $subscribe->get_error_code();
@@ -667,16 +579,11 @@
667 579 case 'blocked_email':
668 580 $result = 'opted_out';
669 581 break;
670 582 case 'active':
583 + case 'pending':
671 584 $result = 'already';
672 585 break;
673 - case 'flooded_email':
674 - $result = 'many_pending_subs';
675 - break;
676 - case 'pending':
677 - $result = 'pending';
678 - break;
679 586 default:
680 587 $result = 'error';
681 588 break;
682 589 }
@@ -701,19 +608,12 @@
701 608 /**
702 609 * Jetpack_Subscriptions::comment_subscribe_init()
703 610 *
704 611 * Set up and add the comment subscription checkbox to the comment form.
705 - *
706 - * @param string $submit_button HTML markup for the submit field.
707 612 */
708 - public function comment_subscribe_init( $submit_button ) {
613 + function comment_subscribe_init() {
709 614 global $post;
710 615
711 - // Subscriptions are only available for posts so far.
712 - if ( ! $post || 'post' !== $post->post_type ) {
713 - return $submit_button;
714 - }
715 -
716 616 $comments_checked = '';
717 617 $blog_checked = '';
718 618
719 619 // Check for a comment / blog submission and set a cookie to retain the setting and check the boxes.
@@ -724,20 +624,20 @@
724 624 if ( isset( $_COOKIE[ 'jetpack_blog_subscribe_' . self::$hash ] ) ) {
725 625 $blog_checked = ' checked="checked"';
726 626 }
727 627
728 - // Some themes call this function, don't show the checkbox again.
628 + // Some themes call this function, don't show the checkbox again
729 629 remove_action( 'comment_form', 'subscription_comment_form' );
730 630
731 - // Check if Mark Jaquith's Subscribe to Comments plugin is active - if so, suppress Jetpack checkbox.
631 + // Check if Mark Jaquith's Subscribe to Comments plugin is active - if so, suppress Jetpack checkbox
732 632
733 633 $str = '';
734 634
735 - if ( false === has_filter( 'comment_form', 'show_subscription_checkbox' ) && 1 === (int) get_option( 'stc_enabled', 1 ) && empty( $post->post_password ) && 'post' === get_post_type() ) {
736 - // Subscribe to comments checkbox.
737 - $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 . ' /> ';
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 . ' /> ';
738 638 $comment_sub_text = __( 'Notify me of follow-up comments by email.', 'jetpack' );
739 - $str .= '<label class="subscribe-label" id="subscribe-label" for="subscribe_comments">' . esc_html(
639 + $str .= '<label class="subscribe-label" id="subscribe-label" for="subscribe_comments">' . esc_html(
740 640 /**
741 641 * Filter the Subscribe to comments text appearing below the comment form.
742 642 *
743 643 * @module subscriptions
@@ -750,13 +650,13 @@
750 650 ) . '</label>';
751 651 $str .= '</p>';
752 652 }
753 653
754 - if ( 1 === (int) get_option( 'stb_enabled', 1 ) ) {
755 - // Subscribe to blog checkbox.
756 - $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 . ' /> ';
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 . ' /> ';
757 657 $blog_sub_text = __( 'Notify me of new posts by email.', 'jetpack' );
758 - $str .= '<label class="subscribe-label" id="subscribe-blog-label" for="subscribe_blog">' . esc_html(
658 + $str .= '<label class="subscribe-label" id="subscribe-blog-label" for="subscribe_blog">' . esc_html(
759 659 /**
760 660 * Filter the Subscribe to blog text appearing below the comment form.
761 661 *
762 662 * @module subscriptions
@@ -778,11 +678,9 @@
778 678 * @since 1.2.0
779 679 *
780 680 * @param string $str Comment Subscription form HTML output.
781 681 */
782 - $str = apply_filters( 'jetpack_comment_subscription_form', $str );
783 -
784 - return $str . $submit_button;
682 + echo apply_filters( 'jetpack_comment_subscription_form', $str );
785 683 }
786 684
787 685 /**
788 686 * Jetpack_Subscriptions::comment_subscribe_init()
@@ -787,49 +685,40 @@
787 685 /**
788 686 * Jetpack_Subscriptions::comment_subscribe_init()
789 687 *
790 688 * When a user checks the comment subscribe box and submits a comment, subscribe them to the comment thread.
791 - *
792 - * @param int|string $comment_id Comment thread being subscribed to.
793 - * @param string $approved Comment status.
794 689 */
795 - public function comment_subscribe_submit( $comment_id, $approved ) {
690 + function comment_subscribe_submit( $comment_id, $approved ) {
796 691 if ( 'spam' === $approved ) {
797 692 return;
798 693 }
799 694
800 695 $comment = get_comment( $comment_id );
801 - if ( ! $comment ) {
802 - return;
803 - }
804 696
805 - // Set cookies for this post/comment.
806 - $this->set_cookies( isset( $_REQUEST['subscribe_comments'] ), $comment->comment_post_ID, isset( $_REQUEST['subscribe_blog'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
697 + // Set cookies for this post/comment
698 + $this->set_cookies( isset( $_REQUEST['subscribe_comments'] ), $comment->comment_post_ID, isset( $_REQUEST['subscribe_blog'] ) );
807 699
808 - if ( ! isset( $_REQUEST['subscribe_comments'] ) && ! isset( $_REQUEST['subscribe_blog'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
700 + if ( !isset( $_REQUEST['subscribe_comments'] ) && !isset( $_REQUEST['subscribe_blog'] ) )
809 701 return;
810 - }
811 702
812 703 $post_ids = array();
813 704
814 - if ( isset( $_REQUEST['subscribe_comments'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
705 + if ( isset( $_REQUEST['subscribe_comments'] ) )
815 706 $post_ids[] = $comment->comment_post_ID;
816 - }
817 707
818 - if ( isset( $_REQUEST['subscribe_blog'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
708 + if ( isset( $_REQUEST['subscribe_blog'] ) )
819 709 $post_ids[] = 0;
820 - }
821 710
822 - $result = self::subscribe(
823 - $comment->comment_author_email,
824 - $post_ids,
825 - true,
826 - array(
827 - 'source' => 'comment-form',
828 - 'widget-in-use' => is_active_widget( false, false, 'blog_subscription', true ) ? 'yes' : 'no',
829 - 'comment_status' => $approved,
830 - 'server_data' => jetpack_subscriptions_cherry_pick_server_data(),
831 - )
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 + )
832 721 );
833 722
834 723 /**
835 724 * Fires on each comment subscription form submission.
@@ -838,9 +727,9 @@
838 727 *
839 728 * @since 5.5.0
840 729 *
841 730 * @param NULL|WP_Error $result Result of form submission: NULL on success, WP_Error otherwise.
842 - * @param array $post_ids An array of post IDs that the user subscribed to, 0 means blog subscription.
731 + * @param Array $post_ids An array of post IDs that the user subscribed to, 0 means blog subscription.
843 732 */
844 733 do_action( 'jetpack_subscriptions_comment_form_submission', $result, $post_ids );
845 734 }
846 735
@@ -849,16 +738,16 @@
849 738 *
850 739 * Set a cookie to save state on the comment and post subscription checkboxes.
851 740 *
852 741 * @param bool $subscribe_to_post Whether the user chose to subscribe to subsequent comments on this post.
853 - * @param int $post_id If $subscribe_to_post is true, the post ID they've subscribed to.
742 + * @param int $post_id If $subscribe_to_post is true, the post ID they've subscribed to.
854 743 * @param bool $subscribe_to_blog Whether the user chose to subscribe to all new posts on the blog.
855 744 */
856 - public function set_cookies( $subscribe_to_post = false, $post_id = null, $subscribe_to_blog = false ) {
857 - $post_id = (int) $post_id;
745 + function set_cookies( $subscribe_to_post = false, $post_id = null, $subscribe_to_blog = false ) {
746 + $post_id = intval( $post_id );
858 747
859 748 /** This filter is already documented in core/wp-includes/comment-functions.php */
860 - $cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
749 + $cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
861 750
862 751 /**
863 752 * Filter the Jetpack Comment cookie path.
864 753 *
@@ -867,9 +756,9 @@
867 756 * @since 2.5.0
868 757 *
869 758 * @param string COOKIEPATH Cookie path.
870 759 */
871 - $cookie_path = apply_filters( 'jetpack_comment_cookie_path', COOKIEPATH );
760 + $cookie_path = apply_filters( 'jetpack_comment_cookie_path', COOKIEPATH );
872 761
873 762 /**
874 763 * Filter the Jetpack Comment cookie domain.
875 764 *
@@ -878,173 +767,358 @@
878 767 * @since 2.5.0
879 768 *
880 769 * @param string COOKIE_DOMAIN Cookie domain.
881 770 */
882 - $cookie_domain = apply_filters( 'jetpack_comment_cookie_domain', COOKIE_DOMAIN );
771 + $cookie_domain = apply_filters( 'jetpack_comment_cookie_domain', COOKIE_DOMAIN );
883 772
884 773 if ( $subscribe_to_post && $post_id >= 0 ) {
885 - setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain, is_ssl(), true );
774 + setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain );
886 775 } else {
887 - setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, '', time() - 3600, $cookie_path, $cookie_domain, is_ssl(), true );
776 + setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, '', time() - 3600, $cookie_path, $cookie_domain );
888 777 }
889 778
890 779 if ( $subscribe_to_blog ) {
891 - setcookie( 'jetpack_blog_subscribe_' . self::$hash, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain, is_ssl(), true );
780 + setcookie( 'jetpack_blog_subscribe_' . self::$hash, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain );
892 781 } else {
893 - setcookie( 'jetpack_blog_subscribe_' . self::$hash, '', time() - 3600, $cookie_path, $cookie_domain, is_ssl(), true );
782 + setcookie( 'jetpack_blog_subscribe_' . self::$hash, '', time() - 3600, $cookie_path, $cookie_domain );
894 783 }
895 784 }
896 785
897 - /**
898 - * Set the social_notifications_subscribe option to `off` when the Subscriptions module is activated in the first time.
899 - *
900 - * @since 8.1
901 - *
902 - * @return void
903 - */
904 - public function set_social_notifications_subscribe() {
905 - if ( false === get_option( 'social_notifications_subscribe' ) ) {
906 - add_option( 'social_notifications_subscribe', 'off' );
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' ) );
907 812 }
908 813 }
909 814
910 815 /**
911 - * Save a flag when a post was ever published.
816 + * Enqueue the form's CSS.
912 817 *
913 - * It saves the post meta when the post was published and becomes a draft.
914 - * Then this meta is used to hide subscription messaging in Publish panel.
915 - *
916 - * @param string $new_status Tthe "new" post status of the transition when saved.
917 - * @param string $old_status The "old" post status of the transition when saved.
918 - * @param object $post obj The post object.
818 + * @since 4.5.0
919 819 */
920 - public function maybe_set_first_published_status( $new_status, $old_status, $post ) {
921 - $was_post_ever_published = get_post_meta( $post->ID, '_jetpack_post_was_ever_published', true );
922 - if ( ! $was_post_ever_published && 'publish' === $old_status && 'draft' === $new_status ) {
923 - update_post_meta( $post->ID, '_jetpack_post_was_ever_published', true );
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 + }
924 839 }
925 - }
926 840
927 - /**
928 - * Checks if the current user can publish posts.
929 - *
930 - * @return bool
931 - */
932 - public function first_published_status_meta_auth_callback() {
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
933 856 /**
934 - * Filter the capability to view if a post was ever published in the Subscription Module.
857 + * Filter the subscription form's ID prefix.
935 858 *
936 859 * @module subscriptions
937 860 *
938 - * @since 13.4
861 + * @since 2.7.0
939 862 *
940 - * @param string $capability User capability needed to view if a post was ever published. Default to publish_posts.
863 + * @param string subscribe-field Subscription form field prefix.
864 + * @param int $widget_id Widget ID.
941 865 */
942 - $capability = apply_filters( 'jetpack_subscriptions_post_was_ever_published_capability', 'publish_posts' );
943 - if ( current_user_can( $capability ) ) {
944 - return true;
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";
945 874 }
946 - return false;
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'];
947 982 }
948 983
949 - /**
950 - * Registers the 'post_was_ever_published' post meta for use in the REST API.
951 - */
952 - public function register_post_meta() {
953 - $jetpack_post_was_ever_published = array(
954 - 'type' => 'boolean',
955 - 'description' => __( 'Whether the post was ever published.', 'jetpack' ),
956 - 'single' => true,
957 - 'default' => false,
958 - 'show_in_rest' => array(
959 - 'name' => 'jetpack_post_was_ever_published',
960 - ),
961 - 'auth_callback' => array( $this, 'first_published_status_meta_auth_callback' ),
962 - );
984 + function increment_subscriber_count( $current_subs_array = array() ) {
985 + $current_subs_array['value']++;
963 986
964 - register_meta( 'post', '_jetpack_post_was_ever_published', $jetpack_post_was_ever_published );
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;
965 990 }
966 991
967 - /**
968 - * Create a Subscribers menu displayed on self-hosted sites.
969 - *
970 - * - It is not displayed on WordPress.com sites.
971 - * - It directs you to Calypso to the existing Subscribers page.
972 - *
973 - * @return void
974 - */
975 - public function add_subscribers_menu() {
976 - /*
977 - * Do not display any menu on WoA and WordPress.com Simple sites (unless Classic wp-admin is enabled).
978 - * They already get a menu item under Users via nav-unification.
979 - */
980 - if ( ( new Host() )->is_wpcom_platform() && get_option( 'wpcom_admin_interface' ) !== 'wp-admin' ) {
981 - return;
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
982 1017 }
983 1018
984 - $status = new Status();
1019 + return $subs_count;
1020 + }
985 1021
986 - /*
987 - * Do not display if we're in Offline mode,
988 - * or if the user is not connected.
989 - */
990 - if (
991 - $status->is_offline_mode()
992 - || ! ( new Connection_Manager( 'jetpack' ) )->is_user_connected()
993 - ) {
994 - return;
995 - }
1022 + function update( $new_instance, $old_instance ) {
1023 + $instance = $old_instance;
996 1024
997 - $blog_id = Connection_Manager::get_site_id( true );
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'];
998 1031
999 - $link = Redirect::get_url(
1000 - 'jetpack-menu-jetpack-manage-subscribers',
1001 - array( 'site' => $blog_id ? $blog_id : $status->get_site_suffix() )
1002 - );
1032 + return $instance;
1033 + }
1003 1034
1004 - Admin_Menu::add_menu(
1005 - __( 'Subscribers', 'jetpack' ),
1006 - __( 'Subscribers', 'jetpack' ) . ' <span class="dashicons dashicons-external"></span>',
1007 - 'manage_options',
1008 - esc_url( $link ),
1009 - null,
1010 - 11
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,
1011 1043 );
1012 1044 }
1013 1045
1014 - /**
1015 - * Record tracks event if categories is created when user enters
1016 - * the edit category page through the newsletter settings page.
1017 - *
1018 - * @return void
1019 - */
1020 - public function track_newsletter_category_creation() {
1046 + function form( $instance ) {
1047 + $instance = wp_parse_args( (array) $instance, $this->defaults() );
1021 1048
1022 - // phpcs:disable WordPress.Security.NonceVerification.Missing
1023 - if ( empty( $_POST['_wp_http_referer'] ) ) {
1024 - return;
1025 - }
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 );
1026 1055
1027 - if ( strpos( sanitize_url( wp_unslash( $_POST['_wp_http_referer'] ) ), 'referer=newsletter-categories' ) > -1 ) {
1056 + $subs_fetch = $this->fetch_subscriber_count();
1028 1057
1029 - $parent = filter_var( empty( $_POST['parent'] ) ? 0 : wp_unslash( $_POST['parent'] ), FILTER_SANITIZE_NUMBER_INT );
1030 -
1031 - $is_child_category = $parent > 0;
1032 -
1033 - $tracking = new Automattic\Jetpack\Tracking();
1034 - $tracking->tracks_record_event(
1035 - wp_get_current_user(),
1036 - 'jetpack_newsletter_add_category',
1037 - array(
1038 - 'is_child_category' => $is_child_category,
1039 - )
1040 - );
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'] ) );
1041 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
1042 1100 }
1043 1101 }
1044 1102
1045 -Jetpack_Subscriptions::init();
1103 +add_shortcode( 'jetpack_subscription_form', 'jetpack_do_subscription_form' );
1104 +add_shortcode( 'blog_subscription_form', 'jetpack_do_subscription_form' );
1046 1105
1047 -require __DIR__ . '/subscriptions/views.php';
1048 -require __DIR__ . '/subscriptions/subscribe-modal/class-jetpack-subscribe-modal.php';
1049 -require __DIR__ . '/subscriptions/subscribe-overlay/class-jetpack-subscribe-overlay.php';
1050 -require __DIR__ . '/subscriptions/subscribe-floating-button/class-jetpack-subscribe-floating-button.php';
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 +}