$padding = get_attribute( $attributes, 'padding', DEFAULT_PADDING_VALUE );
$style = sprintf( 'padding: %1$dpx %2$dpx %1$dpx %2$dpx;', $padding, round( $padding * 1.5 ) );
$submit_button_styles .= $style;
$email_field_styles .= $style;
if ( ! $is_button_only_style ) {
$button_spacing = get_attribute( $attributes, 'spacing', DEFAULT_SPACING_VALUE );
if ( true === get_attribute( $attributes, 'buttonOnNewLine' ) ) {
$submit_button_styles .= sprintf( 'margin-top: %dpx;', $button_spacing );
$submit_button_styles .= 'margin: 0; '; // Reset Safari's 2px default margin for buttons affecting input and button union
$submit_button_styles .= sprintf( 'margin-left: %dpx;', $button_spacing );
if ( has_attribute( $attributes, 'borderColor' ) ) {
$style = sprintf( 'border-color: %s;', get_attribute( $attributes, 'borderColor', '' ) );
$submit_button_styles .= $style;
$email_field_styles .= $style;
$style = sprintf( 'border-radius: %dpx;', get_attribute( $attributes, 'borderRadius', DEFAULT_BORDER_RADIUS_VALUE ) );
$submit_button_styles .= $style;
$email_field_styles .= $style;
$style = sprintf( 'border-width: %dpx;', get_attribute( $attributes, 'borderWeight', DEFAULT_BORDER_WEIGHT_VALUE ) );
$submit_button_styles .= $style;
$email_field_styles .= $style;
if ( has_attribute( $attributes, 'customBorderColor' ) ) {
$style = sprintf( 'border-color: %s; border-style: solid;', get_attribute( $attributes, 'customBorderColor' ) );
$submit_button_styles .= $style;
$email_field_styles .= $style;
if ( ! Request::is_frontend() ) {
$background_color_style = get_attribute_color( 'buttonBackgroundColor', $attributes, '#113AF5' /* default lettre theme color */ );
$text_color_style = get_attribute_color( 'textColor', $attributes, '#FFFFFF' );
$submit_button_styles .= sprintf( ' background-color: %s; color: %s;', $background_color_style, $text_color_style );
'email_field' => $email_field_styles,
'submit_button' => $submit_button_styles,
'submit_button_wrapper' => $submit_button_wrapper_styles,
* Retrieve the resolved color for a given attribute.
* @param string $attribute_name The name of the attribute to resolve.
* @param array $attributes An array of all attributes.
* @param string $default_color A fallback color in case no color can be resolved.
* @return string Returns the resolved color or the default color if no color is found.
function get_attribute_color( $attribute_name, $attributes, $default_color ) {
if ( has_attribute( $attributes, $attribute_name ) ) {
$color_slug = get_attribute( $attributes, $attribute_name );
$resolved_color = get_color_from_slug( $color_slug );
return get_global_style_color( $attribute_name, $default_color );
* Retrieve the global style color based on a provided style key.
* @param string $style_key The key for the desired style.
* @param string $default_color A fallback color in case the global style is not set.
* @return string Returns the color defined in global styles or the default color if not defined.
function get_global_style_color( $style_key, $default_color ) {
$global_styles = wp_get_global_styles(
'block_name' => 'core/button',
'transforms' => array( 'resolve-variables' ),
if ( isset( $global_styles[ $style_key ] ) ) {
return $global_styles[ $style_key ];
* Convert a color slug into its corresponding color value.
* @param string $slug The slug representation of the color.
* @return string|null Returns the color value if found, or null otherwise.
function get_color_from_slug( $slug ) {
$color_palettes = wp_get_global_settings( array( 'color', 'palette' ) );
if ( ! is_array( $color_palettes ) ) {
foreach ( $color_palettes as $palette ) {
if ( is_array( $palette ) ) {
foreach ( $palette as $color ) {
if ( isset( $color['slug'] ) && $color['slug'] === $slug && isset( $color['color'] ) ) {
* Is the Jetpack_Memberships class loaded.
function is_jetpack_memberships_loaded(): bool {
return class_exists( '\Jetpack_Memberships' );
* Subscriptions block render callback.
* @param array $attributes Array containing the block attributes.
function render_block( $attributes ) {
// If the Subscriptions module is not active, don't render the block.
if ( ! ( new Modules() )->is_active( 'subscriptions' ) ) {
if ( is_jetpack_memberships_loaded() ) {
// We only want the sites that have newsletter feature enabled to be graced by this JavaScript.
Jetpack_Gutenberg::load_assets_as_required( __DIR__ );
Jetpack_Gutenberg::load_styles_as_required( FEATURE_NAME );
if ( ! class_exists( 'Jetpack_Subscriptions_Widget' ) ) {
// Prefill the email field with the current user's email if they are logged in via Memberships premium content token
$subscribe_email = Jetpack_Memberships::get_current_user_email();
// If no email, then prefill the email field with the current user's email if they are logged in
if ( empty( $subscribe_email ) ) {
$current_user = wp_get_current_user();
if ( ! empty( $current_user->user_email ) ) {
$subscribe_email = $current_user->user_email;
// The block is using the Jetpack_Subscriptions_Widget backend, hence the need to increase the instance count.
++Jetpack_Subscriptions_Widget::$instance_count;
$classes = get_element_class_names_from_attributes( $attributes );
$styles = get_element_styles_from_attributes( $attributes );
// The default value was previously "true" in block.json. We don't want to rely setting "default" in block.json to falsy,
// because it would change the setting for previously saved blocks. Block editor doesn't store default values in attributes at all.
// Hence users without this set will still get social counts included in the subscriber counter.
// Lowering the subscriber count on their behalf with code change would be controversial.
// We want to disencourage including social count as it's misleading.
$include_social_followers = isset( $attributes['includeSocialFollowers'] ) ? (bool) get_attribute( $attributes, 'includeSocialFollowers' ) : true;
'widget_id' => Jetpack_Subscriptions_Widget::$instance_count,
'subscribe_email' => $subscribe_email,
'is_paid_subscriber' => get_attribute( $attributes, 'isPaidSubscriber', false ),
'wrapper_attributes' => get_block_wrapper_attributes(
'class' => $classes['block_wrapper'],
'subscribe_placeholder' => get_attribute( $attributes, 'subscribePlaceholder', __( 'Type your email…', 'jetpack' ) ),
'submit_button_text' => get_attribute( $attributes, 'submitButtonText', __( 'Subscribe', 'jetpack' ) ),
'submit_button_text_subscribed' => get_attribute( $attributes, 'submitButtonTextSubscribed', __( 'Subscribed', 'jetpack' ) ),
'submit_button_text_upgrade' => get_attribute( $attributes, 'submitButtonTextUpgrade', __( 'Upgrade subscription', 'jetpack' ) ),
'success_message' => get_attribute(
esc_html__( "Success! An email was just sent to confirm your subscription. Please find the email now and click 'Confirm' to start subscribing.", 'jetpack' )
'show_subscribers_total' => (bool) get_attribute( $attributes, 'showSubscribersTotal' ),
'subscribers_total' => get_attribute( $attributes, 'showSubscribersTotal' ) ? get_subscriber_count( $include_social_followers ) : 0,
'referer' => esc_url_raw(
( is_ssl() ? 'https' : 'http' ) . '://' . ( isset( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : '' ) .
( isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '' )
'source' => 'subscribe-block',
'app_source' => get_attribute( $attributes, 'appSource', null ),
'class_name' => get_attribute( $attributes, 'className' ),
'selected_newsletter_categories' => get_attribute( $attributes, 'selectedNewsletterCategoryIds', array() ),
'preselected_newsletter_categories' => get_attribute( $attributes, 'preselectNewsletterCategories', false ),
// Only render the email version in non-frontend contexts.
if ( is_feed() || wp_is_xml_request() ||
( defined( 'REST_REQUEST' ) && REST_REQUEST && ! wp_is_json_request() ) ||
( defined( 'REST_API_REQUEST' ) && REST_API_REQUEST ) ||
( defined( 'WP_CLI' ) && WP_CLI ) ||
wp_is_jsonp_request() ) {
return render_for_email( $data, $styles );
return render_for_website( $data, $classes, $styles );
* Get the post access level for the current post. Defaults to 'everybody' if the query is not for a single post
* @return string the actual post access level (see projects/plugins/jetpack/extensions/blocks/subscriptions/constants.js for the values).
function get_post_access_level_for_current_post() {
// There is no "actual" current post.
return Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_EVERYBODY;
return Jetpack_Memberships::get_post_access_level();
* Renders the subscriptions block at the site.
* @param array $data Array containing block view data.
* @param array $classes Array containing the classes for different block elements.
* @param array $styles Array containing the styles for different block elements.
function render_for_website( $data, $classes, $styles ) {
$blog_id = \Jetpack_Options::get_option( 'id' );
$widget_id_suffix = Jetpack_Subscriptions_Widget::$instance_count > 1 ? '-' . Jetpack_Subscriptions_Widget::$instance_count : '';
$form_id = 'subscribe-blog' . $widget_id_suffix;
$form_url = 'https://wordpress.com/email-subscriptions';
$post_access_level = get_post_access_level_for_current_post();
$is_button_only_style = ! empty( $data['class_name'] ) ? is_button_only_style( $data['class_name'] ) : false;
// Post ID is used for pulling post-specific paid status, and returning to the right post after confirming subscription
} elseif ( is_singular( 'post' ) || is_page() ) {
$post_id = get_queried_object_id();
$post_id = get_option( 'page_on_front' );
$subscribe_field_id = apply_filters( 'subscribe_field_id', 'subscribe-field' . $widget_id_suffix, $data['widget_id'] );
$tier_id = get_post_meta( $post_id, META_NAME_FOR_POST_TIER_ID_SETTINGS, true );
$is_subscribed = Jetpack_Memberships::is_current_user_subscribed();
$button_text = get_submit_button_text( $data );
$show_subscriber_count = $data['show_subscribers_total'] && $data['subscribers_total'] && ! $is_subscribed;
Jetpack_Subscriptions_Widget::render_widget_status_messages(
'success_message' => $data['success_message'],
<div <?php echo wp_kses_data( $data['wrapper_attributes'] ); ?>>
<div class="wp-block-jetpack-subscriptions__container<?php echo ! $is_subscribed ? ' is-not-subscriber' : ''; ?>">
<?php if ( is_top_subscription() ) : ?>
<p id="subscribe-submit" class="is-link"
<?php if ( ! empty( $styles['submit_button_wrapper'] ) ) : ?>
style="<?php echo esc_attr( $styles['submit_button_wrapper'] ); ?>"
href="<?php echo esc_url( 'https://wordpress.com/reader/site/subscription/' . $blog_id ); ?>"
<?php if ( ! empty( $classes['submit_button'] ) ) : ?>
class="<?php echo esc_attr( $classes['submit_button'] ); ?>"
<?php if ( ! empty( $styles['submit_button'] ) ) : ?>
style="<?php echo esc_attr( $styles['submit_button'] ); ?>"
<?php echo sanitize_submit_text( $button_text ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
action="<?php echo esc_url( $form_url ); ?>"
data-blog="<?php echo esc_attr( $blog_id ); ?>"
data-post_access_level="<?php echo esc_attr( $post_access_level ); ?>"
data-subscriber_email="<?php echo esc_attr( $data['subscribe_email'] ); ?>"
id="<?php echo esc_attr( $form_id ); ?>"
<div class="wp-block-jetpack-subscriptions__form-elements">
<?php if ( ! $is_subscribed && ! $is_button_only_style ) : ?>
id="<?php echo esc_attr( $subscribe_field_id . '-label' ); ?>"
for="<?php echo esc_attr( $subscribe_field_id ); ?>"
class="screen-reader-text"
<?php echo esc_html( $data['subscribe_placeholder'] ); ?>
( ! empty( $classes['email_field'] )
? 'class="' . esc_attr( $classes['email_field'] ) . '"'
( ! empty( $styles['email_field'] )
? esc_attr( $styles['email_field'] )
: 'width: 95%; padding: 1px 10px'
esc_attr( $data['subscribe_placeholder'] ),
esc_attr( $data['subscribe_email'] ),
esc_attr( $subscribe_field_id ),
( ! empty( $data['subscribe_email'] )
? 'disabled title="' . esc_attr__( "You're logged in with this email", 'jetpack' ) . '"'
: 'title="' . esc_attr__( 'Please fill in this field.', 'jetpack' ) . '"'
<?php if ( ! empty( $styles['submit_button_wrapper'] ) ) : ?>
style="<?php echo esc_attr( $styles['submit_button_wrapper'] ); ?>"
<input type="hidden" name="action" value="subscribe"/>
<input type="hidden" name="blog_id" value="<?php echo (int) $blog_id; ?>"/>
<input type="hidden" name="source" value="<?php echo esc_url( $data['referer'] ); ?>"/>
<input type="hidden" name="sub-type" value="<?php echo esc_attr( $data['source'] ); ?>"/>
<input type="hidden" name="app_source" value="<?php echo esc_attr( $data['app_source'] ); ?>"/>
<input type="hidden" name="redirect_fragment" value="<?php echo esc_attr( $form_id ); ?>"/>
<input type="hidden" name="lang" value="<?php echo esc_attr( $lang ); ?>"/>
wp_nonce_field( 'blogsub_subscribe_' . $blog_id );
if ( ! empty( $post_id ) ) {
echo '<input type="hidden" name="post_id" value="' . esc_attr( $post_id ) . '"/>';
if ( ! empty( $tier_id ) ) {
echo '<input type="hidden" name="tier_id" value="' . esc_attr( $tier_id ) . '"/>';
if ( $data['preselected_newsletter_categories'] && ! empty( $data['selected_newsletter_categories'] ) ) {
echo '<input type="hidden" name="selected_newsletter_categories" value="' . esc_attr( implode( ',', $data['selected_newsletter_categories'] ) ) . '"/>';
<?php if ( ! empty( $classes['submit_button'] ) ) : ?>
class="<?php echo esc_attr( $classes['submit_button'] ); ?>"
<?php if ( ! empty( $styles['submit_button'] ) ) : ?>
style="<?php echo esc_attr( $styles['submit_button'] ); ?>"
name="jetpack_subscriptions_widget"
<?php echo sanitize_submit_text( $button_text ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php if ( $show_subscriber_count ) : ?>
<div class="wp-block-jetpack-subscriptions__subscount">
<?php echo esc_html( Jetpack_Memberships::get_join_others_text( $data['subscribers_total'] ) ); ?>
* Renders the email version of the subscriptions block.
* @param array $data Array containing block view data.
* @param array $styles Array containing the styles for different block elements.
function render_for_email( $data, $styles ) {
$submit_button_wrapper_style = ! empty( $styles['submit_button_wrapper'] ) ? 'style="' . esc_attr( $styles['submit_button_wrapper'] ) . '"' : '';
$button_text = get_submit_button_text( $data );
$html = '<div ' . wp_kses_data( $data['wrapper_attributes'] ) . '>
<p ' . $submit_button_wrapper_style . '>
<a href="' . esc_url( get_post_permalink() ) . '" style="' . esc_attr( $styles['submit_button'] ) . ' text-decoration: none; white-space: nowrap; margin-left: 0">' . sanitize_submit_text( $button_text ) . '</a>
* WooCommerce Email Editor render callback for the subscriptions block.
* @param string $block_content The block content.
* @param array $parsed_block The parsed block data.
* @param object $rendering_context The email rendering context.
function render_email( $block_content, array $parsed_block, $rendering_context ) {
if ( ! isset( $parsed_block['attrs'] ) || ! is_array( $parsed_block['attrs'] ) || ! function_exists( '\Automattic\Jetpack\Extensions\Button\render_email' ) || ! class_exists( '\Automattic\WooCommerce\EmailEditor\Integrations\Core\Renderer\Blocks\Button' ) ) {
// Map subscription block attributes to button block attributes
$button_attributes = array(
'text' => ! empty( $parsed_block['attrs']['submitButtonText'] ) ? sanitize_text_field( $parsed_block['attrs']['submitButtonText'] ) : __( 'Subscribe', 'jetpack' ),
'url' => get_post_permalink(),
'backgroundColor' => $parsed_block['attrs']['buttonBackgroundColor'] ?? null,
'customBackgroundColor' => $parsed_block['attrs']['customButtonBackgroundColor'] ?? null,
'textColor' => $parsed_block['attrs']['textColor'] ?? null,
'customTextColor' => $parsed_block['attrs']['customTextColor'] ?? null,
'borderRadius' => $parsed_block['attrs']['borderRadius'] ?? 0,
'borderWeight' => $parsed_block['attrs']['borderWeight'] ?? 1,
'borderColor' => $parsed_block['attrs']['borderColor'] ?? null,
'customBorderColor' => $parsed_block['attrs']['customBorderColor'] ?? null,
'fontSize' => $parsed_block['attrs']['fontSize'] ?? null,
'customFontSize' => $parsed_block['attrs']['customFontSize'] ?? null,
'padding' => $parsed_block['attrs']['padding'] ?? null,
// Create a mock button block structure
$button_parsed_block = array(
'attrs' => $button_attributes,
'email_attrs' => $parsed_block['email_attrs'] ?? array(),
// Call the Jetpack button's email rendering
return \Automattic\Jetpack\Extensions\Button\render_email(
* @param string $the_content Post content.
function add_paywall( $the_content ) {
require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
$post_access_level = Jetpack_Memberships::get_post_access_level();
if ( Jetpack_Memberships::user_can_view_post() ) {
if ( $post_access_level !== Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_EVERYBODY ) {
'earn_track_paywalled_post_view',
'post_id' => get_the_ID(),