PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 13.2.4
Jetpack – WP Security, Backup, Speed, & Growth v13.2.4
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 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / extensions / blocks / subscriptions / subscriptions.php
jetpack / extensions / blocks / subscriptions Last commit date
class-jetpack-subscription-site.php 2 years ago constants.php 2 years ago subscriptions.php 2 years ago
subscriptions.php
1256 lines
1 <?php
2 /**
3 * Subscriptions Block.
4 *
5 * @package automattic/jetpack
6 */
7
8 namespace Automattic\Jetpack\Extensions\Subscriptions;
9
10 use Automattic\Jetpack\Blocks;
11 use Automattic\Jetpack\Connection\Manager as Connection_Manager;
12 use Automattic\Jetpack\Extensions\Premium_Content\Subscription_Service\Abstract_Token_Subscription_Service;
13 use Automattic\Jetpack\Extensions\Premium_Content\Subscription_Service\Jetpack_Token_Subscription_Service;
14 use Automattic\Jetpack\Status;
15 use Automattic\Jetpack\Status\Host;
16 use Jetpack;
17 use Jetpack_Gutenberg;
18 use Jetpack_Memberships;
19 use Jetpack_Subscriptions_Widget;
20
21 require_once __DIR__ . '/class-jetpack-subscription-site.php';
22 require_once __DIR__ . '/constants.php';
23 require_once JETPACK__PLUGIN_DIR . 'extensions/blocks/premium-content/_inc/subscription-service/include.php';
24
25 /**
26 * These block defaults should match ./constants.js
27 */
28 const DEFAULT_BORDER_RADIUS_VALUE = 0;
29 const DEFAULT_BORDER_WEIGHT_VALUE = 1;
30 const DEFAULT_FONTSIZE_VALUE = '16px';
31 const DEFAULT_PADDING_VALUE = 15;
32 const DEFAULT_SPACING_VALUE = 10;
33
34 /**
35 * Registers the block for use in Gutenberg
36 * This is done via an action so that we can disable
37 * registration if we need to.
38 */
39 function register_block() {
40 /*
41 * Disable the feature on P2 blogs
42 */
43 if ( function_exists( '\WPForTeams\is_wpforteams_site' ) &&
44 \WPForTeams\is_wpforteams_site( get_current_blog_id() ) ) {
45 return;
46 }
47
48 if (
49 ( defined( 'IS_WPCOM' ) && IS_WPCOM )
50 || ( ( new Connection_Manager( 'jetpack' ) )->has_connected_owner() && ! ( new Status() )->is_offline_mode() )
51 ) {
52 Blocks::jetpack_register_block(
53 __DIR__,
54 array(
55 'render_callback' => __NAMESPACE__ . '\render_block',
56 'supports' => array(
57 'spacing' => array(
58 'margin' => true,
59 'padding' => true,
60 ),
61 'align' => array( 'wide', 'full' ),
62 ),
63 )
64 );
65 }
66
67 /*
68 * If the Subscriptions module is not active,
69 * do not make any further changes on the site.
70 */
71 if ( ! Jetpack::is_module_active( 'subscriptions' ) ) {
72 return;
73 }
74
75 /**
76 * Do not proceed if the newsletter feature is not enabled
77 * or if the 'Jetpack_Memberships' class does not exists.
78 */
79 if ( ! class_exists( '\Jetpack_Memberships' ) ) {
80 return;
81 }
82
83 register_post_meta(
84 'post',
85 META_NAME_FOR_POST_LEVEL_ACCESS_SETTINGS,
86 array(
87 'show_in_rest' => true,
88 'single' => true,
89 'type' => 'string',
90 'auth_callback' => function () {
91 return wp_get_current_user()->has_cap( 'edit_posts' );
92 },
93 )
94 );
95
96 register_post_meta(
97 'post',
98 META_NAME_FOR_POST_DONT_EMAIL_TO_SUBS,
99 array(
100 'default' => false,
101 'show_in_rest' => true,
102 'single' => true,
103 'type' => 'boolean',
104 'auth_callback' => function () {
105 return wp_get_current_user()->has_cap( 'edit_posts' );
106 },
107 )
108 );
109
110 register_post_meta(
111 'post',
112 META_NAME_FOR_POST_TIER_ID_SETTINGS,
113 array(
114 'show_in_rest' => true,
115 'single' => true,
116 'type' => 'integer',
117 'auth_callback' => function () {
118 return wp_get_current_user()->has_cap( 'edit_posts' );
119 },
120 )
121 );
122
123 // This ensures Jetpack will sync this post meta to WPCOM.
124 add_filter(
125 'jetpack_sync_post_meta_whitelist',
126 function ( $allowed_meta ) {
127 return array_merge(
128 $allowed_meta,
129 array(
130 META_NAME_FOR_POST_LEVEL_ACCESS_SETTINGS,
131 META_NAME_FOR_POST_DONT_EMAIL_TO_SUBS,
132 )
133 );
134 }
135 );
136
137 // Hide the content – Priority 8 makes it run before do_blocks gets called for the content
138 add_filter( 'the_content', __NAMESPACE__ . '\add_paywall', 8 );
139
140 // Close comments on the front-end
141 add_filter( 'comments_open', __NAMESPACE__ . '\maybe_close_comments', 10, 2 );
142 add_filter( 'pings_open', __NAMESPACE__ . '\maybe_close_comments', 10, 2 );
143
144 // Hide existing comments
145 add_filter( 'get_comment', __NAMESPACE__ . '\maybe_gate_existing_comments' );
146
147 // Gate the excerpt for a post
148 add_filter( 'get_the_excerpt', __NAMESPACE__ . '\jetpack_filter_excerpt_for_newsletter', 10, 2 );
149
150 // Add a 'Newsletter' column to the Edit posts page
151 // We only display the "Newsletter" column if we have configured the paid newsletter plan
152 if ( Jetpack_Memberships::has_configured_plans_jetpack_recurring_payments( 'newsletter' ) ) {
153 add_action( 'manage_post_posts_columns', __NAMESPACE__ . '\register_newsletter_access_column' );
154 add_action( 'manage_post_posts_custom_column', __NAMESPACE__ . '\render_newsletter_access_rows', 10, 2 );
155 add_action( 'admin_head', __NAMESPACE__ . '\newsletter_access_column_styles' );
156 }
157
158 add_action( 'init', __NAMESPACE__ . '\maybe_prevent_super_cache_caching' );
159
160 Jetpack_Subscription_Site::init()->handle_subscribe_block_placements();
161 }
162 add_action( 'init', __NAMESPACE__ . '\register_block', 9 );
163
164 /**
165 * Returns true when in a WP.com environment.
166 *
167 * @return boolean
168 */
169 function is_wpcom() {
170 return defined( 'IS_WPCOM' ) && IS_WPCOM;
171 }
172
173 /**
174 * Adds a 'Newsletter' column after the 'Title' column in the post list
175 *
176 * @param array $columns An array of column names.
177 * @return array An array of column names.
178 */
179 function register_newsletter_access_column( $columns ) {
180 $position = array_search( 'title', array_keys( $columns ), true );
181 $new_column = array( NEWSLETTER_COLUMN_ID => __( 'Newsletter', 'jetpack' ) );
182 return array_merge(
183 array_slice( $columns, 0, $position + 1, true ),
184 $new_column,
185 array_slice( $columns, $position, null, true )
186 );
187 }
188
189 /**
190 * Displays the newsletter access level.
191 *
192 * @param string $column_id The ID of the column to display.
193 * @param int $post_id The current post ID.
194 */
195 function render_newsletter_access_rows( $column_id, $post_id ) {
196 if ( NEWSLETTER_COLUMN_ID !== $column_id ) {
197 return;
198 }
199
200 $access_level = get_post_meta( $post_id, META_NAME_FOR_POST_LEVEL_ACCESS_SETTINGS, true );
201
202 switch ( $access_level ) {
203 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_PAID_SUBSCRIBERS_ALL_TIERS:
204 echo esc_html__( 'Paid Subscribers (all plans)', 'jetpack' );
205 break;
206 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_PAID_SUBSCRIBERS:
207 echo esc_html__( 'Paid Subscribers', 'jetpack' );
208 break;
209 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_SUBSCRIBERS:
210 echo esc_html__( 'Subscribers', 'jetpack' );
211 break;
212 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_EVERYBODY:
213 echo esc_html__( 'Everybody', 'jetpack' );
214 break;
215 default:
216 echo '';
217 }
218 }
219
220 /**
221 * Adds the Newsletter column styles
222 */
223 function newsletter_access_column_styles() {
224 echo '<style id="jetpack-newsletter-newsletter-access-column"> table.fixed .column-newsletter_access { width: 10%; } </style>';
225 }
226
227 /**
228 * Determine the amount of folks currently subscribed to the blog, splitted out in email_subscribers & social_followers & paid_subscribers
229 *
230 * @return array containing ['value' => ['email_subscribers' => 0, 'paid_subscribers' => 0, 'social_followers' => 0]]
231 */
232 function fetch_subscriber_counts() {
233 $subs_count = 0;
234 if ( is_wpcom() ) {
235 $subs_count = array(
236 'value' => \wpcom_fetch_subs_counts( true ),
237 );
238 } else {
239 $cache_key = 'wpcom_subscribers_totals';
240 $subs_count = get_transient( $cache_key );
241 if ( false === $subs_count || 'failed' === $subs_count['status'] ) {
242 $xml = new \Jetpack_IXR_Client();
243 $xml->query( 'jetpack.fetchSubscriberCounts' );
244
245 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.
246 $subs_count = array(
247 'status' => 'failed',
248 'code' => $xml->getErrorCode(),
249 'message' => $xml->getErrorMessage(),
250 'value' => ( isset( $subs_count['value'] ) ) ? $subs_count['value'] : array(
251 'email_subscribers' => 0,
252 'social_followers' => 0,
253 'paid_subscribers' => 0,
254 ),
255 );
256 } else {
257 $subs_count = array(
258 'status' => 'success',
259 'value' => $xml->getResponse(),
260 );
261 }
262 set_transient( $cache_key, $subs_count, 3600 ); // Try to cache the result for at least 1 hour.
263 }
264 }
265 return $subs_count;
266 }
267
268 /**
269 * Returns subscriber count based on include_social_followers attribute
270 *
271 * @param bool $include_social_followers Whether to include social followers in the count.
272 * @return int
273 */
274 function get_subscriber_count( $include_social_followers ) {
275 $counts = fetch_subscriber_counts();
276
277 if ( $include_social_followers ) {
278 $subscriber_count = $counts['value']['email_subscribers'] + $counts['value']['social_followers'];
279 } else {
280 $subscriber_count = $counts['value']['email_subscribers'];
281 }
282 return $subscriber_count;
283 }
284
285 /**
286 * Returns true if the block attributes contain a value for the given key.
287 *
288 * @param array $attributes Array containing the block attributes.
289 * @param string $key Block attribute key.
290 *
291 * @return boolean
292 */
293 function has_attribute( $attributes, $key ) {
294 return isset( $attributes[ $key ] ) && $attributes[ $key ] !== 'undefined';
295 }
296
297 /**
298 * Returns the value for the given attribute key, with the option of providing a default fallback value.
299 *
300 * @param array $attributes Array containing the block attributes.
301 * @param string $key Block attribute key.
302 * @param mixed $default Optional fallback value in case the key doesn't exist.
303 *
304 * @return mixed
305 */
306 function get_attribute( $attributes, $key, $default = null ) {
307 return has_attribute( $attributes, $key ) ? $attributes[ $key ] : $default;
308 }
309
310 /**
311 * Mimics getColorClassName, getFontSizeClass and getGradientClass from @wordpress/block-editor js package.
312 *
313 * @param string $setting Setting name.
314 * @param string $value Setting value.
315 *
316 * @return string
317 */
318 function get_setting_class_name( $setting, $value ) {
319 if ( ! $setting || ! $value ) {
320 return '';
321 }
322
323 return sprintf( 'has-%s-%s', $value, $setting );
324 }
325
326 /**
327 * Uses block attributes to generate an array containing the classes for various block elements.
328 * Based on Jetpack_Subscriptions_Widget::do_subscription_form() which the block was originally using.
329 *
330 * @param array $attributes Array containing the block attributes.
331 *
332 * @return array
333 */
334 function get_element_class_names_from_attributes( $attributes ) {
335 $text_color_class = get_setting_class_name( 'color', get_attribute( $attributes, 'textColor' ) );
336 $font_size_class = get_setting_class_name( 'font-size', get_attribute( $attributes, 'fontSize' ) );
337 $border_class = get_setting_class_name( 'border-color', get_attribute( $attributes, 'borderColor' ) );
338
339 $button_background_class = get_setting_class_name( 'background-color', get_attribute( $attributes, 'buttonBackgroundColor' ) );
340 $button_gradient_class = get_setting_class_name( 'gradient-background', get_attribute( $attributes, 'buttonGradient' ) );
341
342 $email_field_background_class = get_setting_class_name( 'background-color', get_attribute( $attributes, 'emailFieldBackgroundColor' ) );
343 $email_field_gradient_class = get_setting_class_name( 'gradient-background', get_attribute( $attributes, 'emailFieldGradient' ) );
344
345 $submit_button_classes = array_filter(
346 array(
347 'wp-block-button__link' => true,
348 'no-border-radius' => 0 === get_attribute( $attributes, 'borderRadius', 0 ),
349 $font_size_class => true,
350 $border_class => true,
351 'has-text-color' => ! empty( $text_color_class ),
352 $text_color_class => true,
353 'has-background' => ! empty( $button_background_class ) || ! empty( $button_gradient_class ),
354 $button_background_class => ! empty( $button_background_class ),
355 $button_gradient_class => ! empty( $button_gradient_class ),
356 )
357 );
358
359 $email_field_classes = array_filter(
360 array(
361 'no-border-radius' => 0 === get_attribute( $attributes, 'borderRadius', 0 ),
362 $font_size_class => true,
363 $border_class => true,
364 $email_field_background_class => true,
365 $email_field_gradient_class => true,
366 )
367 );
368
369 $block_wrapper_classes = array_filter(
370 array(
371 'wp-block-jetpack-subscriptions__supports-newline' => true,
372 'wp-block-jetpack-subscriptions__use-newline' => (bool) get_attribute( $attributes, 'buttonOnNewLine' ),
373 'wp-block-jetpack-subscriptions__show-subs' => (bool) get_attribute( $attributes, 'showSubscribersTotal' ),
374 )
375 );
376
377 return array(
378 'block_wrapper' => implode( ' ', array_keys( $block_wrapper_classes ) ),
379 'email_field' => implode( ' ', array_keys( $email_field_classes ) ),
380 'submit_button' => implode( ' ', array_keys( $submit_button_classes ) ),
381 );
382 }
383
384 /**
385 * Uses block attributes to generate an array containing the styles for various block elements.
386 * Based on Jetpack_Subscriptions_Widget::do_subscription_form() which the block was originally using.
387 *
388 * @param array $attributes Array containing the block attributes.
389 *
390 * @return array
391 */
392 function get_element_styles_from_attributes( $attributes ) {
393 $button_background_style = ! has_attribute( $attributes, 'buttonBackgroundColor' ) && has_attribute( $attributes, 'customButtonGradient' )
394 ? get_attribute( $attributes, 'customButtonGradient' )
395 : get_attribute( $attributes, 'customButtonBackgroundColor' );
396
397 $email_field_styles = '';
398 $submit_button_wrapper_styles = '';
399 $submit_button_styles = '';
400
401 if ( ! empty( $button_background_style ) ) {
402 $submit_button_styles .= sprintf( 'background: %s;', $button_background_style );
403 }
404
405 if ( has_attribute( $attributes, 'customTextColor' ) ) {
406 $submit_button_styles .= sprintf( 'color: %s;', get_attribute( $attributes, 'customTextColor' ) );
407 }
408
409 if ( has_attribute( $attributes, 'buttonWidth' ) ) {
410 $submit_button_wrapper_styles .= sprintf( 'width: %s;', get_attribute( $attributes, 'buttonWidth' ) );
411 $submit_button_wrapper_styles .= 'max-width: 100%;';
412
413 // Account for custom margins on inline forms.
414 $submit_button_styles .= true === get_attribute( $attributes, 'buttonOnNewLine' )
415 ? sprintf( 'width: calc(100%% - %dpx);', get_attribute( $attributes, 'spacing', DEFAULT_SPACING_VALUE ) )
416 : 'width: 100%;';
417 }
418
419 $font_size = get_attribute( $attributes, 'customFontSize', DEFAULT_FONTSIZE_VALUE );
420 $style = sprintf( 'font-size: %s%s;', $font_size, is_numeric( $font_size ) ? 'px' : '' );
421
422 $submit_button_styles .= $style;
423 $email_field_styles .= $style;
424
425 $padding = get_attribute( $attributes, 'padding', DEFAULT_PADDING_VALUE );
426 $style = sprintf( 'padding: %1$dpx %2$dpx %1$dpx %2$dpx;', $padding, round( $padding * 1.5 ) );
427
428 $submit_button_styles .= $style;
429 $email_field_styles .= $style;
430
431 $button_spacing = get_attribute( $attributes, 'spacing', DEFAULT_SPACING_VALUE );
432 if ( true === get_attribute( $attributes, 'buttonOnNewLine' ) ) {
433 $submit_button_styles .= sprintf( 'margin-top: %dpx;', $button_spacing );
434 } else {
435 $submit_button_styles .= 'margin: 0px; '; // Reset Safari's 2px default margin for buttons affecting input and button union
436 $submit_button_styles .= sprintf( 'margin-left: %dpx;', $button_spacing );
437 }
438
439 if ( has_attribute( $attributes, 'borderColor' ) ) {
440 $style = sprintf( 'border-color: %s;', get_attribute( $attributes, 'borderColor', '' ) );
441 $submit_button_styles .= $style;
442 $email_field_styles .= $style;
443 }
444
445 $style = sprintf( 'border-radius: %dpx;', get_attribute( $attributes, 'borderRadius', DEFAULT_BORDER_RADIUS_VALUE ) );
446 $submit_button_styles .= $style;
447 $email_field_styles .= $style;
448
449 $style = sprintf( 'border-width: %dpx;', get_attribute( $attributes, 'borderWeight', DEFAULT_BORDER_WEIGHT_VALUE ) );
450 $submit_button_styles .= $style;
451 $email_field_styles .= $style;
452
453 if ( has_attribute( $attributes, 'customBorderColor' ) ) {
454 $style = sprintf( 'border-color: %s; border-style: solid;', get_attribute( $attributes, 'customBorderColor' ) );
455
456 $submit_button_styles .= $style;
457 $email_field_styles .= $style;
458 }
459
460 if ( ! jetpack_is_frontend() ) {
461 $background_color_style = get_attribute_color( 'buttonBackgroundColor', $attributes, '#113AF5' /* default lettre theme color */ );
462 $text_color_style = get_attribute_color( 'textColor', $attributes, '#FFFFFF' );
463 $submit_button_styles .= sprintf( ' background-color: %s; color: %s;', $background_color_style, $text_color_style );
464 }
465
466 return array(
467 'email_field' => $email_field_styles,
468 'submit_button' => $submit_button_styles,
469 'submit_button_wrapper' => $submit_button_wrapper_styles,
470 );
471 }
472
473 /**
474 * Retrieve the resolved color for a given attribute.
475 *
476 * @param string $attribute_name The name of the attribute to resolve.
477 * @param array $attributes An array of all attributes.
478 * @param string $default_color A fallback color in case no color can be resolved.
479 *
480 * @return string Returns the resolved color or the default color if no color is found.
481 */
482 function get_attribute_color( $attribute_name, $attributes, $default_color ) {
483 if ( has_attribute( $attributes, $attribute_name ) ) {
484 $color_slug = get_attribute( $attributes, $attribute_name );
485 $resolved_color = get_color_from_slug( $color_slug );
486
487 if ( $resolved_color ) {
488 return $resolved_color;
489 }
490 }
491
492 return get_global_style_color( $attribute_name, $default_color );
493 }
494
495 /**
496 * Retrieve the global style color based on a provided style key.
497 *
498 * @param string $style_key The key for the desired style.
499 * @param string $default_color A fallback color in case the global style is not set.
500 *
501 * @return string Returns the color defined in global styles or the default color if not defined.
502 */
503 function get_global_style_color( $style_key, $default_color ) {
504 $global_styles = wp_get_global_styles(
505 array( 'color' ),
506 array(
507 'block_name' => 'core/button',
508 'transforms' => array( 'resolve-variables' ),
509 )
510 );
511
512 if ( isset( $global_styles[ $style_key ] ) ) {
513 return $global_styles[ $style_key ];
514 }
515
516 return $default_color;
517 }
518
519 /**
520 * Convert a color slug into its corresponding color value.
521 *
522 * @param string $slug The slug representation of the color.
523 *
524 * @return string|null Returns the color value if found, or null otherwise.
525 */
526 function get_color_from_slug( $slug ) {
527 $color_palettes = wp_get_global_settings( array( 'color', 'palette' ) );
528
529 if ( ! is_array( $color_palettes ) ) {
530 return null;
531 }
532
533 foreach ( $color_palettes as $palette ) {
534 if ( is_array( $palette ) ) {
535 foreach ( $palette as $color ) {
536 if ( isset( $color['slug'] ) && $color['slug'] === $slug && isset( $color['color'] ) ) {
537 return $color['color'];
538 }
539 }
540 }
541 }
542
543 return null;
544 }
545
546 /**
547 * Subscriptions block render callback.
548 *
549 * @param array $attributes Array containing the block attributes.
550 *
551 * @return string
552 */
553 function render_block( $attributes ) {
554 // If the Subscriptions module is not active, don't render the block.
555 if ( ! Jetpack::is_module_active( 'subscriptions' ) ) {
556 return '';
557 }
558
559 if ( class_exists( '\Jetpack_Memberships' ) ) {
560 // We only want the sites that have newsletter feature enabled to be graced by this JavaScript.
561 Jetpack_Gutenberg::load_assets_as_required( __DIR__ );
562 } else {
563 Jetpack_Gutenberg::load_styles_as_required( FEATURE_NAME );
564 }
565
566 $subscribe_email = Jetpack_Memberships::get_current_user_email();
567
568 /** This filter is documented in modules/contact-form/grunion-contact-form.php */
569 if ( is_wpcom() || false !== apply_filters( 'jetpack_auto_fill_logged_in_user', false ) ) {
570 $current_user = wp_get_current_user();
571 $subscribe_email = ! empty( $current_user->user_email ) ? $current_user->user_email : '';
572 }
573
574 // The block is using the Jetpack_Subscriptions_Widget backend, hence the need to increase the instance count.
575 ++Jetpack_Subscriptions_Widget::$instance_count;
576
577 $classes = get_element_class_names_from_attributes( $attributes );
578 $styles = get_element_styles_from_attributes( $attributes );
579
580 // The default value was previously "true" in block.json. We don't want to rely setting "default" in block.json to falsy,
581 // because it would change the setting for previously saved blocks. Block editor doesn't store default values in attributes at all.
582 // Hence users without this set will still get social counts included in the subscriber counter.
583 // Lowering the subscriber count on their behalf with code change would be controversial.
584 // We want to disencourage including social count as it's misleading.
585 $include_social_followers = isset( $attributes['includeSocialFollowers'] ) ? (bool) get_attribute( $attributes, 'includeSocialFollowers' ) : true;
586
587 $data = array(
588 'widget_id' => Jetpack_Subscriptions_Widget::$instance_count,
589 'subscribe_email' => $subscribe_email,
590 'is_paid_subscriber' => get_attribute( $attributes, 'isPaidSubscriber', false ),
591 'wrapper_attributes' => get_block_wrapper_attributes(
592 array(
593 'class' => $classes['block_wrapper'],
594 )
595 ),
596 'subscribe_placeholder' => get_attribute( $attributes, 'subscribePlaceholder', __( 'Type your email…', 'jetpack' ) ),
597 'submit_button_text' => get_attribute( $attributes, 'submitButtonText', __( 'Subscribe', 'jetpack' ) ),
598 'submit_button_text_subscribed' => get_attribute( $attributes, 'submitButtonTextSubscribed', __( 'Subscribed', 'jetpack' ) ),
599 'submit_button_text_upgrade' => get_attribute( $attributes, 'submitButtonTextUpgrade', __( 'Upgrade subscription', 'jetpack' ) ),
600 'success_message' => get_attribute(
601 $attributes,
602 'successMessage',
603 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' )
604 ),
605 'show_subscribers_total' => (bool) get_attribute( $attributes, 'showSubscribersTotal' ),
606 'subscribers_total' => get_subscriber_count( $include_social_followers ),
607 'referer' => esc_url_raw(
608 ( is_ssl() ? 'https' : 'http' ) . '://' . ( isset( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : '' ) .
609 ( isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '' )
610 ),
611 'source' => 'subscribe-block',
612 'app_source' => get_attribute( $attributes, 'appSource', null ),
613 );
614
615 if ( ! jetpack_is_frontend() ) {
616 return render_for_email( $data, $styles );
617 }
618
619 return render_for_website( $data, $classes, $styles );
620 }
621
622 /**
623 * Get the post access level for the current post. Defaults to 'everybody' if the query is not for a single post
624 *
625 * @return string the actual post access level (see projects/plugins/jetpack/extensions/blocks/subscriptions/constants.js for the values).
626 */
627 function get_post_access_level_for_current_post() {
628 if ( ! is_singular() ) {
629 // There is no "actual" current post.
630 return Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_EVERYBODY;
631 }
632
633 return Jetpack_Memberships::get_post_access_level();
634 }
635
636 /**
637 * Renders the subscriptions block at the site.
638 *
639 * @param array $data Array containing block view data.
640 * @param array $classes Array containing the classes for different block elements.
641 * @param array $styles Array containing the styles for different block elements.
642 *
643 * @return string
644 */
645 function render_for_website( $data, $classes, $styles ) {
646 $lang = get_locale();
647 $blog_id = \Jetpack_Options::get_option( 'id' );
648 $widget_id_suffix = Jetpack_Subscriptions_Widget::$instance_count > 1 ? '-' . Jetpack_Subscriptions_Widget::$instance_count : '';
649 $form_id = 'subscribe-blog' . $widget_id_suffix;
650 $form_url = 'https://wordpress.com/email-subscriptions';
651 $post_access_level = get_post_access_level_for_current_post();
652
653 // Post ID is used for pulling post-specific paid status, and returning to the right post after confirming subscription
654 $post_id = null;
655 if ( in_the_loop() ) {
656 $post_id = get_the_ID();
657 } elseif ( is_singular( 'post' ) || is_page() ) {
658 $post_id = get_queried_object_id();
659 } else {
660 $post_id = get_option( 'page_on_front' );
661 }
662
663 $subscribe_field_id = apply_filters( 'subscribe_field_id', 'subscribe-field' . $widget_id_suffix, $data['widget_id'] );
664 $tier_id = get_post_meta( $post_id, META_NAME_FOR_POST_TIER_ID_SETTINGS, true );
665 $is_subscribed = Jetpack_Memberships::is_current_user_subscribed();
666 $button_text = get_submit_button_text( $data );
667
668 ob_start();
669
670 Jetpack_Subscriptions_Widget::render_widget_status_messages(
671 array(
672 'success_message' => $data['success_message'],
673 )
674 );
675 ?>
676 <div <?php echo wp_kses_data( $data['wrapper_attributes'] ); ?>>
677 <div class="wp-block-jetpack-subscriptions__container<?php echo ! $is_subscribed ? ' is-not-subscriber' : ''; ?>">
678 <form
679 action="<?php echo esc_url( $form_url ); ?>"
680 method="post"
681 accept-charset="utf-8"
682 data-blog="<?php echo esc_attr( $blog_id ); ?>"
683 data-post_access_level="<?php echo esc_attr( $post_access_level ); ?>"
684 data-subscriber_email="<?php echo esc_attr( $data['subscribe_email'] ); ?>"
685 id="<?php echo esc_attr( $form_id ); ?>"
686 >
687 <div class="wp-block-jetpack-subscriptions__form-elements">
688 <?php if ( ! $is_subscribed ) : ?>
689 <p id="subscribe-email">
690 <label
691 id="<?php echo esc_attr( $subscribe_field_id . '-label' ); ?>"
692 for="<?php echo esc_attr( $subscribe_field_id ); ?>"
693 class="screen-reader-text"
694 >
695 <?php echo esc_html( $data['subscribe_placeholder'] ); ?>
696 </label>
697 <?php
698 printf(
699 '<input
700 required="required"
701 type="email"
702 name="email"
703 %1$s
704 style="%2$s"
705 placeholder="%3$s"
706 value="%4$s"
707 id="%5$s"
708 %6$s
709 />',
710 ( ! empty( $classes['email_field'] )
711 ? 'class="' . esc_attr( $classes['email_field'] ) . '"'
712 : ''
713 ),
714 ( ! empty( $styles['email_field'] )
715 ? esc_attr( $styles['email_field'] )
716 : 'width: 95%; padding: 1px 10px'
717 ),
718 esc_attr( $data['subscribe_placeholder'] ),
719 esc_attr( $data['subscribe_email'] ),
720 esc_attr( $subscribe_field_id ),
721 ( ! empty( $data['subscribe_email'] )
722 ? 'disabled title="' . esc_attr__( "You're logged in with this email", 'jetpack' ) . '"'
723 : ''
724 )
725 );
726 ?>
727 </p>
728 <?php endif; ?>
729 <p id="subscribe-submit"
730 <?php if ( ! empty( $styles['submit_button_wrapper'] ) ) : ?>
731 style="<?php echo esc_attr( $styles['submit_button_wrapper'] ); ?>"
732 <?php endif; ?>
733 >
734 <input type="hidden" name="action" value="<?php echo is_top_subscription() ? 'subscribed' : 'subscribe'; ?>"/>
735 <input type="hidden" name="blog_id" value="<?php echo (int) $blog_id; ?>"/>
736 <input type="hidden" name="source" value="<?php echo esc_url( $data['referer'] ); ?>"/>
737 <input type="hidden" name="sub-type" value="<?php echo esc_attr( $data['source'] ); ?>"/>
738 <input type="hidden" name="app_source" value="<?php echo esc_attr( $data['app_source'] ); ?>"/>
739 <input type="hidden" name="redirect_fragment" value="<?php echo esc_attr( $form_id ); ?>"/>
740 <input type="hidden" name="lang" value="<?php echo esc_attr( $lang ); ?>"/>
741 <?php
742 wp_nonce_field( 'blogsub_subscribe_' . $blog_id );
743
744 if ( ! empty( $post_id ) ) {
745 echo '<input type="hidden" name="post_id" value="' . esc_attr( $post_id ) . '"/>';
746 }
747
748 if ( ! empty( $tier_id ) ) {
749 echo '<input type="hidden" name="tier_id" value="' . esc_attr( $tier_id ) . '"/>';
750 }
751 ?>
752 <button type="submit"
753 <?php if ( ! empty( $classes['submit_button'] ) ) : ?>
754 class="<?php echo esc_attr( $classes['submit_button'] ); ?>"
755 <?php endif; ?>
756 <?php if ( ! empty( $styles['submit_button'] ) ) : ?>
757 style="<?php echo esc_attr( $styles['submit_button'] ); ?>"
758 <?php endif; ?>
759 name="jetpack_subscriptions_widget"
760 >
761 <?php echo sanitize_submit_text( $button_text ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
762 </button>
763 </p>
764 </div>
765 </form>
766 <?php if ( $data['show_subscribers_total'] && $data['subscribers_total'] ) : ?>
767 <div class="wp-block-jetpack-subscriptions__subscount">
768 <?php
769 echo esc_html( Jetpack_Memberships::get_join_others_text( $data['subscribers_total'] ) );
770 ?>
771 </div>
772 <?php endif; ?>
773 </div>
774 </div>
775 <?php
776 return ob_get_clean();
777 }
778
779 /**
780 * Renders the email version of the subscriptions block.
781 *
782 * @param array $data Array containing block view data.
783 * @param array $styles Array containing the styles for different block elements.
784 *
785 * @return string
786 */
787 function render_for_email( $data, $styles ) {
788 $submit_button_wrapper_style = ! empty( $styles['submit_button_wrapper'] ) ? 'style="' . esc_attr( $styles['submit_button_wrapper'] ) . '"' : '';
789 $button_text = get_submit_button_text( $data );
790
791 $html = '<div ' . wp_kses_data( $data['wrapper_attributes'] ) . '>
792 <div>
793 <div>
794 <div>
795 <p ' . $submit_button_wrapper_style . '>
796 <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>
797 </p>
798 </div>
799 </div>
800 </div>
801 </div>';
802
803 return $html;
804 }
805
806 /**
807 * Filter excerpts looking for subscription data.
808 *
809 * @param string $excerpt The extrapolated excerpt string.
810 * @param \WP_Post $post The current post being processed (in `get_the_excerpt`).
811 *
812 * @return mixed
813 */
814 function jetpack_filter_excerpt_for_newsletter( $excerpt, $post = null ) {
815 // The blogmagazine theme is overriding WP core `get_the_excerpt` filter and only passing the excerpt
816 // TODO: Until this is fixed, return the excerpt without gating. See https://github.com/Automattic/jetpack/pull/28102#issuecomment-1369161116
817 if ( $post && str_contains( $post->post_content, '<!-- wp:jetpack/subscriptions -->' ) ) {
818 $excerpt .= sprintf(
819 // translators: %s is the permalink url to the current post.
820 __( "<p><a href='%s'>View post</a> to subscribe to site newsletter.</p>", 'jetpack' ),
821 get_post_permalink()
822 );
823 }
824 return $excerpt;
825 }
826
827 /**
828 * Gate access to posts
829 *
830 * @param string $the_content Post content.
831 *
832 * @return string
833 */
834 function add_paywall( $the_content ) {
835 require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
836
837 $post_access_level = Jetpack_Memberships::get_post_access_level();
838
839 if ( Jetpack_Memberships::user_can_view_post() ) {
840 if ( $post_access_level !== Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_EVERYBODY ) {
841 do_action(
842 'earn_track_paywalled_post_view',
843 array(
844 'post_id' => get_the_ID(),
845 )
846 );
847 }
848 return $the_content;
849 }
850
851 $paywalled_content = get_paywall_content( $post_access_level ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
852
853 if ( has_block( \Automattic\Jetpack\Extensions\Paywall\BLOCK_NAME ) ) {
854 if ( strpos( $the_content, \Automattic\Jetpack\Extensions\Paywall\BLOCK_HTML ) ) {
855 return strstr( $the_content, \Automattic\Jetpack\Extensions\Paywall\BLOCK_HTML, true ) . $paywalled_content;
856 }
857 // WordPress generates excerpts by either rendering or stripping blocks before invoking the `the_content` filter.
858 // In the context of generating an excerpt, the Paywall block specifically renders THE_EXCERPT_BLOCK.
859 if ( strpos( $the_content, \Automattic\Jetpack\Extensions\Paywall\THE_EXCERPT_BLOCK ) ) {
860 return strstr( $the_content, \Automattic\Jetpack\Extensions\Paywall\THE_EXCERPT_BLOCK, true );
861 }
862 }
863
864 return $paywalled_content;
865 }
866
867 /**
868 * Gate access to comments. We want to close comments on private sites.
869 *
870 * @param bool $default_comments_open Default state of the comments_open filter.
871 * @param int $post_id Current post id.
872 *
873 * @return bool
874 */
875 function maybe_close_comments( $default_comments_open, $post_id ) {
876 if ( ! $default_comments_open || ! $post_id ) {
877 return $default_comments_open;
878 }
879
880 require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
881 return Jetpack_Memberships::user_can_view_post();
882 }
883
884 /**
885 * Gate access to existing comments
886 *
887 * @param string $comment The comment.
888 *
889 * @return string
890 */
891 function maybe_gate_existing_comments( $comment ) {
892 if ( empty( $comment ) ) {
893 return $comment;
894 }
895
896 require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
897 if ( Jetpack_Memberships::user_can_view_post() ) {
898 return $comment;
899 }
900 return '';
901 }
902
903 /**
904 * Is the Jetpack_Token_Subscription_Service class loaded
905 *
906 * @return bool
907 */
908 function is_jetpack_token_subscription_service_loaded(): bool {
909 return class_exists( 'Automattic\Jetpack\Extensions\Premium_Content\Subscription_Service\Jetpack_Token_Subscription_Service' );
910 }
911
912 /**
913 * Adds support for WP Super cache and Boost cache
914 */
915 function maybe_prevent_super_cache_caching() {
916 // Prevents cached page to be served if the Membership cookie is present
917 if ( is_jetpack_token_subscription_service_loaded() ) {
918 do_action( 'wpsc_add_cookie', Jetpack_Token_Subscription_Service::JWT_AUTH_TOKEN_COOKIE_NAME );
919 }
920
921 if ( is_user_auth() ) {
922 // Do not cache the page if user is auth with Membership token
923 if ( ! defined( 'DONOTCACHEPAGE' ) ) {
924 define( 'DONOTCACHEPAGE', true );
925 }
926 }
927 }
928
929 /**
930 * Returns paywall content blocks
931 *
932 * @param string $post_access_level The newsletter access level.
933 * @return string
934 */
935 function get_paywall_content( $post_access_level ) {
936 if ( Jetpack_Memberships::user_is_pending_subscriber() ) {
937 return get_paywall_blocks_subscribe_pending();
938 }
939 if ( doing_filter( 'get_the_excerpt' ) ) {
940 return '';
941 }
942 return get_paywall_blocks( $post_access_level );
943 }
944
945 /**
946 * Returns the current URL.
947 *
948 * TODO: Copied from https://github.com/Automattic/jetpack/blob/bb885061dc3ee7a80a78a5f0116ab3fcebfddb09/projects/packages/boost-core/src/lib/class-url.php#L39
949 * TODO: Move to a shared package
950 *
951 * @return string
952 */
953 function get_current_url() {
954 // Fallback to the site URL if we're unable to determine the URL from $_SERVER global.
955 $current_url = site_url();
956
957 if ( isset( $_SERVER ) && is_array( $_SERVER ) ) {
958 // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitization happens at the end
959 $scheme = isset( $_SERVER['HTTPS'] ) && 'on' === $_SERVER['HTTPS'] ? 'https' : 'http';
960 $host = ! empty( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : null;
961 $path = ! empty( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '';
962
963 // Support for local plugin development and testing using ngrok.
964 if ( ! empty( $_SERVER['HTTP_X_ORIGINAL_HOST'] ) && str_contains( $_SERVER['HTTP_X_ORIGINAL_HOST'], 'ngrok.io' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- This is validating.
965 $host = wp_unslash( $_SERVER['HTTP_X_ORIGINAL_HOST'] );
966 }
967 // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
968
969 if ( $host ) {
970 $current_url = esc_url_raw( sprintf( '%s://%s%s', $scheme, $host, $path ) );
971 }
972 }
973
974 return $current_url;
975 }
976
977 /**
978 * Get the submit button text based on the subscription status.
979 *
980 * @param array $data Array containing block view data.
981 *
982 * @return string
983 */
984 function get_submit_button_text( $data ) {
985 if ( ! Jetpack_Memberships::is_current_user_subscribed() ) {
986 return $data['submit_button_text'];
987 }
988 if ( ! Jetpack_Memberships::user_can_view_post() ) {
989 return $data['submit_button_text_upgrade'];
990 }
991 return '✓ ' . $data['submit_button_text_subscribed'];
992 }
993
994 /**
995 * Returns true if there are no more tiers to upgrade to.
996 *
997 * @return boolean
998 */
999 function is_top_subscription() {
1000 if ( ! Jetpack_Memberships::is_current_user_subscribed() ) {
1001 return false;
1002 }
1003 if ( ! Jetpack_Memberships::user_can_view_post() ) {
1004 return false;
1005 }
1006 return true;
1007 }
1008
1009 /**
1010 * Sanitize the submit button text.
1011 *
1012 * @param string $text String containing the submit button text.
1013 *
1014 * @return string
1015 */
1016 function sanitize_submit_text( $text ) {
1017 return wp_kses(
1018 html_entity_decode( $text, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ),
1019 Jetpack_Subscriptions_Widget::$allowed_html_tags_for_submit_button
1020 );
1021 }
1022
1023 /**
1024 * Returns paywall content blocks if user is not authenticated
1025 *
1026 * @param string $newsletter_access_level The newsletter access level.
1027 * @return string
1028 */
1029 function get_paywall_blocks( $newsletter_access_level ) {
1030 $custom_paywall = apply_filters( 'jetpack_custom_paywall_blocks', false );
1031 if ( ! empty( $custom_paywall ) ) {
1032 return $custom_paywall;
1033 }
1034 if ( ! jetpack_is_frontend() ) { // emails
1035 return get_paywall_simple();
1036 }
1037 require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
1038 // Only display paid texts when Stripe is connected and the post is marked for paid subscribers
1039 $is_paid_post = $newsletter_access_level === 'paid_subscribers' && Jetpack_Memberships::has_connected_account();
1040 $is_paid_subscriber = Jetpack_Memberships::user_is_paid_subscriber();
1041
1042 $access_heading = $is_paid_subscriber
1043 ? esc_html__( 'Upgrade to continue reading', 'jetpack' )
1044 : esc_html__( 'Subscribe to continue reading', 'jetpack' );
1045
1046 $subscribe_text = $is_paid_post
1047 // translators: %s is the name of the site.
1048 ? $is_paid_subscriber
1049 ? esc_html__( 'Upgrade to get access to the rest of this post and other exclusive content.', 'jetpack' )
1050 : esc_html__( 'Become a paid subscriber to get access to the rest of this post and other exclusive content.', 'jetpack' )
1051 // translators: %s is the name of the site.
1052 : esc_html__( 'Subscribe to get access to the rest of this post and other subscriber-only content.', 'jetpack' );
1053
1054 $sign_in = '';
1055 $switch_accounts = '';
1056
1057 if ( ( new Host() )->is_wpcom_simple() ) {
1058 // On WPCOM we will redirect directly to the current page
1059 $redirect_url = get_current_url();
1060 } else {
1061 // On self-hosted we will save and hide the token
1062 $redirect_url = get_site_url() . '/wp-json/jetpack/v4/subscribers/auth';
1063 $redirect_url = add_query_arg( 'redirect_url', get_current_url(), $redirect_url );
1064 }
1065
1066 $sign_in_link = add_query_arg(
1067 array(
1068 'site_id' => intval( \Jetpack_Options::get_option( 'id' ) ),
1069 'redirect_url' => rawurlencode( $redirect_url ),
1070 ),
1071 'https://subscribe.wordpress.com/memberships/jwt/'
1072 );
1073 if ( is_user_auth() ) {
1074 if ( ( new Host() )->is_wpcom_simple() ) {
1075 $switch_accounts_link = wp_logout_url( $sign_in_link );
1076 $switch_accounts = '<!-- wp:paragraph {"align":"center","style":{"typography":{"fontSize":"14px"}}} -->
1077 <p class="has-text-align-center" style="font-size:14px"><a href="' . $switch_accounts_link . '">' . __( 'Switch Accounts', 'jetpack' ) . '</a></p>
1078 <!-- /wp:paragraph -->';
1079
1080 }
1081 } else {
1082 if ( ( new Host() )->is_wpcom_simple() ) {
1083 // custom domain
1084 $sign_in_link = wpcom_logmein_redirect_url( get_current_url(), false, null, 'link', get_current_blog_id() );
1085 }
1086 $access_question = get_paywall_access_question( $newsletter_access_level );
1087 $sign_in = '<!-- wp:paragraph {"align":"center","style":{"typography":{"fontSize":"14px"}}} -->
1088 <p class="has-text-align-center" style="font-size:14px"><a href="' . $sign_in_link . '">' . $access_question . '</a></p>
1089 <!-- /wp:paragraph -->';
1090 }
1091
1092 $lock_svg = plugins_url( 'images/lock-paywall.svg', JETPACK__PLUGIN_FILE );
1093
1094 return '
1095 <!-- wp:group {"style":{"border":{"width":"1px","radius":"4px"},"spacing":{"padding":{"top":"32px","bottom":"32px","left":"32px","right":"32px"}}},"borderColor":"primary","className":"jetpack-subscribe-paywall","layout":{"type":"constrained","contentSize":"400px"}} -->
1096 <div class="wp-block-group jetpack-subscribe-paywall has-border-color has-primary-border-color" style="border-width:1px;border-radius:4px;padding-top:32px;padding-right:32px;padding-bottom:32px;padding-left:32px">
1097 <!-- wp:image {"align":"center","width":24,"height":24,"sizeSlug":"large","linkDestination":"none"} -->
1098 <figure class="wp-block-image aligncenter size-large is-resized"><img src="' . $lock_svg . '" alt="" width="24" height="24"/></figure>
1099 <!-- /wp:image -->
1100
1101 <!-- wp:heading {"textAlign":"center","style":{"typography":{"fontStyle":"normal","fontWeight":"600","fontSize":"24px"},"layout":{"selfStretch":"fit"}}} -->
1102 <h2 class="wp-block-heading has-text-align-center" style="font-size:24px;font-style:normal;font-weight:600">' . $access_heading . '</h2>
1103 <!-- /wp:heading -->
1104
1105 <!-- wp:paragraph {"align":"center","style":{"typography":{"fontSize":"14px"},"spacing":{"margin":{"top":"10px","bottom":"10px"}}}} -->
1106 <p class="has-text-align-center" style="margin-top:10px;margin-bottom:10px;font-size:14px">' . $subscribe_text . '</p>
1107 <!-- /wp:paragraph -->
1108
1109 <!-- wp:jetpack/subscriptions {"borderRadius":50,"borderColor":"primary","className":"is-style-compact","isPaidSubscriber":' . ( $is_paid_subscriber ? 'true' : 'false' ) . '} /-->
1110 ' . $sign_in . '
1111 ' . $switch_accounts . '
1112 </div>
1113 <!-- /wp:group -->
1114 ';
1115 }
1116
1117 /**
1118 * Returns Get Access question for the paywall
1119 *
1120 * @param string $post_access_level The newsletter access level.
1121 * @return string.
1122 */
1123 function get_paywall_access_question( $post_access_level ) {
1124 switch ( $post_access_level ) {
1125 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_PAID_SUBSCRIBERS:
1126 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_PAID_SUBSCRIBERS_ALL_TIERS:
1127 $tier = Jetpack_Memberships::get_post_tier();
1128 if ( $tier !== null ) {
1129 return sprintf(
1130 // translators: Placeholder is the tier name
1131 __( 'Already a higher-tier paid subscriber?', 'jetpack' ),
1132 esc_html( $tier->post_title )
1133 );
1134 } else {
1135 return esc_html__( 'Already a paid subscriber?', 'jetpack' );
1136 }
1137 default:
1138 return esc_html__( 'Already a subscriber?', 'jetpack' );
1139 }
1140 }
1141
1142 /**
1143 * Returns true if user is auth for subscriptions check, otherwise returns false.
1144 *
1145 * @return bool
1146 */
1147 function is_user_auth(): bool {
1148 if ( ( new Host() )->is_wpcom_simple() && is_user_logged_in() ) {
1149 return true;
1150 }
1151 if ( current_user_can( 'manage_options' ) ) {
1152 return true;
1153 }
1154
1155 if ( is_jetpack_token_subscription_service_loaded() ) {
1156 if ( Jetpack_Token_Subscription_Service::has_token_from_cookie() ) {
1157 return true;
1158 }
1159 }
1160 return false;
1161 }
1162
1163 /**
1164 * Returns paywall content blocks when email confirmation is pending
1165 *
1166 * @return string
1167 */
1168 function get_paywall_blocks_subscribe_pending() {
1169 $subscribe_email = Jetpack_Memberships::get_current_user_email();
1170
1171 /** This filter is documented in modules/contact-form/grunion-contact-form.php */
1172 if ( is_wpcom() || false !== apply_filters( 'jetpack_auto_fill_logged_in_user', false ) ) {
1173 $current_user = wp_get_current_user();
1174 $subscribe_email = ! empty( $current_user->user_email ) ? $current_user->user_email : '';
1175 }
1176
1177 $access_heading = esc_html__( 'Confirm your subscription to continue reading', 'jetpack' );
1178
1179 /* translators: %s: email address */
1180 $subscribe_text = sprintf( esc_html__( 'Head to your inbox and confirm your email address %s.', 'jetpack' ), $subscribe_email );
1181
1182 $lock_svg = plugins_url( 'images/lock-paywall.svg', JETPACK__PLUGIN_FILE );
1183
1184 return '
1185 <!-- wp:group {"style":{"border":{"width":"1px","radius":"4px"},"spacing":{"padding":{"top":"32px","bottom":"32px","left":"32px","right":"32px"}}},"borderColor":"primary","className":"jetpack-subscribe-paywall","layout":{"type":"constrained","contentSize":"400px"}} -->
1186 <div class="wp-block-group jetpack-subscribe-paywall has-border-color has-primary-border-color" style="border-width:1px;border-radius:4px;padding-top:32px;padding-right:32px;padding-bottom:32px;padding-left:32px">
1187 <!-- wp:image {"align":"center","width":24,"height":24,"sizeSlug":"large","linkDestination":"none"} -->
1188 <figure class="wp-block-image aligncenter size-large is-resized"><img src="' . $lock_svg . '" alt="" width="24" height="24"/></figure>
1189 <!-- /wp:image -->
1190
1191 <!-- wp:heading {"textAlign":"center","style":{"typography":{"fontStyle":"normal","fontWeight":"600","fontSize":"24px", "maxWidth":"initial"},"layout":{"selfStretch":"fit"}}} -->
1192 <h2 class="wp-block-heading has-text-align-center" style="font-size:24px;font-style:normal;font-weight:600;max-width:initial">' . $access_heading . '</h2>
1193 <!-- /wp:heading -->
1194
1195 <!-- wp:paragraph {"align":"center","style":{"typography":{"fontSize":"14px"},"spacing":{"margin":{"top":"10px","bottom":"10px"}}}} -->
1196 <p class="has-text-align-center" style="margin-top:10px;margin-bottom:10px;font-size:14px">' . $subscribe_text . '</p>
1197 <!-- /wp:paragraph -->
1198 </div>
1199 <!-- /wp:group -->
1200 ';
1201 }
1202
1203 /**
1204 * Return content for non frontend views like emails.
1205 *
1206 * @return string
1207 */
1208 function get_paywall_simple() {
1209 $access_heading = esc_html__( "You're currently a free subscriber. Upgrade your subscription to get access to the rest of this post and other paid-subscriber only content.", 'jetpack' );
1210
1211 $subscribe_text = esc_html__( 'Upgrade subscription', 'jetpack' );
1212
1213 return '
1214 <!-- wp:columns -->
1215 <div class="wp-block-columns" style="display: inline-block; width: 90%">
1216 <!-- wp:column -->
1217 <div class="wp-block-column" style="background-color: #F6F7F7; padding: 32px; 24px;">
1218 <!-- wp:paragraph -->
1219 <p class="has-text-align-center"
1220 style="text-align: center;
1221 color: #50575E;
1222 font-weight: 400;
1223 font-size: 16px;
1224 font-family: \'SF Pro Text\', sans-serif;
1225 line-height: 28.8px;">
1226 ' . $access_heading . '
1227 </p>
1228 <!-- /wp:paragraph -->
1229
1230 <!-- wp:buttons -->
1231 <div class="wp-block-buttons" style="text-align: center;">
1232 <!-- wp:button -->
1233 <div class="wp-block-button" style="display: inline-block; margin: 10px 0;">
1234 <a href="' . esc_url( get_post_permalink() ) . '" class="wp-block-button__link wp-element-button"
1235 data-wpcom-track data-tracks-link-desc="paywall-email-click"
1236 style="display: inline-block;
1237 padding: 15px 20px;
1238 background-color: #0675C4;
1239 color: #FFFFFF;
1240 text-decoration: none;
1241 border-radius: 5px;
1242 font-family: \'SF Pro Display\', sans-serif;
1243 font-weight: 500;
1244 font-size: 16px;
1245 text-align: center;">' . $subscribe_text . '</a>
1246 </div>
1247 <!-- /wp:button -->
1248 </div>
1249 <!-- /wp:buttons -->
1250 </div>
1251 <!-- /wp:column -->
1252 </div>
1253 <!-- /wp:columns -->
1254 ';
1255 }
1256