PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.4.3
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.4.3
3.4.3 3.4.2 3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 2.3.0 2.3.1 All 196 releases
← All changes | includes/class-convertkit-output-restrict-content.php +213 -73 3.3.63.4.3 View file →
@@ -98,8 +98,17 @@
98 98 */
99 99 public $token = false;
100 100
101 101 /**
102 + * Whether the login modal has been output.
103 + *
104 + * @since 3.4.2
105 + *
106 + * @var bool
107 + */
108 + public $login_modal_output = false;
109 +
110 + /**
102 111 * Constructor. Registers actions and filters to possibly limit output of a Page/Post/CPT's
103 112 * content on the frontend site.
104 113 *
105 114 * @since 2.1.0
@@ -108,8 +117,9 @@
108 117
109 118 add_action( 'rest_api_init', array( $this, 'register_routes' ) );
110 119 add_action( 'init', array( $this, 'initialize_classes' ), 2 );
111 120 add_action( 'init', array( $this, 'maybe_run_subscriber_authentication' ), 3 );
121 + add_action( 'wp', array( $this, 'maybe_run_subscriber_logout' ), 3 );
112 122 add_action( 'wp', array( $this, 'maybe_run_subscriber_verification' ), 4 );
113 123 add_action( 'wp', array( $this, 'register_content_filter' ), 5 );
114 124 add_filter( 'get_previous_post_where', array( $this, 'maybe_change_previous_post_where_clause' ), 10, 5 );
115 125 add_filter( 'get_next_post_where', array( $this, 'maybe_change_next_post_where_clause' ), 10, 5 );
@@ -154,11 +164,12 @@
154 164 },
155 165 'sanitize_callback' => 'absint',
156 166 ),
157 167
158 - // Resource Type: Validate resource type is included in the request and is a string.
168 + // Resource Type: Validate resource type is a string, if included in the request.
169 + // It's not included when logging in using the Member Content Login block.
159 170 'convertkit_resource_type' => array(
160 - 'required' => true,
171 + 'required' => false,
161 172 'validate_callback' => function ( $param ) {
162 173
163 174 return is_string( $param );
164 175
@@ -165,11 +176,12 @@
165 176 },
166 177 'sanitize_callback' => 'sanitize_text_field',
167 178 ),
168 179
169 - // Resource ID: Validate resource ID is included in the request and is an integer.
180 + // Resource ID: Validate resource ID is an integer, if included in the request.
181 + // It's not included when logging in using the Member Content Login block.
170 182 'convertkit_resource_id' => array(
171 - 'required' => true,
183 + 'required' => false,
172 184 'validate_callback' => function ( $param ) {
173 185
174 186 return is_numeric( $param );
175 187
@@ -175,8 +187,32 @@
175 187
176 188 },
177 189 'sanitize_callback' => 'absint',
178 190 ),
191 +
192 + // Spam protection response, if a spam protection provider is enabled.
193 + 'spam_protection_response' => array(
194 + 'required' => false,
195 + 'validate_callback' => function ( $param ) {
196 +
197 + return is_string( $param );
198 +
199 + },
200 + 'sanitize_callback' => 'sanitize_text_field',
201 + ),
202 +
203 + // Whether to display the heading above the login form.
204 + // It's not displayed by the Member Content Login block, as it refers to
205 + // reading the Member Content the subscriber is logging in to view.
206 + 'display_heading' => array(
207 + 'required' => false,
208 + 'default' => true,
209 + 'validate_callback' => function ( $param ) {
210 +
211 + return is_bool( $param );
212 +
213 + },
214 + ),
179 215 ),
180 216 'callback' => function ( $request ) {
181 217
182 218 // Initialize classes that will be used.
@@ -188,8 +224,26 @@
188 224 $post_id = $request->get_param( 'convertkit_post_id' );
189 225 $resource_type = $request->get_param( 'convertkit_resource_type' );
190 226 $resource_id = $request->get_param( 'convertkit_resource_id' );
191 227
228 + // Check spam protection (reCAPTCHA or Cloudflare Turnstile, depending on Plugin settings).
229 + $result = $output_restrict_content->verify_spam_protection( $request->get_param( 'spam_protection_response' ) );
230 +
231 + // If spam protection failed, build the email form view with the error message.
232 + if ( is_wp_error( $result ) ) {
233 + $output_restrict_content->error = $result;
234 +
235 + ob_start();
236 + include CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/' . ( $request->get_param( 'display_heading' ) ? 'login-modal-content-email.php' : 'login-email.php' );
237 + $output = trim( ob_get_clean() );
238 + return rest_ensure_response(
239 + array(
240 + 'success' => false,
241 + 'data' => $output,
242 + )
243 + );
244 + }
245 +
192 246 // Run subscriber authentication.
193 247 $result = $output_restrict_content->subscriber_authentication_send_code(
194 248 $email,
195 249 $post_id
@@ -201,9 +255,9 @@
201 255 $output_restrict_content->error = $result;
202 256
203 257 // Build email form view to return for output with error message.
204 258 ob_start();
205 - include CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal-content-email.php';
259 + include CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/' . ( $request->get_param( 'display_heading' ) ? 'login-modal-content-email.php' : 'login-email.php' );
206 260 $output = trim( ob_get_clean() );
207 261 return rest_ensure_response(
208 262 array(
209 263 'success' => false,
@@ -364,18 +418,12 @@
364 418 if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'convertkit_restrict_content_login' ) ) {
365 419 return;
366 420 }
367 421
368 - // Bail if the expected email, resource type, resource ID or Post ID are missing from the request.
422 + // Bail if the expected email or Post ID are missing from the request.
369 423 if ( ! array_key_exists( 'convertkit_email', $_REQUEST ) ) {
370 424 return;
371 425 }
372 - if ( ! array_key_exists( 'convertkit_resource_type', $_REQUEST ) ) {
373 - return;
374 - }
375 - if ( ! array_key_exists( 'convertkit_resource_id', $_REQUEST ) ) {
376 - return;
377 - }
378 426 if ( ! array_key_exists( 'convertkit_post_id', $_REQUEST ) ) {
379 427 return;
380 428 }
381 429
@@ -385,24 +433,21 @@
385 433 }
386 434
387 435 // Sanitize inputs.
388 436 $email = sanitize_text_field( wp_unslash( $_REQUEST['convertkit_email'] ) );
389 - $this->resource_type = sanitize_text_field( wp_unslash( $_REQUEST['convertkit_resource_type'] ) );
390 - $this->resource_id = absint( $_REQUEST['convertkit_resource_id'] );
437 + $this->resource_type = ( array_key_exists( 'convertkit_resource_type', $_REQUEST ) ? sanitize_text_field( wp_unslash( $_REQUEST['convertkit_resource_type'] ) ) : '' );
438 + $this->resource_id = ( array_key_exists( 'convertkit_resource_id', $_REQUEST ) ? absint( $_REQUEST['convertkit_resource_id'] ) : 0 );
391 439 $this->post_id = absint( $_REQUEST['convertkit_post_id'] );
392 440
393 441 // If Restrict Content is by tag, tag the subscriber.
394 442 if ( $this->resource_type === 'tag' ) {
395 - // Check reCAPTCHA.
396 - $recaptcha = new ConvertKit_Recaptcha();
397 - $recaptcha_response = $recaptcha->verify_recaptcha(
398 - ( isset( $_POST['g-recaptcha-response'] ) ? sanitize_text_field( wp_unslash( $_POST['g-recaptcha-response'] ) ) : '' ),
399 - 'convertkit_restrict_content_tag'
400 - );
443 + // Check spam protection (reCAPTCHA or Cloudflare Turnstile, depending on Plugin settings).
444 + $spam_protection = new ConvertKit_Spam_Protection();
445 + $spam_check = $spam_protection->verify( 'convertkit_restrict_content_tag' );
401 446
402 - // Bail if reCAPTCHA failed.
403 - if ( is_wp_error( $recaptcha_response ) ) {
404 - $this->error = $recaptcha_response;
447 + // Bail if spam protection failed.
448 + if ( is_wp_error( $spam_check ) ) {
449 + $this->error = $spam_check;
405 450 return;
406 451 }
407 452
408 453 // Tag subscriber.
@@ -412,8 +457,17 @@
412 457 if ( is_wp_error( $result ) ) {
413 458 $this->error = $result;
414 459 return;
415 460 }
461 + } else {
462 + // Check spam protection (reCAPTCHA or Cloudflare Turnstile, depending on Plugin settings).
463 + $spam_check = $this->verify_spam_protection();
464 +
465 + // Bail if spam protection failed.
466 + if ( is_wp_error( $spam_check ) ) {
467 + $this->error = $spam_check;
468 + return;
469 + }
416 470 }
417 471
418 472 // Run subscriber authentication.
419 473 $result = $this->subscriber_authentication_send_code( $email, $this->post_id );
@@ -490,8 +544,133 @@
490 544
491 545 }
492 546
493 547 /**
548 + * Logs the subscriber out by deleting their subscriber ID cookie, when the
549 + * log out button is clicked in the Member Content Login block.
550 + *
551 + * @since 3.4.2
552 + */
553 + public function maybe_run_subscriber_logout() {
554 +
555 + // Bail if no logout request was made.
556 + if ( ! array_key_exists( 'convertkit_logout', $_REQUEST ) ) {
557 + return;
558 + }
559 +
560 + // Bail if no nonce was specified.
561 + if ( ! array_key_exists( '_wpnonce', $_REQUEST ) ) {
562 + return;
563 + }
564 +
565 + // Bail if the nonce failed validation.
566 + if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'convertkit_member_content_logout' ) ) {
567 + return;
568 + }
569 +
570 + // Delete the subscriber ID cookie.
571 + $subscriber = new ConvertKit_Subscriber();
572 + $subscriber->forget();
573 +
574 + // Reload the Post, so the login form displays.
575 + wp_safe_redirect( $this->get_url( get_the_ID(), true ) );
576 + exit();
577 +
578 + }
579 +
580 + /**
581 + * Verifies the spam protection response for the login form, using the spam
582 + * protection provider enabled in the Plugin's settings.
583 + *
584 + * @since 3.4.2
585 + *
586 + * @param bool|string $response Spam protection response, if supplied by a REST API request.
587 + * @return bool|WP_Error
588 + */
589 + public function verify_spam_protection( $response = false ) {
590 +
591 + $spam_protection = new ConvertKit_Spam_Protection();
592 + $provider = $spam_protection->get_active_provider();
593 +
594 + // Return true if no spam protection provider is enabled.
595 + if ( $provider === false ) {
596 + return true;
597 + }
598 +
599 + // Verify the response included in the REST API request.
600 + if ( ! empty( $response ) ) {
601 + return $provider->verify( $response, 'convertkit_member_content_login' );
602 + }
603 +
604 + // Verify the response included in the form submission.
605 + return $spam_protection->verify( 'convertkit_member_content_login' );
606 +
607 + }
608 +
609 + /**
610 + * Enqueues the CSS and JS required by the login form and modal.
611 + *
612 + * @since 3.4.2
613 + */
614 + public function enqueue_scripts_and_styles() {
615 +
616 + // Only load styles if the Disable CSS option is off.
617 + if ( ! $this->settings->css_disabled() ) {
618 + convertkit_enqueue_frontend_css();
619 + }
620 +
621 + // Bail if scripts are disabled.
622 + if ( $this->settings->scripts_disabled() ) {
623 + return;
624 + }
625 +
626 + // Enqueue scripts.
627 + convertkit_enqueue_frontend_js();
628 +
629 + // Define variables.
630 + wp_localize_script(
631 + 'convertkit-js',
632 + 'convertkit_restrict_content',
633 + array(
634 + 'nonce' => wp_create_nonce( 'wp_rest' ),
635 + 'subscriber_authentication_url' => rest_url( 'kit/v1/restrict-content/subscriber-authentication' ),
636 + 'subscriber_verification_url' => rest_url( 'kit/v1/restrict-content/subscriber-verification' ),
637 + 'debug' => $this->settings->debug_enabled(),
638 + )
639 + );
640 +
641 + }
642 +
643 + /**
644 + * Outputs the login modal in the footer, ensuring it is only output once
645 + * when a Post contains multiple Member Content Login blocks.
646 + *
647 + * @since 3.4.2
648 + *
649 + * @param int $post_id Post ID.
650 + * @param bool|int $resource_id Resource ID.
651 + * @param bool|string $resource_type Resource Type.
652 + */
653 + public function output_login_modal( $post_id, $resource_id = 0, $resource_type = '' ) {
654 +
655 + if ( $this->login_modal_output ) {
656 + return;
657 + }
658 +
659 + $this->login_modal_output = true;
660 +
661 + add_action(
662 + 'wp_footer',
663 + function () use ( $post_id, $resource_id, $resource_type ) {
664 +
665 + include_once CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal.php';
666 +
667 + }
668 + );
669 +
670 + }
671 +
672 + /**
494 673 * Sends an email to the subscriber with a code and link to authenticate they have access to the email address submitted.
495 674 *
496 675 * @since 3.1.0
497 676 *
@@ -1347,32 +1526,11 @@
1347 1526 * @return string HTML
1348 1527 */
1349 1528 private function get_call_to_action( $post_id ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
1350 1529
1351 - // Only load styles if the Disable CSS option is off.
1352 - if ( ! $this->settings->css_disabled() ) {
1353 - // Enqueue styles.
1354 - convertkit_enqueue_frontend_css();
1355 - }
1530 + // Enqueue CSS and JS.
1531 + $this->enqueue_scripts_and_styles();
1356 1532
1357 - // Only load scripts if the Disable Scripts option is off.
1358 - if ( ! $this->settings->scripts_disabled() ) {
1359 - // Enqueue scripts.
1360 - convertkit_enqueue_frontend_js();
1361 -
1362 - // Define variables.
1363 - wp_localize_script(
1364 - 'convertkit-js',
1365 - 'convertkit_restrict_content',
1366 - array(
1367 - 'nonce' => wp_create_nonce( 'wp_rest' ),
1368 - 'subscriber_authentication_url' => rest_url( 'kit/v1/restrict-content/subscriber-authentication' ),
1369 - 'subscriber_verification_url' => rest_url( 'kit/v1/restrict-content/subscriber-verification' ),
1370 - 'debug' => $this->settings->debug_enabled(),
1371 - )
1372 - );
1373 - }
1374 -
1375 1533 // Output code form if this request is after the user entered their email address,
1376 1534 // which means we're going through the authentication flow.
1377 1535 if ( $this->in_authentication_flow() ) {
1378 1536 ob_start();
@@ -1405,16 +1563,9 @@
1405 1563
1406 1564 // If scripts are enabled, output the email login form in a modal, which will be displayed
1407 1565 // when the 'log in' link is clicked.
1408 1566 if ( ! $this->settings->scripts_disabled() ) {
1409 - add_action(
1410 - 'wp_footer',
1411 - function () use ( $post_id, $resource_id, $resource_type ) {
1412 -
1413 - include_once CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal.php';
1414 -
1415 - }
1416 - );
1567 + $this->output_login_modal( $post_id, $resource_id, $resource_type );
1417 1568 }
1418 1569
1419 1570 // Output.
1420 1571 ob_start();
@@ -1435,16 +1586,9 @@
1435 1586
1436 1587 // If scripts are enabled, output the email login form in a modal, which will be displayed
1437 1588 // when the 'log in' link is clicked.
1438 1589 if ( ! $this->settings->scripts_disabled() ) {
1439 - add_action(
1440 - 'wp_footer',
1441 - function () use ( $post_id, $resource_id, $resource_type ) {
1442 -
1443 - include_once CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal.php';
1444 -
1445 - }
1446 - );
1590 + $this->output_login_modal( $post_id, $resource_id, $resource_type );
1447 1591 }
1448 1592
1449 1593 // Output.
1450 1594 ob_start();
@@ -1458,21 +1602,17 @@
1458 1602
1459 1603 // If scripts are enabled, output the email login form in a modal, which will be displayed
1460 1604 // when the 'log in' link is clicked.
1461 1605 if ( ! $this->settings->scripts_disabled() ) {
1462 - add_action(
1463 - 'wp_footer',
1464 - function () use ( $post_id, $resource_id, $resource_type ) {
1606 + $this->output_login_modal( $post_id, $resource_id, $resource_type );
1607 + }
1465 1608
1466 - include_once CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/login-modal.php';
1467 -
1468 - }
1469 - );
1609 + // Enqueue the active spam protection provider's client-side script.
1610 + $spam = new ConvertKit_Spam_Protection();
1611 + $spam_provider = $spam->get_active_provider();
1612 + if ( $spam_provider !== false ) {
1613 + $spam_provider->enqueue_scripts();
1470 1614 }
1471 -
1472 - // Enqueue Google reCAPTCHA JS.
1473 - $recaptcha = new ConvertKit_Recaptcha();
1474 - $recaptcha->enqueue_scripts();
1475 1615
1476 1616 // Output.
1477 1617 ob_start();
1478 1618 include CONVERTKIT_PLUGIN_PATH . '/views/frontend/restrict-content/tag.php';