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