PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.3-beta
16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.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 All 507 releases
← All changes | modules/subscriptions.php +227 -130 12.0.3 → 16.3-beta View file →
@@ -1,25 +1,39 @@
1 1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName)
2 2 /**
3 - * Module Name: Subscriptions
4 - * Module Description: Let visitors subscribe to new posts and comments via email
3 + * Module Name: Newsletter
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
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;
19 +use Automattic\Jetpack\Connection\Manager as Connection_Manager;
18 20 use Automattic\Jetpack\Connection\XMLRPC_Async_Call;
21 +use Automattic\Jetpack\Newsletter\Settings as Newsletter_Settings;
22 +use Automattic\Jetpack\Newsletter\Urls as Newsletter_Urls;
23 +use Automattic\Jetpack\Redirect;
24 +use Automattic\Jetpack\Status;
25 +use Automattic\Jetpack\Status\Host;
19 26
27 +if ( ! defined( 'ABSPATH' ) ) {
28 + exit( 0 );
29 +}
30 +
20 31 add_action( 'jetpack_modules_loaded', 'jetpack_subscriptions_load' );
21 32
33 +// Loads the User Content Link Redirection feature.
34 +require_once __DIR__ . '/subscriptions/jetpack-user-content-link-redirection.php';
35 +
22 36 /**
23 37 * Loads the Subscriptions module.
24 38 */
25 39 function jetpack_subscriptions_load() {
@@ -36,13 +50,13 @@
36 50 function jetpack_subscriptions_cherry_pick_server_data() {
37 51 $data = array();
38 52
39 53 foreach ( $_SERVER as $key => $value ) {
40 - if ( ! is_string( $value ) || 0 === strpos( $key, 'HTTP_COOKIE' ) ) {
54 + if ( ! is_string( $value ) || str_starts_with( $key, 'HTTP_COOKIE' ) ) {
41 55 continue;
42 56 }
43 57
44 - if ( 0 === strpos( $key, 'HTTP_' ) || in_array( $key, array( 'REMOTE_ADDR', 'REQUEST_URI', 'DOCUMENT_URI' ), true ) ) {
58 + if ( str_starts_with( $key, 'HTTP_' ) || in_array( $key, array( 'REMOTE_ADDR', 'REQUEST_URI', 'DOCUMENT_URI' ), true ) ) {
45 59 $data[ $key ] = $value;
46 60 }
47 61 }
48 62
@@ -50,8 +64,10 @@
50 64 }
51 65
52 66 /**
53 67 * Main class file for the Subscriptions module.
68 + *
69 + * @phan-constructor-used-for-side-effects
54 70 */
55 71 class Jetpack_Subscriptions {
56 72 /**
57 73 * Whether Jetpack has been instantiated or not.
@@ -120,12 +136,31 @@
120 136 add_filter( 'post_updated_messages', array( $this, 'update_published_message' ), 18, 1 );
121 137
122 138 // Set "social_notifications_subscribe" option during the first-time activation.
123 139 add_action( 'jetpack_activate_module_subscriptions', array( $this, 'set_social_notifications_subscribe' ) );
140 + add_action( 'jetpack_activate_module_subscriptions', array( $this, 'set_featured_image_in_email_default' ) );
141 + add_action( 'jetpack_activate_module_subscriptions', array( $this, 'set_newsletter_send_default' ) );
124 142
125 143 // Hide subscription messaging in Publish panel for posts that were published in the past
126 144 add_action( 'init', array( $this, 'register_post_meta' ), 20 );
127 145 add_action( 'transition_post_status', array( $this, 'maybe_set_first_published_status' ), 10, 3 );
146 +
147 + // Add Subscribers menu to Jetpack navigation.
148 + add_action( 'jetpack_admin_menu', array( $this, 'add_subscribers_menu' ) );
149 +
150 + // Customize the configuration URL to lead to the Subscriptions settings.
151 + add_filter(
152 + 'jetpack_module_configuration_url_subscriptions',
153 + function () {
154 + return Newsletter_Urls::get_newsletter_settings_url();
155 + }
156 + );
157 +
158 + // Track categories created through the category editor page
159 + add_action( 'wp_ajax_add-tag', array( $this, 'track_newsletter_category_creation' ), 1 );
160 +
161 + $newsletter_settings = new Newsletter_Settings();
162 + $newsletter_settings::init();
128 163 }
129 164
130 165 /**
131 166 * Jetpack_Subscriptions::xmlrpc_methods()
@@ -347,14 +382,14 @@
347 382 'discussion',
348 383 'stc_enabled'
349 384 );
350 385
351 - /** Email me whenever: Someone follows my blog */
386 + /** Email me whenever: Someone subscribes to my blog */
352 387 /* @since 8.1 */
353 388
354 389 add_settings_section(
355 390 'notifications_section',
356 - __( 'Someone follows my blog', 'jetpack' ),
391 + __( 'Someone subscribes to my blog', 'jetpack' ),
357 392 array( $this, 'social_notifications_subscribe_section' ),
358 393 'discussion'
359 394 );
360 395
@@ -370,39 +405,8 @@
370 405 'discussion',
371 406 'social_notifications_subscribe',
372 407 array( $this, 'social_notifications_subscribe_validate' )
373 408 );
374 -
375 - /** Subscription Messaging Options */
376 -
377 - register_setting(
378 - 'reading',
379 - 'subscription_options',
380 - array( $this, 'validate_settings' )
381 - );
382 -
383 - add_settings_section(
384 - 'email_settings',
385 - __( 'Follower Settings', 'jetpack' ),
386 - array( $this, 'reading_section' ),
387 - 'reading'
388 - );
389 -
390 - add_settings_field(
391 - 'invitation',
392 - __( 'Blog follow email text', 'jetpack' ),
393 - array( $this, 'setting_invitation' ),
394 - 'reading',
395 - 'email_settings'
396 - );
397 -
398 - add_settings_field(
399 - 'comment-follow',
400 - __( 'Comment follow email text', 'jetpack' ),
401 - array( $this, 'setting_comment_follow' ),
402 - 'reading',
403 - 'email_settings'
404 - );
405 409 }
406 410
407 411 /**
408 412 * Discussions setting section blurb.
@@ -461,9 +465,9 @@
461 465 <?php
462 466 }
463 467
464 468 /**
465 - * Someone follows my blog section
469 + * Someone subscribes to my blog section
466 470 *
467 471 * @since 8.1
468 472 */
469 473 public function social_notifications_subscribe_section() {
@@ -487,9 +491,9 @@
487 491 <?php
488 492 }
489 493
490 494 /**
491 - * Someone follows my blog Toggle
495 + * Someone subscribes to my blog Toggle
492 496 *
493 497 * @since 8.1
494 498 */
495 499 public function social_notifications_subscribe_field() {
@@ -499,9 +503,9 @@
499 503 <label>
500 504 <input type="checkbox" name="social_notifications_subscribe" id="social_notifications_subscribe" value="1" <?php checked( $checked ); ?> />
501 505 <?php
502 506 /* translators: this is a label for a setting that starts with "Email me whenever" */
503 - esc_html_e( 'Someone follows my blog', 'jetpack' );
507 + esc_html_e( 'Someone subscribes to my blog', 'jetpack' );
504 508 ?>
505 509 </label>
506 510 <?php
507 511 }
@@ -506,9 +510,9 @@
506 510 <?php
507 511 }
508 512
509 513 /**
510 - * Validate "Someone follows my blog" option
514 + * Validate "Someone subscribes to my blog" option
511 515 *
512 516 * @since 8.1
513 517 *
514 518 * @param String $input the input string to be validated.
@@ -524,81 +528,8 @@
524 528 return 'on';
525 529 }
526 530
527 531 /**
528 - * Validate settings for the Subscriptions module.
529 - *
530 - * @param array $settings Settings to be validated.
531 - */
532 - public function validate_settings( $settings ) {
533 - global $allowedposttags;
534 -
535 - $default = $this->get_default_settings();
536 -
537 - // Blog Follow.
538 - $settings['invitation'] = trim( wp_kses( $settings['invitation'], $allowedposttags ) );
539 - if ( empty( $settings['invitation'] ) ) {
540 - $settings['invitation'] = $default['invitation'];
541 - }
542 -
543 - // Comments Follow (single post).
544 - $settings['comment_follow'] = trim( wp_kses( $settings['comment_follow'], $allowedposttags ) );
545 - if ( empty( $settings['comment_follow'] ) ) {
546 - $settings['comment_follow'] = $default['comment_follow'];
547 - }
548 -
549 - return $settings;
550 - }
551 -
552 - /**
553 - * HTML output helper for Reading section.
554 - */
555 - public function reading_section() {
556 - echo '<p id="follower-settings">';
557 - esc_html_e( 'These settings change emails sent from your blog to followers.', 'jetpack' );
558 - echo '</p>';
559 - }
560 -
561 - /**
562 - * HTML output helper for Invitation section.
563 - */
564 - public function setting_invitation() {
565 - $settings = $this->get_settings();
566 - echo '<textarea name="subscription_options[invitation]" class="large-text" cols="50" rows="5">' . esc_textarea( $settings['invitation'] ) . '</textarea>';
567 - 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>';
568 - }
569 -
570 - /**
571 - * HTML output helper for Comment Follow section.
572 - */
573 - public function setting_comment_follow() {
574 - $settings = $this->get_settings();
575 - echo '<textarea name="subscription_options[comment_follow]" class="large-text" cols="50" rows="5">' . esc_textarea( $settings['comment_follow'] ) . '</textarea>';
576 - 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>';
577 - }
578 -
579 - /**
580 - * Get default settings for the Subscriptions module.
581 - */
582 - public function get_default_settings() {
583 - $site_url = get_home_url();
584 - $display_url = preg_replace( '(^https?://)', '', untrailingslashit( $site_url ) );
585 -
586 - return array(
587 - /* translators: Both %1$s and %2$s is site address */
588 - '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 ),
589 - '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' ),
590 - );
591 - }
592 -
593 - /**
594 - * Reeturn merged `subscription_options` option with module default settings.
595 - */
596 - public function get_settings() {
597 - return wp_parse_args( (array) get_option( 'subscription_options' ), $this->get_default_settings() );
598 - }
599 -
600 - /**
601 532 * Jetpack_Subscriptions::subscribe()
602 533 *
603 534 * Send a synchronous XML-RPC subscribe to blog posts or subscribe to post comments request.
604 535 *
@@ -637,8 +568,9 @@
637 568
638 569 if ( $async ) {
639 570 XMLRPC_Async_Call::add_call( 'jetpack.subscribeToSite', 0, $email, $post_id, serialize( $extra_data ) ); //phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
640 571 } else {
572 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $xml is set when $async is false
641 573 $xml->addCall( 'jetpack.subscribeToSite', $email, $post_id, serialize( $extra_data ) ); //phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
642 574 }
643 575 }
644 576
@@ -646,19 +578,24 @@
646 578 return;
647 579 }
648 580
649 581 // Call.
582 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $xml is set when $async is false, otherwise we return early
650 583 $xml->query();
651 584
585 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $xml is set when $async is false, otherwise we return early
652 586 if ( $xml->isError() ) {
587 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $xml is set when $async is false, otherwise we return early
653 588 return $xml->get_jetpack_error();
654 589 }
655 590
591 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $xml is set when $async is false
656 592 $responses = $xml->getResponse();
657 593
658 594 $r = array();
659 595 foreach ( (array) $responses as $response ) {
660 596 if ( isset( $response['faultCode'] ) || isset( $response['faultString'] ) ) {
597 + // @phan-suppress-next-line PhanPossiblyUndeclaredVariable -- $xml is set when $async is false
661 598 $r[] = $xml->get_jetpack_error( $response['faultCode'], $response['faultString'] );
662 599 continue;
663 600 }
664 601
@@ -698,10 +635,10 @@
698 635 * When a user submits their email via the blog subscription widget, check the details and call the subsribe() method.
699 636 */
700 637 public function widget_submit() {
701 638 // Check the nonce.
702 - if ( is_user_logged_in() ) {
703 - check_admin_referer( 'blogsub_subscribe_' . get_current_blog_id() );
639 + if ( ! wp_verify_nonce( isset( $_REQUEST['_wpnonce'] ) ? sanitize_key( $_REQUEST['_wpnonce'] ) : '', 'blogsub_subscribe_' . \Jetpack_Options::get_option( 'id' ) ) ) {
640 + return false;
704 641 }
705 642
706 643 if ( empty( $_REQUEST['email'] ) || ! is_string( $_REQUEST['email'] ) ) {
707 644 return false;
@@ -776,9 +713,9 @@
776 713 */
777 714 do_action( 'jetpack_subscriptions_form_submission', $result );
778 715
779 716 wp_safe_redirect( "$redirect#$redirect_fragment" );
780 - exit;
717 + exit( 0 );
781 718 }
782 719
783 720 /**
784 721 * Jetpack_Subscriptions::comment_subscribe_init()
@@ -789,8 +726,13 @@
789 726 */
790 727 public function comment_subscribe_init( $submit_button ) {
791 728 global $post;
792 729
730 + // Subscriptions are only available for posts so far.
731 + if ( ! $post || 'post' !== $post->post_type ) {
732 + return $submit_button;
733 + }
734 +
793 735 $comments_checked = '';
794 736 $blog_checked = '';
795 737
796 738 // Check for a comment / blog submission and set a cookie to retain the setting and check the boxes.
@@ -869,8 +811,19 @@
869 811 * @param int|string $comment_id Comment thread being subscribed to.
870 812 * @param string $approved Comment status.
871 813 */
872 814 public function comment_subscribe_submit( $comment_id, $approved ) {
815 + /**
816 + * Filters whether to skip comment subscription processing.
817 + *
818 + * @since 15.5
819 + *
820 + * @param bool $skip Whether to skip comment subscription. Default false.
821 + */
822 + if ( apply_filters( 'jetpack_subscription_comment_subscribe_skip', false ) ) {
823 + return;
824 + }
825 +
873 826 if ( 'spam' === $approved ) {
874 827 return;
875 828 }
876 829
@@ -933,9 +886,9 @@
933 886 public function set_cookies( $subscribe_to_post = false, $post_id = null, $subscribe_to_blog = false ) {
934 887 $post_id = (int) $post_id;
935 888
936 889 /** This filter is already documented in core/wp-includes/comment-functions.php */
937 - $cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
890 + $cookie_lifetime = apply_filters( 'comment_cookie_lifetime', YEAR_IN_SECONDS );
938 891
939 892 /**
940 893 * Filter the Jetpack Comment cookie path.
941 894 *
@@ -958,15 +911,15 @@
958 911 */
959 912 $cookie_domain = apply_filters( 'jetpack_comment_cookie_domain', COOKIE_DOMAIN );
960 913
961 914 if ( $subscribe_to_post && $post_id >= 0 ) {
962 - setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain, is_ssl(), true );
915 + setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, '1', time() + $cookie_lifetime, $cookie_path, $cookie_domain, is_ssl(), true );
963 916 } else {
964 917 setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, '', time() - 3600, $cookie_path, $cookie_domain, is_ssl(), true );
965 918 }
966 919
967 920 if ( $subscribe_to_blog ) {
968 - setcookie( 'jetpack_blog_subscribe_' . self::$hash, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain, is_ssl(), true );
921 + setcookie( 'jetpack_blog_subscribe_' . self::$hash, '1', time() + $cookie_lifetime, $cookie_path, $cookie_domain, is_ssl(), true );
969 922 } else {
970 923 setcookie( 'jetpack_blog_subscribe_' . self::$hash, '', time() - 3600, $cookie_path, $cookie_domain, is_ssl(), true );
971 924 }
972 925 }
@@ -984,8 +937,26 @@
984 937 }
985 938 }
986 939
987 940 /**
941 + * Set the featured image in email option to `1` when the Subscriptions module is activated in the first time.
942 + *
943 + * @return void
944 + */
945 + public function set_featured_image_in_email_default() {
946 + add_option( 'wpcom_featured_image_in_email', 1 );
947 + }
948 +
949 + /**
950 + * Set the email post to subscribers default option to `1` when the Subscriptions module is activated for the first time.
951 + *
952 + * @return void
953 + */
954 + public function set_newsletter_send_default() {
955 + add_option( 'wpcom_newsletter_send_default', 1 );
956 + }
957 +
958 + /**
988 959 * Save a flag when a post was ever published.
989 960 *
990 961 * It saves the post meta when the post was published and becomes a draft.
991 962 * Then this meta is used to hide subscription messaging in Publish panel.
@@ -994,8 +965,13 @@
994 965 * @param string $old_status The "old" post status of the transition when saved.
995 966 * @param object $post obj The post object.
996 967 */
997 968 public function maybe_set_first_published_status( $new_status, $old_status, $post ) {
969 + // Subscriptions are only available for posts so far.
970 + if ( ! $post instanceof \WP_Post || 'post' !== $post->post_type ) {
971 + return;
972 + }
973 +
998 974 $was_post_ever_published = get_post_meta( $post->ID, '_jetpack_post_was_ever_published', true );
999 975 if ( ! $was_post_ever_published && 'publish' === $old_status && 'draft' === $new_status ) {
1000 976 update_post_meta( $post->ID, '_jetpack_post_was_ever_published', true );
1001 977 }
@@ -1001,14 +977,24 @@
1001 977 }
1002 978 }
1003 979
1004 980 /**
1005 - * Checks if the current user can publish posts.
981 + * Checks if the current user can edit posts.
1006 982 *
1007 983 * @return bool
1008 984 */
1009 985 public function first_published_status_meta_auth_callback() {
1010 - if ( current_user_can( 'publish_posts' ) ) {
986 + /**
987 + * Filter the capability required to edit the "was ever published" post meta.
988 + *
989 + * @module subscriptions
990 + *
991 + * @since 13.4
992 + *
993 + * @param string $capability User capability needed to edit the "was ever published" meta. Default to edit_posts.
994 + */
995 + $capability = apply_filters( 'jetpack_subscriptions_post_was_ever_published_capability', 'edit_posts' );
996 + if ( current_user_can( $capability ) ) {
1011 997 return true;
1012 998 }
1013 999 return false;
1014 1000 }
@@ -1017,22 +1003,133 @@
1017 1003 * Registers the 'post_was_ever_published' post meta for use in the REST API.
1018 1004 */
1019 1005 public function register_post_meta() {
1020 1006 $jetpack_post_was_ever_published = array(
1021 - 'type' => 'boolean',
1022 - 'description' => __( 'Whether the post was ever published.', 'jetpack' ),
1023 - 'single' => true,
1024 - 'default' => false,
1025 - 'show_in_rest' => array(
1007 + 'type' => 'boolean',
1008 + 'description' => __( 'Whether the post was ever published.', 'jetpack' ),
1009 + 'single' => true,
1010 + 'default' => false,
1011 + 'show_in_rest' => array(
1026 1012 'name' => 'jetpack_post_was_ever_published',
1027 1013 ),
1028 - 'auth_callback' => array( $this, 'first_published_status_meta_auth_callback' ),
1014 + 'auth_callback' => array( $this, 'first_published_status_meta_auth_callback' ),
1015 + 'object_subtype' => 'post', // Subscriptions are only for the post post type so far, so we can limit this meta to posts only.
1029 1016 );
1030 1017
1031 1018 register_meta( 'post', '_jetpack_post_was_ever_published', $jetpack_post_was_ever_published );
1032 1019 }
1033 1020
1021 + /**
1022 + * Create a Subscribers menu displayed on self-hosted sites.
1023 + *
1024 + * - It is not displayed on WordPress.com sites.
1025 + * - It directs you to Calypso to the existing Subscribers page.
1026 + * - It is retired once the Newsletter modernization filter is on, since the
1027 + * unified Newsletter page then owns the Subscribers tab.
1028 + *
1029 + * @return void
1030 + */
1031 + public function add_subscribers_menu() {
1032 + /*
1033 + * Referenced as a string literal (mirrors Newsletter\Settings::MODERNIZATION_FILTER)
1034 + * to keep this bootstrap path safe if the packaged Newsletter Settings class does
1035 + * not expose the constant yet.
1036 + */
1037 + if ( apply_filters( 'rsm_jetpack_ui_modernization_newsletter', true ) ) {
1038 + return;
1039 + }
1040 +
1041 + /*
1042 + * Do not display any menu on WoA and WordPress.com Simple sites (unless Classic wp-admin is enabled).
1043 + * They already get a menu item under Users via nav-unification.
1044 + */
1045 + if ( ( new Host() )->is_wpcom_platform() && get_option( 'wpcom_admin_interface' ) !== 'wp-admin' ) {
1046 + return;
1047 + }
1048 +
1049 + $status = new Status();
1050 +
1051 + /*
1052 + * Do not display if we're in Offline mode,
1053 + * or if the user is not connected.
1054 + */
1055 + if (
1056 + $status->is_offline_mode()
1057 + || ! ( new Connection_Manager( 'jetpack' ) )->is_user_connected()
1058 + ) {
1059 + return;
1060 + }
1061 +
1062 + /**
1063 + * Enables the new in development subscribers in wp-admin dashboard.
1064 + *
1065 + * @since 9.5.0
1066 + *
1067 + * @param bool If the new dashboard is enabled. Defaults on for every site; hosts
1068 + * can opt out with this filter.
1069 + */
1070 + if ( apply_filters( 'jetpack_wp_admin_subscriber_management_enabled', true ) ) {
1071 + return;
1072 + }
1073 +
1074 + $blog_id = Connection_Manager::get_site_id( true );
1075 +
1076 + $link = Redirect::get_url(
1077 + 'jetpack-menu-jetpack-manage-subscribers',
1078 + array( 'site' => $blog_id ? $blog_id : $status->get_site_suffix() )
1079 + );
1080 +
1081 + $position = defined( Admin_Menu::class . '::POSITION_EXTERNAL' ) ? Admin_Menu::POSITION_EXTERNAL : 100;
1082 +
1083 + Admin_Menu::add_menu(
1084 + __( 'Subscribers', 'jetpack' ),
1085 + __( 'Subscribers', 'jetpack' ) . ' <span aria-hidden="true">↗</span>',
1086 + 'manage_options',
1087 + esc_url( $link ),
1088 + null,
1089 + $position,
1090 + array( 'key' => 'jetpack-subscribers' )
1091 + );
1092 + }
1093 +
1094 + /**
1095 + * Record tracks event if categories is created when user enters
1096 + * the edit category page through the newsletter settings page.
1097 + *
1098 + * @return void
1099 + */
1100 + public function track_newsletter_category_creation() {
1101 +
1102 + // phpcs:disable WordPress.Security.NonceVerification.Missing
1103 + if ( empty( $_POST['_wp_http_referer'] ) ) {
1104 + return;
1105 + }
1106 +
1107 + if ( strpos( sanitize_url( wp_unslash( $_POST['_wp_http_referer'] ) ), 'referer=newsletter-categories' ) > -1 ) {
1108 +
1109 + $parent = filter_var( empty( $_POST['parent'] ) ? 0 : wp_unslash( $_POST['parent'] ), FILTER_SANITIZE_NUMBER_INT );
1110 +
1111 + $is_child_category = $parent > 0;
1112 +
1113 + $tracking = new Automattic\Jetpack\Tracking();
1114 + $tracking->tracks_record_event(
1115 + wp_get_current_user(),
1116 + 'jetpack_newsletter_add_category',
1117 + array(
1118 + 'is_child_category' => $is_child_category,
1119 + )
1120 + );
1121 + }
1122 + }
1034 1123 }
1035 1124
1036 1125 Jetpack_Subscriptions::init();
1037 1126
1038 1127 require __DIR__ . '/subscriptions/views.php';
1128 +require __DIR__ . '/subscriptions/subscribe-modal/class-jetpack-subscribe-modal.php';
1129 +require __DIR__ . '/subscriptions/subscribe-overlay/class-jetpack-subscribe-overlay.php';
1130 +require __DIR__ . '/subscriptions/subscribe-floating-button/class-jetpack-subscribe-floating-button.php';
1131 +require __DIR__ . '/subscriptions/newsletter-widget/class-jetpack-newsletter-dashboard-widget.php';
1132 +require_once __DIR__ . '/subscriptions/email-design-editor/class-jetpack-email-design-editor.php';
1133 +
1134 +require_once __DIR__ . '/subscriptions/abilities/class-newsletter-abilities.php';
1135 +\Automattic\Jetpack\Plugin\Abilities\Newsletter_Abilities::init();