← All changes
|
includes/blocks/class-convertkit-block-form-builder.php
+113
-30
3.3.2
→
3.4.4
View file →
| @@ -24,8 +24,28 @@ | ||
| 24 | 24 | */ |
| 25 | 25 | public $subscriber_id = false; |
| 26 | 26 | |
| 27 | 27 | /** |
| 28 | + * Holds the WP_Error object if the form submission failed, | |
| 29 | + * to display on screen as a notice. | |
| 30 | + * | |
| 31 | + * @since 3.4.4 | |
| 32 | + * | |
| 33 | + * @var bool|WP_Error | |
| 34 | + */ | |
| 35 | + public $error = false; | |
| 36 | + | |
| 37 | + /** | |
| 38 | + * Holds the number of times this block has been rendered on the Post, | |
| 39 | + * to ensure error notice IDs are unique. | |
| 40 | + * | |
| 41 | + * @since 3.4.4 | |
| 42 | + * | |
| 43 | + * @var int | |
| 44 | + */ | |
| 45 | + public $render_count = 0; | |
| 46 | + | |
| 47 | + /** | |
| 28 | 48 | * Constructor |
| 29 | 49 | * |
| 30 | 50 | * @since 3.0.0 |
| 31 | 51 | */ |
| @@ -76,17 +96,15 @@ | ||
| 76 | 96 | if ( ! array_key_exists( 'post_id', $_REQUEST['convertkit'] ) ) { |
| 77 | 97 | return; |
| 78 | 98 | } |
| 79 | 99 | |
| 80 | - // Check reCAPTCHA. | |
| 81 | - $recaptcha = new ConvertKit_Recaptcha(); | |
| 82 | - $recaptcha_response = $recaptcha->verify_recaptcha( | |
| 83 | - ( isset( $_POST['g-recaptcha-response'] ) ? sanitize_text_field( wp_unslash( $_POST['g-recaptcha-response'] ) ) : '' ), | |
| 84 | - 'convertkit_form_builder' | |
| 85 | - ); | |
| 100 | + // Check spam protection. | |
| 101 | + $spam_protection = new ConvertKit_Spam_Protection(); | |
| 86 | 102 | |
| 87 | - // Bail if reCAPTCHA failed. | |
| 88 | - if ( is_wp_error( $recaptcha_response ) ) { | |
| 103 | + // Bail if spam protection failed. | |
| 104 | + $spam_protection_result = $spam_protection->verify( 'convertkit_form_builder' ); | |
| 105 | + if ( is_wp_error( $spam_protection_result ) ) { | |
| 106 | + $this->error = $spam_protection_result; | |
| 89 | 107 | return; |
| 90 | 108 | } |
| 91 | 109 | |
| 92 | 110 | // Sanitize form data. |
| @@ -91,8 +109,18 @@ | ||
| 91 | 109 | |
| 92 | 110 | // Sanitize form data. |
| 93 | 111 | $form_data = map_deep( wp_unslash( $_REQUEST['convertkit'] ), 'sanitize_text_field' ); |
| 94 | 112 | |
| 113 | + // Bail if the email address is invalid. The entry isn't stored, as an invalid | |
| 114 | + // email address is of no use to the creator. | |
| 115 | + if ( ! is_email( $form_data['email'] ) ) { | |
| 116 | + $this->error = new WP_Error( | |
| 117 | + 'convertkit_block_form_builder_invalid_email', | |
| 118 | + __( 'Please enter a valid email address.', 'convertkit' ) | |
| 119 | + ); | |
| 120 | + return; | |
| 121 | + } | |
| 122 | + | |
| 95 | 123 | // Build custom fields, if any were specified. |
| 96 | 124 | $custom_fields = array(); |
| 97 | 125 | if ( array_key_exists( 'custom_fields', $form_data ) ) { |
| 98 | 126 | $custom_fields = $form_data['custom_fields']; |
| @@ -124,8 +152,13 @@ | ||
| 124 | 152 | 'api_error' => __( 'Plugin Access Token not configured', 'convertkit' ), |
| 125 | 153 | ) |
| 126 | 154 | ); |
| 127 | 155 | } |
| 156 | + | |
| 157 | + $this->error = new WP_Error( | |
| 158 | + 'convertkit_block_form_builder_no_access_token', | |
| 159 | + __( 'Sorry, we were unable to subscribe you. Please try again later.', 'convertkit' ) | |
| 160 | + ); | |
| 128 | 161 | return; |
| 129 | 162 | } |
| 130 | 163 | |
| 131 | 164 | // Initialize the API. |
| @@ -150,9 +183,9 @@ | ||
| 150 | 183 | $subscriber_state, |
| 151 | 184 | $custom_fields |
| 152 | 185 | ); |
| 153 | 186 | |
| 154 | - // Bail if an error occured. | |
| 187 | + // Bail if an error occurred. | |
| 155 | 188 | if ( is_wp_error( $result ) ) { |
| 156 | 189 | // Store entry and return. |
| 157 | 190 | if ( $form_data['store_entries'] ) { |
| 158 | 191 | $entries->upsert( |
| @@ -168,8 +201,10 @@ | ||
| 168 | 201 | 'api_error' => $result->get_error_message(), |
| 169 | 202 | ) |
| 170 | 203 | ); |
| 171 | 204 | } |
| 205 | + | |
| 206 | + $this->error = $result; | |
| 172 | 207 | return; |
| 173 | 208 | } |
| 174 | 209 | |
| 175 | 210 | // Store entry. |
| @@ -400,10 +435,20 @@ | ||
| 400 | 435 | ), |
| 401 | 436 | ), |
| 402 | 437 | ), |
| 403 | 438 | |
| 439 | + // Help descriptions, displayed when no Access Token / resources exist and this block/shortcode is added. | |
| 440 | + 'no_access_token' => array( | |
| 441 | + 'notice' => __( 'Not connected to Kit.', 'convertkit' ), | |
| 442 | + 'link' => convertkit_get_setup_wizard_plugin_link(), | |
| 443 | + 'link_text' => __( 'Click here to connect your Kit account.', 'convertkit' ), | |
| 444 | + 'instruction_text' => __( 'Connect your Kit account at Settings > Kit, and then refresh this page to configure this block.', 'convertkit' ), | |
| 445 | + ), | |
| 446 | + | |
| 404 | 447 | 'has_access_token' => $settings->has_access_and_refresh_token(), |
| 405 | - 'has_resources' => $convertkit_forms->exist(), | |
| 448 | + | |
| 449 | + // This block works without resources, so we don't need to check if resources exist. | |
| 450 | + 'has_resources' => true, | |
| 406 | 451 | ); |
| 407 | 452 | |
| 408 | 453 | } |
| 409 | 454 | |
| @@ -510,19 +555,29 @@ | ||
| 510 | 555 | * @return bool|array |
| 511 | 556 | */ |
| 512 | 557 | public function get_fields() { |
| 513 | 558 | |
| 514 | - // Get Kit Forms. | |
| 515 | - $forms = new ConvertKit_Resource_Forms( 'block_form_builder' ); | |
| 516 | - $forms_options = array(); | |
| 559 | + // Get Kit Forms. Non-legacy forms populate the sidebar dropdown; | |
| 560 | + // legacy forms are exposed separately as a fallback so the sidebar can | |
| 561 | + // keep displaying a previously-saved legacy form as the current | |
| 562 | + // selection without offering other legacy forms as new choices. | |
| 563 | + $forms = new ConvertKit_Resource_Forms( 'block_form_builder' ); | |
| 564 | + $forms_options = array(); | |
| 565 | + $forms_legacy_options = array(); | |
| 517 | 566 | if ( $forms->exist() ) { |
| 518 | 567 | foreach ( $forms->get() as $form ) { |
| 519 | - // Legacy forms don't include a `format` key, so define them as inline. | |
| 520 | - $forms_options[ $form['id'] ] = sprintf( | |
| 568 | + $label = sprintf( | |
| 521 | 569 | '%s [%s]', |
| 522 | 570 | sanitize_text_field( $form['name'] ), |
| 571 | + // Legacy forms don't include a `format` key, so define them as inline. | |
| 523 | 572 | ( ! empty( $form['format'] ) ? sanitize_text_field( $form['format'] ) : 'inline' ) |
| 524 | 573 | ); |
| 574 | + | |
| 575 | + if ( ! empty( $form['format'] ) ) { | |
| 576 | + $forms_options[ $form['id'] ] = $label; | |
| 577 | + } else { | |
| 578 | + $forms_legacy_options[ $form['id'] ] = $label; | |
| 579 | + } | |
| 525 | 580 | } |
| 526 | 581 | } |
| 527 | 582 | |
| 528 | 583 | // Get Kit Tags. |
| @@ -568,12 +623,13 @@ | ||
| 568 | 623 | 'value' => 0, |
| 569 | 624 | ), |
| 570 | 625 | ), |
| 571 | 626 | 'form_id' => array( |
| 572 | - 'label' => __( 'Form', 'convertkit' ), | |
| 573 | - 'type' => 'select', | |
| 574 | - 'description' => __( 'The Kit form to add the subscriber to. Useful if you want to send an incentive email.', 'convertkit' ), | |
| 575 | - 'values' => $forms_options, | |
| 627 | + 'label' => __( 'Form', 'convertkit' ), | |
| 628 | + 'type' => 'select', | |
| 629 | + 'description' => __( 'The Kit form to add the subscriber to. Useful if you want to send an incentive email.', 'convertkit' ), | |
| 630 | + 'values' => $forms_options, | |
| 631 | + 'legacy_values' => $forms_legacy_options, | |
| 576 | 632 | ), |
| 577 | 633 | 'tag_id' => array( |
| 578 | 634 | 'label' => __( 'Tag', 'convertkit' ), |
| 579 | 635 | 'type' => 'select', |
| @@ -720,26 +776,26 @@ | ||
| 720 | 776 | '<button type="submit"$1>$2</button>', |
| 721 | 777 | $block_content |
| 722 | 778 | ); |
| 723 | 779 | |
| 724 | - // Return the button if reCAPTCHA does not need to be used. | |
| 725 | - $settings = new ConvertKit_Settings(); | |
| 726 | - if ( ! $settings->has_recaptcha_site_and_secret_keys() ) { | |
| 780 | + // Return the button if no spam protection provider is active. | |
| 781 | + $spam_protection = new ConvertKit_Spam_Protection(); | |
| 782 | + $provider = $spam_protection->get_active_provider(); | |
| 783 | + if ( ! $provider ) { | |
| 727 | 784 | return $block_content; |
| 728 | 785 | } |
| 729 | 786 | |
| 730 | - // Enqueue reCAPTCHA JS. | |
| 731 | - $recaptcha = new ConvertKit_Recaptcha(); | |
| 732 | - $recaptcha->enqueue_scripts(); | |
| 787 | + // Enqueue the spam protection provider's JS. | |
| 788 | + $provider->enqueue_scripts(); | |
| 733 | 789 | |
| 734 | - // Add reCAPTCHA attributes to button. | |
| 790 | + // Parse the button's DOM. | |
| 735 | 791 | $parser = new ConvertKit_HTML_Parser( $block_content ); |
| 736 | 792 | $button = $parser->xpath->query( '//button' )->item( 0 ); |
| 737 | - $button->setAttribute( 'data-sitekey', esc_attr( $settings->recaptcha_site_key() ) ); // @phpstan-ignore-line | |
| 738 | - $button->setAttribute( 'data-callback', 'convertKitRecaptchaFormSubmit' ); // @phpstan-ignore-line | |
| 739 | - $button->setAttribute( 'data-action', 'convertkit_form_builder' ); // @phpstan-ignore-line | |
| 740 | - $button->setAttribute( 'class', trim( $button->getAttribute( 'class' ) . ' g-recaptcha' ) ); // @phpstan-ignore-line | |
| 741 | 793 | |
| 794 | + // Attach the spam protection provider's attributes/elements to the form/button as necessary. | |
| 795 | + // $button is narrowed from DOMNode to DOMElement by the //button xpath expression above. | |
| 796 | + $provider->attach_to_form_button_dom( $parser, $button, 'convertkit_form_builder' ); // @phpstan-ignore-line | |
| 797 | + | |
| 742 | 798 | // Return button HTML. |
| 743 | 799 | return $parser->get_body_html(); |
| 744 | 800 | |
| 745 | 801 | } |
| @@ -783,8 +839,35 @@ | ||
| 783 | 839 | $subscribed_message = $parser->html->createElement( 'div' ); |
| 784 | 840 | $subscribed_message->setAttribute( 'class', 'convertkit-form-builder-subscribed-message' ); |
| 785 | 841 | $subscribed_message->appendChild( $parser->html->createTextNode( $atts['text_if_subscribed'] ) ); |
| 786 | 842 | $form->insertBefore( $subscribed_message, $form->firstChild ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase |
| 843 | + } | |
| 844 | + | |
| 845 | + // Add error notice if the submission failed. | |
| 846 | + if ( is_wp_error( $this->error ) ) { | |
| 847 | + ++$this->render_count; | |
| 848 | + $error_id = 'convertkit-form-builder-error-' . $this->render_count; | |
| 849 | + | |
| 850 | + $error_notice = $parser->html->createElement( 'div' ); | |
| 851 | + $error_notice->setAttribute( 'id', $error_id ); | |
| 852 | + $error_notice->setAttribute( 'class', 'convertkit-form-builder-notice convertkit-form-builder-notice-error' ); | |
| 853 | + $error_notice->setAttribute( 'role', 'alert' ); | |
| 854 | + $error_notice->setAttribute( 'tabindex', '-1' ); | |
| 855 | + $error_notice->appendChild( $parser->html->createTextNode( $this->error->get_error_message() ) ); | |
| 856 | + $form->insertBefore( $error_notice, $form->firstChild ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase | |
| 857 | + | |
| 858 | + // Focus the email field if it caused the error, so screen readers and | |
| 859 | + // browsers move to it. Otherwise focus the notice, as the error isn't | |
| 860 | + // specific to a field. | |
| 861 | + // Query within the form, as it's not yet appended to the document. | |
| 862 | + $email_field = $parser->xpath->query( './/input[@name="convertkit[email]"]', $form )->item( 0 ); | |
| 863 | + if ( $email_field && $this->error->get_error_code() === 'convertkit_block_form_builder_invalid_email' ) { | |
| 864 | + $email_field->setAttribute( 'aria-invalid', 'true' ); // @phpstan-ignore-line | |
| 865 | + $email_field->setAttribute( 'aria-describedby', $error_id ); // @phpstan-ignore-line | |
| 866 | + $email_field->setAttribute( 'autofocus', 'autofocus' ); // @phpstan-ignore-line | |
| 867 | + } else { | |
| 868 | + $error_notice->setAttribute( 'autofocus', 'autofocus' ); | |
| 869 | + } | |
| 787 | 870 | } |
| 788 | 871 | |
| 789 | 872 | // Add hidden fields. |
| 790 | 873 | $fields = array( |