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