PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 14.1.1
Jetpack – WP Security, Backup, Speed, & Growth v14.1.1
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 1 year ago constants.php 2 years ago subscriptions.php 1 year ago
subscriptions.php
1322 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\Extensions\Premium_Content\Subscription_Service\Abstract_Token_Subscription_Service;
12 use Automattic\Jetpack\Extensions\Premium_Content\Subscription_Service\Jetpack_Token_Subscription_Service;
13 use Automattic\Jetpack\Status\Host;
14 use Jetpack;
15 use Jetpack_Gutenberg;
16 use Jetpack_Memberships;
17 use Jetpack_Subscriptions_Widget;
18
19 require_once __DIR__ . '/class-jetpack-subscription-site.php';
20 require_once __DIR__ . '/constants.php';
21 require_once JETPACK__PLUGIN_DIR . 'extensions/blocks/premium-content/_inc/subscription-service/include.php';
22
23 /**
24 * These block defaults should match ./constants.js
25 */
26 const DEFAULT_BORDER_RADIUS_VALUE = 0;
27 const DEFAULT_BORDER_WEIGHT_VALUE = 1;
28 const DEFAULT_FONTSIZE_VALUE = '16px';
29 const DEFAULT_PADDING_VALUE = 15;
30 const DEFAULT_SPACING_VALUE = 10;
31
32 /**
33 * Registers the block for use in Gutenberg
34 * This is done via an action so that we can disable
35 * registration if we need to.
36 */
37 function register_block() {
38 /*
39 * Disable the feature on P2 blogs
40 */
41 if ( function_exists( '\WPForTeams\is_wpforteams_site' ) &&
42 \WPForTeams\is_wpforteams_site( get_current_blog_id() ) ) {
43 return;
44 }
45
46 /**
47 * Do not proceed if the newsletter feature (Subscriptions module) is not enabled
48 */
49 if ( ! Jetpack::is_module_active( 'subscriptions' ) ) {
50 return;
51 }
52
53 require_once JETPACK__PLUGIN_DIR . '/modules/memberships/class-jetpack-memberships.php';
54
55 if ( \Jetpack_Memberships::should_enable_monetize_blocks_in_editor() ) {
56 Blocks::jetpack_register_block(
57 __DIR__,
58 array(
59 'render_callback' => __NAMESPACE__ . '\render_block',
60 'supports' => array(
61 'spacing' => array(
62 'margin' => true,
63 'padding' => true,
64 ),
65 'align' => array( 'wide', 'full' ),
66 ),
67 )
68 );
69 }
70
71 register_post_meta(
72 'post',
73 META_NAME_FOR_POST_LEVEL_ACCESS_SETTINGS,
74 array(
75 'show_in_rest' => true,
76 'single' => true,
77 'type' => 'string',
78 'auth_callback' => function () {
79 return wp_get_current_user()->has_cap( 'edit_posts' );
80 },
81 )
82 );
83
84 register_post_meta(
85 'post',
86 META_NAME_FOR_POST_DONT_EMAIL_TO_SUBS,
87 array(
88 'default' => false,
89 'show_in_rest' => true,
90 'single' => true,
91 'type' => 'boolean',
92 'auth_callback' => function () {
93 return wp_get_current_user()->has_cap( 'edit_posts' );
94 },
95 )
96 );
97
98 register_post_meta(
99 'post',
100 META_NAME_FOR_POST_TIER_ID_SETTINGS,
101 array(
102 'show_in_rest' => true,
103 'single' => true,
104 'type' => 'integer',
105 'auth_callback' => function () {
106 return wp_get_current_user()->has_cap( 'edit_posts' );
107 },
108 )
109 );
110
111 register_post_meta(
112 'post',
113 META_NAME_CONTAINS_PAYWALLED_CONTENT,
114 array(
115 'show_in_rest' => true,
116 'single' => true,
117 'type' => 'boolean',
118 'auth_callback' => function () {
119 return wp_get_current_user()->has_cap( 'edit_posts' );
120 },
121 )
122 );
123
124 // This ensures Jetpack will sync this post meta to WPCOM.
125 add_filter(
126 'jetpack_sync_post_meta_whitelist',
127 function ( $allowed_meta ) {
128 return array_merge(
129 $allowed_meta,
130 array(
131 META_NAME_FOR_POST_LEVEL_ACCESS_SETTINGS,
132 META_NAME_FOR_POST_DONT_EMAIL_TO_SUBS,
133 META_NAME_CONTAINS_PAYWALLED_CONTENT,
134 )
135 );
136 }
137 );
138
139 // Hide the content – Priority 8 makes it run before do_blocks gets called for the content
140 add_filter( 'the_content', __NAMESPACE__ . '\add_paywall', 8 );
141
142 // Close comments on the front-end
143 add_filter( 'comments_open', __NAMESPACE__ . '\maybe_close_comments', 10, 2 );
144 add_filter( 'pings_open', __NAMESPACE__ . '\maybe_close_comments', 10, 2 );
145
146 // Hide existing comments
147 add_filter( 'get_comment', __NAMESPACE__ . '\maybe_gate_existing_comments' );
148
149 // Gate the excerpt for a post
150 add_filter( 'get_the_excerpt', __NAMESPACE__ . '\jetpack_filter_excerpt_for_newsletter', 10, 2 );
151
152 // Add a 'Newsletter' column to the Edit posts page
153 // We only display the "Newsletter" column if we have configured the paid newsletter plan
154 if ( defined( 'WP_ADMIN' ) && WP_ADMIN && Jetpack_Memberships::has_configured_plans_jetpack_recurring_payments( 'newsletter' ) ) {
155 add_action( 'manage_post_posts_columns', __NAMESPACE__ . '\register_newsletter_access_column' );
156 add_action( 'manage_post_posts_custom_column', __NAMESPACE__ . '\render_newsletter_access_rows', 10, 2 );
157 add_action( 'admin_head', __NAMESPACE__ . '\newsletter_access_column_styles' );
158 }
159
160 add_action( 'init', __NAMESPACE__ . '\maybe_prevent_super_cache_caching' );
161
162 add_action( 'wp_after_insert_post', __NAMESPACE__ . '\add_paywalled_content_post_meta', 99, 2 );
163
164 add_filter(
165 'jetpack_options_whitelist',
166 function ( $options ) {
167 $options[] = 'jetpack_subscriptions_subscribe_post_end_enabled';
168 $options[] = 'jetpack_subscriptions_subscribe_navigation_enabled';
169
170 return $options;
171 }
172 );
173
174 // If called via REST API, we need to register later in the lifecycle
175 if ( ( new Host() )->is_wpcom_platform() && ! jetpack_is_frontend() ) {
176 add_action(
177 'restapi_theme_init',
178 function () {
179 Jetpack_Subscription_Site::init()->handle_subscribe_block_placements();
180 }
181 );
182 } else {
183 Jetpack_Subscription_Site::init()->handle_subscribe_block_placements();
184 }
185 }
186 add_action( 'init', __NAMESPACE__ . '\register_block', 9 );
187
188 /**
189 * Returns true when in a WP.com environment.
190 *
191 * @return boolean
192 */
193 function is_wpcom() {
194 return defined( 'IS_WPCOM' ) && IS_WPCOM;
195 }
196
197 /**
198 * Adds a 'Newsletter' column after the 'Title' column in the post list
199 *
200 * @param array $columns An array of column names.
201 * @return array An array of column names.
202 */
203 function register_newsletter_access_column( $columns ) {
204 $position = array_search( 'title', array_keys( $columns ), true );
205 $new_column = array( NEWSLETTER_COLUMN_ID => __( 'Newsletter', 'jetpack' ) );
206 return array_merge(
207 array_slice( $columns, 0, $position + 1, true ),
208 $new_column,
209 array_slice( $columns, $position, null, true )
210 );
211 }
212
213 /**
214 * Add a meta to prevent publication on firehose, ES AI or Reader
215 *
216 * @param int $post_id Post id being saved.
217 * @param \WP_Post $post Post being saved.
218 * @return void
219 */
220 function add_paywalled_content_post_meta( int $post_id, \WP_Post $post ) {
221 if ( $post->post_type !== 'post' ) {
222 return;
223 }
224
225 $access_level = get_post_meta( $post_id, META_NAME_FOR_POST_LEVEL_ACCESS_SETTINGS, true );
226
227 $is_paywalled = false;
228 switch ( $access_level ) {
229 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_PAID_SUBSCRIBERS_ALL_TIERS:
230 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_PAID_SUBSCRIBERS:
231 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_SUBSCRIBERS:
232 $is_paywalled = true;
233 }
234 if ( $is_paywalled ) {
235 update_post_meta( $post_id, META_NAME_CONTAINS_PAYWALLED_CONTENT, $is_paywalled );
236 }
237 if ( ! $is_paywalled ) {
238 delete_post_meta( $post_id, META_NAME_CONTAINS_PAYWALLED_CONTENT );
239 }
240 }
241
242 /**
243 * Displays the newsletter access level.
244 *
245 * @param string $column_id The ID of the column to display.
246 * @param int $post_id The current post ID.
247 */
248 function render_newsletter_access_rows( $column_id, $post_id ) {
249 if ( NEWSLETTER_COLUMN_ID !== $column_id ) {
250 return;
251 }
252
253 $access_level = get_post_meta( $post_id, META_NAME_FOR_POST_LEVEL_ACCESS_SETTINGS, true );
254
255 switch ( $access_level ) {
256 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_PAID_SUBSCRIBERS_ALL_TIERS:
257 echo esc_html__( 'Paid Subscribers (all plans)', 'jetpack' );
258 break;
259 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_PAID_SUBSCRIBERS:
260 echo esc_html__( 'Paid Subscribers', 'jetpack' );
261 break;
262 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_SUBSCRIBERS:
263 echo esc_html__( 'Subscribers', 'jetpack' );
264 break;
265 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_EVERYBODY:
266 echo esc_html__( 'Everybody', 'jetpack' );
267 break;
268 default:
269 echo '';
270 }
271 }
272
273 /**
274 * Adds the Newsletter column styles
275 */
276 function newsletter_access_column_styles() {
277 echo '<style id="jetpack-newsletter-newsletter-access-column"> table.fixed .column-newsletter_access { width: 10%; } </style>';
278 }
279
280 /**
281 * Determine the amount of folks currently subscribed to the blog, splitted out in email_subscribers & social_followers & paid_subscribers
282 *
283 * @return array containing ['value' => ['email_subscribers' => 0, 'paid_subscribers' => 0, 'social_followers' => 0]]
284 */
285 function fetch_subscriber_counts() {
286 $subs_count = 0;
287 if ( is_wpcom() ) {
288 $subs_count = array(
289 'value' => \wpcom_fetch_subs_counts( true ),
290 );
291 } else {
292 $cache_key = 'wpcom_subscribers_totals';
293 $subs_count = get_transient( $cache_key );
294 if ( false === $subs_count || 'failed' === $subs_count['status'] ) {
295 $xml = new \Jetpack_IXR_Client();
296 $xml->query( 'jetpack.fetchSubscriberCounts' );
297
298 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.
299 $subs_count = array(
300 'status' => 'failed',
301 'code' => $xml->getErrorCode(),
302 'message' => $xml->getErrorMessage(),
303 'value' => ( isset( $subs_count['value'] ) ) ? $subs_count['value'] : array(
304 'email_subscribers' => 0,
305 'social_followers' => 0,
306 'paid_subscribers' => 0,
307 ),
308 );
309 } else {
310 $subs_count = array(
311 'status' => 'success',
312 'value' => $xml->getResponse(),
313 );
314 }
315 set_transient( $cache_key, $subs_count, 3600 ); // Try to cache the result for at least 1 hour.
316 }
317 }
318 return $subs_count;
319 }
320
321 /**
322 * Returns subscriber count based on include_social_followers attribute
323 *
324 * @param bool $include_social_followers Whether to include social followers in the count.
325 * @return int
326 */
327 function get_subscriber_count( $include_social_followers ) {
328 $counts = fetch_subscriber_counts();
329
330 if ( $include_social_followers ) {
331 $subscriber_count = $counts['value']['email_subscribers'] + $counts['value']['social_followers'];
332 } else {
333 $subscriber_count = $counts['value']['email_subscribers'];
334 }
335 return $subscriber_count;
336 }
337
338 /**
339 * Returns true if the block attributes contain a value for the given key.
340 *
341 * @param array $attributes Array containing the block attributes.
342 * @param string $key Block attribute key.
343 *
344 * @return boolean
345 */
346 function has_attribute( $attributes, $key ) {
347 return isset( $attributes[ $key ] ) && $attributes[ $key ] !== 'undefined';
348 }
349
350 /**
351 * Returns the value for the given attribute key, with the option of providing a default fallback value.
352 *
353 * @param array $attributes Array containing the block attributes.
354 * @param string $key Block attribute key.
355 * @param mixed $default Optional fallback value in case the key doesn't exist.
356 *
357 * @return mixed
358 */
359 function get_attribute( $attributes, $key, $default = null ) {
360 return has_attribute( $attributes, $key ) ? $attributes[ $key ] : $default;
361 }
362
363 /**
364 * Mimics getColorClassName, getFontSizeClass and getGradientClass from @wordpress/block-editor js package.
365 *
366 * @param string $setting Setting name.
367 * @param string $value Setting value.
368 *
369 * @return string
370 */
371 function get_setting_class_name( $setting, $value ) {
372 if ( ! $setting || ! $value ) {
373 return '';
374 }
375
376 return sprintf( 'has-%s-%s', $value, $setting );
377 }
378
379 /**
380 * Uses block attributes to generate an array containing the classes for various block elements.
381 * Based on Jetpack_Subscriptions_Widget::do_subscription_form() which the block was originally using.
382 *
383 * @param array $attributes Array containing the block attributes.
384 *
385 * @return array
386 */
387 function get_element_class_names_from_attributes( $attributes ) {
388 $text_color_class = get_setting_class_name( 'color', get_attribute( $attributes, 'textColor' ) );
389 $font_size_class = get_setting_class_name( 'font-size', get_attribute( $attributes, 'fontSize' ) );
390 $border_class = get_setting_class_name( 'border-color', get_attribute( $attributes, 'borderColor' ) );
391
392 $button_background_class = get_setting_class_name( 'background-color', get_attribute( $attributes, 'buttonBackgroundColor' ) );
393 $button_gradient_class = get_setting_class_name( 'gradient-background', get_attribute( $attributes, 'buttonGradient' ) );
394
395 $email_field_background_class = get_setting_class_name( 'background-color', get_attribute( $attributes, 'emailFieldBackgroundColor' ) );
396 $email_field_gradient_class = get_setting_class_name( 'gradient-background', get_attribute( $attributes, 'emailFieldGradient' ) );
397
398 $submit_button_classes = array_filter(
399 array(
400 'wp-block-button__link' => true,
401 'no-border-radius' => 0 === get_attribute( $attributes, 'borderRadius', 0 ),
402 $font_size_class => true,
403 $border_class => true,
404 'has-text-color' => ! empty( $text_color_class ),
405 $text_color_class => true,
406 'has-background' => ! empty( $button_background_class ) || ! empty( $button_gradient_class ),
407 $button_background_class => ! empty( $button_background_class ),
408 $button_gradient_class => ! empty( $button_gradient_class ),
409 )
410 );
411
412 $email_field_classes = array_filter(
413 array(
414 'no-border-radius' => 0 === get_attribute( $attributes, 'borderRadius', 0 ),
415 $font_size_class => true,
416 $border_class => true,
417 $email_field_background_class => true,
418 $email_field_gradient_class => true,
419 )
420 );
421
422 $block_wrapper_classes = array_filter(
423 array(
424 'wp-block-jetpack-subscriptions__supports-newline' => true,
425 'wp-block-jetpack-subscriptions__use-newline' => (bool) get_attribute( $attributes, 'buttonOnNewLine' ),
426 'wp-block-jetpack-subscriptions__show-subs' => (bool) get_attribute( $attributes, 'showSubscribersTotal' ),
427 )
428 );
429
430 return array(
431 'block_wrapper' => implode( ' ', array_keys( $block_wrapper_classes ) ),
432 'email_field' => implode( ' ', array_keys( $email_field_classes ) ),
433 'submit_button' => implode( ' ', array_keys( $submit_button_classes ) ),
434 );
435 }
436
437 /**
438 * Checks if block style is "button only"
439 *
440 * @param string $class_name Block attribute className; multiple names are spearated by space.
441 *
442 * @return bool
443 */
444 function is_button_only_style( $class_name ) {
445 if ( empty( $class_name ) ) {
446 return false;
447 }
448
449 $class_names = explode( ' ', $class_name );
450 return in_array( 'is-style-button', $class_names, true );
451 }
452
453 /**
454 * Uses block attributes to generate an array containing the styles for various block elements.
455 * Based on Jetpack_Subscriptions_Widget::do_subscription_form() which the block was originally using.
456 *
457 * @param array $attributes Array containing the block attributes.
458 *
459 * @return array
460 */
461 function get_element_styles_from_attributes( $attributes ) {
462 $is_button_only_style = is_button_only_style( get_attribute( $attributes, 'className', '' ) );
463
464 $button_background_style = ! has_attribute( $attributes, 'buttonBackgroundColor' ) && has_attribute( $attributes, 'customButtonGradient' )
465 ? get_attribute( $attributes, 'customButtonGradient' )
466 : get_attribute( $attributes, 'customButtonBackgroundColor' );
467
468 $email_field_styles = '';
469 $submit_button_wrapper_styles = '';
470 $submit_button_styles = '';
471
472 if ( ! empty( $button_background_style ) ) {
473 $submit_button_styles .= sprintf( 'background: %s;', $button_background_style );
474 }
475
476 if ( has_attribute( $attributes, 'customTextColor' ) ) {
477 $submit_button_styles .= sprintf( 'color: %s;', get_attribute( $attributes, 'customTextColor' ) );
478 }
479
480 if ( has_attribute( $attributes, 'buttonWidth' ) ) {
481 $submit_button_wrapper_styles .= sprintf( 'width: %s;', get_attribute( $attributes, 'buttonWidth' ) );
482 $submit_button_wrapper_styles .= 'max-width: 100%;';
483
484 // Account for custom margins on inline forms.
485 $submit_button_styles .= true === get_attribute( $attributes, 'buttonOnNewLine' )
486 ? sprintf( 'width: calc(100%% - %dpx);', get_attribute( $attributes, 'spacing', DEFAULT_SPACING_VALUE ) )
487 : 'width: 100%;';
488 }
489
490 $font_size = get_attribute( $attributes, 'customFontSize', DEFAULT_FONTSIZE_VALUE );
491 $style = sprintf( 'font-size: %s%s;', $font_size, is_numeric( $font_size ) ? 'px' : '' );
492
493 $submit_button_styles .= $style;
494 $email_field_styles .= $style;
495
496 $padding = get_attribute( $attributes, 'padding', DEFAULT_PADDING_VALUE );
497 $style = sprintf( 'padding: %1$dpx %2$dpx %1$dpx %2$dpx;', $padding, round( $padding * 1.5 ) );
498
499 $submit_button_styles .= $style;
500 $email_field_styles .= $style;
501
502 $button_spacing = get_attribute( $attributes, 'spacing', DEFAULT_SPACING_VALUE );
503 if ( ! $is_button_only_style ) {
504 $button_spacing = get_attribute( $attributes, 'spacing', DEFAULT_SPACING_VALUE );
505 if ( true === get_attribute( $attributes, 'buttonOnNewLine' ) ) {
506 $submit_button_styles .= sprintf( 'margin-top: %dpx;', $button_spacing );
507 } else {
508 $submit_button_styles .= 'margin: 0; '; // Reset Safari's 2px default margin for buttons affecting input and button union
509 $submit_button_styles .= sprintf( 'margin-left: %dpx;', $button_spacing );
510 }
511 }
512
513 if ( has_attribute( $attributes, 'borderColor' ) ) {
514 $style = sprintf( 'border-color: %s;', get_attribute( $attributes, 'borderColor', '' ) );
515 $submit_button_styles .= $style;
516 $email_field_styles .= $style;
517 }
518
519 $style = sprintf( 'border-radius: %dpx;', get_attribute( $attributes, 'borderRadius', DEFAULT_BORDER_RADIUS_VALUE ) );
520 $submit_button_styles .= $style;
521 $email_field_styles .= $style;
522
523 $style = sprintf( 'border-width: %dpx;', get_attribute( $attributes, 'borderWeight', DEFAULT_BORDER_WEIGHT_VALUE ) );
524 $submit_button_styles .= $style;
525 $email_field_styles .= $style;
526
527 if ( has_attribute( $attributes, 'customBorderColor' ) ) {
528 $style = sprintf( 'border-color: %s; border-style: solid;', get_attribute( $attributes, 'customBorderColor' ) );
529
530 $submit_button_styles .= $style;
531 $email_field_styles .= $style;
532 }
533
534 if ( ! jetpack_is_frontend() ) {
535 $background_color_style = get_attribute_color( 'buttonBackgroundColor', $attributes, '#113AF5' /* default lettre theme color */ );
536 $text_color_style = get_attribute_color( 'textColor', $attributes, '#FFFFFF' );
537 $submit_button_styles .= sprintf( ' background-color: %s; color: %s;', $background_color_style, $text_color_style );
538 }
539
540 return array(
541 'email_field' => $email_field_styles,
542 'submit_button' => $submit_button_styles,
543 'submit_button_wrapper' => $submit_button_wrapper_styles,
544 );
545 }
546
547 /**
548 * Retrieve the resolved color for a given attribute.
549 *
550 * @param string $attribute_name The name of the attribute to resolve.
551 * @param array $attributes An array of all attributes.
552 * @param string $default_color A fallback color in case no color can be resolved.
553 *
554 * @return string Returns the resolved color or the default color if no color is found.
555 */
556 function get_attribute_color( $attribute_name, $attributes, $default_color ) {
557 if ( has_attribute( $attributes, $attribute_name ) ) {
558 $color_slug = get_attribute( $attributes, $attribute_name );
559 $resolved_color = get_color_from_slug( $color_slug );
560
561 if ( $resolved_color ) {
562 return $resolved_color;
563 }
564 }
565
566 return get_global_style_color( $attribute_name, $default_color );
567 }
568
569 /**
570 * Retrieve the global style color based on a provided style key.
571 *
572 * @param string $style_key The key for the desired style.
573 * @param string $default_color A fallback color in case the global style is not set.
574 *
575 * @return string Returns the color defined in global styles or the default color if not defined.
576 */
577 function get_global_style_color( $style_key, $default_color ) {
578 $global_styles = wp_get_global_styles(
579 array( 'color' ),
580 array(
581 'block_name' => 'core/button',
582 'transforms' => array( 'resolve-variables' ),
583 )
584 );
585
586 if ( isset( $global_styles[ $style_key ] ) ) {
587 return $global_styles[ $style_key ];
588 }
589
590 return $default_color;
591 }
592
593 /**
594 * Convert a color slug into its corresponding color value.
595 *
596 * @param string $slug The slug representation of the color.
597 *
598 * @return string|null Returns the color value if found, or null otherwise.
599 */
600 function get_color_from_slug( $slug ) {
601 $color_palettes = wp_get_global_settings( array( 'color', 'palette' ) );
602
603 if ( ! is_array( $color_palettes ) ) {
604 return null;
605 }
606
607 foreach ( $color_palettes as $palette ) {
608 if ( is_array( $palette ) ) {
609 foreach ( $palette as $color ) {
610 if ( isset( $color['slug'] ) && $color['slug'] === $slug && isset( $color['color'] ) ) {
611 return $color['color'];
612 }
613 }
614 }
615 }
616
617 return null;
618 }
619
620 /**
621 * Subscriptions block render callback.
622 *
623 * @param array $attributes Array containing the block attributes.
624 *
625 * @return string
626 */
627 function render_block( $attributes ) {
628 // If the Subscriptions module is not active, don't render the block.
629 if ( ! Jetpack::is_module_active( 'subscriptions' ) ) {
630 return '';
631 }
632
633 if ( class_exists( '\Jetpack_Memberships' ) ) {
634 // We only want the sites that have newsletter feature enabled to be graced by this JavaScript.
635 Jetpack_Gutenberg::load_assets_as_required( __DIR__ );
636 } else {
637 Jetpack_Gutenberg::load_styles_as_required( FEATURE_NAME );
638 }
639
640 if ( ! class_exists( 'Jetpack_Subscriptions_Widget' ) ) {
641 return '';
642 }
643
644 $subscribe_email = Jetpack_Memberships::get_current_user_email();
645
646 /** This filter is documented in \Automattic\Jetpack\Forms\ContactForm\Contact_Form */
647 if ( is_wpcom() || false !== apply_filters( 'jetpack_auto_fill_logged_in_user', false ) ) {
648 $current_user = wp_get_current_user();
649 if ( ! empty( $current_user->user_email ) ) {
650 $subscribe_email = $current_user->user_email;
651 }
652 }
653
654 // The block is using the Jetpack_Subscriptions_Widget backend, hence the need to increase the instance count.
655 ++Jetpack_Subscriptions_Widget::$instance_count;
656
657 $classes = get_element_class_names_from_attributes( $attributes );
658 $styles = get_element_styles_from_attributes( $attributes );
659
660 // The default value was previously "true" in block.json. We don't want to rely setting "default" in block.json to falsy,
661 // because it would change the setting for previously saved blocks. Block editor doesn't store default values in attributes at all.
662 // Hence users without this set will still get social counts included in the subscriber counter.
663 // Lowering the subscriber count on their behalf with code change would be controversial.
664 // We want to disencourage including social count as it's misleading.
665 $include_social_followers = isset( $attributes['includeSocialFollowers'] ) ? (bool) get_attribute( $attributes, 'includeSocialFollowers' ) : true;
666
667 $data = array(
668 'widget_id' => Jetpack_Subscriptions_Widget::$instance_count,
669 'subscribe_email' => $subscribe_email,
670 'is_paid_subscriber' => get_attribute( $attributes, 'isPaidSubscriber', false ),
671 'wrapper_attributes' => get_block_wrapper_attributes(
672 array(
673 'class' => $classes['block_wrapper'],
674 )
675 ),
676 'subscribe_placeholder' => get_attribute( $attributes, 'subscribePlaceholder', __( 'Type your email…', 'jetpack' ) ),
677 'submit_button_text' => get_attribute( $attributes, 'submitButtonText', __( 'Subscribe', 'jetpack' ) ),
678 'submit_button_text_subscribed' => get_attribute( $attributes, 'submitButtonTextSubscribed', __( 'Subscribed', 'jetpack' ) ),
679 'submit_button_text_upgrade' => get_attribute( $attributes, 'submitButtonTextUpgrade', __( 'Upgrade subscription', 'jetpack' ) ),
680 'success_message' => get_attribute(
681 $attributes,
682 'successMessage',
683 esc_html__( "Success! An email was just sent to confirm your subscription. Please find the email now and click 'Confirm' to start subscribing.", 'jetpack' )
684 ),
685 'show_subscribers_total' => (bool) get_attribute( $attributes, 'showSubscribersTotal' ),
686 'subscribers_total' => get_attribute( $attributes, 'showSubscribersTotal' ) ? get_subscriber_count( $include_social_followers ) : 0,
687 'referer' => esc_url_raw(
688 ( is_ssl() ? 'https' : 'http' ) . '://' . ( isset( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : '' ) .
689 ( isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '' )
690 ),
691 'source' => 'subscribe-block',
692 'app_source' => get_attribute( $attributes, 'appSource', null ),
693 'class_name' => get_attribute( $attributes, 'className' ),
694 );
695
696 if ( ! jetpack_is_frontend() ) {
697 return render_for_email( $data, $styles );
698 }
699
700 return render_for_website( $data, $classes, $styles );
701 }
702
703 /**
704 * Get the post access level for the current post. Defaults to 'everybody' if the query is not for a single post
705 *
706 * @return string the actual post access level (see projects/plugins/jetpack/extensions/blocks/subscriptions/constants.js for the values).
707 */
708 function get_post_access_level_for_current_post() {
709 if ( ! is_singular() ) {
710 // There is no "actual" current post.
711 return Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_EVERYBODY;
712 }
713
714 return Jetpack_Memberships::get_post_access_level();
715 }
716
717 /**
718 * Renders the subscriptions block at the site.
719 *
720 * @param array $data Array containing block view data.
721 * @param array $classes Array containing the classes for different block elements.
722 * @param array $styles Array containing the styles for different block elements.
723 *
724 * @return string
725 */
726 function render_for_website( $data, $classes, $styles ) {
727 $lang = get_locale();
728 $blog_id = \Jetpack_Options::get_option( 'id' );
729 $widget_id_suffix = Jetpack_Subscriptions_Widget::$instance_count > 1 ? '-' . Jetpack_Subscriptions_Widget::$instance_count : '';
730 $form_id = 'subscribe-blog' . $widget_id_suffix;
731 $form_url = 'https://wordpress.com/email-subscriptions';
732 $post_access_level = get_post_access_level_for_current_post();
733 $is_button_only_style = ! empty( $data['class_name'] ) ? is_button_only_style( $data['class_name'] ) : false;
734
735 // Post ID is used for pulling post-specific paid status, and returning to the right post after confirming subscription
736 $post_id = null;
737 if ( in_the_loop() ) {
738 $post_id = get_the_ID();
739 } elseif ( is_singular( 'post' ) || is_page() ) {
740 $post_id = get_queried_object_id();
741 } else {
742 $post_id = get_option( 'page_on_front' );
743 }
744
745 $subscribe_field_id = apply_filters( 'subscribe_field_id', 'subscribe-field' . $widget_id_suffix, $data['widget_id'] );
746 $tier_id = get_post_meta( $post_id, META_NAME_FOR_POST_TIER_ID_SETTINGS, true );
747 $is_subscribed = Jetpack_Memberships::is_current_user_subscribed();
748 $button_text = get_submit_button_text( $data );
749 $show_subscriber_count = $data['show_subscribers_total'] && $data['subscribers_total'] && ! $is_subscribed;
750
751 ob_start();
752
753 Jetpack_Subscriptions_Widget::render_widget_status_messages(
754 array(
755 'success_message' => $data['success_message'],
756 )
757 );
758 ?>
759 <div <?php echo wp_kses_data( $data['wrapper_attributes'] ); ?>>
760 <div class="wp-block-jetpack-subscriptions__container<?php echo ! $is_subscribed ? ' is-not-subscriber' : ''; ?>">
761 <?php if ( is_top_subscription() ) : ?>
762 <p id="subscribe-submit" class="is-link"
763 <?php if ( ! empty( $styles['submit_button_wrapper'] ) ) : ?>
764 style="<?php echo esc_attr( $styles['submit_button_wrapper'] ); ?>"
765 <?php endif; ?>
766 >
767 <a
768 href="<?php echo esc_url( 'https://wordpress.com/read/site/subscription/' . $blog_id ); ?>"
769 <?php if ( ! empty( $classes['submit_button'] ) ) : ?>
770 class="<?php echo esc_attr( $classes['submit_button'] ); ?>"
771 <?php endif; ?>
772 <?php if ( ! empty( $styles['submit_button'] ) ) : ?>
773 style="<?php echo esc_attr( $styles['submit_button'] ); ?>"
774 <?php endif; ?>
775 >
776 <?php echo sanitize_submit_text( $button_text ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
777 </a>
778 </p>
779 <?php else : ?>
780 <form
781 action="<?php echo esc_url( $form_url ); ?>"
782 method="post"
783 accept-charset="utf-8"
784 data-blog="<?php echo esc_attr( $blog_id ); ?>"
785 data-post_access_level="<?php echo esc_attr( $post_access_level ); ?>"
786 data-subscriber_email="<?php echo esc_attr( $data['subscribe_email'] ); ?>"
787 id="<?php echo esc_attr( $form_id ); ?>"
788 >
789 <div class="wp-block-jetpack-subscriptions__form-elements">
790 <?php if ( ! $is_subscribed && ! $is_button_only_style ) : ?>
791 <p id="subscribe-email">
792 <label
793 id="<?php echo esc_attr( $subscribe_field_id . '-label' ); ?>"
794 for="<?php echo esc_attr( $subscribe_field_id ); ?>"
795 class="screen-reader-text"
796 >
797 <?php echo esc_html( $data['subscribe_placeholder'] ); ?>
798 </label>
799 <?php
800 printf(
801 '<input
802 required="required"
803 type="email"
804 name="email"
805 %1$s
806 style="%2$s"
807 placeholder="%3$s"
808 value="%4$s"
809 id="%5$s"
810 %6$s
811 />',
812 ( ! empty( $classes['email_field'] )
813 ? 'class="' . esc_attr( $classes['email_field'] ) . '"'
814 : ''
815 ),
816 ( ! empty( $styles['email_field'] )
817 ? esc_attr( $styles['email_field'] )
818 : 'width: 95%; padding: 1px 10px'
819 ),
820 esc_attr( $data['subscribe_placeholder'] ),
821 esc_attr( $data['subscribe_email'] ),
822 esc_attr( $subscribe_field_id ),
823 ( ! empty( $data['subscribe_email'] )
824 ? 'disabled title="' . esc_attr__( "You're logged in with this email", 'jetpack' ) . '"'
825 : 'title="' . esc_attr__( 'Please fill in this field.', 'jetpack' ) . '"'
826 )
827 );
828 ?>
829 </p>
830 <?php endif; ?>
831 <p id="subscribe-submit"
832 <?php if ( ! empty( $styles['submit_button_wrapper'] ) ) : ?>
833 style="<?php echo esc_attr( $styles['submit_button_wrapper'] ); ?>"
834 <?php endif; ?>
835 >
836 <input type="hidden" name="action" value="subscribe"/>
837 <input type="hidden" name="blog_id" value="<?php echo (int) $blog_id; ?>"/>
838 <input type="hidden" name="source" value="<?php echo esc_url( $data['referer'] ); ?>"/>
839 <input type="hidden" name="sub-type" value="<?php echo esc_attr( $data['source'] ); ?>"/>
840 <input type="hidden" name="app_source" value="<?php echo esc_attr( $data['app_source'] ); ?>"/>
841 <input type="hidden" name="redirect_fragment" value="<?php echo esc_attr( $form_id ); ?>"/>
842 <input type="hidden" name="lang" value="<?php echo esc_attr( $lang ); ?>"/>
843 <?php
844 wp_nonce_field( 'blogsub_subscribe_' . $blog_id );
845
846 if ( ! empty( $post_id ) ) {
847 echo '<input type="hidden" name="post_id" value="' . esc_attr( $post_id ) . '"/>';
848 }
849
850 if ( ! empty( $tier_id ) ) {
851 echo '<input type="hidden" name="tier_id" value="' . esc_attr( $tier_id ) . '"/>';
852 }
853 ?>
854 <button type="submit"
855 <?php if ( ! empty( $classes['submit_button'] ) ) : ?>
856 class="<?php echo esc_attr( $classes['submit_button'] ); ?>"
857 <?php endif; ?>
858 <?php if ( ! empty( $styles['submit_button'] ) ) : ?>
859 style="<?php echo esc_attr( $styles['submit_button'] ); ?>"
860 <?php endif; ?>
861 name="jetpack_subscriptions_widget"
862 >
863 <?php echo sanitize_submit_text( $button_text ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
864 </button>
865 </p>
866 </div>
867 </form>
868 <?php endif; ?>
869 <?php if ( $show_subscriber_count ) : ?>
870 <div class="wp-block-jetpack-subscriptions__subscount">
871 <?php echo esc_html( Jetpack_Memberships::get_join_others_text( $data['subscribers_total'] ) ); ?>
872 </div>
873 <?php endif; ?>
874 </div>
875 </div>
876 <?php
877 return ob_get_clean();
878 }
879
880 /**
881 * Renders the email version of the subscriptions block.
882 *
883 * @param array $data Array containing block view data.
884 * @param array $styles Array containing the styles for different block elements.
885 *
886 * @return string
887 */
888 function render_for_email( $data, $styles ) {
889 $submit_button_wrapper_style = ! empty( $styles['submit_button_wrapper'] ) ? 'style="' . esc_attr( $styles['submit_button_wrapper'] ) . '"' : '';
890 $button_text = get_submit_button_text( $data );
891
892 $html = '<div ' . wp_kses_data( $data['wrapper_attributes'] ) . '>
893 <div>
894 <div>
895 <div>
896 <p ' . $submit_button_wrapper_style . '>
897 <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>
898 </p>
899 </div>
900 </div>
901 </div>
902 </div>';
903
904 return $html;
905 }
906
907 /**
908 * Filter excerpts looking for subscription data.
909 *
910 * @param string $excerpt The extrapolated excerpt string.
911 * @param \WP_Post $post The current post being processed (in `get_the_excerpt`).
912 *
913 * @return mixed
914 */
915 function jetpack_filter_excerpt_for_newsletter( $excerpt, $post = null ) {
916 // The blogmagazine theme is overriding WP core `get_the_excerpt` filter and only passing the excerpt
917 // TODO: Until this is fixed, return the excerpt without gating. See https://github.com/Automattic/jetpack/pull/28102#issuecomment-1369161116
918 if ( $post && str_contains( $post->post_content, '<!-- wp:jetpack/subscriptions -->' ) ) {
919 $excerpt .= sprintf(
920 // translators: %s is the permalink url to the current post.
921 __( "<p><a href='%s'>View post</a> to subscribe to site newsletter.</p>", 'jetpack' ),
922 get_post_permalink()
923 );
924 }
925 return $excerpt;
926 }
927
928 /**
929 * Gate access to posts
930 *
931 * @param string $the_content Post content.
932 *
933 * @return string
934 */
935 function add_paywall( $the_content ) {
936 require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
937
938 $post_access_level = Jetpack_Memberships::get_post_access_level();
939
940 if ( Jetpack_Memberships::user_can_view_post() ) {
941 if ( $post_access_level !== Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_EVERYBODY ) {
942 do_action(
943 'earn_track_paywalled_post_view',
944 array(
945 'post_id' => get_the_ID(),
946 )
947 );
948 }
949 return $the_content;
950 }
951
952 $paywalled_content = get_paywall_content( $post_access_level ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
953
954 if ( has_block( \Automattic\Jetpack\Extensions\Paywall\BLOCK_NAME ) ) {
955 if ( strpos( $the_content, \Automattic\Jetpack\Extensions\Paywall\BLOCK_HTML ) ) {
956 return strstr( $the_content, \Automattic\Jetpack\Extensions\Paywall\BLOCK_HTML, true ) . $paywalled_content;
957 }
958 // WordPress generates excerpts by either rendering or stripping blocks before invoking the `the_content` filter.
959 // In the context of generating an excerpt, the Paywall block specifically renders THE_EXCERPT_BLOCK.
960 if ( strpos( $the_content, \Automattic\Jetpack\Extensions\Paywall\THE_EXCERPT_BLOCK ) ) {
961 return strstr( $the_content, \Automattic\Jetpack\Extensions\Paywall\THE_EXCERPT_BLOCK, true );
962 }
963 }
964
965 return $paywalled_content;
966 }
967
968 /**
969 * Gate access to comments. We want to close comments on private sites.
970 *
971 * @param bool $default_comments_open Default state of the comments_open filter.
972 * @param int $post_id Current post id.
973 *
974 * @return bool
975 */
976 function maybe_close_comments( $default_comments_open, $post_id ) {
977 if ( ! $default_comments_open || ! $post_id ) {
978 return $default_comments_open;
979 }
980
981 require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
982 return Jetpack_Memberships::user_can_view_post();
983 }
984
985 /**
986 * Gate access to existing comments
987 *
988 * @param string $comment The comment.
989 *
990 * @return string
991 */
992 function maybe_gate_existing_comments( $comment ) {
993 if ( empty( $comment ) ) {
994 return $comment;
995 }
996
997 require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
998 if ( Jetpack_Memberships::user_can_view_post() ) {
999 return $comment;
1000 }
1001 return '';
1002 }
1003
1004 /**
1005 * Is the Jetpack_Token_Subscription_Service class loaded
1006 *
1007 * @return bool
1008 */
1009 function is_jetpack_token_subscription_service_loaded(): bool {
1010 return class_exists( 'Automattic\Jetpack\Extensions\Premium_Content\Subscription_Service\Jetpack_Token_Subscription_Service' );
1011 }
1012
1013 /**
1014 * Adds support for WP Super cache and Boost cache
1015 */
1016 function maybe_prevent_super_cache_caching() {
1017 // Prevents cached page to be served if the Membership cookie is present
1018 if ( is_jetpack_token_subscription_service_loaded() ) {
1019 do_action( 'wpsc_add_cookie', Jetpack_Token_Subscription_Service::JWT_AUTH_TOKEN_COOKIE_NAME );
1020 }
1021
1022 if ( is_user_auth() ) {
1023 // Do not cache the page if user is auth with Membership token
1024 if ( ! defined( 'DONOTCACHEPAGE' ) ) {
1025 define( 'DONOTCACHEPAGE', true );
1026 }
1027 }
1028 }
1029
1030 /**
1031 * Returns paywall content blocks
1032 *
1033 * @param string $post_access_level The newsletter access level.
1034 * @return string
1035 */
1036 function get_paywall_content( $post_access_level ) {
1037 if ( Jetpack_Memberships::user_is_pending_subscriber() ) {
1038 return get_paywall_blocks_subscribe_pending();
1039 }
1040 if ( doing_filter( 'get_the_excerpt' ) ) {
1041 return '';
1042 }
1043 return get_paywall_blocks( $post_access_level );
1044 }
1045
1046 /**
1047 * Returns the current URL.
1048 *
1049 * TODO: Copied from https://github.com/Automattic/jetpack/blob/bb885061dc3ee7a80a78a5f0116ab3fcebfddb09/projects/packages/boost-core/src/lib/class-url.php#L39
1050 * TODO: Move to a shared package
1051 *
1052 * @return string
1053 */
1054 function get_current_url() {
1055 // Fallback to the site URL if we're unable to determine the URL from $_SERVER global.
1056 $current_url = site_url();
1057
1058 if ( isset( $_SERVER ) && is_array( $_SERVER ) ) {
1059 // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitization happens at the end
1060 $scheme = isset( $_SERVER['HTTPS'] ) && 'on' === $_SERVER['HTTPS'] ? 'https' : 'http';
1061 $host = ! empty( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : null;
1062 $path = ! empty( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '';
1063
1064 // Support for local plugin development and testing using ngrok.
1065 if ( ! empty( $_SERVER['HTTP_X_ORIGINAL_HOST'] ) && str_contains( $_SERVER['HTTP_X_ORIGINAL_HOST'], 'ngrok.io' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- This is validating.
1066 $host = wp_unslash( $_SERVER['HTTP_X_ORIGINAL_HOST'] );
1067 }
1068 // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1069
1070 if ( $host ) {
1071 $current_url = esc_url_raw( sprintf( '%s://%s%s', $scheme, $host, $path ) );
1072 }
1073 }
1074
1075 return $current_url;
1076 }
1077
1078 /**
1079 * Get the submit button text based on the subscription status.
1080 *
1081 * @param array $data Array containing block view data.
1082 *
1083 * @return string
1084 */
1085 function get_submit_button_text( $data ) {
1086 if ( ! Jetpack_Memberships::is_current_user_subscribed() ) {
1087 return $data['submit_button_text'];
1088 }
1089 if ( ! Jetpack_Memberships::user_can_view_post() ) {
1090 return $data['submit_button_text_upgrade'];
1091 }
1092 return '✓ ' . $data['submit_button_text_subscribed'];
1093 }
1094
1095 /**
1096 * Returns true if there are no more tiers to upgrade to.
1097 *
1098 * @return boolean
1099 */
1100 function is_top_subscription() {
1101 if ( ! Jetpack_Memberships::is_current_user_subscribed() ) {
1102 return false;
1103 }
1104 if ( ! Jetpack_Memberships::user_can_view_post() ) {
1105 return false;
1106 }
1107 return true;
1108 }
1109
1110 /**
1111 * Sanitize the submit button text.
1112 *
1113 * @param string $text String containing the submit button text.
1114 *
1115 * @return string
1116 */
1117 function sanitize_submit_text( $text ) {
1118 return wp_kses(
1119 html_entity_decode( $text, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ),
1120 Jetpack_Subscriptions_Widget::$allowed_html_tags_for_submit_button
1121 );
1122 }
1123
1124 /**
1125 * Returns paywall content blocks if user is not authenticated
1126 *
1127 * @param string $newsletter_access_level The newsletter access level.
1128 * @return string
1129 */
1130 function get_paywall_blocks( $newsletter_access_level ) {
1131 $custom_paywall = apply_filters( 'jetpack_custom_paywall_blocks', false );
1132 if ( ! empty( $custom_paywall ) ) {
1133 return $custom_paywall;
1134 }
1135 if ( ! jetpack_is_frontend() ) { // emails
1136 return get_paywall_simple();
1137 }
1138 require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
1139 // Only display paid texts when Stripe is connected and the post is marked for paid subscribers
1140 $is_paid_post = $newsletter_access_level === 'paid_subscribers' && Jetpack_Memberships::has_connected_account();
1141 $is_paid_subscriber = Jetpack_Memberships::user_is_paid_subscriber();
1142
1143 $access_heading = $is_paid_subscriber
1144 ? esc_html__( 'Upgrade to continue reading', 'jetpack' )
1145 : esc_html__( 'Subscribe to continue reading', 'jetpack' );
1146
1147 $subscribe_text = $is_paid_post
1148 // translators: %s is the name of the site.
1149 ? $is_paid_subscriber
1150 ? esc_html__( 'Upgrade to get access to the rest of this post and other exclusive content.', 'jetpack' )
1151 : esc_html__( 'Become a paid subscriber to get access to the rest of this post and other exclusive content.', 'jetpack' )
1152 // translators: %s is the name of the site.
1153 : esc_html__( 'Subscribe to get access to the rest of this post and other subscriber-only content.', 'jetpack' );
1154
1155 $login_block = '';
1156
1157 if ( is_user_auth() ) {
1158 if ( ( new Host() )->is_wpcom_simple() ) {
1159 // We cannot use wpcom_logmein_redirect_url since it returns redirect URL when user is already logged in.
1160 $login_link = add_query_arg(
1161 array(
1162 'redirect_to' => rawurlencode( get_current_url() ),
1163 'blog_id' => get_current_blog_id(),
1164 ),
1165 'https://wordpress.com/log-in/link'
1166 );
1167 $switch_accounts_link = wp_logout_url( $login_link );
1168 $login_block = '<!-- wp:paragraph {"align":"center","style":{"typography":{"fontSize":"14px"}}} -->
1169 <p class="has-text-align-center" style="font-size:14px">
1170 <a href="' . $switch_accounts_link . '">' . __( 'Switch accounts', 'jetpack' ) . '</a>
1171 </p>
1172 <!-- /wp:paragraph -->';
1173 }
1174 } else {
1175 $access_question = $is_paid_post ? esc_html__( 'Already a paid subscriber?', 'jetpack' ) : esc_html__( 'Already a subscriber?', 'jetpack' );
1176 $login_block = '<!-- wp:group {"style":{"typography":{"fontSize":"14px"}},"layout":{"type":"flex","justifyContent":"center"}} -->
1177 <div class="wp-block-group" style="font-size:14px">
1178 <!-- wp:jetpack/subscriber-login {"logInLabel":"' . $access_question . '"} /-->
1179 </div>
1180 <!-- /wp:group -->';
1181 }
1182
1183 $lock_svg = plugins_url( 'images/lock-paywall.svg', JETPACK__PLUGIN_FILE );
1184
1185 return '
1186 <!-- 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"}} -->
1187 <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">
1188 <!-- wp:image {"align":"center","width":24,"height":24,"sizeSlug":"large","linkDestination":"none"} -->
1189 <figure class="wp-block-image aligncenter size-large is-resized"><img src="' . $lock_svg . '" alt="" width="24" height="24"/></figure>
1190 <!-- /wp:image -->
1191
1192 <!-- wp:heading {"textAlign":"center","style":{"typography":{"fontStyle":"normal","fontWeight":"600","fontSize":"24px"},"layout":{"selfStretch":"fit"}}} -->
1193 <h2 class="wp-block-heading has-text-align-center" style="font-size:24px;font-style:normal;font-weight:600">' . $access_heading . '</h2>
1194 <!-- /wp:heading -->
1195
1196 <!-- wp:paragraph {"align":"center","style":{"typography":{"fontSize":"14px"},"spacing":{"margin":{"top":"10px","bottom":"10px"}}}} -->
1197 <p class="has-text-align-center" style="margin-top:10px;margin-bottom:10px;font-size:14px">' . $subscribe_text . '</p>
1198 <!-- /wp:paragraph -->
1199
1200 <!-- wp:jetpack/subscriptions {"borderRadius":50,"borderColor":"primary","className":"is-style-compact","isPaidSubscriber":' . ( $is_paid_subscriber ? 'true' : 'false' ) . '} /-->
1201 ' . $login_block . '
1202 </div>
1203 <!-- /wp:group -->
1204 ';
1205 }
1206
1207 /**
1208 * Returns true if user is auth for subscriptions check, otherwise returns false.
1209 *
1210 * @return bool
1211 */
1212 function is_user_auth(): bool {
1213 if ( ( new Host() )->is_wpcom_simple() && is_user_logged_in() ) {
1214 return true;
1215 }
1216 if ( current_user_can( 'manage_options' ) ) {
1217 return true;
1218 }
1219
1220 if ( is_jetpack_token_subscription_service_loaded() ) {
1221 if ( Jetpack_Token_Subscription_Service::has_token_from_cookie() ) {
1222 return true;
1223 }
1224 }
1225 return false;
1226 }
1227
1228 /**
1229 * Returns paywall content blocks when email confirmation is pending
1230 *
1231 * @return string
1232 */
1233 function get_paywall_blocks_subscribe_pending() {
1234 $subscribe_email = Jetpack_Memberships::get_current_user_email();
1235
1236 /** This filter is documented in \Automattic\Jetpack\Forms\ContactForm\Contact_Form */
1237 if ( is_wpcom() || false !== apply_filters( 'jetpack_auto_fill_logged_in_user', false ) ) {
1238 $current_user = wp_get_current_user();
1239 if ( ! empty( $current_user->user_email ) ) {
1240 $subscribe_email = $current_user->user_email;
1241 }
1242 }
1243
1244 $access_heading = esc_html__( 'Confirm your subscription to continue reading', 'jetpack' );
1245
1246 /* translators: %s: email address */
1247 $subscribe_text = sprintf( esc_html__( 'Head to your inbox and confirm your email address %s.', 'jetpack' ), $subscribe_email );
1248
1249 $lock_svg = plugins_url( 'images/lock-paywall.svg', JETPACK__PLUGIN_FILE );
1250
1251 return '
1252 <!-- 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"}} -->
1253 <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">
1254 <!-- wp:image {"align":"center","width":24,"height":24,"sizeSlug":"large","linkDestination":"none"} -->
1255 <figure class="wp-block-image aligncenter size-large is-resized"><img src="' . $lock_svg . '" alt="" width="24" height="24"/></figure>
1256 <!-- /wp:image -->
1257
1258 <!-- wp:heading {"textAlign":"center","style":{"typography":{"fontStyle":"normal","fontWeight":"600","fontSize":"24px", "maxWidth":"initial"},"layout":{"selfStretch":"fit"}}} -->
1259 <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>
1260 <!-- /wp:heading -->
1261
1262 <!-- wp:paragraph {"align":"center","style":{"typography":{"fontSize":"14px"},"spacing":{"margin":{"top":"10px","bottom":"10px"}}}} -->
1263 <p class="has-text-align-center" style="margin-top:10px;margin-bottom:10px;font-size:14px">' . $subscribe_text . '</p>
1264 <!-- /wp:paragraph -->
1265 </div>
1266 <!-- /wp:group -->
1267 ';
1268 }
1269
1270 /**
1271 * Return content for non frontend views like emails.
1272 *
1273 * @return string
1274 */
1275 function get_paywall_simple() {
1276 $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' );
1277
1278 $subscribe_text = esc_html__( 'Upgrade subscription', 'jetpack' );
1279
1280 return '
1281 <!-- wp:columns -->
1282 <div class="wp-block-columns" style="display: inline-block; width: 90%">
1283 <!-- wp:column -->
1284 <div class="wp-block-column" style="background-color: #F6F7F7; padding: 32px; 24px;">
1285 <!-- wp:paragraph -->
1286 <p class="has-text-align-center"
1287 style="text-align: center;
1288 color: #50575E;
1289 font-weight: 400;
1290 font-size: 16px;
1291 font-family: \'SF Pro Text\', sans-serif;
1292 line-height: 28.8px;">
1293 ' . $access_heading . '
1294 </p>
1295 <!-- /wp:paragraph -->
1296 <!-- wp:buttons -->
1297 <div class="wp-block-buttons" style="text-align: center;">
1298 <!-- wp:button -->
1299 <div class="wp-block-button" style="display: inline-block; margin: 10px 0;">
1300 <a href="' . esc_url( get_post_permalink() ) . '" class="wp-block-button__link wp-element-button"
1301 data-wpcom-track data-tracks-link-desc="paywall-email-click"
1302 style="display: inline-block;
1303 padding: 15px 20px;
1304 background-color: #0675C4;
1305 color: #FFFFFF;
1306 text-decoration: none;
1307 border-radius: 5px;
1308 font-family: \'SF Pro Display\', sans-serif;
1309 font-weight: 500;
1310 font-size: 16px;
1311 text-align: center;">' . $subscribe_text . '</a>
1312 </div>
1313 <!-- /wp:button -->
1314 </div>
1315 <!-- /wp:buttons -->
1316 </div>
1317 <!-- /wp:column -->
1318 </div>
1319 <!-- /wp:columns -->
1320 ';
1321 }
1322