PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.4.4
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.4.4
3.4.4 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 All 197 releases
← All changes | includes/blocks/class-convertkit-block-form-builder.php +113 -30 3.3.23.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(