PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 13.4.5
Jetpack – WP Security, Backup, Speed, & Growth v13.4.5
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
1319 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 ( defined( 'WP_ADMIN' ) && WP_ADMIN && 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 // If called via REST API, we need to register later in the lifecycle
186 if ( ( new Host() )->is_wpcom_platform() && ! jetpack_is_frontend() ) {
187 add_action(
188 'restapi_theme_init',
189 function () {
190 Jetpack_Subscription_Site::init()->handle_subscribe_block_placements();
191 }
192 );
193 } else {
194 Jetpack_Subscription_Site::init()->handle_subscribe_block_placements();
195 }
196 }
197 add_action( 'init', __NAMESPACE__ . '\register_block', 9 );
198
199 /**
200 * Returns true when in a WP.com environment.
201 *
202 * @return boolean
203 */
204 function is_wpcom() {
205 return defined( 'IS_WPCOM' ) && IS_WPCOM;
206 }
207
208 /**
209 * Adds a 'Newsletter' column after the 'Title' column in the post list
210 *
211 * @param array $columns An array of column names.
212 * @return array An array of column names.
213 */
214 function register_newsletter_access_column( $columns ) {
215 $position = array_search( 'title', array_keys( $columns ), true );
216 $new_column = array( NEWSLETTER_COLUMN_ID => __( 'Newsletter', 'jetpack' ) );
217 return array_merge(
218 array_slice( $columns, 0, $position + 1, true ),
219 $new_column,
220 array_slice( $columns, $position, null, true )
221 );
222 }
223
224 /**
225 * Add a meta to prevent publication on firehose, ES AI or Reader
226 *
227 * @param int $post_id Post id being saved.
228 * @return void
229 */
230 function add_paywalled_content_post_meta( int $post_id ) {
231 $access_level = get_post_meta( $post_id, META_NAME_FOR_POST_LEVEL_ACCESS_SETTINGS, true );
232
233 $is_paywalled = false;
234 switch ( $access_level ) {
235 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_PAID_SUBSCRIBERS_ALL_TIERS:
236 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_PAID_SUBSCRIBERS:
237 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_SUBSCRIBERS:
238 $is_paywalled = true;
239 }
240 update_post_meta( $post_id, META_NAME_CONTAINS_PAYWALLED_CONTENT, $is_paywalled );
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 * Uses block attributes to generate an array containing the styles for various block elements.
440 * Based on Jetpack_Subscriptions_Widget::do_subscription_form() which the block was originally using.
441 *
442 * @param array $attributes Array containing the block attributes.
443 *
444 * @return array
445 */
446 function get_element_styles_from_attributes( $attributes ) {
447 $button_background_style = ! has_attribute( $attributes, 'buttonBackgroundColor' ) && has_attribute( $attributes, 'customButtonGradient' )
448 ? get_attribute( $attributes, 'customButtonGradient' )
449 : get_attribute( $attributes, 'customButtonBackgroundColor' );
450
451 $email_field_styles = '';
452 $submit_button_wrapper_styles = '';
453 $submit_button_styles = '';
454
455 if ( ! empty( $button_background_style ) ) {
456 $submit_button_styles .= sprintf( 'background: %s;', $button_background_style );
457 }
458
459 if ( has_attribute( $attributes, 'customTextColor' ) ) {
460 $submit_button_styles .= sprintf( 'color: %s;', get_attribute( $attributes, 'customTextColor' ) );
461 }
462
463 if ( has_attribute( $attributes, 'buttonWidth' ) ) {
464 $submit_button_wrapper_styles .= sprintf( 'width: %s;', get_attribute( $attributes, 'buttonWidth' ) );
465 $submit_button_wrapper_styles .= 'max-width: 100%;';
466
467 // Account for custom margins on inline forms.
468 $submit_button_styles .= true === get_attribute( $attributes, 'buttonOnNewLine' )
469 ? sprintf( 'width: calc(100%% - %dpx);', get_attribute( $attributes, 'spacing', DEFAULT_SPACING_VALUE ) )
470 : 'width: 100%;';
471 }
472
473 $font_size = get_attribute( $attributes, 'customFontSize', DEFAULT_FONTSIZE_VALUE );
474 $style = sprintf( 'font-size: %s%s;', $font_size, is_numeric( $font_size ) ? 'px' : '' );
475
476 $submit_button_styles .= $style;
477 $email_field_styles .= $style;
478
479 $padding = get_attribute( $attributes, 'padding', DEFAULT_PADDING_VALUE );
480 $style = sprintf( 'padding: %1$dpx %2$dpx %1$dpx %2$dpx;', $padding, round( $padding * 1.5 ) );
481
482 $submit_button_styles .= $style;
483 $email_field_styles .= $style;
484
485 $button_spacing = get_attribute( $attributes, 'spacing', DEFAULT_SPACING_VALUE );
486 if ( true === get_attribute( $attributes, 'buttonOnNewLine' ) ) {
487 $submit_button_styles .= sprintf( 'margin-top: %dpx;', $button_spacing );
488 } else {
489 $submit_button_styles .= 'margin: 0px; '; // Reset Safari's 2px default margin for buttons affecting input and button union
490 $submit_button_styles .= sprintf( 'margin-left: %dpx;', $button_spacing );
491 }
492
493 if ( has_attribute( $attributes, 'borderColor' ) ) {
494 $style = sprintf( 'border-color: %s;', get_attribute( $attributes, 'borderColor', '' ) );
495 $submit_button_styles .= $style;
496 $email_field_styles .= $style;
497 }
498
499 $style = sprintf( 'border-radius: %dpx;', get_attribute( $attributes, 'borderRadius', DEFAULT_BORDER_RADIUS_VALUE ) );
500 $submit_button_styles .= $style;
501 $email_field_styles .= $style;
502
503 $style = sprintf( 'border-width: %dpx;', get_attribute( $attributes, 'borderWeight', DEFAULT_BORDER_WEIGHT_VALUE ) );
504 $submit_button_styles .= $style;
505 $email_field_styles .= $style;
506
507 if ( has_attribute( $attributes, 'customBorderColor' ) ) {
508 $style = sprintf( 'border-color: %s; border-style: solid;', get_attribute( $attributes, 'customBorderColor' ) );
509
510 $submit_button_styles .= $style;
511 $email_field_styles .= $style;
512 }
513
514 if ( ! jetpack_is_frontend() ) {
515 $background_color_style = get_attribute_color( 'buttonBackgroundColor', $attributes, '#113AF5' /* default lettre theme color */ );
516 $text_color_style = get_attribute_color( 'textColor', $attributes, '#FFFFFF' );
517 $submit_button_styles .= sprintf( ' background-color: %s; color: %s;', $background_color_style, $text_color_style );
518 }
519
520 return array(
521 'email_field' => $email_field_styles,
522 'submit_button' => $submit_button_styles,
523 'submit_button_wrapper' => $submit_button_wrapper_styles,
524 );
525 }
526
527 /**
528 * Retrieve the resolved color for a given attribute.
529 *
530 * @param string $attribute_name The name of the attribute to resolve.
531 * @param array $attributes An array of all attributes.
532 * @param string $default_color A fallback color in case no color can be resolved.
533 *
534 * @return string Returns the resolved color or the default color if no color is found.
535 */
536 function get_attribute_color( $attribute_name, $attributes, $default_color ) {
537 if ( has_attribute( $attributes, $attribute_name ) ) {
538 $color_slug = get_attribute( $attributes, $attribute_name );
539 $resolved_color = get_color_from_slug( $color_slug );
540
541 if ( $resolved_color ) {
542 return $resolved_color;
543 }
544 }
545
546 return get_global_style_color( $attribute_name, $default_color );
547 }
548
549 /**
550 * Retrieve the global style color based on a provided style key.
551 *
552 * @param string $style_key The key for the desired style.
553 * @param string $default_color A fallback color in case the global style is not set.
554 *
555 * @return string Returns the color defined in global styles or the default color if not defined.
556 */
557 function get_global_style_color( $style_key, $default_color ) {
558 $global_styles = wp_get_global_styles(
559 array( 'color' ),
560 array(
561 'block_name' => 'core/button',
562 'transforms' => array( 'resolve-variables' ),
563 )
564 );
565
566 if ( isset( $global_styles[ $style_key ] ) ) {
567 return $global_styles[ $style_key ];
568 }
569
570 return $default_color;
571 }
572
573 /**
574 * Convert a color slug into its corresponding color value.
575 *
576 * @param string $slug The slug representation of the color.
577 *
578 * @return string|null Returns the color value if found, or null otherwise.
579 */
580 function get_color_from_slug( $slug ) {
581 $color_palettes = wp_get_global_settings( array( 'color', 'palette' ) );
582
583 if ( ! is_array( $color_palettes ) ) {
584 return null;
585 }
586
587 foreach ( $color_palettes as $palette ) {
588 if ( is_array( $palette ) ) {
589 foreach ( $palette as $color ) {
590 if ( isset( $color['slug'] ) && $color['slug'] === $slug && isset( $color['color'] ) ) {
591 return $color['color'];
592 }
593 }
594 }
595 }
596
597 return null;
598 }
599
600 /**
601 * Subscriptions block render callback.
602 *
603 * @param array $attributes Array containing the block attributes.
604 *
605 * @return string
606 */
607 function render_block( $attributes ) {
608 // If the Subscriptions module is not active, don't render the block.
609 if ( ! Jetpack::is_module_active( 'subscriptions' ) ) {
610 return '';
611 }
612
613 if ( class_exists( '\Jetpack_Memberships' ) ) {
614 // We only want the sites that have newsletter feature enabled to be graced by this JavaScript.
615 Jetpack_Gutenberg::load_assets_as_required( __DIR__ );
616 } else {
617 Jetpack_Gutenberg::load_styles_as_required( FEATURE_NAME );
618 }
619
620 $subscribe_email = Jetpack_Memberships::get_current_user_email();
621
622 /** This filter is documented in \Automattic\Jetpack\Forms\ContactForm\Contact_Form */
623 if ( is_wpcom() || false !== apply_filters( 'jetpack_auto_fill_logged_in_user', false ) ) {
624 $current_user = wp_get_current_user();
625 $subscribe_email = ! empty( $current_user->user_email ) ? $current_user->user_email : '';
626 }
627
628 // The block is using the Jetpack_Subscriptions_Widget backend, hence the need to increase the instance count.
629 ++Jetpack_Subscriptions_Widget::$instance_count;
630
631 $classes = get_element_class_names_from_attributes( $attributes );
632 $styles = get_element_styles_from_attributes( $attributes );
633
634 // The default value was previously "true" in block.json. We don't want to rely setting "default" in block.json to falsy,
635 // because it would change the setting for previously saved blocks. Block editor doesn't store default values in attributes at all.
636 // Hence users without this set will still get social counts included in the subscriber counter.
637 // Lowering the subscriber count on their behalf with code change would be controversial.
638 // We want to disencourage including social count as it's misleading.
639 $include_social_followers = isset( $attributes['includeSocialFollowers'] ) ? (bool) get_attribute( $attributes, 'includeSocialFollowers' ) : true;
640
641 $data = array(
642 'widget_id' => Jetpack_Subscriptions_Widget::$instance_count,
643 'subscribe_email' => $subscribe_email,
644 'is_paid_subscriber' => get_attribute( $attributes, 'isPaidSubscriber', false ),
645 'wrapper_attributes' => get_block_wrapper_attributes(
646 array(
647 'class' => $classes['block_wrapper'],
648 )
649 ),
650 'subscribe_placeholder' => get_attribute( $attributes, 'subscribePlaceholder', __( 'Type your email…', 'jetpack' ) ),
651 'submit_button_text' => get_attribute( $attributes, 'submitButtonText', __( 'Subscribe', 'jetpack' ) ),
652 'submit_button_text_subscribed' => get_attribute( $attributes, 'submitButtonTextSubscribed', __( 'Subscribed', 'jetpack' ) ),
653 'submit_button_text_upgrade' => get_attribute( $attributes, 'submitButtonTextUpgrade', __( 'Upgrade subscription', 'jetpack' ) ),
654 'success_message' => get_attribute(
655 $attributes,
656 'successMessage',
657 esc_html__( "Success! An email was just sent to confirm your subscription. Please find the email now and click 'Confirm' to start subscribing.", 'jetpack' )
658 ),
659 'show_subscribers_total' => (bool) get_attribute( $attributes, 'showSubscribersTotal' ),
660 'subscribers_total' => get_subscriber_count( $include_social_followers ),
661 'referer' => esc_url_raw(
662 ( is_ssl() ? 'https' : 'http' ) . '://' . ( isset( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : '' ) .
663 ( isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '' )
664 ),
665 'source' => 'subscribe-block',
666 'app_source' => get_attribute( $attributes, 'appSource', null ),
667 );
668
669 if ( ! jetpack_is_frontend() ) {
670 return render_for_email( $data, $styles );
671 }
672
673 return render_for_website( $data, $classes, $styles );
674 }
675
676 /**
677 * Get the post access level for the current post. Defaults to 'everybody' if the query is not for a single post
678 *
679 * @return string the actual post access level (see projects/plugins/jetpack/extensions/blocks/subscriptions/constants.js for the values).
680 */
681 function get_post_access_level_for_current_post() {
682 if ( ! is_singular() ) {
683 // There is no "actual" current post.
684 return Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_EVERYBODY;
685 }
686
687 return Jetpack_Memberships::get_post_access_level();
688 }
689
690 /**
691 * Renders the subscriptions block at the site.
692 *
693 * @param array $data Array containing block view data.
694 * @param array $classes Array containing the classes for different block elements.
695 * @param array $styles Array containing the styles for different block elements.
696 *
697 * @return string
698 */
699 function render_for_website( $data, $classes, $styles ) {
700 $lang = get_locale();
701 $blog_id = \Jetpack_Options::get_option( 'id' );
702 $widget_id_suffix = Jetpack_Subscriptions_Widget::$instance_count > 1 ? '-' . Jetpack_Subscriptions_Widget::$instance_count : '';
703 $form_id = 'subscribe-blog' . $widget_id_suffix;
704 $form_url = 'https://wordpress.com/email-subscriptions';
705 $post_access_level = get_post_access_level_for_current_post();
706
707 // Post ID is used for pulling post-specific paid status, and returning to the right post after confirming subscription
708 $post_id = null;
709 if ( in_the_loop() ) {
710 $post_id = get_the_ID();
711 } elseif ( is_singular( 'post' ) || is_page() ) {
712 $post_id = get_queried_object_id();
713 } else {
714 $post_id = get_option( 'page_on_front' );
715 }
716
717 $subscribe_field_id = apply_filters( 'subscribe_field_id', 'subscribe-field' . $widget_id_suffix, $data['widget_id'] );
718 $tier_id = get_post_meta( $post_id, META_NAME_FOR_POST_TIER_ID_SETTINGS, true );
719 $is_subscribed = Jetpack_Memberships::is_current_user_subscribed();
720 $button_text = get_submit_button_text( $data );
721 $show_subscriber_count = $data['show_subscribers_total'] && $data['subscribers_total'] && ! $is_subscribed;
722
723 ob_start();
724
725 Jetpack_Subscriptions_Widget::render_widget_status_messages(
726 array(
727 'success_message' => $data['success_message'],
728 )
729 );
730 ?>
731 <div <?php echo wp_kses_data( $data['wrapper_attributes'] ); ?>>
732 <div class="wp-block-jetpack-subscriptions__container<?php echo ! $is_subscribed ? ' is-not-subscriber' : ''; ?>">
733 <?php if ( is_top_subscription() ) : ?>
734 <p id="subscribe-submit" class="is-link"
735 <?php if ( ! empty( $styles['submit_button_wrapper'] ) ) : ?>
736 style="<?php echo esc_attr( $styles['submit_button_wrapper'] ); ?>"
737 <?php endif; ?>
738 >
739 <a
740 href="<?php echo esc_url( 'https://wordpress.com/read/site/subscription/' . $blog_id ); ?>"
741 <?php if ( ! empty( $classes['submit_button'] ) ) : ?>
742 class="<?php echo esc_attr( $classes['submit_button'] ); ?>"
743 <?php endif; ?>
744 <?php if ( ! empty( $styles['submit_button'] ) ) : ?>
745 style="<?php echo esc_attr( $styles['submit_button'] ); ?>"
746 <?php endif; ?>
747 >
748 <?php echo sanitize_submit_text( $button_text ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
749 </a>
750 </p>
751 <?php else : ?>
752 <form
753 action="<?php echo esc_url( $form_url ); ?>"
754 method="post"
755 accept-charset="utf-8"
756 data-blog="<?php echo esc_attr( $blog_id ); ?>"
757 data-post_access_level="<?php echo esc_attr( $post_access_level ); ?>"
758 data-subscriber_email="<?php echo esc_attr( $data['subscribe_email'] ); ?>"
759 id="<?php echo esc_attr( $form_id ); ?>"
760 >
761 <div class="wp-block-jetpack-subscriptions__form-elements">
762 <?php if ( ! $is_subscribed ) : ?>
763 <p id="subscribe-email">
764 <label
765 id="<?php echo esc_attr( $subscribe_field_id . '-label' ); ?>"
766 for="<?php echo esc_attr( $subscribe_field_id ); ?>"
767 class="screen-reader-text"
768 >
769 <?php echo esc_html( $data['subscribe_placeholder'] ); ?>
770 </label>
771 <?php
772 printf(
773 '<input
774 required="required"
775 type="email"
776 name="email"
777 %1$s
778 style="%2$s"
779 placeholder="%3$s"
780 value="%4$s"
781 id="%5$s"
782 %6$s
783 />',
784 ( ! empty( $classes['email_field'] )
785 ? 'class="' . esc_attr( $classes['email_field'] ) . '"'
786 : ''
787 ),
788 ( ! empty( $styles['email_field'] )
789 ? esc_attr( $styles['email_field'] )
790 : 'width: 95%; padding: 1px 10px'
791 ),
792 esc_attr( $data['subscribe_placeholder'] ),
793 esc_attr( $data['subscribe_email'] ),
794 esc_attr( $subscribe_field_id ),
795 ( ! empty( $data['subscribe_email'] )
796 ? 'disabled title="' . esc_attr__( "You're logged in with this email", 'jetpack' ) . '"'
797 : 'title="' . esc_attr__( 'Please fill in this field.', 'jetpack' ) . '"'
798 )
799 );
800 ?>
801 </p>
802 <?php endif; ?>
803 <p id="subscribe-submit"
804 <?php if ( ! empty( $styles['submit_button_wrapper'] ) ) : ?>
805 style="<?php echo esc_attr( $styles['submit_button_wrapper'] ); ?>"
806 <?php endif; ?>
807 >
808 <input type="hidden" name="action" value="subscribe"/>
809 <input type="hidden" name="blog_id" value="<?php echo (int) $blog_id; ?>"/>
810 <input type="hidden" name="source" value="<?php echo esc_url( $data['referer'] ); ?>"/>
811 <input type="hidden" name="sub-type" value="<?php echo esc_attr( $data['source'] ); ?>"/>
812 <input type="hidden" name="app_source" value="<?php echo esc_attr( $data['app_source'] ); ?>"/>
813 <input type="hidden" name="redirect_fragment" value="<?php echo esc_attr( $form_id ); ?>"/>
814 <input type="hidden" name="lang" value="<?php echo esc_attr( $lang ); ?>"/>
815 <?php
816 wp_nonce_field( 'blogsub_subscribe_' . $blog_id );
817
818 if ( ! empty( $post_id ) ) {
819 echo '<input type="hidden" name="post_id" value="' . esc_attr( $post_id ) . '"/>';
820 }
821
822 if ( ! empty( $tier_id ) ) {
823 echo '<input type="hidden" name="tier_id" value="' . esc_attr( $tier_id ) . '"/>';
824 }
825 ?>
826 <button type="submit"
827 <?php if ( ! empty( $classes['submit_button'] ) ) : ?>
828 class="<?php echo esc_attr( $classes['submit_button'] ); ?>"
829 <?php endif; ?>
830 <?php if ( ! empty( $styles['submit_button'] ) ) : ?>
831 style="<?php echo esc_attr( $styles['submit_button'] ); ?>"
832 <?php endif; ?>
833 name="jetpack_subscriptions_widget"
834 >
835 <?php echo sanitize_submit_text( $button_text ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
836 </button>
837 </p>
838 </div>
839 </form>
840 <?php endif; ?>
841 <?php if ( $show_subscriber_count ) : ?>
842 <div class="wp-block-jetpack-subscriptions__subscount">
843 <?php echo esc_html( Jetpack_Memberships::get_join_others_text( $data['subscribers_total'] ) ); ?>
844 </div>
845 <?php endif; ?>
846 </div>
847 </div>
848 <?php
849 return ob_get_clean();
850 }
851
852 /**
853 * Renders the email version of the subscriptions block.
854 *
855 * @param array $data Array containing block view data.
856 * @param array $styles Array containing the styles for different block elements.
857 *
858 * @return string
859 */
860 function render_for_email( $data, $styles ) {
861 $submit_button_wrapper_style = ! empty( $styles['submit_button_wrapper'] ) ? 'style="' . esc_attr( $styles['submit_button_wrapper'] ) . '"' : '';
862 $button_text = get_submit_button_text( $data );
863
864 $html = '<div ' . wp_kses_data( $data['wrapper_attributes'] ) . '>
865 <div>
866 <div>
867 <div>
868 <p ' . $submit_button_wrapper_style . '>
869 <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>
870 </p>
871 </div>
872 </div>
873 </div>
874 </div>';
875
876 return $html;
877 }
878
879 /**
880 * Filter excerpts looking for subscription data.
881 *
882 * @param string $excerpt The extrapolated excerpt string.
883 * @param \WP_Post $post The current post being processed (in `get_the_excerpt`).
884 *
885 * @return mixed
886 */
887 function jetpack_filter_excerpt_for_newsletter( $excerpt, $post = null ) {
888 // The blogmagazine theme is overriding WP core `get_the_excerpt` filter and only passing the excerpt
889 // TODO: Until this is fixed, return the excerpt without gating. See https://github.com/Automattic/jetpack/pull/28102#issuecomment-1369161116
890 if ( $post && str_contains( $post->post_content, '<!-- wp:jetpack/subscriptions -->' ) ) {
891 $excerpt .= sprintf(
892 // translators: %s is the permalink url to the current post.
893 __( "<p><a href='%s'>View post</a> to subscribe to site newsletter.</p>", 'jetpack' ),
894 get_post_permalink()
895 );
896 }
897 return $excerpt;
898 }
899
900 /**
901 * Gate access to posts
902 *
903 * @param string $the_content Post content.
904 *
905 * @return string
906 */
907 function add_paywall( $the_content ) {
908 require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
909
910 $post_access_level = Jetpack_Memberships::get_post_access_level();
911
912 if ( Jetpack_Memberships::user_can_view_post() ) {
913 if ( $post_access_level !== Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_EVERYBODY ) {
914 do_action(
915 'earn_track_paywalled_post_view',
916 array(
917 'post_id' => get_the_ID(),
918 )
919 );
920 }
921 return $the_content;
922 }
923
924 $paywalled_content = get_paywall_content( $post_access_level ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
925
926 if ( has_block( \Automattic\Jetpack\Extensions\Paywall\BLOCK_NAME ) ) {
927 if ( strpos( $the_content, \Automattic\Jetpack\Extensions\Paywall\BLOCK_HTML ) ) {
928 return strstr( $the_content, \Automattic\Jetpack\Extensions\Paywall\BLOCK_HTML, true ) . $paywalled_content;
929 }
930 // WordPress generates excerpts by either rendering or stripping blocks before invoking the `the_content` filter.
931 // In the context of generating an excerpt, the Paywall block specifically renders THE_EXCERPT_BLOCK.
932 if ( strpos( $the_content, \Automattic\Jetpack\Extensions\Paywall\THE_EXCERPT_BLOCK ) ) {
933 return strstr( $the_content, \Automattic\Jetpack\Extensions\Paywall\THE_EXCERPT_BLOCK, true );
934 }
935 }
936
937 return $paywalled_content;
938 }
939
940 /**
941 * Gate access to comments. We want to close comments on private sites.
942 *
943 * @param bool $default_comments_open Default state of the comments_open filter.
944 * @param int $post_id Current post id.
945 *
946 * @return bool
947 */
948 function maybe_close_comments( $default_comments_open, $post_id ) {
949 if ( ! $default_comments_open || ! $post_id ) {
950 return $default_comments_open;
951 }
952
953 require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
954 return Jetpack_Memberships::user_can_view_post();
955 }
956
957 /**
958 * Gate access to existing comments
959 *
960 * @param string $comment The comment.
961 *
962 * @return string
963 */
964 function maybe_gate_existing_comments( $comment ) {
965 if ( empty( $comment ) ) {
966 return $comment;
967 }
968
969 require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
970 if ( Jetpack_Memberships::user_can_view_post() ) {
971 return $comment;
972 }
973 return '';
974 }
975
976 /**
977 * Is the Jetpack_Token_Subscription_Service class loaded
978 *
979 * @return bool
980 */
981 function is_jetpack_token_subscription_service_loaded(): bool {
982 return class_exists( 'Automattic\Jetpack\Extensions\Premium_Content\Subscription_Service\Jetpack_Token_Subscription_Service' );
983 }
984
985 /**
986 * Adds support for WP Super cache and Boost cache
987 */
988 function maybe_prevent_super_cache_caching() {
989 // Prevents cached page to be served if the Membership cookie is present
990 if ( is_jetpack_token_subscription_service_loaded() ) {
991 do_action( 'wpsc_add_cookie', Jetpack_Token_Subscription_Service::JWT_AUTH_TOKEN_COOKIE_NAME );
992 }
993
994 if ( is_user_auth() ) {
995 // Do not cache the page if user is auth with Membership token
996 if ( ! defined( 'DONOTCACHEPAGE' ) ) {
997 define( 'DONOTCACHEPAGE', true );
998 }
999 }
1000 }
1001
1002 /**
1003 * Returns paywall content blocks
1004 *
1005 * @param string $post_access_level The newsletter access level.
1006 * @return string
1007 */
1008 function get_paywall_content( $post_access_level ) {
1009 if ( Jetpack_Memberships::user_is_pending_subscriber() ) {
1010 return get_paywall_blocks_subscribe_pending();
1011 }
1012 if ( doing_filter( 'get_the_excerpt' ) ) {
1013 return '';
1014 }
1015 return get_paywall_blocks( $post_access_level );
1016 }
1017
1018 /**
1019 * Returns the current URL.
1020 *
1021 * TODO: Copied from https://github.com/Automattic/jetpack/blob/bb885061dc3ee7a80a78a5f0116ab3fcebfddb09/projects/packages/boost-core/src/lib/class-url.php#L39
1022 * TODO: Move to a shared package
1023 *
1024 * @return string
1025 */
1026 function get_current_url() {
1027 // Fallback to the site URL if we're unable to determine the URL from $_SERVER global.
1028 $current_url = site_url();
1029
1030 if ( isset( $_SERVER ) && is_array( $_SERVER ) ) {
1031 // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitization happens at the end
1032 $scheme = isset( $_SERVER['HTTPS'] ) && 'on' === $_SERVER['HTTPS'] ? 'https' : 'http';
1033 $host = ! empty( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : null;
1034 $path = ! empty( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '';
1035
1036 // Support for local plugin development and testing using ngrok.
1037 if ( ! empty( $_SERVER['HTTP_X_ORIGINAL_HOST'] ) && str_contains( $_SERVER['HTTP_X_ORIGINAL_HOST'], 'ngrok.io' ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- This is validating.
1038 $host = wp_unslash( $_SERVER['HTTP_X_ORIGINAL_HOST'] );
1039 }
1040 // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1041
1042 if ( $host ) {
1043 $current_url = esc_url_raw( sprintf( '%s://%s%s', $scheme, $host, $path ) );
1044 }
1045 }
1046
1047 return $current_url;
1048 }
1049
1050 /**
1051 * Get the submit button text based on the subscription status.
1052 *
1053 * @param array $data Array containing block view data.
1054 *
1055 * @return string
1056 */
1057 function get_submit_button_text( $data ) {
1058 if ( ! Jetpack_Memberships::is_current_user_subscribed() ) {
1059 return $data['submit_button_text'];
1060 }
1061 if ( ! Jetpack_Memberships::user_can_view_post() ) {
1062 return $data['submit_button_text_upgrade'];
1063 }
1064 return '✓ ' . $data['submit_button_text_subscribed'];
1065 }
1066
1067 /**
1068 * Returns true if there are no more tiers to upgrade to.
1069 *
1070 * @return boolean
1071 */
1072 function is_top_subscription() {
1073 if ( ! Jetpack_Memberships::is_current_user_subscribed() ) {
1074 return false;
1075 }
1076 if ( ! Jetpack_Memberships::user_can_view_post() ) {
1077 return false;
1078 }
1079 return true;
1080 }
1081
1082 /**
1083 * Sanitize the submit button text.
1084 *
1085 * @param string $text String containing the submit button text.
1086 *
1087 * @return string
1088 */
1089 function sanitize_submit_text( $text ) {
1090 return wp_kses(
1091 html_entity_decode( $text, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ),
1092 Jetpack_Subscriptions_Widget::$allowed_html_tags_for_submit_button
1093 );
1094 }
1095
1096 /**
1097 * Returns paywall content blocks if user is not authenticated
1098 *
1099 * @param string $newsletter_access_level The newsletter access level.
1100 * @return string
1101 */
1102 function get_paywall_blocks( $newsletter_access_level ) {
1103 $custom_paywall = apply_filters( 'jetpack_custom_paywall_blocks', false );
1104 if ( ! empty( $custom_paywall ) ) {
1105 return $custom_paywall;
1106 }
1107 if ( ! jetpack_is_frontend() ) { // emails
1108 return get_paywall_simple();
1109 }
1110 require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
1111 // Only display paid texts when Stripe is connected and the post is marked for paid subscribers
1112 $is_paid_post = $newsletter_access_level === 'paid_subscribers' && Jetpack_Memberships::has_connected_account();
1113 $is_paid_subscriber = Jetpack_Memberships::user_is_paid_subscriber();
1114
1115 $access_heading = $is_paid_subscriber
1116 ? esc_html__( 'Upgrade to continue reading', 'jetpack' )
1117 : esc_html__( 'Subscribe to continue reading', 'jetpack' );
1118
1119 $subscribe_text = $is_paid_post
1120 // translators: %s is the name of the site.
1121 ? $is_paid_subscriber
1122 ? esc_html__( 'Upgrade to get access to the rest of this post and other exclusive content.', 'jetpack' )
1123 : esc_html__( 'Become a paid subscriber to get access to the rest of this post and other exclusive content.', 'jetpack' )
1124 // translators: %s is the name of the site.
1125 : esc_html__( 'Subscribe to get access to the rest of this post and other subscriber-only content.', 'jetpack' );
1126
1127 $login_block = '';
1128
1129 if ( is_user_auth() ) {
1130 if ( ( new Host() )->is_wpcom_simple() ) {
1131 // We cannot use wpcom_logmein_redirect_url since it returns redirect URL when user is already logged in.
1132 $login_link = add_query_arg(
1133 array(
1134 'redirect_to' => rawurlencode( get_current_url() ),
1135 'blog_id' => get_current_blog_id(),
1136 ),
1137 'https://wordpress.com/log-in/link'
1138 );
1139 $switch_accounts_link = wp_logout_url( $login_link );
1140 $login_block = '<!-- wp:paragraph {"align":"center","style":{"typography":{"fontSize":"14px"}}} -->
1141 <p class="has-text-align-center" style="font-size:14px">
1142 <a href="' . $switch_accounts_link . '">' . __( 'Switch accounts', 'jetpack' ) . '</a>
1143 </p>
1144 <!-- /wp:paragraph -->';
1145 }
1146 } else {
1147 $access_question = get_paywall_access_question( $newsletter_access_level );
1148 $login_block = '<!-- wp:group {"style":{"typography":{"fontSize":"14px"}},"layout":{"type":"flex","justifyContent":"center"}} -->
1149 <div class="wp-block-group" style="font-size:14px">
1150 <!-- wp:jetpack/subscriber-login {"logInLabel":"' . $access_question . '"} /-->
1151 </div>
1152 <!-- /wp:group -->';
1153 }
1154
1155 $lock_svg = plugins_url( 'images/lock-paywall.svg', JETPACK__PLUGIN_FILE );
1156
1157 return '
1158 <!-- 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"}} -->
1159 <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">
1160 <!-- wp:image {"align":"center","width":24,"height":24,"sizeSlug":"large","linkDestination":"none"} -->
1161 <figure class="wp-block-image aligncenter size-large is-resized"><img src="' . $lock_svg . '" alt="" width="24" height="24"/></figure>
1162 <!-- /wp:image -->
1163
1164 <!-- wp:heading {"textAlign":"center","style":{"typography":{"fontStyle":"normal","fontWeight":"600","fontSize":"24px"},"layout":{"selfStretch":"fit"}}} -->
1165 <h2 class="wp-block-heading has-text-align-center" style="font-size:24px;font-style:normal;font-weight:600">' . $access_heading . '</h2>
1166 <!-- /wp:heading -->
1167
1168 <!-- wp:paragraph {"align":"center","style":{"typography":{"fontSize":"14px"},"spacing":{"margin":{"top":"10px","bottom":"10px"}}}} -->
1169 <p class="has-text-align-center" style="margin-top:10px;margin-bottom:10px;font-size:14px">' . $subscribe_text . '</p>
1170 <!-- /wp:paragraph -->
1171
1172 <!-- wp:jetpack/subscriptions {"borderRadius":50,"borderColor":"primary","className":"is-style-compact","isPaidSubscriber":' . ( $is_paid_subscriber ? 'true' : 'false' ) . '} /-->
1173 ' . $login_block . '
1174 </div>
1175 <!-- /wp:group -->
1176 ';
1177 }
1178
1179 /**
1180 * Returns Get Access question for the paywall
1181 *
1182 * @param string $post_access_level The newsletter access level.
1183 * @return string
1184 */
1185 function get_paywall_access_question( $post_access_level ) {
1186 switch ( $post_access_level ) {
1187 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_PAID_SUBSCRIBERS:
1188 case Abstract_Token_Subscription_Service::POST_ACCESS_LEVEL_PAID_SUBSCRIBERS_ALL_TIERS:
1189 $tier = Jetpack_Memberships::get_post_tier();
1190 if ( $tier !== null ) {
1191 return sprintf(
1192 // translators: Placeholder is the tier name
1193 __( 'Already a higher-tier paid subscriber?', 'jetpack' ),
1194 esc_html( $tier->post_title )
1195 );
1196 } else {
1197 return esc_html__( 'Already a paid subscriber?', 'jetpack' );
1198 }
1199 default:
1200 return esc_html__( 'Already a subscriber?', 'jetpack' );
1201 }
1202 }
1203
1204 /**
1205 * Returns true if user is auth for subscriptions check, otherwise returns false.
1206 *
1207 * @return bool
1208 */
1209 function is_user_auth(): bool {
1210 if ( ( new Host() )->is_wpcom_simple() && is_user_logged_in() ) {
1211 return true;
1212 }
1213 if ( current_user_can( 'manage_options' ) ) {
1214 return true;
1215 }
1216
1217 if ( is_jetpack_token_subscription_service_loaded() ) {
1218 if ( Jetpack_Token_Subscription_Service::has_token_from_cookie() ) {
1219 return true;
1220 }
1221 }
1222 return false;
1223 }
1224
1225 /**
1226 * Returns paywall content blocks when email confirmation is pending
1227 *
1228 * @return string
1229 */
1230 function get_paywall_blocks_subscribe_pending() {
1231 $subscribe_email = Jetpack_Memberships::get_current_user_email();
1232
1233 /** This filter is documented in \Automattic\Jetpack\Forms\ContactForm\Contact_Form */
1234 if ( is_wpcom() || false !== apply_filters( 'jetpack_auto_fill_logged_in_user', false ) ) {
1235 $current_user = wp_get_current_user();
1236 if ( ! empty( $current_user->user_email ) ) {
1237 $subscribe_email = $current_user->user_email;
1238 }
1239 }
1240
1241 $access_heading = esc_html__( 'Confirm your subscription to continue reading', 'jetpack' );
1242
1243 /* translators: %s: email address */
1244 $subscribe_text = sprintf( esc_html__( 'Head to your inbox and confirm your email address %s.', 'jetpack' ), $subscribe_email );
1245
1246 $lock_svg = plugins_url( 'images/lock-paywall.svg', JETPACK__PLUGIN_FILE );
1247
1248 return '
1249 <!-- 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"}} -->
1250 <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">
1251 <!-- wp:image {"align":"center","width":24,"height":24,"sizeSlug":"large","linkDestination":"none"} -->
1252 <figure class="wp-block-image aligncenter size-large is-resized"><img src="' . $lock_svg . '" alt="" width="24" height="24"/></figure>
1253 <!-- /wp:image -->
1254
1255 <!-- wp:heading {"textAlign":"center","style":{"typography":{"fontStyle":"normal","fontWeight":"600","fontSize":"24px", "maxWidth":"initial"},"layout":{"selfStretch":"fit"}}} -->
1256 <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>
1257 <!-- /wp:heading -->
1258
1259 <!-- wp:paragraph {"align":"center","style":{"typography":{"fontSize":"14px"},"spacing":{"margin":{"top":"10px","bottom":"10px"}}}} -->
1260 <p class="has-text-align-center" style="margin-top:10px;margin-bottom:10px;font-size:14px">' . $subscribe_text . '</p>
1261 <!-- /wp:paragraph -->
1262 </div>
1263 <!-- /wp:group -->
1264 ';
1265 }
1266
1267 /**
1268 * Return content for non frontend views like emails.
1269 *
1270 * @return string
1271 */
1272 function get_paywall_simple() {
1273 $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' );
1274
1275 $subscribe_text = esc_html__( 'Upgrade subscription', 'jetpack' );
1276
1277 return '
1278 <!-- wp:columns -->
1279 <div class="wp-block-columns" style="display: inline-block; width: 90%">
1280 <!-- wp:column -->
1281 <div class="wp-block-column" style="background-color: #F6F7F7; padding: 32px; 24px;">
1282 <!-- wp:paragraph -->
1283 <p class="has-text-align-center"
1284 style="text-align: center;
1285 color: #50575E;
1286 font-weight: 400;
1287 font-size: 16px;
1288 font-family: \'SF Pro Text\', sans-serif;
1289 line-height: 28.8px;">
1290 ' . $access_heading . '
1291 </p>
1292 <!-- /wp:paragraph -->
1293 <!-- wp:buttons -->
1294 <div class="wp-block-buttons" style="text-align: center;">
1295 <!-- wp:button -->
1296 <div class="wp-block-button" style="display: inline-block; margin: 10px 0;">
1297 <a href="' . esc_url( get_post_permalink() ) . '" class="wp-block-button__link wp-element-button"
1298 data-wpcom-track data-tracks-link-desc="paywall-email-click"
1299 style="display: inline-block;
1300 padding: 15px 20px;
1301 background-color: #0675C4;
1302 color: #FFFFFF;
1303 text-decoration: none;
1304 border-radius: 5px;
1305 font-family: \'SF Pro Display\', sans-serif;
1306 font-weight: 500;
1307 font-size: 16px;
1308 text-align: center;">' . $subscribe_text . '</a>
1309 </div>
1310 <!-- /wp:button -->
1311 </div>
1312 <!-- /wp:buttons -->
1313 </div>
1314 <!-- /wp:column -->
1315 </div>
1316 <!-- /wp:columns -->
1317 ';
1318 }
1319