Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
15.71% covered (danger)
15.71%
11 / 70
0.00% covered (danger)
0.00%
0 / 5
CRAP
n/a
0 / 0
Automattic\Jetpack\Extensions\Subscriber_Login\register_block
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
42
Automattic\Jetpack\Extensions\Subscriber_Login\get_current_url
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
20
Automattic\Jetpack\Extensions\Subscriber_Login\get_subscriber_login_url
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
3.01
Automattic\Jetpack\Extensions\Subscriber_Login\is_subscriber_logged_in
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
Automattic\Jetpack\Extensions\Subscriber_Login\render_block
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
72
1<?php
2/**
3 * Subscriber Login Block.
4 *
5 * @since 13.1
6 *
7 * @package automattic/jetpack
8 */
9
10namespace Automattic\Jetpack\Extensions\Subscriber_Login;
11
12use Automattic\Jetpack\Blocks;
13use Automattic\Jetpack\Extensions\Premium_Content\Subscription_Service\Abstract_Token_Subscription_Service;
14use Automattic\Jetpack\Status\Host;
15use Automattic\Jetpack\Status\Request;
16use Jetpack;
17use Jetpack_Gutenberg;
18use Jetpack_Memberships;
19use Jetpack_Options;
20
21if ( ! defined( 'ABSPATH' ) ) {
22    exit( 0 );
23}
24
25require_once __DIR__ . '/class-jetpack-subscription-site.php';
26
27/**
28 * Registers the block for use in Gutenberg
29 * This is done via an action so that we can disable
30 * registration if we need to.
31 */
32function register_block() {
33    if (
34        ! Jetpack::is_module_active( 'subscriptions' ) ||
35        ! class_exists( 'Jetpack_Memberships' ) ||
36        ! class_exists( 'Automattic\Jetpack\Extensions\Premium_Content\Subscription_Service\Abstract_Token_Subscription_Service' )
37    ) {
38        return;
39    }
40
41    Blocks::jetpack_register_block(
42        __DIR__,
43        array( 'render_callback' => __NAMESPACE__ . '\render_block' )
44    );
45
46    add_filter(
47        'jetpack_options_whitelist',
48        function ( $options ) {
49            $options[] = 'jetpack_subscriptions_login_navigation_enabled';
50
51            return $options;
52        }
53    );
54
55    // If called via REST API, we need to register later in the lifecycle
56    if ( ( new Host() )->is_wpcom_platform() && ! Request::is_frontend() ) {
57        add_action(
58            'restapi_theme_init',
59            function () {
60                Jetpack_Subscription_Site::init()->handle_subscriber_login_block_placements();
61            }
62        );
63    } else {
64        Jetpack_Subscription_Site::init()->handle_subscriber_login_block_placements();
65    }
66}
67add_action( 'init', __NAMESPACE__ . '\register_block' );
68
69/**
70 * Returns current URL.
71 *
72 * @return string
73 */
74function get_current_url() {
75    if ( ! isset( $_SERVER['HTTP_HOST'] ) || ! isset( $_SERVER['REQUEST_URI'] ) ) {
76        return '';
77    }
78
79    return ( is_ssl() ? 'https://' : 'http://' ) . wp_unslash( $_SERVER['HTTP_HOST'] ) . wp_unslash( $_SERVER['REQUEST_URI'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
80}
81
82/**
83 * Returns subscriber log in URL.
84 *
85 * @param string $redirect Path to redirect to on login.
86 *
87 * @return string
88 */
89function get_subscriber_login_url( $redirect ) {
90    $redirect = ! empty( $redirect ) ? $redirect : get_site_url();
91
92    if ( ( new Host() )->is_wpcom_simple() ) {
93        // On WPCOM we will redirect immediately
94        return wpcom_logmein_redirect_url( $redirect, false, null, 'link', get_current_blog_id() );
95    }
96
97    // On self-hosted we will save and hide the token.
98    // rawurlencode the redirect before nesting it: it is already percent-encoded
99    // (e.g. an emoji or non-ASCII slug comes through as %F0%9F%8C%91), and add_query_arg
100    // does not encode the values it inserts. Without this extra layer the value is
101    // over-decoded to raw bytes by the time it reaches the subscribers/auth endpoint,
102    // which strips it and 404s. See NL-273.
103    $redirect_url = get_site_url() . '/wp-json/jetpack/v4/subscribers/auth';
104    $redirect_url = add_query_arg( 'redirect_url', rawurlencode( $redirect ), $redirect_url );
105
106    return add_query_arg(
107        array(
108            'site_id'      => intval( Jetpack_Options::get_option( 'id' ) ),
109            'redirect_url' => rawurlencode( $redirect_url ),
110        ),
111        'https://subscribe.wordpress.com/memberships/jwt/'
112    );
113}
114
115/**
116 * Determines whether the current visitor is a confirmed subscriber -- someone who
117 * actually holds a premium-content session token, not merely someone with a
118 * WordPress session on this site.
119 *
120 * A bare WordPress session is not proof of a subscription (see NL-787): the
121 * previous is_user_logged_in() || has_token_from_cookie() check hid this block's
122 * only "Log in" link -- the sole way to mint a fresh token via the
123 * subscribe.wordpress.com magic-link round trip -- for anyone the site owner
124 * simply added as a WP user (or anyone else with an ordinary session and no
125 * token), leaving them with no way to recover.
126 *
127 * @return bool
128 */
129function is_subscriber_logged_in() {
130    return is_user_logged_in() && Abstract_Token_Subscription_Service::has_token_from_cookie();
131}
132
133/**
134 * Renders Subscriber Login block.
135 *
136 * @param array $attributes The block attributes.
137 *
138 * @return string
139 */
140function render_block( $attributes ) {
141    Jetpack_Gutenberg::load_assets_as_required( __DIR__ );
142
143    $block_template             = '<div %1$s><a href="%2$s">%3$s</a></div>';
144    $redirect_url               = ! empty( $attributes['redirectToCurrent'] ) ? get_current_url() : get_site_url();
145    $log_in_label               = ! empty( $attributes['logInLabel'] ) ? sanitize_text_field( $attributes['logInLabel'] ) : esc_html__( 'Log in', 'jetpack' );
146    $log_out_label              = ! empty( $attributes['logOutLabel'] ) ? sanitize_text_field( $attributes['logOutLabel'] ) : esc_html__( 'Log out', 'jetpack' );
147    $show_manage_link           = ! empty( $attributes['showManageSubscriptionsLink'] );
148    $manage_subscriptions_label = ! empty( $attributes['manageSubscriptionsLabel'] ) ? sanitize_text_field( $attributes['manageSubscriptionsLabel'] ) : esc_html__( 'Manage subscription', 'jetpack' );
149
150    if ( ! is_subscriber_logged_in() ) {
151        return sprintf(
152            $block_template,
153            get_block_wrapper_attributes(),
154            get_subscriber_login_url( $redirect_url ),
155            $log_in_label
156        );
157    }
158
159    if ( $show_manage_link && Jetpack_Memberships::is_current_user_subscribed() ) {
160        return sprintf(
161            $block_template,
162            get_block_wrapper_attributes(),
163            'https://wordpress.com/reader/site/subscription/' . Jetpack_Memberships::get_blog_id(),
164            $manage_subscriptions_label
165        );
166    }
167
168    return sprintf(
169        $block_template,
170        get_block_wrapper_attributes(),
171        wp_logout_url( $redirect_url ),
172        $log_out_label
173    );
174}