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