PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 12.5.2
Jetpack – WP Security, Backup, Speed, & Growth v12.5.2
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 All 500 releases
jetpack / extensions / blocks / subscriptions / subscriptions.php
subscriptions.php
906 lines 32.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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\Token_Subscription_Service;
13 use Automattic\Jetpack\Status;
14 use Jetpack;
15 use Jetpack_Gutenberg;
16 use Jetpack_Memberships;
17 use Jetpack_Subscriptions_Widget;
18
19 require_once __DIR__ . '/constants.php';
20
21 /**
22 * These block defaults should match ./constants.js
23 */
24 const DEFAULT_BORDER_RADIUS_VALUE = 0;
25 const DEFAULT_BORDER_WEIGHT_VALUE = 1;
26 const DEFAULT_FONTSIZE_VALUE = '16px';
27 const DEFAULT_PADDING_VALUE = 15;
28 const DEFAULT_SPACING_VALUE = 10;
29
30 /**
31 * Registers the block for use in Gutenberg
32 * This is done via an action so that we can disable
33 * registration if we need to.
34 */
35 function register_block() {
36 /*
37 * Disable the feature on P2 blogs
38 */
39 if ( function_exists( '\WPForTeams\is_wpforteams_site' ) &&
40 \WPForTeams\is_wpforteams_site( get_current_blog_id() ) ) {
41 return;
42 }
43
44 if (
45 ( defined( 'IS_WPCOM' ) && IS_WPCOM )
46 || ( ( new Connection_Manager( 'jetpack' ) )->has_connected_owner() && ! ( new Status() )->is_offline_mode() )
47 ) {
48 Blocks::jetpack_register_block(
49 BLOCK_NAME,
50 array(
51 'render_callback' => __NAMESPACE__ . '\render_block',
52 'supports' => array(
53 'spacing' => array(
54 'margin' => true,
55 'padding' => true,
56 ),
57 'align' => array( 'wide', 'full' ),
58 ),
59 )
60 );
61 }
62
63 /*
64 * If the Subscriptions module is not active,
65 * do not make any further changes on the site.
66 */
67 if ( ! Jetpack::is_module_active( 'subscriptions' ) ) {
68 return;
69 }
70
71 /**
72 * Do not proceed if the newsletter feature is not enabled
73 * or if the 'Jetpack_Memberships' class does not exists.
74 */
75 if ( ! class_exists( '\Jetpack_Memberships' ) ) {
76 return;
77 }
78
79 register_post_meta(
80 'post',
81 META_NAME_FOR_POST_LEVEL_ACCESS_SETTINGS,
82 array(
83 'show_in_rest' => true,
84 'single' => true,
85 'type' => 'string',
86 'auth_callback' => function () {
87 return wp_get_current_user()->has_cap( 'edit_posts' );
88 },
89 )
90 );
91
92 // This ensures Jetpack will sync this post meta to WPCOM.
93 add_filter(
94 'jetpack_sync_post_meta_whitelist',
95 function ( $allowed_meta ) {
96 return array_merge( $allowed_meta, array( META_NAME_FOR_POST_LEVEL_ACCESS_SETTINGS ) );
97 }
98 );
99
100 // Hide the content – Priority 8 makes it run before do_blocks gets called for the content
101 add_filter( 'the_content', __NAMESPACE__ . '\add_paywall', 8 );
102
103 // Close comments on the front-end
104 add_filter( 'comments_open', __NAMESPACE__ . '\maybe_close_comments', 10, 2 );
105 add_filter( 'pings_open', __NAMESPACE__ . '\maybe_close_comments', 10, 2 );
106
107 // Hide existing comments
108 add_filter( 'get_comment', __NAMESPACE__ . '\maybe_gate_existing_comments' );
109
110 // Gate the excerpt for a post
111 add_filter( 'get_the_excerpt', __NAMESPACE__ . '\jetpack_filter_excerpt_for_newsletter', 10, 2 );
112
113 // Add a 'Newsletter access' column to the Edit posts page
114 add_action( 'manage_post_posts_columns', __NAMESPACE__ . '\register_newsletter_access_column' );
115 add_action( 'manage_post_posts_custom_column', __NAMESPACE__ . '\render_newsletter_access_rows', 10, 2 );
116 }
117 add_action( 'init', __NAMESPACE__ . '\register_block', 9 );
118
119 /**
120 * Returns true when in a WP.com environment.
121 *
122 * @return boolean
123 */
124 function is_wpcom() {
125 return defined( 'IS_WPCOM' ) && IS_WPCOM;
126 }
127
128 /**
129 * Adds a 'Newsletter' column after the 'Title' column in the post list
130 *
131 * @param array $columns An array of column names.
132 * @return array An array of column names.
133 */
134 function register_newsletter_access_column( $columns ) {
135 if ( ! Jetpack_Memberships::has_configured_plans_jetpack_recurring_payments( 'newsletter' ) ) {
136 // We only display the "NL access" column if we have published one paid-newsletter
137 return $columns;
138 }
139
140 $position = array_search( 'title', array_keys( $columns ), true );
141 $new_column = array( NEWSLETTER_COLUMN_ID => '<span>' . __( 'Newsletter', 'jetpack' ) . '</span>' );
142 return array_merge(
143 array_slice( $columns, 0, $position + 1, true ),
144 $new_column,
145 array_slice( $columns, $position, null, true )
146 );
147 }
148
149 /**
150 * Displays the newsletter access level.
151 *
152 * @param string $column_id The ID of the column to display.
153 * @param int $post_id The current post ID.
154 */
155 function render_newsletter_access_rows( $column_id, $post_id ) {
156 if ( NEWSLETTER_COLUMN_ID !== $column_id ) {
157 return;
158 }
159
160 $access_level = get_post_meta( $post_id, META_NAME_FOR_POST_LEVEL_ACCESS_SETTINGS, true );
161
162 switch ( $access_level ) {
163 case Token_Subscription_Service::POST_ACCESS_LEVEL_PAID_SUBSCRIBERS:
164 echo esc_html__( 'Paid Subscribers', 'jetpack' );
165 break;
166 case Token_Subscription_Service::POST_ACCESS_LEVEL_SUBSCRIBERS:
167 echo esc_html__( 'Subscribers', 'jetpack' );
168 break;
169 case Token_Subscription_Service::POST_ACCESS_LEVEL_EVERYBODY:
170 echo esc_html__( 'Everybody', 'jetpack' );
171 break;
172 default:
173 echo '';
174 }
175 }
176
177 /**
178 * Determine the amount of folks currently subscribed to the blog, splitted out in email_subscribers & social_followers & paid_subscribers
179 *
180 * @return array containing ['value' => ['email_subscribers' => 0, 'paid_subscribers' => 0, 'social_followers' => 0]]
181 */
182 function fetch_subscriber_counts() {
183 $subs_count = 0;
184 if ( is_wpcom() ) {
185 $subs_count = array(
186 'value' => \wpcom_fetch_subs_counts( true ),
187 );
188 } else {
189 $cache_key = 'wpcom_subscribers_totals';
190 $subs_count = get_transient( $cache_key );
191 if ( false === $subs_count || 'failed' === $subs_count['status'] ) {
192 $xml = new \Jetpack_IXR_Client();
193 $xml->query( 'jetpack.fetchSubscriberCounts' );
194
195 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.
196 $subs_count = array(
197 'status' => 'failed',
198 'code' => $xml->getErrorCode(),
199 'message' => $xml->getErrorMessage(),
200 'value' => ( isset( $subs_count['value'] ) ) ? $subs_count['value'] : array(
201 'email_subscribers' => 0,
202 'social_followers' => 0,
203 'paid_subscribers' => 0,
204 ),
205 );
206 } else {
207 $subs_count = array(
208 'status' => 'success',
209 'value' => $xml->getResponse(),
210 );
211 }
212 set_transient( $cache_key, $subs_count, 3600 ); // Try to cache the result for at least 1 hour.
213 }
214 }
215 return $subs_count;
216 }
217
218 /**
219 * Returns subscriber count based on include_social_followers attribute
220 *
221 * @param bool $include_social_followers Whether to include social followers in the count.
222 * @return int
223 */
224 function get_subscriber_count( $include_social_followers ) {
225 $counts = fetch_subscriber_counts();
226
227 if ( $include_social_followers ) {
228 $subscriber_count = $counts['value']['email_subscribers'] + $counts['value']['social_followers'];
229 } else {
230 $subscriber_count = $counts['value']['email_subscribers'];
231 }
232 return $subscriber_count;
233 }
234
235 /**
236 * Returns true if the block attributes contain a value for the given key.
237 *
238 * @param array $attributes Array containing the block attributes.
239 * @param string $key Block attribute key.
240 *
241 * @return boolean
242 */
243 function has_attribute( $attributes, $key ) {
244 return isset( $attributes[ $key ] ) && $attributes[ $key ] !== 'undefined';
245 }
246
247 /**
248 * Returns the value for the given attribute key, with the option of providing a default fallback value.
249 *
250 * @param array $attributes Array containing the block attributes.
251 * @param string $key Block attribute key.
252 * @param mixed $default Optional fallback value in case the key doesn't exist.
253 *
254 * @return mixed
255 */
256 function get_attribute( $attributes, $key, $default = null ) {
257 return has_attribute( $attributes, $key ) ? $attributes[ $key ] : $default;
258 }
259
260 /**
261 * Mimics getColorClassName, getFontSizeClass and getGradientClass from @wordpress/block-editor js package.
262 *
263 * @param string $setting Setting name.
264 * @param string $value Setting value.
265 *
266 * @return string
267 */
268 function get_setting_class_name( $setting, $value ) {
269 if ( ! $setting || ! $value ) {
270 return '';
271 }
272
273 return sprintf( 'has-%s-%s', $value, $setting );
274 }
275
276 /**
277 * Uses block attributes to generate an array containing the classes for various block elements.
278 * Based on Jetpack_Subscriptions_Widget::do_subscription_form() which the block was originally using.
279 *
280 * @param array $attributes Array containing the block attributes.
281 *
282 * @return array
283 */
284 function get_element_class_names_from_attributes( $attributes ) {
285 $text_color_class = get_setting_class_name( 'color', get_attribute( $attributes, 'textColor' ) );
286 $font_size_class = get_setting_class_name( 'font-size', get_attribute( $attributes, 'fontSize' ) );
287 $border_class = get_setting_class_name( 'border-color', get_attribute( $attributes, 'borderColor' ) );
288
289 $button_background_class = get_setting_class_name( 'background-color', get_attribute( $attributes, 'buttonBackgroundColor' ) );
290 $button_gradient_class = get_setting_class_name( 'gradient-background', get_attribute( $attributes, 'buttonGradient' ) );
291
292 $email_field_background_class = get_setting_class_name( 'background-color', get_attribute( $attributes, 'emailFieldBackgroundColor' ) );
293 $email_field_gradient_class = get_setting_class_name( 'gradient-background', get_attribute( $attributes, 'emailFieldGradient' ) );
294
295 $submit_button_classes = array_filter(
296 array(
297 'wp-block-button__link' => true,
298 'no-border-radius' => 0 === get_attribute( $attributes, 'borderRadius', 0 ),
299 $font_size_class => true,
300 $border_class => true,
301 'has-text-color' => ! empty( $text_color_class ),
302 $text_color_class => true,
303 'has-background' => ! empty( $button_background_class ) || ! empty( $button_gradient_class ),
304 $button_background_class => ! empty( $button_background_class ),
305 $button_gradient_class => ! empty( $button_gradient_class ),
306 )
307 );
308
309 $email_field_classes = array_filter(
310 array(
311 'no-border-radius' => 0 === get_attribute( $attributes, 'borderRadius', 0 ),
312 $font_size_class => true,
313 $border_class => true,
314 $email_field_background_class => true,
315 $email_field_gradient_class => true,
316 )
317 );
318
319 $block_wrapper_classes = array_filter(
320 array(
321 'wp-block-jetpack-subscriptions__supports-newline' => true,
322 'wp-block-jetpack-subscriptions__use-newline' => (bool) get_attribute( $attributes, 'buttonOnNewLine' ),
323 'wp-block-jetpack-subscriptions__show-subs' => (bool) get_attribute( $attributes, 'showSubscribersTotal' ),
324 )
325 );
326
327 return array(
328 'block_wrapper' => implode( ' ', array_keys( $block_wrapper_classes ) ),
329 'email_field' => implode( ' ', array_keys( $email_field_classes ) ),
330 'submit_button' => implode( ' ', array_keys( $submit_button_classes ) ),
331 );
332 }
333
334 /**
335 * Uses block attributes to generate an array containing the styles for various block elements.
336 * Based on Jetpack_Subscriptions_Widget::do_subscription_form() which the block was originally using.
337 *
338 * @param array $attributes Array containing the block attributes.
339 *
340 * @return array
341 */
342 function get_element_styles_from_attributes( $attributes ) {
343 $button_background_style = ! has_attribute( $attributes, 'buttonBackgroundColor' ) && has_attribute( $attributes, 'customButtonGradient' )
344 ? get_attribute( $attributes, 'customButtonGradient' )
345 : get_attribute( $attributes, 'customButtonBackgroundColor' );
346
347 $email_field_styles = '';
348 $submit_button_wrapper_styles = '';
349 $submit_button_styles = '';
350
351 if ( ! empty( $button_background_style ) ) {
352 $submit_button_styles .= sprintf( 'background: %s;', $button_background_style );
353 }
354
355 if ( has_attribute( $attributes, 'customTextColor' ) ) {
356 $submit_button_styles .= sprintf( 'color: %s;', get_attribute( $attributes, 'customTextColor' ) );
357 }
358
359 if ( has_attribute( $attributes, 'buttonWidth' ) ) {
360 $submit_button_wrapper_styles .= sprintf( 'width: %s;', get_attribute( $attributes, 'buttonWidth' ) );
361 $submit_button_wrapper_styles .= 'max-width: 100%;';
362
363 // Account for custom margins on inline forms.
364 $submit_button_styles .= true === get_attribute( $attributes, 'buttonOnNewLine' )
365 ? sprintf( 'width: calc(100%% - %dpx);', get_attribute( $attributes, 'spacing', DEFAULT_SPACING_VALUE ) )
366 : 'width: 100%;';
367 }
368
369 $font_size = get_attribute( $attributes, 'customFontSize', DEFAULT_FONTSIZE_VALUE );
370 $style = sprintf( 'font-size: %s%s;', $font_size, is_numeric( $font_size ) ? 'px' : '' );
371
372 $submit_button_styles .= $style;
373 $email_field_styles .= $style;
374
375 $padding = get_attribute( $attributes, 'padding', DEFAULT_PADDING_VALUE );
376 $style = sprintf( 'padding: %1$dpx %2$dpx %1$dpx %2$dpx;', $padding, round( $padding * 1.5 ) );
377
378 $submit_button_styles .= $style;
379 $email_field_styles .= $style;
380
381 $button_spacing = get_attribute( $attributes, 'spacing', DEFAULT_SPACING_VALUE );
382 if ( true === get_attribute( $attributes, 'buttonOnNewLine' ) ) {
383 $submit_button_styles .= sprintf( 'margin-top: %dpx;', $button_spacing );
384 } else {
385 $submit_button_styles .= 'margin: 0px; '; // Reset Safari's 2px default margin for buttons affecting input and button union
386 $submit_button_styles .= sprintf( 'margin-left: %dpx;', $button_spacing );
387 }
388
389 if ( has_attribute( $attributes, 'borderColor' ) ) {
390 $style = sprintf( 'border-color: %s;', get_attribute( $attributes, 'borderColor', '' ) );
391 $submit_button_styles .= $style;
392 $email_field_styles .= $style;
393 }
394
395 $style = sprintf( 'border-radius: %dpx;', get_attribute( $attributes, 'borderRadius', DEFAULT_BORDER_RADIUS_VALUE ) );
396 $submit_button_styles .= $style;
397 $email_field_styles .= $style;
398
399 $style = sprintf( 'border-width: %dpx;', get_attribute( $attributes, 'borderWeight', DEFAULT_BORDER_WEIGHT_VALUE ) );
400 $submit_button_styles .= $style;
401 $email_field_styles .= $style;
402
403 if ( has_attribute( $attributes, 'customBorderColor' ) ) {
404 $style = sprintf( 'border-color: %s; border-style: solid;', get_attribute( $attributes, 'customBorderColor' ) );
405
406 $submit_button_styles .= $style;
407 $email_field_styles .= $style;
408 }
409
410 return array(
411 'email_field' => $email_field_styles,
412 'submit_button' => $submit_button_styles,
413 'submit_button_wrapper' => $submit_button_wrapper_styles,
414 );
415 }
416
417 /**
418 * Subscriptions block render callback.
419 *
420 * @param array $attributes Array containing the block attributes.
421 *
422 * @return string
423 */
424 function render_block( $attributes ) {
425 // If the Subscriptions module is not active, don't render the block.
426 if ( ! Jetpack::is_module_active( 'subscriptions' ) ) {
427 return '';
428 }
429
430 if ( class_exists( '\Jetpack_Memberships' ) ) {
431 // We only want the sites that have newsletter feature enabled to be graced by this JavaScript and thickbox.
432 Jetpack_Gutenberg::load_assets_as_required( FEATURE_NAME, array( 'thickbox' ) );
433 if ( ! wp_style_is( 'enqueued' ) ) {
434 wp_enqueue_style( 'thickbox' );
435 }
436 } else {
437 Jetpack_Gutenberg::load_styles_as_required( FEATURE_NAME );
438 }
439
440 $subscribe_email = '';
441
442 /** This filter is documented in modules/contact-form/grunion-contact-form.php */
443 if ( is_wpcom() || false !== apply_filters( 'jetpack_auto_fill_logged_in_user', false ) ) {
444 $current_user = wp_get_current_user();
445 $subscribe_email = ! empty( $current_user->user_email ) ? $current_user->user_email : '';
446 }
447
448 // The block is using the Jetpack_Subscriptions_Widget backend, hence the need to increase the instance count.
449 ++Jetpack_Subscriptions_Widget::$instance_count;
450
451 $classes = get_element_class_names_from_attributes( $attributes );
452 $styles = get_element_styles_from_attributes( $attributes );
453 $include_social_followers = isset( $attributes['includeSocialFollowers'] ) ? (bool) get_attribute( $attributes, 'includeSocialFollowers' ) : true;
454
455 $data = array(
456 'widget_id' => Jetpack_Subscriptions_Widget::$instance_count,
457 'subscribe_email' => $subscribe_email,
458
459 'wrapper_attributes' => get_block_wrapper_attributes(
460 array(
461 'class' => $classes['block_wrapper'],
462 )
463 ),
464 'subscribe_placeholder' => get_attribute( $attributes, 'subscribePlaceholder', esc_html__( 'Type your email…', 'jetpack' ) ),
465 'submit_button_text' => get_attribute( $attributes, 'submitButtonText', esc_html__( 'Subscribe', 'jetpack' ) ),
466 'success_message' => get_attribute(
467 $attributes,
468 'successMessage',
469 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' )
470 ),
471 'show_subscribers_total' => (bool) get_attribute( $attributes, 'showSubscribersTotal' ),
472 'subscribers_total' => get_subscriber_count( $include_social_followers ),
473 'referer' => esc_url_raw(
474 ( is_ssl() ? 'https' : 'http' ) . '://' . ( isset( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : '' ) .
475 ( isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '' )
476 ),
477 'source' => 'subscribe-block',
478 );
479
480 if ( is_wpcom() ) {
481 return render_wpcom_subscribe_form( $data, $classes, $styles );
482 }
483
484 return render_jetpack_subscribe_form( $data, $classes, $styles );
485 }
486
487 /**
488 * Get the post access level for the current post. Defaults to 'everybody' if the query is not for a single post
489 *
490 * @return string the actual post access level (see projects/plugins/jetpack/extensions/blocks/subscriptions/constants.js for the values).
491 */
492 function get_post_access_level_for_current_post() {
493 if ( ! is_singular() ) {
494 // There is no "actual" current post.
495 return Token_Subscription_Service::POST_ACCESS_LEVEL_EVERYBODY;
496 }
497
498 return Jetpack_Memberships::get_post_access_level();
499 }
500
501 /**
502 * Renders the WP.com version of the subscriptions block.
503 *
504 * @param array $data Array containing block view data.
505 * @param array $classes Array containing the classes for different block elements.
506 * @param array $styles Array containing the styles for different block elements.
507 *
508 * @return string
509 */
510 function render_wpcom_subscribe_form( $data, $classes, $styles ) {
511 global $current_blog;
512
513 $form_id = 'subscribe-blog' . ( Jetpack_Subscriptions_Widget::$instance_count > 1 ? '-' . Jetpack_Subscriptions_Widget::$instance_count : '' );
514 $url = defined( 'SUBSCRIBE_BLOG_URL' ) ? SUBSCRIBE_BLOG_URL : '';
515
516 ob_start();
517
518 Jetpack_Subscriptions_Widget::render_widget_status_messages(
519 array(
520 'success_message' => $data['success_message'],
521 )
522 );
523
524 $post_access_level = get_post_access_level_for_current_post();
525
526 ?>
527 <div <?php echo wp_kses_data( $data['wrapper_attributes'] ); ?>>
528 <div class="wp-block-jetpack-subscriptions__container">
529 <form
530 action="<?php echo esc_url( $url ); ?>"
531 method="post"
532 accept-charset="utf-8"
533 data-blog="<?php echo esc_attr( get_current_blog_id() ); ?>"
534 data-post_access_level="<?php echo esc_attr( $post_access_level ); ?>"
535 id="<?php echo esc_attr( $form_id ); ?>"
536 >
537 <?php
538 $email_field_id = 'subscribe-field';
539 $email_field_id .= Jetpack_Subscriptions_Widget::$instance_count > 1
540 ? '-' . Jetpack_Subscriptions_Widget::$instance_count
541 : '';
542 $label_field_id = $email_field_id . '-label';
543 ?>
544 <p id="subscribe-email">
545 <label
546 id="<?php echo esc_attr( $label_field_id ); ?>"
547 for="<?php echo esc_attr( $email_field_id ); ?>"
548 class="screen-reader-text"
549 >
550 <?php echo esc_html( $data['subscribe_placeholder'] ); ?>
551 </label>
552
553 <?php
554 printf(
555 '<input
556 required="required"
557 type="email"
558 name="email"
559 %1$s
560 style="%2$s"
561 placeholder="%3$s"
562 value="%4$s"
563 id="%5$s"
564 />',
565 ( ! empty( $classes['email_field'] )
566 ? 'class="' . esc_attr( $classes['email_field'] ) . '"'
567 : ''
568 ),
569 ( ! empty( $styles['email_field'] )
570 ? esc_attr( $styles['email_field'] )
571 : 'width: 95%; padding: 1px 10px'
572 ),
573 esc_attr( $data['subscribe_placeholder'] ),
574 esc_attr( $data['subscribe_email'] ),
575 esc_attr( $email_field_id )
576 );
577 ?>
578 </p>
579
580 <p id="subscribe-submit"
581 <?php if ( ! empty( $styles['submit_button_wrapper'] ) ) : ?>
582 style="<?php echo esc_attr( $styles['submit_button_wrapper'] ); ?>"
583 <?php endif; ?>
584 >
585 <input type="hidden" name="action" value="subscribe"/>
586 <input type="hidden" name="blog_id" value="<?php echo (int) $current_blog->blog_id; ?>"/>
587 <input type="hidden" name="source" value="<?php echo esc_url( $data['referer'] ); ?>"/>
588 <input type="hidden" name="sub-type" value="<?php echo esc_attr( $data['source'] ); ?>"/>
589 <input type="hidden" name="redirect_fragment" value="<?php echo esc_attr( $form_id ); ?>"/>
590 <?php wp_nonce_field( 'blogsub_subscribe_' . $current_blog->blog_id, '_wpnonce', false ); ?>
591 <button type="submit"
592 <?php if ( ! empty( $classes['submit_button'] ) ) : ?>
593 class="<?php echo esc_attr( $classes['submit_button'] ); ?>"
594 <?php endif; ?>
595 <?php if ( ! empty( $styles['submit_button'] ) ) : ?>
596 style="<?php echo esc_attr( $styles['submit_button'] ); ?>"
597 <?php endif; ?>
598 >
599 <?php
600 echo wp_kses(
601 html_entity_decode( $data['submit_button_text'], ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ),
602 Jetpack_Subscriptions_Widget::$allowed_html_tags_for_submit_button
603 );
604 ?>
605 </button>
606 </p>
607 </form>
608 <?php if ( $data['show_subscribers_total'] && $data['subscribers_total'] ) : ?>
609 <div class="wp-block-jetpack-subscriptions__subscount">
610 <?php
611 /* translators: %s: number of folks following the blog */
612 echo esc_html( sprintf( _n( 'Join %s other follower', 'Join %s other followers', $data['subscribers_total'], 'jetpack' ), number_format_i18n( $data['subscribers_total'] ) ) );
613 ?>
614 </div>
615 <?php endif; ?>
616 </div>
617 </div>
618 <?php
619
620 return ob_get_clean();
621 }
622
623 /**
624 * Renders the Jetpack version of the subscriptions block.
625 *
626 * @param array $data Array containing block view data.
627 * @param array $classes Array containing the classes for different block elements.
628 * @param array $styles Array containing the styles for different block elements.
629 *
630 * @return string
631 */
632 function render_jetpack_subscribe_form( $data, $classes, $styles ) {
633 $form_id = sprintf( 'subscribe-blog-%s', $data['widget_id'] );
634 $subscribe_field_id = apply_filters( 'subscribe_field_id', 'subscribe-field', $data['widget_id'] );
635 ob_start();
636
637 Jetpack_Subscriptions_Widget::render_widget_status_messages(
638 array(
639 'success_message' => $data['success_message'],
640 )
641 );
642
643 $blog_id = \Jetpack_Options::get_option( 'id' );
644 $post_access_level = get_post_access_level_for_current_post();
645
646 ?>
647 <div <?php echo wp_kses_data( $data['wrapper_attributes'] ); ?>>
648 <div class="jetpack_subscription_widget">
649 <div class="wp-block-jetpack-subscriptions__container">
650 <form
651 action="#"
652 method="post"
653 accept-charset="utf-8"
654 data-blog="<?php echo esc_attr( $blog_id ); ?>"
655 data-post_access_level="<?php echo esc_attr( $post_access_level ); ?>"
656 id="<?php echo esc_attr( $form_id ); ?>"
657 >
658 <p id="subscribe-email">
659 <label id="jetpack-subscribe-label"
660 class="screen-reader-text"
661 for="<?php echo esc_attr( $subscribe_field_id . '-' . $data['widget_id'] ); ?>">
662 <?php echo esc_html( $data['subscribe_placeholder'] ); ?>
663 </label>
664 <input type="email" name="email" required="required"
665 <?php if ( ! empty( $classes['email_field'] ) ) : ?>
666 class="<?php echo esc_attr( $classes['email_field'] ); ?> required"
667 <?php endif; ?>
668 <?php if ( ! empty( $styles['email_field'] ) ) : ?>
669 style="<?php echo esc_attr( $styles['email_field'] ); ?>"
670 <?php endif; ?>
671 value="<?php echo esc_attr( $data['subscribe_email'] ); ?>"
672 id="<?php echo esc_attr( $subscribe_field_id . '-' . $data['widget_id'] ); ?>"
673 placeholder="<?php echo esc_attr( $data['subscribe_placeholder'] ); ?>"
674 />
675 </p>
676
677 <p id="subscribe-submit"
678 <?php if ( ! empty( $styles['submit_button_wrapper'] ) ) : ?>
679 style="<?php echo esc_attr( $styles['submit_button_wrapper'] ); ?>"
680 <?php endif; ?>
681 >
682 <input type="hidden" name="action" value="subscribe"/>
683 <input type="hidden" name="blog_id" value="<?php echo (int) $blog_id; ?>"/>
684 <input type="hidden" name="source" value="<?php echo esc_url( $data['referer'] ); ?>"/>
685 <input type="hidden" name="sub-type" value="<?php echo esc_attr( $data['source'] ); ?>"/>
686 <input type="hidden" name="redirect_fragment" value="<?php echo esc_attr( $form_id ); ?>"/>
687 <?php
688 if ( is_user_logged_in() ) {
689 wp_nonce_field( 'blogsub_subscribe_' . get_current_blog_id(), '_wpnonce', false );
690 }
691 ?>
692 <button type="submit"
693 <?php if ( ! empty( $classes['submit_button'] ) ) : ?>
694 class="<?php echo esc_attr( $classes['submit_button'] ); ?>"
695 <?php endif; ?>
696 <?php if ( ! empty( $styles['submit_button'] ) ) : ?>
697 style="<?php echo esc_attr( $styles['submit_button'] ); ?>"
698 <?php endif; ?>
699 name="jetpack_subscriptions_widget"
700 >
701 <?php
702 echo wp_kses(
703 html_entity_decode( $data['submit_button_text'], ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ),
704 Jetpack_Subscriptions_Widget::$allowed_html_tags_for_submit_button
705 );
706 ?>
707 </button>
708 </p>
709 </form>
710
711 <?php if ( $data['show_subscribers_total'] && $data['subscribers_total'] ) : ?>
712 <div class="wp-block-jetpack-subscriptions__subscount">
713 <?php
714 /* translators: %s: number of folks following the blog */
715 echo esc_html( sprintf( _n( 'Join %s other subscriber', 'Join %s other subscribers', $data['subscribers_total'], 'jetpack' ), number_format_i18n( $data['subscribers_total'] ) ) );
716 ?>
717 </div>
718 <?php endif; ?>
719 </div>
720 </div>
721 </div>
722 <?php
723
724 return ob_get_clean();
725 }
726
727 /**
728 * Filter excerpts looking for subscription data.
729 *
730 * @param string $excerpt The extrapolated excerpt string.
731 * @param \WP_Post $post The current post being processed (in `get_the_excerpt`).
732 *
733 * @return mixed
734 */
735 function jetpack_filter_excerpt_for_newsletter( $excerpt, $post = null ) {
736 // The blogmagazine theme is overriding WP core `get_the_excerpt` filter and only passing the excerpt
737 // TODO: Until this is fixed, return the excerpt without gating. See https://github.com/Automattic/jetpack/pull/28102#issuecomment-1369161116
738 if ( $post && false !== strpos( $post->post_content, '<!-- wp:jetpack/subscriptions -->' ) ) {
739 $excerpt .= sprintf(
740 // translators: %s is the permalink url to the current post.
741 __( "<p><a href='%s'>View post</a> to subscribe to site newsletter.</p>", 'jetpack' ),
742 get_post_permalink()
743 );
744 }
745 return $excerpt;
746 }
747
748 /**
749 * Gate access to posts
750 *
751 * @param string $the_content Post content.
752 *
753 * @return string
754 */
755 function add_paywall( $the_content ) {
756 $block_name = 'jetpack/paywall';
757 require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
758
759 if ( Jetpack_Memberships::user_can_view_post() ) {
760 return $the_content;
761 }
762
763 if ( has_block( $block_name ) ) {
764 $post_access_level = Jetpack_Memberships::get_post_access_level();
765 if ( jetpack_is_frontend() ) {
766 $paywalled_content = get_paywall_blocks( $post_access_level );
767 } else {
768 // emails
769 $paywalled_content = get_paywall_simple();
770 }
771 $paywalled_content = strstr( $the_content, '<!-- wp:' . $block_name . ' /-->', true ) . $paywalled_content;
772 }
773
774 return $paywalled_content;
775 }
776
777 /**
778 * Gate access to comments. We want to close comments on private sites.
779 *
780 * @param bool $default_comments_open Default state of the comments_open filter.
781 * @param int $post_id Current post id.
782 *
783 * @return bool
784 */
785 function maybe_close_comments( $default_comments_open, $post_id ) {
786 if ( ! $default_comments_open || ! $post_id ) {
787 return $default_comments_open;
788 }
789
790 require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
791 return Jetpack_Memberships::user_can_view_post();
792 }
793
794 /**
795 * Gate access to existing comments
796 *
797 * @param string $comment The comment.
798 *
799 * @return string
800 */
801 function maybe_gate_existing_comments( $comment ) {
802 if ( empty( $comment ) ) {
803 return $comment;
804 }
805
806 require_once JETPACK__PLUGIN_DIR . 'modules/memberships/class-jetpack-memberships.php';
807 if ( Jetpack_Memberships::user_can_view_post() ) {
808 return $comment;
809 }
810 return '';
811 }
812
813 /**
814 * Returns paywall content blocks
815 *
816 * @param string $newsletter_access_level The newsletter access level.
817 * @return string
818 */
819 function get_paywall_blocks( $newsletter_access_level ) {
820 // Only display paid texts when Stripe is connected and the post is marked for paid subscribers
821 $is_paid_post = $newsletter_access_level === 'paid_subscribers'
822 && ! empty( Jetpack_Memberships::get_connected_account_id() );
823
824 $access_heading = esc_html__( 'Subscribe to continue reading', 'jetpack' );
825
826 $subscribe_text = $is_paid_post
827 // translators: %s is the name of the site.
828 ? esc_html__( 'Become a paid subscriber to get access to the rest of this post and other exclusive content.', 'jetpack' )
829 // translators: %s is the name of the site.
830 : esc_html__( 'Subscribe to get access to the rest of this post and other subscriber-only content.', 'jetpack' );
831
832 $lock_svg = plugins_url( 'images/lock-paywall.svg', JETPACK__PLUGIN_FILE );
833
834 return '
835 <!-- wp:group {"style":{"border":{"width":"1px","radius":"4px"},"spacing":{"padding":{"top":"var:preset|spacing|70","bottom":"var:preset|spacing|70","left":"32px","right":"32px"}}},"borderColor":"primary","className":"jetpack-subscribe-paywall","layout":{"type":"constrained","contentSize":"400px"}} -->
836 <div class="wp-block-group jetpack-subscribe-paywall has-border-color has-primary-border-color" style="border-width:1px;border-radius:4px;padding-top:var(--wp--preset--spacing--70);padding-right:32px;padding-bottom:var(--wp--preset--spacing--70);padding-left:32px">
837 <!-- wp:image {"align":"center","width":24,"height":24,"sizeSlug":"large","linkDestination":"none"} -->
838 <figure class="wp-block-image aligncenter size-large is-resized"><img src="' . $lock_svg . '" alt="" width="24" height="24"/></figure>
839 <!-- /wp:image -->
840
841 <!-- wp:heading {"textAlign":"center","style":{"typography":{"fontStyle":"normal","fontWeight":"600","fontSize":"24px"},"layout":{"selfStretch":"fit"}}} -->
842 <h2 class="wp-block-heading has-text-align-center" style="font-size:24px;font-style:normal;font-weight:600">' . $access_heading . '</h2>
843 <!-- /wp:heading -->
844
845 <!-- wp:paragraph {"align":"center","style":{"typography":{"fontSize":"14px"},"spacing":{"margin":{"top":"10px","bottom":"10px"}}}} -->
846 <p class="has-text-align-center" style="margin-top:10px;margin-bottom:10px;font-size:14px">' . $subscribe_text . '</p>
847 <!-- /wp:paragraph -->
848
849 <!-- wp:jetpack/subscriptions {"borderRadius":50,"borderColor":"primary","className":"is-style-compact"} /--></div>
850 <!-- /wp:group -->
851 ';
852 }
853
854 /**
855 * Return content for non frontend views like emails.
856 *
857 * @return string
858 */
859 function get_paywall_simple() {
860 $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' );
861
862 $subscribe_text = esc_html__( 'Upgrade subscription', 'jetpack' );
863
864 return '
865 <!-- wp:columns -->
866 <div class="wp-block-columns" style="display: inline-block; width: 90%">
867 <!-- wp:column -->
868 <div class="wp-block-column" style="background-color: #F6F7F7; padding: 32px; 24px;">
869 <!-- wp:paragraph -->
870 <p class="has-text-align-center"
871 style="text-align: center;
872 color: #50575E;
873 font-weight: 400;
874 font-size: 16px;
875 font-family: \'SF Pro Text\', sans-serif;
876 line-height: 28.8px;">
877 ' . $access_heading . '
878 </p>
879 <!-- /wp:paragraph -->
880
881 <!-- wp:buttons -->
882 <div class="wp-block-buttons" style="text-align: center;">
883 <!-- wp:button -->
884 <div class="wp-block-button" style="display: inline-block; margin: 10px 0;">
885 <a href="#" class="wp-block-button__link wp-element-button"
886 style="display: inline-block;
887 padding: 15px 20px;
888 background-color: #0675C4;
889 color: #FFFFFF;
890 text-decoration: none;
891 border-radius: 5px;
892 font-family: \'SF Pro Display\', sans-serif;
893 font-weight: 500;
894 font-size: 16px;
895 text-align: center;">' . $subscribe_text . '</a>
896 </div>
897 <!-- /wp:button -->
898 </div>
899 <!-- /wp:buttons -->
900 </div>
901 <!-- /wp:column -->
902 </div>
903 <!-- /wp:columns -->
904 ';
905 }
906