← All changes
|
includes/blocks/class-convertkit-block-form-builder.php
+191
-48
3.3.6
→
3.4.6
View file →
| @@ -24,8 +24,38 @@ | ||
| 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 | + * used to identify each block on the page and ensure error notice IDs are unique. | |
| 40 | + * | |
| 41 | + * @since 3.4.4 | |
| 42 | + * | |
| 43 | + * @var int | |
| 44 | + */ | |
| 45 | + public $render_count = 0; | |
| 46 | + | |
| 47 | + /** | |
| 48 | + * Holds the index of the block that was submitted, so the error notice | |
| 49 | + * is only displayed on that block. | |
| 50 | + * | |
| 51 | + * @since 3.4.6 | |
| 52 | + * | |
| 53 | + * @var int | |
| 54 | + */ | |
| 55 | + public $submitted_block_index = 0; | |
| 56 | + | |
| 57 | + /** | |
| 28 | 58 | * Constructor |
| 29 | 59 | * |
| 30 | 60 | * @since 3.0.0 |
| 31 | 61 | */ |
| @@ -76,17 +106,20 @@ | ||
| 76 | 106 | if ( ! array_key_exists( 'post_id', $_REQUEST['convertkit'] ) ) { |
| 77 | 107 | return; |
| 78 | 108 | } |
| 79 | 109 | |
| 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 | - ); | |
| 110 | + // Store the submitted block's index, so any error is only displayed on that block. | |
| 111 | + if ( array_key_exists( 'block_index', $_REQUEST['convertkit'] ) ) { | |
| 112 | + $this->submitted_block_index = absint( $_REQUEST['convertkit']['block_index'] ); | |
| 113 | + } | |
| 86 | 114 | |
| 87 | - // Bail if reCAPTCHA failed. | |
| 88 | - if ( is_wp_error( $recaptcha_response ) ) { | |
| 115 | + // Check spam protection. | |
| 116 | + $spam_protection = new ConvertKit_Spam_Protection(); | |
| 117 | + | |
| 118 | + // Bail if spam protection failed. | |
| 119 | + $spam_protection_result = $spam_protection->verify( 'convertkit_form_builder' ); | |
| 120 | + if ( is_wp_error( $spam_protection_result ) ) { | |
| 121 | + $this->error = $spam_protection_result; | |
| 89 | 122 | return; |
| 90 | 123 | } |
| 91 | 124 | |
| 92 | 125 | // Sanitize form data. |
| @@ -91,8 +124,18 @@ | ||
| 91 | 124 | |
| 92 | 125 | // Sanitize form data. |
| 93 | 126 | $form_data = map_deep( wp_unslash( $_REQUEST['convertkit'] ), 'sanitize_text_field' ); |
| 94 | 127 | |
| 128 | + // Bail if the email address is invalid. The entry isn't stored, as an invalid | |
| 129 | + // email address is of no use to the creator. | |
| 130 | + if ( ! is_email( $form_data['email'] ) ) { | |
| 131 | + $this->error = new WP_Error( | |
| 132 | + 'convertkit_block_form_builder_invalid_email', | |
| 133 | + __( 'Please enter a valid email address.', 'convertkit' ) | |
| 134 | + ); | |
| 135 | + return; | |
| 136 | + } | |
| 137 | + | |
| 95 | 138 | // Build custom fields, if any were specified. |
| 96 | 139 | $custom_fields = array(); |
| 97 | 140 | if ( array_key_exists( 'custom_fields', $form_data ) ) { |
| 98 | 141 | $custom_fields = $form_data['custom_fields']; |
| @@ -97,12 +140,15 @@ | ||
| 97 | 140 | if ( array_key_exists( 'custom_fields', $form_data ) ) { |
| 98 | 141 | $custom_fields = $form_data['custom_fields']; |
| 99 | 142 | } |
| 100 | 143 | |
| 144 | + // Get First Name, if the Name field was included in the form. | |
| 145 | + $first_name = array_key_exists( 'first_name', $form_data ) ? $form_data['first_name'] : ''; | |
| 146 | + | |
| 101 | 147 | // Get Form, Tag and Sequence IDs, if any were specified. |
| 102 | - $form_id = array_key_exists( 'form_id', $form_data ) ? $form_data['form_id'] : false; | |
| 103 | - $tag_id = array_key_exists( 'tag_id', $form_data ) ? $form_data['tag_id'] : false; | |
| 104 | - $sequence_id = array_key_exists( 'sequence_id', $form_data ) ? $form_data['sequence_id'] : false; | |
| 148 | + $form_id = array_key_exists( 'form_id', $form_data ) ? absint( $form_data['form_id'] ) : 0; | |
| 149 | + $tag_id = array_key_exists( 'tag_id', $form_data ) ? absint( $form_data['tag_id'] ) : 0; | |
| 150 | + $sequence_id = array_key_exists( 'sequence_id', $form_data ) ? absint( $form_data['sequence_id'] ) : 0; | |
| 105 | 151 | |
| 106 | 152 | // Initialize classes that will be used. |
| 107 | 153 | $settings = new ConvertKit_Settings(); |
| 108 | 154 | $entries = new ConvertKit_Form_Entries(); |
| @@ -114,9 +160,9 @@ | ||
| 114 | 160 | $entries->upsert( |
| 115 | 161 | array( |
| 116 | 162 | 'post_id' => $form_data['post_id'], |
| 117 | 163 | 'email' => $form_data['email'], |
| 118 | - 'first_name' => $form_data['first_name'], | |
| 164 | + 'first_name' => $first_name, | |
| 119 | 165 | 'custom_fields' => $custom_fields, |
| 120 | 166 | 'form_id' => $form_id, |
| 121 | 167 | 'tag_id' => $tag_id, |
| 122 | 168 | 'sequence_id' => $sequence_id, |
| @@ -124,8 +170,13 @@ | ||
| 124 | 170 | 'api_error' => __( 'Plugin Access Token not configured', 'convertkit' ), |
| 125 | 171 | ) |
| 126 | 172 | ); |
| 127 | 173 | } |
| 174 | + | |
| 175 | + $this->error = new WP_Error( | |
| 176 | + 'convertkit_block_form_builder_no_access_token', | |
| 177 | + __( 'Sorry, we were unable to subscribe you. Please try again later.', 'convertkit' ) | |
| 178 | + ); | |
| 128 | 179 | return; |
| 129 | 180 | } |
| 130 | 181 | |
| 131 | 182 | // Initialize the API. |
| @@ -140,14 +191,14 @@ | ||
| 140 | 191 | |
| 141 | 192 | // Determine the subscriber state. |
| 142 | 193 | // If a Form is specified, mark the subscriber as inactive, so the form's double optin is honored. |
| 143 | 194 | // If a Tag or Sequence is specified, mark the subscriber as active, as there's no double optin for tags or sequences. |
| 144 | - $subscriber_state = $form_id !== false ? 'inactive' : 'active'; | |
| 195 | + $subscriber_state = $form_id ? 'inactive' : 'active'; | |
| 145 | 196 | |
| 146 | 197 | // Create subscriber. |
| 147 | 198 | $result = $api->create_subscriber( |
| 148 | 199 | sanitize_email( $form_data['email'] ), |
| 149 | - array_key_exists( 'first_name', $form_data ) ? $form_data['first_name'] : '', | |
| 200 | + $first_name, | |
| 150 | 201 | $subscriber_state, |
| 151 | 202 | $custom_fields |
| 152 | 203 | ); |
| 153 | 204 | |
| @@ -158,9 +209,9 @@ | ||
| 158 | 209 | $entries->upsert( |
| 159 | 210 | array( |
| 160 | 211 | 'post_id' => $form_data['post_id'], |
| 161 | 212 | 'email' => $form_data['email'], |
| 162 | - 'first_name' => $form_data['first_name'], | |
| 213 | + 'first_name' => $first_name, | |
| 163 | 214 | 'custom_fields' => $custom_fields, |
| 164 | 215 | 'form_id' => $form_id, |
| 165 | 216 | 'tag_id' => $tag_id, |
| 166 | 217 | 'sequence_id' => $sequence_id, |
| @@ -168,8 +219,10 @@ | ||
| 168 | 219 | 'api_error' => $result->get_error_message(), |
| 169 | 220 | ) |
| 170 | 221 | ); |
| 171 | 222 | } |
| 223 | + | |
| 224 | + $this->error = $result; | |
| 172 | 225 | return; |
| 173 | 226 | } |
| 174 | 227 | |
| 175 | 228 | // Store entry. |
| @@ -177,9 +230,9 @@ | ||
| 177 | 230 | $entries->upsert( |
| 178 | 231 | array( |
| 179 | 232 | 'post_id' => $form_data['post_id'], |
| 180 | 233 | 'email' => $form_data['email'], |
| 181 | - 'first_name' => $form_data['first_name'], | |
| 234 | + 'first_name' => $first_name, | |
| 182 | 235 | 'custom_fields' => $custom_fields, |
| 183 | 236 | 'form_id' => $form_id, |
| 184 | 237 | 'tag_id' => $tag_id, |
| 185 | 238 | 'sequence_id' => $sequence_id, |
| @@ -187,11 +240,14 @@ | ||
| 187 | 240 | ) |
| 188 | 241 | ); |
| 189 | 242 | } |
| 190 | 243 | |
| 244 | + // Get the subscriber ID, as $result is overwritten by the form, tag and sequence requests below. | |
| 245 | + $subscriber_id = $result['subscriber']['id']; | |
| 246 | + | |
| 191 | 247 | // Store the subscriber ID in a cookie. |
| 192 | 248 | $subscriber = new ConvertKit_Subscriber(); |
| 193 | - $subscriber->set( $result['subscriber']['id'] ); | |
| 249 | + $subscriber->set( $subscriber_id ); | |
| 194 | 250 | |
| 195 | 251 | // If a form was specified, add the subscriber to the form. |
| 196 | 252 | if ( $form_id ) { |
| 197 | 253 | // For Legacy Forms, a different endpoint is used. |
| @@ -198,14 +254,14 @@ | ||
| 198 | 254 | $forms = new ConvertKit_Resource_Forms(); |
| 199 | 255 | if ( $forms->is_legacy( $form_id ) ) { |
| 200 | 256 | $result = $api->add_subscriber_to_legacy_form( |
| 201 | 257 | $form_id, |
| 202 | - $result['subscriber']['id'] | |
| 258 | + $subscriber_id | |
| 203 | 259 | ); |
| 204 | 260 | } else { |
| 205 | 261 | $result = $api->add_subscriber_to_form( |
| 206 | 262 | $form_id, |
| 207 | - $result['subscriber']['id'], | |
| 263 | + $subscriber_id, | |
| 208 | 264 | get_permalink( absint( $form_data['post_id'] ) ) |
| 209 | 265 | ); |
| 210 | 266 | } |
| 211 | 267 | |
| @@ -213,9 +269,9 @@ | ||
| 213 | 269 | $entries->upsert( |
| 214 | 270 | array( |
| 215 | 271 | 'post_id' => $form_data['post_id'], |
| 216 | 272 | 'email' => $form_data['email'], |
| 217 | - 'first_name' => $form_data['first_name'], | |
| 273 | + 'first_name' => $first_name, | |
| 218 | 274 | 'custom_fields' => $custom_fields, |
| 219 | 275 | 'form_id' => $form_id, |
| 220 | 276 | 'tag_id' => $tag_id, |
| 221 | 277 | 'sequence_id' => $sequence_id, |
| @@ -227,9 +283,9 @@ | ||
| 227 | 283 | } |
| 228 | 284 | |
| 229 | 285 | // If a tag was specified, add the subscriber to the tag. |
| 230 | 286 | if ( $tag_id ) { |
| 231 | - $result = $api->tag_subscriber( $tag_id, $result['subscriber']['id'] ); | |
| 287 | + $result = $api->tag_subscriber( $tag_id, $subscriber_id ); | |
| 232 | 288 | |
| 233 | 289 | if ( $form_data['store_entries'] ) { |
| 234 | 290 | $entries->upsert( |
| 235 | 291 | array( |
| @@ -234,9 +290,9 @@ | ||
| 234 | 290 | $entries->upsert( |
| 235 | 291 | array( |
| 236 | 292 | 'post_id' => $form_data['post_id'], |
| 237 | 293 | 'email' => $form_data['email'], |
| 238 | - 'first_name' => $form_data['first_name'], | |
| 294 | + 'first_name' => $first_name, | |
| 239 | 295 | 'custom_fields' => $custom_fields, |
| 240 | 296 | 'form_id' => $form_id, |
| 241 | 297 | 'tag_id' => $tag_id, |
| 242 | 298 | 'sequence_id' => $sequence_id, |
| @@ -248,9 +304,9 @@ | ||
| 248 | 304 | } |
| 249 | 305 | |
| 250 | 306 | // If a sequence was specified, add the subscriber to the sequence. |
| 251 | 307 | if ( $sequence_id ) { |
| 252 | - $result = $api->add_subscriber_to_sequence( $sequence_id, $result['subscriber']['id'] ); | |
| 308 | + $result = $api->add_subscriber_to_sequence( $sequence_id, $subscriber_id ); | |
| 253 | 309 | |
| 254 | 310 | if ( $form_data['store_entries'] ) { |
| 255 | 311 | $entries->upsert( |
| 256 | 312 | array( |
| @@ -255,9 +311,9 @@ | ||
| 255 | 311 | $entries->upsert( |
| 256 | 312 | array( |
| 257 | 313 | 'post_id' => $form_data['post_id'], |
| 258 | 314 | 'email' => $form_data['email'], |
| 259 | - 'first_name' => $form_data['first_name'], | |
| 315 | + 'first_name' => $first_name, | |
| 260 | 316 | 'custom_fields' => $custom_fields, |
| 261 | 317 | 'form_id' => $form_id, |
| 262 | 318 | 'tag_id' => $tag_id, |
| 263 | 319 | 'sequence_id' => $sequence_id, |
| @@ -273,10 +329,10 @@ | ||
| 273 | 329 | if ( array_key_exists( 'redirect', $form_data ) && wp_http_validate_url( sanitize_url( $form_data['redirect'] ) ) ) { |
| 274 | 330 | // Redirect to the URL specified in the form. |
| 275 | 331 | $redirect = sanitize_url( $form_data['redirect'] ); |
| 276 | 332 | } else { |
| 277 | - // Redirect to the Post the form was displayed on, to show a success message. | |
| 278 | - $redirect = get_permalink( absint( $form_data['post_id'] ) ); | |
| 333 | + // Redirect to the page the form was displayed on, to show a success message. | |
| 334 | + $redirect = $this->get_current_url( absint( $form_data['post_id'] ) ); | |
| 279 | 335 | } |
| 280 | 336 | |
| 281 | 337 | // Redirect. |
| 282 | 338 | wp_redirect( $redirect ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect |
| @@ -400,10 +456,20 @@ | ||
| 400 | 456 | ), |
| 401 | 457 | ), |
| 402 | 458 | ), |
| 403 | 459 | |
| 460 | + // Help descriptions, displayed when no Access Token / resources exist and this block/shortcode is added. | |
| 461 | + 'no_access_token' => array( | |
| 462 | + 'notice' => __( 'Not connected to Kit.', 'convertkit' ), | |
| 463 | + 'link' => convertkit_get_setup_wizard_plugin_link(), | |
| 464 | + 'link_text' => __( 'Click here to connect your Kit account.', 'convertkit' ), | |
| 465 | + 'instruction_text' => __( 'Connect your Kit account at Settings > Kit, and then refresh this page to configure this block.', 'convertkit' ), | |
| 466 | + ), | |
| 467 | + | |
| 404 | 468 | 'has_access_token' => $settings->has_access_and_refresh_token(), |
| 405 | - 'has_resources' => $convertkit_forms->exist(), | |
| 469 | + | |
| 470 | + // This block works without resources, so we don't need to check if resources exist. | |
| 471 | + 'has_resources' => true, | |
| 406 | 472 | ); |
| 407 | 473 | |
| 408 | 474 | } |
| 409 | 475 | |
| @@ -510,19 +576,29 @@ | ||
| 510 | 576 | * @return bool|array |
| 511 | 577 | */ |
| 512 | 578 | public function get_fields() { |
| 513 | 579 | |
| 514 | - // Get Kit Forms. | |
| 515 | - $forms = new ConvertKit_Resource_Forms( 'block_form_builder' ); | |
| 516 | - $forms_options = array(); | |
| 580 | + // Get Kit Forms. Non-legacy forms populate the sidebar dropdown; | |
| 581 | + // legacy forms are exposed separately as a fallback so the sidebar can | |
| 582 | + // keep displaying a previously-saved legacy form as the current | |
| 583 | + // selection without offering other legacy forms as new choices. | |
| 584 | + $forms = new ConvertKit_Resource_Forms( 'block_form_builder' ); | |
| 585 | + $forms_options = array(); | |
| 586 | + $forms_legacy_options = array(); | |
| 517 | 587 | if ( $forms->exist() ) { |
| 518 | 588 | 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( | |
| 589 | + $label = sprintf( | |
| 521 | 590 | '%s [%s]', |
| 522 | 591 | sanitize_text_field( $form['name'] ), |
| 592 | + // Legacy forms don't include a `format` key, so define them as inline. | |
| 523 | 593 | ( ! empty( $form['format'] ) ? sanitize_text_field( $form['format'] ) : 'inline' ) |
| 524 | 594 | ); |
| 595 | + | |
| 596 | + if ( ! empty( $form['format'] ) ) { | |
| 597 | + $forms_options[ $form['id'] ] = $label; | |
| 598 | + } else { | |
| 599 | + $forms_legacy_options[ $form['id'] ] = $label; | |
| 600 | + } | |
| 525 | 601 | } |
| 526 | 602 | } |
| 527 | 603 | |
| 528 | 604 | // Get Kit Tags. |
| @@ -568,12 +644,13 @@ | ||
| 568 | 644 | 'value' => 0, |
| 569 | 645 | ), |
| 570 | 646 | ), |
| 571 | 647 | '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, | |
| 648 | + 'label' => __( 'Form', 'convertkit' ), | |
| 649 | + 'type' => 'select', | |
| 650 | + 'description' => __( 'The Kit form to add the subscriber to. Useful if you want to send an incentive email.', 'convertkit' ), | |
| 651 | + 'values' => $forms_options, | |
| 652 | + 'legacy_values' => $forms_legacy_options, | |
| 576 | 653 | ), |
| 577 | 654 | 'tag_id' => array( |
| 578 | 655 | 'label' => __( 'Tag', 'convertkit' ), |
| 579 | 656 | 'type' => 'select', |
| @@ -658,8 +735,11 @@ | ||
| 658 | 735 | |
| 659 | 736 | // Get Post ID. |
| 660 | 737 | $post_id = is_a( $post, 'WP_Post' ) ? $post->ID : 0; |
| 661 | 738 | |
| 739 | + // Increment the render count, used to identify this block on the page. | |
| 740 | + ++$this->render_count; | |
| 741 | + | |
| 662 | 742 | // Parse attributes, defining fallback defaults if required |
| 663 | 743 | // and moving some attributes (such as Gutenberg's styles), if defined. |
| 664 | 744 | $atts = $this->sanitize_and_declare_atts( $atts ); |
| 665 | 745 | |
| @@ -720,26 +800,26 @@ | ||
| 720 | 800 | '<button type="submit"$1>$2</button>', |
| 721 | 801 | $block_content |
| 722 | 802 | ); |
| 723 | 803 | |
| 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() ) { | |
| 804 | + // Return the button if no spam protection provider is active. | |
| 805 | + $spam_protection = new ConvertKit_Spam_Protection(); | |
| 806 | + $provider = $spam_protection->get_active_provider(); | |
| 807 | + if ( ! $provider ) { | |
| 727 | 808 | return $block_content; |
| 728 | 809 | } |
| 729 | 810 | |
| 730 | - // Enqueue reCAPTCHA JS. | |
| 731 | - $recaptcha = new ConvertKit_Recaptcha(); | |
| 732 | - $recaptcha->enqueue_scripts(); | |
| 811 | + // Enqueue the spam protection provider's JS. | |
| 812 | + $provider->enqueue_scripts(); | |
| 733 | 813 | |
| 734 | - // Add reCAPTCHA attributes to button. | |
| 814 | + // Parse the button's DOM. | |
| 735 | 815 | $parser = new ConvertKit_HTML_Parser( $block_content ); |
| 736 | 816 | $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 | 817 | |
| 818 | + // Attach the spam protection provider's attributes/elements to the form/button as necessary. | |
| 819 | + // $button is narrowed from DOMNode to DOMElement by the //button xpath expression above. | |
| 820 | + $provider->attach_to_form_button_dom( $parser, $button, 'convertkit_form_builder' ); // @phpstan-ignore-line | |
| 821 | + | |
| 742 | 822 | // Return button HTML. |
| 743 | 823 | return $parser->get_body_html(); |
| 744 | 824 | |
| 745 | 825 | } |
| @@ -769,9 +849,9 @@ | ||
| 769 | 849 | } |
| 770 | 850 | |
| 771 | 851 | // Create form element. |
| 772 | 852 | $form = $parser->html->createElement( 'form' ); |
| 773 | - $form->setAttribute( 'action', esc_url( get_permalink( $post_id ) ) ); | |
| 853 | + $form->setAttribute( 'action', esc_url( $this->get_current_url( $post_id ) ) ); | |
| 774 | 854 | $form->setAttribute( 'method', 'post' ); |
| 775 | 855 | |
| 776 | 856 | // Move form builder div contents into form. |
| 777 | 857 | while ( $block_container->hasChildNodes() ) { |
| @@ -777,8 +857,22 @@ | ||
| 777 | 857 | while ( $block_container->hasChildNodes() ) { |
| 778 | 858 | $form->appendChild( $block_container->firstChild ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase |
| 779 | 859 | } |
| 780 | 860 | |
| 861 | + // Suffix field IDs and labels with the block's index from the second block onwards, | |
| 862 | + // so IDs are unique when multiple blocks are on the same page. | |
| 863 | + if ( $this->render_count > 1 ) { | |
| 864 | + foreach ( $parser->xpath->query( './/*[starts-with(@id, "kit-form-builder-")]', $form ) as $element ) { | |
| 865 | + $id = $element->getAttribute( 'id' ); // @phpstan-ignore-line | |
| 866 | + $new_id = $id . '-' . $this->render_count; | |
| 867 | + $element->setAttribute( 'id', $new_id ); // @phpstan-ignore-line | |
| 868 | + | |
| 869 | + foreach ( $parser->xpath->query( './/label[@for="' . $id . '"]', $form ) as $label ) { | |
| 870 | + $label->setAttribute( 'for', $new_id ); // @phpstan-ignore-line | |
| 871 | + } | |
| 872 | + } | |
| 873 | + } | |
| 874 | + | |
| 781 | 875 | // Add subscribed message if required. |
| 782 | 876 | if ( $this->subscriber_id ) { |
| 783 | 877 | $subscribed_message = $parser->html->createElement( 'div' ); |
| 784 | 878 | $subscribed_message->setAttribute( 'class', 'convertkit-form-builder-subscribed-message' ); |
| @@ -785,8 +879,35 @@ | ||
| 785 | 879 | $subscribed_message->appendChild( $parser->html->createTextNode( $atts['text_if_subscribed'] ) ); |
| 786 | 880 | $form->insertBefore( $subscribed_message, $form->firstChild ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase |
| 787 | 881 | } |
| 788 | 882 | |
| 883 | + // Add error notice if the submission failed, and this is the submitted block. | |
| 884 | + // If no block index was submitted (e.g. a cached page from an older version), display it on all blocks. | |
| 885 | + if ( is_wp_error( $this->error ) && ( ! $this->submitted_block_index || $this->submitted_block_index === $this->render_count ) ) { | |
| 886 | + $error_id = 'convertkit-form-builder-error-' . $this->render_count; | |
| 887 | + | |
| 888 | + $error_notice = $parser->html->createElement( 'div' ); | |
| 889 | + $error_notice->setAttribute( 'id', $error_id ); | |
| 890 | + $error_notice->setAttribute( 'class', 'convertkit-form-builder-notice convertkit-form-builder-notice-error' ); | |
| 891 | + $error_notice->setAttribute( 'role', 'alert' ); | |
| 892 | + $error_notice->setAttribute( 'tabindex', '-1' ); | |
| 893 | + $error_notice->appendChild( $parser->html->createTextNode( $this->error->get_error_message() ) ); | |
| 894 | + $form->insertBefore( $error_notice, $form->firstChild ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase | |
| 895 | + | |
| 896 | + // Focus the email field if it caused the error, so screen readers and | |
| 897 | + // browsers move to it. Otherwise focus the notice, as the error isn't | |
| 898 | + // specific to a field. | |
| 899 | + // Query within the form, as it's not yet appended to the document. | |
| 900 | + $email_field = $parser->xpath->query( './/input[@name="convertkit[email]"]', $form )->item( 0 ); | |
| 901 | + if ( $email_field && $this->error->get_error_code() === 'convertkit_block_form_builder_invalid_email' ) { | |
| 902 | + $email_field->setAttribute( 'aria-invalid', 'true' ); // @phpstan-ignore-line | |
| 903 | + $email_field->setAttribute( 'aria-describedby', $error_id ); // @phpstan-ignore-line | |
| 904 | + $email_field->setAttribute( 'autofocus', 'autofocus' ); // @phpstan-ignore-line | |
| 905 | + } else { | |
| 906 | + $error_notice->setAttribute( 'autofocus', 'autofocus' ); | |
| 907 | + } | |
| 908 | + } | |
| 909 | + | |
| 789 | 910 | // Add hidden fields. |
| 790 | 911 | $fields = array( |
| 791 | 912 | 'convertkit[post_id]' => absint( $post_id ), |
| 792 | 913 | 'convertkit[store_entries]' => $atts['store_entries'] ? '1' : '0', |
| @@ -793,8 +914,9 @@ | ||
| 793 | 914 | 'convertkit[redirect]' => esc_url( $atts['redirect'] ), |
| 794 | 915 | 'convertkit[form_id]' => absint( $atts['form_id'] ), |
| 795 | 916 | 'convertkit[tag_id]' => absint( $atts['tag_id'] ), |
| 796 | 917 | 'convertkit[sequence_id]' => absint( $atts['sequence_id'] ), |
| 918 | + 'convertkit[block_index]' => absint( $this->render_count ), | |
| 797 | 919 | '_wpnonce' => wp_create_nonce( 'convertkit_block_form_builder' ), |
| 798 | 920 | ); |
| 799 | 921 | foreach ( $fields as $name => $value ) { |
| 800 | 922 | $hidden = $parser->html->createElement( 'input' ); |
| @@ -808,8 +930,29 @@ | ||
| 808 | 930 | $block_container->appendChild( $form ); |
| 809 | 931 | |
| 810 | 932 | // Return modified content. |
| 811 | 933 | return $parser->get_body_html(); |
| 934 | + | |
| 935 | + } | |
| 936 | + | |
| 937 | + /** | |
| 938 | + * Returns the URL of the page the form is displayed on, so the form submits | |
| 939 | + * back to the same page, falling back to the Post's URL. | |
| 940 | + * | |
| 941 | + * @since 3.4.6 | |
| 942 | + * | |
| 943 | + * @param int $post_id Post ID. | |
| 944 | + * @return string | |
| 945 | + */ | |
| 946 | + private function get_current_url( $post_id ) { | |
| 947 | + | |
| 948 | + // Fallback to the Post's URL if the request URI isn't available. | |
| 949 | + if ( ! isset( $_SERVER['REQUEST_URI'] ) ) { | |
| 950 | + return get_permalink( $post_id ); | |
| 951 | + } | |
| 952 | + | |
| 953 | + // Remove the subscriber ID, which is only used when visiting a link from a Kit email. | |
| 954 | + return remove_query_arg( 'ck_subscriber_id', esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ); | |
| 812 | 955 | |
| 813 | 956 | } |
| 814 | 957 | |
| 815 | 958 | } |