PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.0
Jetpack – WP Security, Backup, Speed, & Growth v16.0
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 +213 -172 12.5.216.0 View file →
@@ -1,29 +1,38 @@
1 1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName)
2 2 /**
3 3 * Module Name: Newsletter
4 - * Module Description: Let visitors subscribe to new posts and comments via email
4 + * Module Description: Grow your subscriber list and deliver your content directly to their email inbox.
5 5 * Sort Order: 9
6 6 * Recommendation Order: 8
7 7 * First Introduced: 1.2
8 8 * Requires Connection: Yes
9 9 * Requires User Connection: Yes
10 - * Auto Activate: No
10 + * Auto Activate: Yes
11 11 * Module Tags: Social
12 12 * Feature: Engagement
13 - * Additional Search Queries: subscriptions, subscription, email, follow, followers, subscribers, signup, newsletter
13 + * Additional Search Queries: subscriptions, subscription, email, follow, followers, subscribers, signup, newsletter, creator
14 14 */
15 15
16 16 // phpcs:disable Universal.Files.SeparateFunctionsFromOO.Mixed -- TODO: Move classes to appropriately-named class files.
17 17
18 +use Automattic\Jetpack\Admin_UI\Admin_Menu;
18 19 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
19 20 use Automattic\Jetpack\Connection\XMLRPC_Async_Call;
21 +use Automattic\Jetpack\Newsletter\Settings as Newsletter_Settings;
20 22 use Automattic\Jetpack\Redirect;
21 23 use Automattic\Jetpack\Status;
22 24 use Automattic\Jetpack\Status\Host;
23 25
26 +if ( ! defined( 'ABSPATH' ) ) {
27 + exit( 0 );
28 +}
29 +
24 30 add_action( 'jetpack_modules_loaded', 'jetpack_subscriptions_load' );
25 31
32 +// Loads the User Content Link Redirection feature.
33 +require_once __DIR__ . '/subscriptions/jetpack-user-content-link-redirection.php';
34 +
26 35 /**
27 36 * Loads the Subscriptions module.
28 37 */
29 38 function jetpack_subscriptions_load() {
@@ -40,13 +49,13 @@
40 49 function jetpack_subscriptions_cherry_pick_server_data() {
41 50 $data = array();
42 51
43 52 foreach ( $_SERVER as $key => $value ) {
44 - if ( ! is_string( $value ) || 0 === strpos( $key, 'HTTP_COOKIE' ) ) {
53 + if ( ! is_string( $value ) || str_starts_with( $key, 'HTTP_COOKIE' ) ) {
45 54 continue;
46 55 }
47 56
48 - if ( 0 === strpos( $key, 'HTTP_' ) || in_array( $key, array( 'REMOTE_ADDR', 'REQUEST_URI', 'DOCUMENT_URI' ), true ) ) {
57 + if ( str_starts_with( $key, 'HTTP_' ) || in_array( $key, array( 'REMOTE_ADDR', 'REQUEST_URI', 'DOCUMENT_URI' ), true ) ) {
49 58 $data[ $key ] = $value;
50 59 }
51 60 }
52 61
@@ -54,8 +63,10 @@
54 63 }
55 64
56 65 /**
57 66 * Main class file for the Subscriptions module.
67 + *
68 + * @phan-constructor-used-for-side-effects
58 69 */
59 70 class Jetpack_Subscriptions {
60 71 /**
61 72 * Whether Jetpack has been instantiated or not.
@@ -124,8 +135,10 @@
124 135 add_filter( 'post_updated_messages', array( $this, 'update_published_message' ), 18, 1 );
125 136
126 137 // Set "social_notifications_subscribe" option during the first-time activation.
127 138 add_action( 'jetpack_activate_module_subscriptions', array( $this, 'set_social_notifications_subscribe' ) );
139 + add_action( 'jetpack_activate_module_subscriptions', array( $this, 'set_featured_image_in_email_default' ) );
140 + add_action( 'jetpack_activate_module_subscriptions', array( $this, 'set_newsletter_send_default' ) );
128 141
129 142 // Hide subscription messaging in Publish panel for posts that were published in the past
130 143 add_action( 'init', array( $this, 'register_post_meta' ), 20 );
131 144 add_action( 'transition_post_status', array( $this, 'maybe_set_first_published_status' ), 10, 3 );
@@ -131,8 +144,22 @@
131 144 add_action( 'transition_post_status', array( $this, 'maybe_set_first_published_status' ), 10, 3 );
132 145
133 146 // Add Subscribers menu to Jetpack navigation.
134 147 add_action( 'jetpack_admin_menu', array( $this, 'add_subscribers_menu' ) );
148 +
149 + // Customize the configuration URL to lead to the Subscriptions settings.
150 + add_filter(
151 + 'jetpack_module_configuration_url_subscriptions',
152 + function () {
153 + return Jetpack::admin_url( array( 'page' => 'jetpack#/newsletter' ) );
154 + }
155 + );
156 +
157 + // Track categories created through the category editor page
158 + add_action( 'wp_ajax_add-tag', array( $this, 'track_newsletter_category_creation' ), 1 );
159 +
160 + $newsletter_settings = new Newsletter_Settings();
161 + $newsletter_settings::init();
135 162 }
136 163
137 164 /**
138 165 * Jetpack_Subscriptions::xmlrpc_methods()
@@ -354,32 +381,14 @@
354 381 'discussion',
355 382 'stc_enabled'
356 383 );
357 384
358 - /** Enable Subscribe Modal */
359 -
360 - /** This filter is documented in plugins/jetpack/modules/subscriptions/subscribe-module/class-jetpack-subscribe-module.php */
361 - if ( apply_filters( 'jetpack_subscriptions_modal_enabled', false ) ) {
362 - add_settings_field(
363 - 'jetpack_subscriptions_comment_subscribe',
364 - __( 'Enable Subscribe Modal', 'jetpack' ),
365 - array( $this, 'subscribe_modal_setting' ),
366 - 'discussion',
367 - 'jetpack_subscriptions'
368 - );
369 -
370 - register_setting(
371 - 'discussion',
372 - 'sm_enabled'
373 - );
374 - }
375 -
376 - /** Email me whenever: Someone follows my blog */
385 + /** Email me whenever: Someone subscribes to my blog */
377 386 /* @since 8.1 */
378 387
379 388 add_settings_section(
380 389 'notifications_section',
381 - __( 'Someone follows my blog', 'jetpack' ),
390 + __( 'Someone subscribes to my blog', 'jetpack' ),
382 391 array( $this, 'social_notifications_subscribe_section' ),
383 392 'discussion'
384 393 );
385 394
@@ -395,39 +404,8 @@
395 404 'discussion',
396 405 'social_notifications_subscribe',
397 406 array( $this, 'social_notifications_subscribe_validate' )
398 407 );
399 -
400 - /** Subscription Messaging Options */
401 -
402 - register_setting(
403 - 'reading',
404 - 'subscription_options',
405 - array( $this, 'validate_settings' )
406 - );
407 -
408 - add_settings_section(
409 - 'email_settings',
410 - __( 'Follower Settings', 'jetpack' ),
411 - array( $this, 'reading_section' ),
412 - 'reading'
413 - );
414 -
415 - add_settings_field(
416 - 'invitation',
417 - __( 'Blog follow email text', 'jetpack' ),
418 - array( $this, 'setting_invitation' ),
419 - 'reading',
420 - 'email_settings'
421 - );
422 -
423 - add_settings_field(
424 - 'comment-follow',
425 - __( 'Comment follow email text', 'jetpack' ),
426 - array( $this, 'setting_comment_follow' ),
427 - 'reading',
428 - 'email_settings'
429 - );
430 408 }
431 409
432 410 /**
433 411 * Discussions setting section blurb.
@@ -486,25 +464,9 @@
486 464 <?php
487 465 }
488 466
489 467 /**
490 - * Subscribe Modal Toggle.
491 - */
492 - public function subscribe_modal_setting() {
493 -
494 - $sm_enabled = get_option( 'sm_enabled', 1 );
495 - ?>
496 -
497 - <p class="description">
498 - <input type="checkbox" name="sm_enabled" id="jetpack-subscribe-modal" value="1" <?php checked( $sm_enabled, 1 ); ?> />
499 - <?php esc_html_e( 'Show a popup subscribe modal to readers.', 'jetpack' ); ?>
500 - </p>
501 -
502 - <?php
503 - }
504 -
505 - /**
506 - * Someone follows my blog section
468 + * Someone subscribes to my blog section
507 469 *
508 470 * @since 8.1
509 471 */
510 472 public function social_notifications_subscribe_section() {
@@ -528,9 +490,9 @@
528 490 <?php
529 491 }
530 492
531 493 /**
532 - * Someone follows my blog Toggle
494 + * Someone subscribes to my blog Toggle
533 495 *
534 496 * @since 8.1
535 497 */
536 498 public function social_notifications_subscribe_field() {
@@ -540,9 +502,9 @@
540 502 <label>
541 503 <input type="checkbox" name="social_notifications_subscribe" id="social_notifications_subscribe" value="1" <?php checked( $checked ); ?> />
542 504 <?php
543 505 /* translators: this is a label for a setting that starts with "Email me whenever" */
544 - esc_html_e( 'Someone follows my blog', 'jetpack' );
506 + esc_html_e( 'Someone subscribes to my blog', 'jetpack' );
545 507 ?>
546 508 </label>
547 509 <?php
548 510 }
@@ -547,9 +509,9 @@
547 509 <?php
548 510 }
549 511
550 512 /**
551 - * Validate "Someone follows my blog" option
513 + * Validate "Someone subscribes to my blog" option
552 514 *
553 515 * @since 8.1
554 516 *
555 517 * @param String $input the input string to be validated.
@@ -565,81 +527,8 @@
565 527 return 'on';
566 528 }
567 529
568 530 /**
569 - * Validate settings for the Subscriptions module.
570 - *
571 - * @param array $settings Settings to be validated.
572 - */
573 - public function validate_settings( $settings ) {
574 - global $allowedposttags;
575 -
576 - $default = $this->get_default_settings();
577 -
578 - // Blog Follow.
579 - $settings['invitation'] = trim( wp_kses( $settings['invitation'], $allowedposttags ) );
580 - if ( empty( $settings['invitation'] ) ) {
581 - $settings['invitation'] = $default['invitation'];
582 - }
583 -
584 - // Comments Follow (single post).
585 - $settings['comment_follow'] = trim( wp_kses( $settings['comment_follow'], $allowedposttags ) );
586 - if ( empty( $settings['comment_follow'] ) ) {
587 - $settings['comment_follow'] = $default['comment_follow'];
588 - }
589 -
590 - return $settings;
591 - }
592 -
593 - /**
594 - * HTML output helper for Reading section.
595 - */
596 - public function reading_section() {
597 - echo '<p id="follower-settings">';
598 - esc_html_e( 'These settings change emails sent from your blog to followers.', 'jetpack' );
599 - echo '</p>';
600 - }
601 -
602 - /**
603 - * HTML output helper for Invitation section.
604 - */
605 - public function setting_invitation() {
606 - $settings = $this->get_settings();
607 - echo '<textarea name="subscription_options[invitation]" class="large-text" cols="50" rows="5">' . esc_textarea( $settings['invitation'] ) . '</textarea>';
608 - echo '<p><span class="description">' . esc_html__( 'Introduction text sent when someone follows your blog. (Site and confirmation details will be automatically added for you.)', 'jetpack' ) . '</span></p>';
609 - }
610 -
611 - /**
612 - * HTML output helper for Comment Follow section.
613 - */
614 - public function setting_comment_follow() {
615 - $settings = $this->get_settings();
616 - echo '<textarea name="subscription_options[comment_follow]" class="large-text" cols="50" rows="5">' . esc_textarea( $settings['comment_follow'] ) . '</textarea>';
617 - echo '<p><span class="description">' . esc_html__( 'Introduction text sent when someone follows a post on your blog. (Site and confirmation details will be automatically added for you.)', 'jetpack' ) . '</span></p>';
618 - }
619 -
620 - /**
621 - * Get default settings for the Subscriptions module.
622 - */
623 - public function get_default_settings() {
624 - $site_url = get_home_url();
625 - $display_url = preg_replace( '(^https?://)', '', untrailingslashit( $site_url ) );
626 -
627 - return array(
628 - /* translators: Both %1$s and %2$s is site address */
629 - 'invitation' => sprintf( __( "Howdy,\nYou recently subscribed to <a href='%1\$s'>%2\$s</a> and we need to verify the email you provided. Once you confirm below, you'll be able to receive and read new posts.\n\nIf you believe this is an error, ignore this message and nothing more will happen.", 'jetpack' ), $site_url, $display_url ),
630 - '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' ),
631 - );
632 - }
633 -
634 - /**
635 - * Reeturn merged `subscription_options` option with module default settings.
636 - */
637 - public function get_settings() {
638 - return wp_parse_args( (array) get_option( 'subscription_options' ), $this->get_default_settings() );
639 - }
640 -
641 - /**
642 531 * Jetpack_Subscriptions::subscribe()
643 532 *
644 533 * Send a synchronous XML-RPC subscribe to blog posts or subscribe to post comments request.
645 534 *
@@ -678,8 +567,9 @@
678 567
679 568 if ( $async ) {
680 569 XMLRPC_Async_Call::add_call( 'jetpack.subscribeToSite', 0, $email, $post_id, serialize( $extra_data ) ); //phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
681 570 } else {
571 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $xml is set when $async is false
682 572 $xml->addCall( 'jetpack.subscribeToSite', $email, $post_id, serialize( $extra_data ) ); //phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
683 573 }
684 574 }
685 575
@@ -687,19 +577,24 @@
687 577 return;
688 578 }
689 579
690 580 // Call.
581 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $xml is set when $async is false, otherwise we return early
691 582 $xml->query();
692 583
584 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $xml is set when $async is false, otherwise we return early
693 585 if ( $xml->isError() ) {
586 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $xml is set when $async is false, otherwise we return early
694 587 return $xml->get_jetpack_error();
695 588 }
696 589
590 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $xml is set when $async is false
697 591 $responses = $xml->getResponse();
698 592
699 593 $r = array();
700 594 foreach ( (array) $responses as $response ) {
701 595 if ( isset( $response['faultCode'] ) || isset( $response['faultString'] ) ) {
596 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $xml is set when $async is false
702 597 $r[] = $xml->get_jetpack_error( $response['faultCode'], $response['faultString'] );
703 598 continue;
704 599 }
705 600
@@ -739,10 +634,10 @@
739 634 * When a user submits their email via the blog subscription widget, check the details and call the subsribe() method.
740 635 */
741 636 public function widget_submit() {
742 637 // Check the nonce.
743 - if ( is_user_logged_in() ) {
744 - check_admin_referer( 'blogsub_subscribe_' . get_current_blog_id() );
638 + if ( ! wp_verify_nonce( isset( $_REQUEST['_wpnonce'] ) ? sanitize_key( $_REQUEST['_wpnonce'] ) : '', 'blogsub_subscribe_' . \Jetpack_Options::get_option( 'id' ) ) ) {
639 + return false;
745 640 }
746 641
747 642 if ( empty( $_REQUEST['email'] ) || ! is_string( $_REQUEST['email'] ) ) {
748 643 return false;
@@ -817,9 +712,9 @@
817 712 */
818 713 do_action( 'jetpack_subscriptions_form_submission', $result );
819 714
820 715 wp_safe_redirect( "$redirect#$redirect_fragment" );
821 - exit;
716 + exit( 0 );
822 717 }
823 718
824 719 /**
825 720 * Jetpack_Subscriptions::comment_subscribe_init()
@@ -830,8 +725,13 @@
830 725 */
831 726 public function comment_subscribe_init( $submit_button ) {
832 727 global $post;
833 728
729 + // Subscriptions are only available for posts so far.
730 + if ( ! $post || 'post' !== $post->post_type ) {
731 + return $submit_button;
732 + }
733 +
834 734 $comments_checked = '';
835 735 $blog_checked = '';
836 736
837 737 // Check for a comment / blog submission and set a cookie to retain the setting and check the boxes.
@@ -910,8 +810,19 @@
910 810 * @param int|string $comment_id Comment thread being subscribed to.
911 811 * @param string $approved Comment status.
912 812 */
913 813 public function comment_subscribe_submit( $comment_id, $approved ) {
814 + /**
815 + * Filters whether to skip comment subscription processing.
816 + *
817 + * @since 15.5
818 + *
819 + * @param bool $skip Whether to skip comment subscription. Default false.
820 + */
821 + if ( apply_filters( 'jetpack_subscription_comment_subscribe_skip', false ) ) {
822 + return;
823 + }
824 +
914 825 if ( 'spam' === $approved ) {
915 826 return;
916 827 }
917 828
@@ -974,9 +885,9 @@
974 885 public function set_cookies( $subscribe_to_post = false, $post_id = null, $subscribe_to_blog = false ) {
975 886 $post_id = (int) $post_id;
976 887
977 888 /** This filter is already documented in core/wp-includes/comment-functions.php */
978 - $cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
889 + $cookie_lifetime = apply_filters( 'comment_cookie_lifetime', YEAR_IN_SECONDS );
979 890
980 891 /**
981 892 * Filter the Jetpack Comment cookie path.
982 893 *
@@ -999,15 +910,15 @@
999 910 */
1000 911 $cookie_domain = apply_filters( 'jetpack_comment_cookie_domain', COOKIE_DOMAIN );
1001 912
1002 913 if ( $subscribe_to_post && $post_id >= 0 ) {
1003 - setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain, is_ssl(), true );
914 + setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, '1', time() + $cookie_lifetime, $cookie_path, $cookie_domain, is_ssl(), true );
1004 915 } else {
1005 916 setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, '', time() - 3600, $cookie_path, $cookie_domain, is_ssl(), true );
1006 917 }
1007 918
1008 919 if ( $subscribe_to_blog ) {
1009 - setcookie( 'jetpack_blog_subscribe_' . self::$hash, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain, is_ssl(), true );
920 + setcookie( 'jetpack_blog_subscribe_' . self::$hash, '1', time() + $cookie_lifetime, $cookie_path, $cookie_domain, is_ssl(), true );
1010 921 } else {
1011 922 setcookie( 'jetpack_blog_subscribe_' . self::$hash, '', time() - 3600, $cookie_path, $cookie_domain, is_ssl(), true );
1012 923 }
1013 924 }
@@ -1025,8 +936,26 @@
1025 936 }
1026 937 }
1027 938
1028 939 /**
940 + * Set the featured image in email option to `1` when the Subscriptions module is activated in the first time.
941 + *
942 + * @return void
943 + */
944 + public function set_featured_image_in_email_default() {
945 + add_option( 'wpcom_featured_image_in_email', 1 );
946 + }
947 +
948 + /**
949 + * Set the email post to subscribers default option to `1` when the Subscriptions module is activated for the first time.
950 + *
951 + * @return void
952 + */
953 + public function set_newsletter_send_default() {
954 + add_option( 'wpcom_newsletter_send_default', 1 );
955 + }
956 +
957 + /**
1029 958 * Save a flag when a post was ever published.
1030 959 *
1031 960 * It saves the post meta when the post was published and becomes a draft.
1032 961 * Then this meta is used to hide subscription messaging in Publish panel.
@@ -1035,8 +964,13 @@
1035 964 * @param string $old_status The "old" post status of the transition when saved.
1036 965 * @param object $post obj The post object.
1037 966 */
1038 967 public function maybe_set_first_published_status( $new_status, $old_status, $post ) {
968 + // Subscriptions are only available for posts so far.
969 + if ( ! $post instanceof \WP_Post || 'post' !== $post->post_type ) {
970 + return;
971 + }
972 +
1039 973 $was_post_ever_published = get_post_meta( $post->ID, '_jetpack_post_was_ever_published', true );
1040 974 if ( ! $was_post_ever_published && 'publish' === $old_status && 'draft' === $new_status ) {
1041 975 update_post_meta( $post->ID, '_jetpack_post_was_ever_published', true );
1042 976 }
@@ -1042,14 +976,24 @@
1042 976 }
1043 977 }
1044 978
1045 979 /**
1046 - * Checks if the current user can publish posts.
980 + * Checks if the current user can edit posts.
1047 981 *
1048 982 * @return bool
1049 983 */
1050 984 public function first_published_status_meta_auth_callback() {
1051 - if ( current_user_can( 'publish_posts' ) ) {
985 + /**
986 + * Filter the capability required to edit the "was ever published" post meta.
987 + *
988 + * @module subscriptions
989 + *
990 + * @since 13.4
991 + *
992 + * @param string $capability User capability needed to edit the "was ever published" meta. Default to edit_posts.
993 + */
994 + $capability = apply_filters( 'jetpack_subscriptions_post_was_ever_published_capability', 'edit_posts' );
995 + if ( current_user_can( $capability ) ) {
1052 996 return true;
1053 997 }
1054 998 return false;
1055 999 }
@@ -1058,16 +1002,17 @@
1058 1002 * Registers the 'post_was_ever_published' post meta for use in the REST API.
1059 1003 */
1060 1004 public function register_post_meta() {
1061 1005 $jetpack_post_was_ever_published = array(
1062 - 'type' => 'boolean',
1063 - 'description' => __( 'Whether the post was ever published.', 'jetpack' ),
1064 - 'single' => true,
1065 - 'default' => false,
1066 - 'show_in_rest' => array(
1006 + 'type' => 'boolean',
1007 + 'description' => __( 'Whether the post was ever published.', 'jetpack' ),
1008 + 'single' => true,
1009 + 'default' => false,
1010 + 'show_in_rest' => array(
1067 1011 'name' => 'jetpack_post_was_ever_published',
1068 1012 ),
1069 - 'auth_callback' => array( $this, 'first_published_status_meta_auth_callback' ),
1013 + 'auth_callback' => array( $this, 'first_published_status_meta_auth_callback' ),
1014 + 'object_subtype' => 'post', // Subscriptions are only for the post post type so far, so we can limit this meta to posts only.
1070 1015 );
1071 1016
1072 1017 register_meta( 'post', '_jetpack_post_was_ever_published', $jetpack_post_was_ever_published );
1073 1018 }
@@ -1076,17 +1021,62 @@
1076 1021 * Create a Subscribers menu displayed on self-hosted sites.
1077 1022 *
1078 1023 * - It is not displayed on WordPress.com sites.
1079 1024 * - It directs you to Calypso to the existing Subscribers page.
1025 + * - Once the Newsletter modernization filter is on, the unified Newsletter
1026 + * page owns the Subscribers tab, so the Calypso shortcut is replaced by a
1027 + * transitional announcement page pointing there.
1080 1028 *
1081 1029 * @return void
1082 1030 */
1083 1031 public function add_subscribers_menu() {
1084 1032 /*
1085 - * Do not display any menu on WoA and WordPress.com Simple sites.
1033 + * Staged-rollout default for both modernization filters: on for
1034 + * Automatticians and for the percentage cohort (currently 0%, bucketed by
1035 + * the stable wpcom blog ID), off everywhere else. Delegated to the canonical
1036 + * Newsletter\Settings::is_modernization_rollout_enabled() so the cohort math
1037 + * has a single source of truth; guarded with method_exists so this bootstrap
1038 + * path stays safe if the packaged Newsletter Settings class is an older copy
1039 + * that doesn't expose the helper yet.
1040 + */
1041 + $modernization_rollout_default = method_exists( '\Automattic\Jetpack\Newsletter\Settings', 'is_modernization_rollout_enabled' )
1042 + && \Automattic\Jetpack\Newsletter\Settings::is_modernization_rollout_enabled();
1043 +
1044 + /*
1045 + * Once the Newsletter modernization filter is on, the unified Newsletter
1046 + * page owns the Subscribers tab and this standalone Calypso shortcut is
1047 + * retired. In its place, a transitional announcement page tells people
1048 + * where subscriber management moved and lets them remove the menu item.
1049 + *
1050 + * This is evaluated first — before the WoA/Simple and connection guards
1051 + * below — and returns, so the legacy Calypso shortcut is never added once
1052 + * the filter is on.
1053 + *
1054 + * The announcement page itself is registered here only on self-hosted
1055 + * Jetpack. On WordPress.com (Simple and WoA) jetpack-mu-wpcom's
1056 + * wpcom-admin-menu owns the Subscribers entry and registers the
1057 + * announcement page there; doing it here as well would duplicate the menu
1058 + * (and double the page-view tracking) on Atomic, where both run.
1059 + *
1060 + * Referenced as a string literal (mirrors Newsletter\Settings::MODERNIZATION_FILTER)
1061 + * to keep this bootstrap path safe if the packaged Newsletter Settings class does
1062 + * not expose the constant yet.
1063 + */
1064 + if ( apply_filters( 'rsm_jetpack_ui_modernization_newsletter', $modernization_rollout_default ) ) {
1065 + if (
1066 + ! ( new Host() )->is_wpcom_platform()
1067 + && class_exists( '\Automattic\Jetpack\Newsletter\Subscribers_Announcement' )
1068 + ) {
1069 + \Automattic\Jetpack\Newsletter\Subscribers_Announcement::add_menu();
1070 + }
1071 + return;
1072 + }
1073 +
1074 + /*
1075 + * Do not display any menu on WoA and WordPress.com Simple sites (unless Classic wp-admin is enabled).
1086 1076 * They already get a menu item under Users via nav-unification.
1087 1077 */
1088 - if ( ( new Host() )->is_wpcom_platform() ) {
1078 + if ( ( new Host() )->is_wpcom_platform() && get_option( 'wpcom_admin_interface' ) !== 'wp-admin' ) {
1089 1079 return;
1090 1080 }
1091 1081
1092 1082 $status = new Status();
@@ -1101,22 +1091,67 @@
1101 1091 ) {
1102 1092 return;
1103 1093 }
1104 1094
1095 + /**
1096 + * Enables the new in development subscribers in wp-admin dashboard.
1097 + *
1098 + * @since 9.5.0
1099 + *
1100 + * @param bool If the new dashboard is enabled. Defaults to the staged-rollout
1101 + * cohort: true for Automatticians and the percentage cohort
1102 + * (currently 0%), false elsewhere.
1103 + */
1104 + if ( apply_filters( 'jetpack_wp_admin_subscriber_management_enabled', $modernization_rollout_default ) ) {
1105 + return;
1106 + }
1107 +
1108 + $blog_id = Connection_Manager::get_site_id( true );
1109 +
1105 1110 $link = Redirect::get_url(
1106 - 'jetpack-menu-calypso-subscribers',
1107 - array( 'site' => $status->get_site_suffix() )
1111 + 'jetpack-menu-jetpack-manage-subscribers',
1112 + array( 'site' => $blog_id ? $blog_id : $status->get_site_suffix() )
1108 1113 );
1109 1114
1110 - add_submenu_page(
1111 - 'jetpack',
1112 - esc_attr__( 'Subscribers', 'jetpack' ),
1113 - __( 'Subscribers', 'jetpack' ) . ' <span class="dashicons dashicons-external"></span>',
1115 + Admin_Menu::add_menu(
1116 + __( 'Subscribers', 'jetpack' ),
1117 + __( 'Subscribers', 'jetpack' ) . ' <span aria-hidden="true">↗</span>',
1114 1118 'manage_options',
1115 1119 esc_url( $link ),
1116 - null
1120 + null,
1121 + 15
1117 1122 );
1118 1123 }
1124 +
1125 + /**
1126 + * Record tracks event if categories is created when user enters
1127 + * the edit category page through the newsletter settings page.
1128 + *
1129 + * @return void
1130 + */
1131 + public function track_newsletter_category_creation() {
1132 +
1133 + // phpcs:disable WordPress.Security.NonceVerification.Missing
1134 + if ( empty( $_POST['_wp_http_referer'] ) ) {
1135 + return;
1136 + }
1137 +
1138 + if ( strpos( sanitize_url( wp_unslash( $_POST['_wp_http_referer'] ) ), 'referer=newsletter-categories' ) > -1 ) {
1139 +
1140 + $parent = filter_var( empty( $_POST['parent'] ) ? 0 : wp_unslash( $_POST['parent'] ), FILTER_SANITIZE_NUMBER_INT );
1141 +
1142 + $is_child_category = $parent > 0;
1143 +
1144 + $tracking = new Automattic\Jetpack\Tracking();
1145 + $tracking->tracks_record_event(
1146 + wp_get_current_user(),
1147 + 'jetpack_newsletter_add_category',
1148 + array(
1149 + 'is_child_category' => $is_child_category,
1150 + )
1151 + );
1152 + }
1153 + }
1119 1154 }
1120 1155
1121 1156 Jetpack_Subscriptions::init();
1122 1157
@@ -1121,4 +1156,10 @@
1121 1156 Jetpack_Subscriptions::init();
1122 1157
1123 1158 require __DIR__ . '/subscriptions/views.php';
1124 1159 require __DIR__ . '/subscriptions/subscribe-modal/class-jetpack-subscribe-modal.php';
1160 +require __DIR__ . '/subscriptions/subscribe-overlay/class-jetpack-subscribe-overlay.php';
1161 +require __DIR__ . '/subscriptions/subscribe-floating-button/class-jetpack-subscribe-floating-button.php';
1162 +require __DIR__ . '/subscriptions/newsletter-widget/class-jetpack-newsletter-dashboard-widget.php';
1163 +
1164 +require_once __DIR__ . '/subscriptions/abilities/class-newsletter-abilities.php';
1165 +\Automattic\Jetpack\Plugin\Abilities\Newsletter_Abilities::init();